Arduino неправильно считывает последовательный ввод из программы C #
Я пытаюсь создать программу для управления светодиодным контроллером на базе 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();
}
// }
}
@BoatHouse, 👍1
Обсуждение2 ответа
Лучший ответ:
Ваш последовательный код получения неверен в некоторых отношениях:
Читать из Serial следует только в том случае, если действительно есть что читать. Для первого чтения это не проблема, так как вы проверяли, что возвращаемое значение равно -1 (это код возврата для «в буфере нет новых данных»). Но после этого вы читаете без проверки, если есть что читать.нужно понимать,что данные действительно отправляются последовательно.байты приходят один за другим и нет понятия как сообщения на последовательном интерфейсе (который на самом деле называется UART в не-ардуино средах). Более того, где-то в передаче могут быть небольшие паузы, так как вы не можете предугадать, когда именно ПК отправит данные (у ПК тоже есть куча других задач). один байт, который приведет вас к оператору
if
, но остальные данные поступят позже. Все данные тогда читаются как -1, так как в буфере нет данных.Перед чтением необходимо проверить, действительно ли есть данные для чтения. Это можно сделать с помощью
Serial.available()
. Он вернет количество новых байтов данных в буфере. Например, если вы хотите прочитать 8 байтов, вы должны подождать, пока в буфере не окажется хотя бы 8 байтов, используяif(Serial.available >= 8)
.Вы отправляете данные с ПК в виде текста в кодировке ASCII. Это означает, что каждое число выше 9 состоит из нескольких байтов данных. 4 значения, которые вас интересуют, больше 200, что означает, что вам нужно прочитать 3 байта, чтобы получить их. Но вы вызываете
Serial.read()
только один раз для каждого значения. Это даст вам первую цифру, которая равна 2 для всех значений в показанных данных. Более того, поскольку вы читаете только 1 байт, где есть целое число (которое может быть несколькими байтами) плюс 1 или 2 байта для новой строки (в зависимости от выбранной строки, заканчивающейся\n
или " \n\r"), все чтение не соответствует структуре данных. Теперь прочитанные данные (если они есть) также зависят от того, сколько данных было отправлено для значений ранее.Имейте в виду, что с текстом, закодированным ASCII, вам может потребоваться несколько вызовов
Serial.read()
для фактического чтения полного числа. Я опишу путь сюда в следующем пункте.Как я уже упоминал выше, в последовательном интерфейсе нет протокола сообщений. Потеря данных приведет к смещению и, следовательно, к искажению данных. Но вы можете наложить собственный протокол на последовательный поток данных. Рекомендуется сначала получить полное сообщение (возможно, с несколькими значениями данных внутри него), а затем обработать его. Для этого я бы предложил новую структуру данных
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
Во-первых, это НЕ способ чтения из серийного 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
- Библиотека FastLED: Как настроить яркость одного пикселя в абсолютном масштабе?
- Адресная RGB-полоса работает по отдельности, но не может настроить все светодиоды на полностью белый цвет.
- FastLED fill_rainbow - Неожиданный красный пиксель
- Как читать текстовый файл построчно
- Быстродействующий массив fill_solid
- Управление светодиодной лентой с помощью ИК-контроллера с помощью Arduino
- Что сделать, чтобы увеличить количество светодиодов, которые можно запитать с помощью pro micro?
- RGB светодиодная лента 100м.
Вы должны читать только из Serial, когда действительно есть что читать в буфере., @chrisl
Как я могу проверить, есть ли там что-то?, @BoatHouse
Serial.read() читает один символ (или -1, если его там нет). https://majenko.co.uk/blog/reading-serial-arduino, @Majenko