Arduino перестает отправлять/принимать данные через Serialport через случайное время

У меня есть приложение C# WPF, которое взаимодействует с моей Arduino Uno через последовательный порт. Все работает нормально от 3 до 20 секунд, после чего я не могу ничего отправить или получить с/на arduino.

Что я пытаюсь архивировать: Подключил arduino uno к оптическому датчику мыши для чтения из его регистров, теперь я хочу обработать данные на своем компьютере.

Я пробовал использовать другую Arduino Uno, а также другой оптический датчик мыши, который ничего не изменил. Открытие и закрытие последовательного порта после каждой связи у меня не сработало. Я видел несколько вопросов здесь, на бирже стека, я просмотрел всех, но они не смогли мне помочь. Serial.flush(), кажется, не работает, или это какое-то переполнение, которое я не могу понять.

Я был бы очень признателен, если бы кто-нибудь объяснил мне, в чем корень этой проблемы.

Для полноты вот мой код C#:

 public class SerialCommunication
    {
        public SerialPort serialPort = new SerialPort();

        public SerialCommunication()
        {
            string[] ports = SerialPort.GetPortNames();
            if (ports.Count() == 1)
            {
                MessageBox.Show("No serialport is available");
                return;
            }
            serialPort.PortName = ports[1];
            serialPort.BaudRate = 115200;
            serialPort.Parity = Parity.None;
            serialPort.DataBits = 8;
            serialPort.Handshake = Handshake.None;
            serialPort.StopBits = StopBits.One;
            serialPort.NewLine = "\r\n";
            serialPort.WriteTimeout = 2000;
            serialPort.ReadTimeout = -1;

            serialPort.Open();
        }
    }

public partial class MainWindow : Window
    {
        public static readonly object _lock = new object();

        private SerialCommunication serialCommunication = new SerialCommunication();

        public MainWindow()
        {
            InitializeComponent();
            GetWindow(this).KeyDown += HandelKeyDown;

            serialCommunication.serialPort.DataReceived += (sender, args) =>
            {
                string bufferstring = ((SerialPort)sender).ReadLine();
                OnMessageReceived(bufferstring);
                //serialCommunication.serialPort.DiscardOutBuffer();
                //serialCommunication.serialPort.DiscardInBuffer();
                //Поток.Сон(3);
                //байт[] буфер = новый байт[15];
                //((SerialPort)sender).Read(buffer, 0, buffer.Length);
                //строка bufferstring = Encoding.UTF8.GetString(buffer);
            };
            serialCommunication.serialPort.WriteLine("OK");
        }

        private void OnMessageReceived(string message)
        {
            if (message.Length != 15 || message.Contains('O') || message.Contains('K') || message.Contains('\n') || message.Contains('\r'))
            {
                serialCommunication.serialPort.DiscardInBuffer();
                serialCommunication.serialPort.DiscardOutBuffer();
                serialCommunication.serialPort.WriteLine("OK");
                return;
            }

            //Формат: "y: 0x%02X,x: 0x%02X"
            string x = message.Substring(3, 4);
            string y = message.Substring(11, 4);

            int offsetX = (Convert.ToInt32(x, 16) - 128);
            int offsetY = (Convert.ToInt32(y, 16) - 128);

            // следующие три строки только визуализируют полученные данные, но не изменяют их
            MoveX(offsetX / 50);
            MoveY(offsetY / 50);
            ShowMessage(message);
            Thread.Sleep(2);
            serialCommunication.serialPort.WriteLine("OK");
        }
}

Фотография того, как выглядит приложение и данные, которые оно получает: Справа данные, вверху текущая позиция

Вот мой код Arduino:

#include <Arduino.h>

// 5в
#define POWER 13
// Последовательный ввод/вывод
#define SDIO 11
// Серийные часы
#define SCK 12

// Регистры
#define DELTAY 0x02
#define DELTAX 0x03

#define serialInputBufferSize 16
char dataBuffer[serialInputBufferSize];
unsigned short inputBufferLength = 0;

bool sendPosition = true;

char tmp[16];

void InitMouse()
{
  digitalWrite(SCK, HIGH);
  delayMicroseconds(5);
  digitalWrite(SCK, LOW);
  delayMicroseconds(1);
  digitalWrite(SCK, HIGH);

  // Подождите, пока не истечет время таймера последовательной транзакции:
  delay(1000);
}

