Arduino неправильно считывает последовательный ввод из программы C #

rgb-led led-strip

Я пытаюсь создать программу для управления светодиодным контроллером на базе Arduino. Я почти уверен, что моя программа на С# правильно выводит на последовательный порт, поскольку я дважды проверил вывод в Debug.WriteLine(). Предполагается, что программа отправляет информацию в Arduino в следующем формате (включая новые строки):

mode
number of colors
blend speed
beats until color change
Red   <--- 
Green <---
Blue  <---
Brightness <---

на данный момент важны только последние 4, поскольку я пытаюсь заставить светодиоды сначала отображать один сплошной цвет, прежде чем беспокоиться о других режимах или шаблонах. Вывод цикла отладочной строки моего последнего теста выглядит следующим образом

1
1
0
1
209
255
255
219

Это должно преобразовываться в соответствующие переменные Arduino таким же образом, но вместо этого я получаю только белый свет, который кажется случайным образом ярче или темнее, когда я путаюсь с ползунками цвета/яркости

Мой код для Arduino ниже. Я очень новичок в этом, поэтому, вероятно, это даже не близко к правильному. getData() внизу - это функция, которая, скорее всего, является виновником, но на всякий случай я добавлю остальные:

#include <SPI.h>
#include <Adafruit_DotStar.h>

#define NUMPIXELS 60

Adafruit_DotStar strip = Adafruit_DotStar(NUMPIXELS, DOTSTAR_BRG);

// Переменные
int mode;
int numColors;
int fadeSpeed;
int BBCC;
int R;
int G;
int B;
int Bright;
String buff;


void setup() {
  strip.begin();
  strip.show();
  //Серийный.начало(9600);
}

void loop() {
  getData();


  if (mode == 1)
  {
    //strip.setPixelColor(0, R, G, B);
    strip.setBrightness(Bright);
    strip.show();
  }
}

void getData()
{
 // если (Серийный.доступный() > 0)
 // {
    buff = Serial.read();
    int result = buff.toInt();
   mode = result;
   if (mode != -1)
   {
    buff = Serial.read();
    int result = buff.toInt();
    numColors = result;
    buff = Serial.read();
    result = buff.toInt();
    fadeSpeed = result;
    buff = Serial.read();
    result = buff.toInt();
    BBCC = result;
    buff = Serial.read();
    result = buff.toInt();
    R = result;
    buff = Serial.read();
    result = buff.toInt();
    G = result;
    buff = Serial.read();
    result = buff.toInt();
    B = result;
    buff = Serial.read();
    result = buff.toInt();
    Bright = result;//Serial.read();
    strip.setPixelColor(0, R, 0, 0);
    //strip.setBrightness(яркий);
    strip.show();
   }
   else
   {
    strip.setPixelColor(0, R, 0, 0);
    //strip.setBrightness(яркий);
    strip.show();
   }
// }
}

, 👍1

Обсуждение

Вы должны читать только из Serial, когда действительно есть что читать в буфере., @chrisl

Как я могу проверить, есть ли там что-то?, @BoatHouse

Serial.read() читает один символ (или -1, если его там нет). https://majenko.co.uk/blog/reading-serial-arduino, @Majenko


2 ответа


Лучший ответ:

3