byte readRegister(byte addr)
{
  byte r = 0;

  // Установить линию данных для вывода
  pinMode(SDIO, OUTPUT);
  for (int i = 7; i >= 0; i--)
  {
    digitalWrite(SCK, LOW);
    digitalWrite(SDIO, addr & (1 << i));
    digitalWrite(SCK, HIGH);
  }

  // Переключаем линию данных с вывода на ввод
  pinMode(SDIO, INPUT);
  // В техническом описании указано время ожидания не менее 100 мкс
  // при переключении с записи на чтение.
  // Это нужно чипу для подготовки данных
  delayMicroseconds(110);

  // Просматриваем регистр и считываем его данные
  for (int i = 7; i >= 0; i--)
  {
    digitalWrite(SCK, LOW);
    digitalWrite(SCK, HIGH);
    r |= (digitalRead(SDIO) << i);
  }

  // Задержка в конце гарантирует >100 мкс до следующей транзакции
  delayMicroseconds(110);

  return r;
}

byte y = 0;
byte x = 0;

void WaitForMovementThenSendPositionOffset()
{
  x = 0;
  y = 0;

  while (((y == 0 && x == 0) || x == NULL || y == NULL) && sendPosition)
  {
    delay(1); // kann eigentlich weg
    y = readRegister(DELTAY);
    x = readRegister(DELTAX);
  }

  if (sprintf(tmp, "y: 0x%02X,x: 0x%02X", y, x) > 0)
  {
    tmp[16] = 0;
    if (!Serial.println(tmp))
    {
      // Серийный.конец();
      // Serial.begin(9600);
      // Serial.begin(115200);
      Serial.flush();
    }

    sendPosition = false;
  }
}

void serialEvent()
{
  sendPosition = false;

  // Читаем последовательные данные в буфер
  while (Serial.available())
  {
    dataBuffer[inputBufferLength++] = Serial.read();
  }

  Serial.flush();

  if (strstr(dataBuffer, "OK"))
  {
    sendPosition = true;
    WaitForMovementThenSendPositionOffset();
  }
}

void setup()
{
  pinMode(SCK, OUTPUT);
  pinMode(SDIO, INPUT);
  pinMode(POWER, OUTPUT);

  digitalWrite(POWER, HIGH);

  Serial.setTimeout(1000);
  // Serial.begin(9600);
  Serial.begin(115200);

  InitMouse();
}

void loop()
{
}

, 👍0

Обсуждение

какую работу по устранению неполадок вы сделали?, @jsotola

@jsotola, как упоминалось выше: я попытался использовать другой Arduino Uno, а также другой оптический датчик мыши, который ничего не изменил. Открытие и закрытие последовательного порта после каждой связи у меня не сработало. Я видел несколько вопросов здесь, на бирже стека, я просмотрел всех, но они не смогли мне помочь. Я также пытался использовать delay() и другие бесполезные вещи в течение 2 дней., @Michael Santos

начните с основ... уменьшите скорость передачи... используйте простой скетч Arduino, который многократно отправляет "ABC123" по одному символу за раз с короткой паузой между символами... наблюдайте результат на стороне C# соединения, @jsotola

@jsotola я попробовал скорость 9600 бод, которая не помогла, на стороне С# я получаю желаемые результаты. Я добавлю фото того, как выглядит мое приложение wpf и данные, которые оно получает, но спасибо, @Michael Santos

вам нужно изменить скетч Arduino на код minium, который все еще демонстрирует нежелательное поведение ... поэтому я сказал начать с отправки ABC123 и посмотреть, произойдет ли это по-прежнему., @jsotola

@jsotola хорошо, я попробую с "ABC123", @Michael Santos


2 ответа


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

0

Проблема была в методе serialEvent(). Я не сохранял буферный индекс, где я красил полученное сообщение, поэтому он всегда оставался там, пока у Arduino не закончилась память, а также я не дал Arduino достаточно времени для получения сообщения, поэтому я добавил delay (1) там.

Вот мой обновленный код:

void serialEvent()
{
  inputBufferLength = 0;
  sendPosition = false;
  delay(1);
  // Читаем последовательные данные в буфер
  while (Serial.available())
  {
    if (inputBufferLength < serialInputBufferSize)
    {
      dataBuffer[inputBufferLength++] = Serial.read();
    }
    else
    {
      Serial.read();
    }
  }

  Serial.flush();

  if (strstr(dataBuffer, "OK"))
  {
    sendPosition = true;
    WaitForMovementThenSendPositionOffset();
  }
}
,

0

Каждый раз, когда вы получаете данные, он пытается прочитать всю строку (SerialPort.ReadLine()). Если полная строка недоступна, текущий поток будет заблокирован до тех пор, пока она не появится или пока не истечет время ожидания.

Эти вызовы будут накапливаться и в конечном итоге приведут к сбою приложения.

,

Если я перезапущу приложение С#, оно все равно не сработает. Я должен перезапустить ардуино, тогда он снова работает. Я думаю, что проблема на стороне Arduino, но я попробую вместо этого использовать буфер с SerialPort.Read()., @Michael Santos