Ваш последовательный код получения неверен в некоторых отношениях:

  1. Читать из Serial следует только в том случае, если действительно есть что читать. Для первого чтения это не проблема, так как вы проверяли, что возвращаемое значение равно -1 (это код возврата для «в буфере нет новых данных»). Но после этого вы читаете без проверки, если есть что читать.нужно понимать,что данные действительно отправляются последовательно.байты приходят один за другим и нет понятия как сообщения на последовательном интерфейсе (который на самом деле называется UART в не-ардуино средах). Более того, где-то в передаче могут быть небольшие паузы, так как вы не можете предугадать, когда именно ПК отправит данные (у ПК тоже есть куча других задач). один байт, который приведет вас к оператору if, но остальные данные поступят позже. Все данные тогда читаются как -1, так как в буфере нет данных.

    Перед чтением необходимо проверить, действительно ли есть данные для чтения. Это можно сделать с помощью Serial.available(). Он вернет количество новых байтов данных в буфере. Например, если вы хотите прочитать 8 байтов, вы должны подождать, пока в буфере не окажется хотя бы 8 байтов, используя if(Serial.available >= 8).

  2. Вы отправляете данные с ПК в виде текста в кодировке ASCII. Это означает, что каждое число выше 9 состоит из нескольких байтов данных. 4 значения, которые вас интересуют, больше 200, что означает, что вам нужно прочитать 3 байта, чтобы получить их. Но вы вызываете Serial.read() только один раз для каждого значения. Это даст вам первую цифру, которая равна 2 для всех значений в показанных данных. Более того, поскольку вы читаете только 1 байт, где есть целое число (которое может быть несколькими байтами) плюс 1 или 2 байта для новой строки (в зависимости от выбранной строки, заканчивающейся \n или " \n\r"), все чтение не соответствует структуре данных. Теперь прочитанные данные (если они есть) также зависят от того, сколько данных было отправлено для значений ранее.

    Имейте в виду, что с текстом, закодированным ASCII, вам может потребоваться несколько вызовов Serial.read() для фактического чтения полного числа. Я опишу путь сюда в следующем пункте.

  3. Как я уже упоминал выше, в последовательном интерфейсе нет протокола сообщений. Потеря данных приведет к смещению и, следовательно, к искажению данных. Но вы можете наложить собственный протокол на последовательный поток данных. Рекомендуется сначала получить полное сообщение (возможно, с несколькими значениями данных внутри него), а затем обработать его. Для этого я бы предложил новую структуру данных

    mode|number of colors|blend speed|beats until color change|Red|Green|Blue|Brightness\n
    

    Новая строка здесь реализована только с символом новой строки (\n). Значения разделены вертикальной чертой. Вы должны поместить if(Serial.available()>0) в свою функцию цикла. Затем внутри этого оператора if вы читаете один символ и добавляете его в буфер. Если вы читаете символ \n, вы начинаете обрабатывать буфер. Затем вы можете преобразовать одно значение за другим из буфера и сохранить их в переменные. После этого вы очищаете буфер. Вы можете увидеть этот стиль в примере SerialEvent, который поставляется с Arduino IDE. Вам также следует прочитать статью в блоге Маженко о чтении последовательного интерфейса на Arduino.


Тем не менее, вы должны быть осторожны с использованием объекта String на Arduino. Конечно, String удобны, но они динамически выделяют необходимую память во время выполнения. На микроконтроллерах, которые обычно имеют очень ограниченную оперативную память, это может привести к фрагментации кучи (куча — это область памяти для динамического выделения памяти). При использовании объектов String базовый буфер часто создается заново. Поскольку буферы выделяются и удаляются позже, это может привести к небольшим областям памяти, которые не используются, но настолько малы, чтобы фактически использоваться буфером. Таким образом вы можете очень быстро заполнить оперативную память.

Лучше использовать не String, а напрямую массив char с фиксированной длиной. Маженко также написал статью о проблеме String: The Evils of Arduino Strings

,

0

Во-первых, это НЕ способ чтения из серийного USART!

buff = Serial.read();
int result = buff.toInt();

Serail.read возвращает один байт, вы обрабатываете его как строку, а затем пытаетесь проанализировать его; вы никогда не увидите ничего, кроме 0-9, если вам повезет (очень маловероятно, что бафф содержит действительную строку, скорее всего, он просто будет продолжать работать, пока не найдет \0 и не вернет какое-то случайное число, или просто потерпит неудачу.

EDIT: кажется, я ошибся, 'read()' возвращает int, номинально возвращается только байт, но 16-битный -1 (0xffff) возвращается для ошибки; здесь документация может быть более понятной.

Во-вторых, последовательная библиотека arduino имеет огромный недостаток: -1 используется как ошибка при чтении, но -1 (т.е. 255) может быть допустимым фрагментом данных, что делает возвращаемое значение бесполезным, так как вы не знаю, является ли 255 правильным значением или ошибкой. Единственный способ обойти это — использовать Serial.available, поэтому вы должны использовать его для управления своим кодом, иначе вы не будете знать, что происходит.

Наконец, вы можете записывать двоичные данные в C# с помощью .Write(byte[] ...), а затем читать их в двоичном виде, поэтому вам не нужно их анализировать.

,

путаница -1 и 255 существует только в 8-битных значениях ... 16-битное целое больше не будет путаницы .... -1 = 0xff = 255 .... -1 = 0xffff = 65535, @jsotola

я только что подтвердил это на Uno ... определил buff как целое число int buff; .... serial.read() возвращает -1 (0xFFFF), когда нет данных ... и возвращает 255 ( 0x00FF) правильно при отправке 255, @jsotola

используйте Realterm для отправки 0xff, @jsotola