Контроль передачи данных
- Войдите на сайт для отправки комментариев
День добрый.
Возник вопрос контроля передачи данных в виде строк. С проверкой длины строки, корректности строки а также подтверждением получения и синхронизацией номеров строк. Проблема в том, что я не могу допустить потери, хоть 1 строки. Если такое происходит, программа должна остановится.
Структура проекта.
Компьютер, основная Mega (возможно Due, если ресурсов не хватит), вспомогательная Mega, дополнительная Mega.
Передача в обе стороны между Компьютером и основной Mega (Serial, UDP), основная с вспомогательной Mega (Serial). Дополнительная живет своей жизью.
Передача между компьютером и основной Mega, должна осуществляться как можно быстрее. В идеале, передача должна быть до 20 строк в секунду.
Формат данных
?NumberPacket(Номер пакета);XXX(Длина пакета);XXX(Тип операции);XXX(Параметр);XX(Тип данных);XXXXXXXXXX(Значение)!\n
Пока работа не закончена. Вот второй результат, в первом был delayMicroseconds(100), убрал по совету leshak.
Я любить, поэтому не судите строго.
/****************************** Переменные перечисления **********************************/
/// <summary>
/// Перечисление - Тип протокола передачи данных
/// </summary>
enum ConnectionTypeEnum
{
/// <summary>
/// Не выбран
/// </summary>
CT_None = 0,
/// <summary>
/// Serial порт
/// </summary>
CT_Serial = 1,
/// <summary>
/// UDP
/// </summary>
CT_UDP = 2
};
/// <summary>
/// Переменная определяющая тип связи с компьютером
/// </summary>
ConnectionTypeEnum connectionType = CT_Serial;
/// <summary>
/// Перечисление - Тип операции
/// </summary>
enum OperationTypeEnum
{
/// <summary>
/// Не выбрана
/// </summary>
OT_None = 0,
/// <summary>
/// Отправка данных с Arduino
/// </summary>
OT_SendDataFromArduino = 1,
/// <summary>
/// Отправка данных с PC
/// </summary>
OT_SendDataFromPC = 2,
/// <summary>
/// Получение данных с Arduino
/// </summary>
OT_ReciveDataFromArduino = 3,
/// <summary>
/// Получение данных с PC
/// </summary>
OT_ReciveDataFromPC = 4,
/// <summary>
/// Проверка вернувшихся данных с Arduino
/// </summary>
OT_CheckReturnFromArduino = 5,
/// <summary>
/// Проверка вернувшихся данных с PC
/// </summary>
OT_CheckReturnFromPC = 6,
/// <summary>
/// Старт ТПА
/// </summary>
OT_StartTPA = 7,
/// <summary>
/// Начало инициализации Arduino
/// </summary>
OT_InitArduinoBegin = 8,
/// <summary>
/// Инициализация Arduino
/// </summary>
OT_InitArduino = 9,
/// <summary>
/// Конец инициализации Arduino
/// </summary>
OT_InitArduinoFinish = 10,
/// <summary>
/// Начало настройки Arduino
/// </summary>
OT_SetupArduinoBegin = 11,
/// <summary>
/// Настройка Arduino
/// </summary>
OT_SetupArduino = 12,
/// <summary>
/// Конец настройки Arduino
/// </summary>
OT_SetupArduinoFinish = 13,
/// <summary>
/// Проверка соединения с Arduino
/// </summary>
OT_CheckConnectionFromArduino = 14,
/// <summary>
/// Проверка соединения с PC
/// </summary>
OT_CheckConnectionFromPC = 15,
/// <summary>
/// Команда ручного режима
/// </summary>
OT_CommandManualMode = 16,
/// <summary>
/// Стоп ТПА
/// </summary>
OT_StopTPA = 17
};
/// <summary>
/// Переменная хранящая тип операции
/// </summary>
OperationTypeEnum operationType = OT_None;
/// <summary>
/// Перечисление - Тип параметра
/// </summary>
enum ParametrEnum
{
/// <summary>
/// Не установлен
/// </summary>
P_None = 0
};
/// <summary>
/// Перечисление - Тип данных параметра
/// </summary>
enum DataTypeEnum
{
/// <summary>
/// Не указан
/// </summary>
DT_None = 0,
/// <summary>
/// Int
/// </summary>
DT_Int = 1
};
/****************************** /Переменные перечисления **********************************/
/****************************** Переменные для работы с Serial портом **********************************/
/// <summary>
/// Константа - Скорость Serial порта
/// </summary>
#define speedSerial 115200
/****************************** /Переменные для работы с Serial портом **********************************/
/****************************** Переменные для работы передачи данных **********************************/
/// <summary>
/// Константа - Символ новой строки
/// </summary>
#define newLine '\n'
/// <summary>
/// Константа - Символ начала пакета
/// </summary>
#define beginChar '?'
/// <summary>
/// Константа - Символ разделитель параметров пакета
/// </summary>
#define breakChar ';'
/// <summary>
/// Константа - Символ конца пакета
/// </summary>
#define endChar '!'
/// <summary>
/// Константа - Символ для очистки значения char
/// </summary>
#define clearChar '\0'
/// <summary>
/// Переменная хранящая завершено ли получения данных
/// </summary>
boolean finishReceiveData = false;
/// <summary>
/// Константа хранящая мак. размер массива хранящего пакет данных
/// </summary>
#define receiveSizeArrayCOM 38
/// <summary>
/// Переменная хранящее номер пакета в системе
/// </summary>
int globalNumberPacket = 0;
/// Объект String для хранения полученной строки
/// </summary>
String receiveData = "";
/// <summary>
/// Переменная хранящее номер символа начала пакета в пакете
/// </summary>
int indexBeginCharInPackage;
/// <summary>
/// Переменная хранящее номер символа конца пакета в пакете
/// </summary>
int indexEndCharInPackage;
/// <summary>
/// Переменная кол-во символов разделителя блоков данных в пакете
/// </summary>
int breakCharCountInPackage;
/// <summary>
/// Переменная хранящее номер пакета
/// </summary>
String numberPacket;
/// <summary>
/// Переменная хранящее длину пакета
/// </summary>
String lenPacket;
/// <summary>
/// Переменная хранящее тип операции пакета
/// </summary>
String operationTypePacket;
/// <summary>
/// Переменная хранящее тип параметра пакета
/// </summary>
String parametrPacket;
/// <summary>
/// Переменная хранящее тип данных параметра пакета
/// </summary>
String dataTypePacket;
/// <summary>
/// Переменная хранящее значение параметра пакета
/// </summary>
String valuePacket;
/// <summary>
/// Переменная хранящее номер пакета
/// </summary>
int receiveNumberIntPacket = 0;
/// <summary>
/// Переменная хранящее тип операции пакета
/// </summary>
int receiveLenIntPacket;
/// <summary>
/// Переменная хранящее тип операции пакета
/// </summary>
int receiveOperationTypeIntPacket;
/// <summary>
/// Переменная хранящее реальную длину пакета, проверка после получения
/// </summary>
int receiveLength;
/****************************** /Переменные для работы передачи данных **********************************/
/****************************** Переменные запуска ТПА **********************************/
/// <summary>
/// Переменная хранящее состояние программы запущена или нет
/// </summary>
boolean beginWorkTPA = false;
/// <summary>
/// Переменная хранящее состояние программы инициализирован ли ТПА
/// </summary>
boolean beginInitializationTPA = false;
/****************************** /Переменные запуска ТПА **********************************/
/****************************** Переменные отладки **********************************/
/// <summary>
/// Переменная хранящее время старта цикла, для проверки скорости выполнения цикла
/// </summary>
unsigned long timeCycleWork = 0;
#define IS_DEBUG true
/****************************** /Переменные отладки **********************************/
/// <summary>
/// Настройка программы
/// </summary>
void setup ()
{
// TODO: Добавить описание
switch (connectionType)
{
case CT_Serial:
Serial.begin(speedSerial);
break;
}
}
/// <summary>
/// Основной цикл
/// </summary>
void loop()
{
// Проверка времени обработки данных
//timeCycleWork = millis();
// /Проверка времени обработки данных
// Выбираем протокол передачи данных
switch (connectionType)
{
case CT_Serial:
// Протокол передачи данных на основе Serial порта
// Получение данных с Serial порта
GetReceiveDataFromSerial();
break;
}
// Завершено ли получения данных
if (finishReceiveData)
{
// Парсинг полученных данных
ParseReceiveData();
}
}
/****************************** Функции работы с Serial портом **********************************/
/// <summary>
/// Получение данных с Serial порта
/// </summary>
void GetReceiveDataFromSerial()
{
// TODO: Добавить описание
while(Serial.available())
{
/// <summary>
/// Переменная хранящая последний полученный char
/// </summary>
char incomingChar = (char)Serial.read();
if (incomingChar != newLine)
{
receiveData += incomingChar;
}
else
{
Serial.flush();
finishReceiveData = true;
}
}
}
/// <summary>
/// Отправка данных по Serial порту
/// </summary>
void SendDataFromSerial(String SendValue, int OperationType, int Parametr, int DataType)
{
// TODO: Добавить описание
/*if (globalNumberPacket > 500)
{
Serial.println("Errore before");
}
globalNumberPacket++;
if (globalNumberPacket > 500)
{
Serial.println("Errore after");
}*/
RepeatSendDataFromSerial(SendValue, OperationType, Parametr, DataType);
}
/// <summary>
/// TODO: Добавить описание
/// </summary>
void SendConfirmationDataFromSerial(String SendValue, int Parametr, int DataType)
{
// TODO: Добавить описание
RepeatSendDataFromSerial(SendValue, OT_CheckReturnFromArduino, Parametr, DataType);
ClearReceiveDataVariable();
}
/// <summary>
/// Повтор отправки данных по Serial порту
/// </summary>
void RepeatSendDataFromSerial(String SendValue, int OperationType, int Parametr, int DataType)
{
// TODO: Добавить описание
int sendValueLen = String(globalNumberPacket).length() + String(OperationType).length() + String(Parametr).length() + String(DataType).length() + SendValue.length() + 7;
sendValueLen += String(sendValueLen).length();
String sendPacket = beginChar + String(globalNumberPacket) + breakChar + String(sendValueLen) + breakChar + OperationType + breakChar + Parametr + breakChar + DataType + breakChar + SendValue + endChar + "\n";
Serial.print(sendPacket);
}
/****************************** /Функции работы с Serial портом **********************************/
/****************************** Функции работы с полученными данными **********************************/
/// <summary>
/// Очистка переменных получения данных, после получения данный
/// </summary>
void ClearReceiveDataVariable()
{
// TODO: Добавить описание
indexBeginCharInPackage = indexEndCharInPackage = breakCharCountInPackage = receiveLenIntPacket = receiveLength = receiveOperationTypeIntPacket = 0;
receiveData = numberPacket = lenPacket = operationTypePacket = parametrPacket = dataTypePacket = valuePacket = "";
finishReceiveData = false;
}
/// <summary>
/// Парсинг полученных данных
/// </summary>
void ParseReceiveData()
{
// Получаем длину пакета
receiveLength = receiveData.length();
// Пробегаемся по массиву хранящий пакет данных
for (int i = 0; i < receiveLength; i++)
{
char receiveDataCurrentChar = receiveData.charAt(i);
switch (receiveDataCurrentChar) {
case beginChar:
// Получаем индекс символ начала пакета в пакете
indexBeginCharInPackage = i;
break;
case endChar:
// Получаем индекс символ конца пакета в пакете
indexEndCharInPackage = i;
break;
case breakChar:
// Получаем кол-во символов разделителя блоков данных в пакете
breakCharCountInPackage++;
break;
default:
// В зависимости от номера блока данных в пакете, заполняем переменные блоков
switch (breakCharCountInPackage) {
case 0:
// Номер пакета в системе
numberPacket += receiveDataCurrentChar;
break;
case 1:
// Длина пакета
lenPacket += receiveDataCurrentChar;
break;
case 2:
// Тип операции пакета
operationTypePacket += receiveDataCurrentChar;
break;
case 3:
// Тип операции пакета
parametrPacket += receiveDataCurrentChar;
break;
case 4:
// Тип данных параметра пакета
dataTypePacket += receiveDataCurrentChar;
break;
case 5:
// Значение параметра пакета
valuePacket += receiveDataCurrentChar;
break;
}
break;
}
}
if (breakCharCountInPackage != 5)
{
ErroreDataReceived("1",1);
return;
}
// Номер пакета в системе, Int
receiveNumberIntPacket = numberPacket.toInt();
// Номер пакета в системе, Int
receiveLenIntPacket = lenPacket.toInt();
// Тип операции пакета, Int
receiveOperationTypeIntPacket = operationTypePacket.toInt();
Serial.println(receiveData);
// Проверка корректности полученного пакета
if (indexBeginCharInPackage == 0 && breakCharCountInPackage == 5 // Индекс символа начала пакета, должен быть равен 0, кол-во блоков данных должно быть 5
&& indexEndCharInPackage == (receiveLength - 1) // Индекс символа конца пакета, должен быть равен значению блока длины пакета - 1
&& receiveLenIntPacket == receiveLength) // Реальная длина пакета должна быть равна значению блока длины пакета
{
// Определяем тип операции пакета
switch(receiveOperationTypeIntPacket)
{
case OT_StartTPA:
// Старт ТПА
SendConfirmationDataFromSerial(valuePacket, parametrPacket.toInt(), dataTypePacket.toInt());
// Устанавливаем состояние программы запущена
beginWorkTPA = true;
break;
default:
int globalNumberPacketAndOne = globalNumberPacket;
if (receiveOperationTypeIntPacket != OT_CheckReturnFromPC && receiveNumberIntPacket == globalNumberPacketAndOne)
{
SendConfirmationDataFromSerial(valuePacket, parametrPacket.toInt(), dataTypePacket.toInt());
}
else
{
if (receiveNumberIntPacket == globalNumberPacketAndOne)
{
switch(receiveOperationTypeIntPacket)
{
case OT_StopTPA:
// Стоп ТПА
SendConfirmationDataFromSerial(valuePacket, parametrPacket.toInt(), dataTypePacket.toInt());
// Устанавливаем состояние программы остановлена
beginWorkTPA = false;
break;
}
}
else
{
ErroreDataReceived("2",2);
}
}
break;
}
}
else
{
ErroreDataReceived("3",3);
}
}
/****************************** /Функции работы с полученными данными **********************************/
/****************************** Функции работы с ошибками **********************************/
/// <summary>
/// Ошибка получения данных
/// </summary>
void ErroreDataReceived(String DataReceived, int TypeErrore)
{
// TODO: Добавить описание
ClearReceiveDataVariable();
Errore(DataReceived, 0);
}
/// <summary>
/// Ошибка получения данных
/// </summary>
void Errore(String Data, int TypeErrore)
{
// TODO: Добавить описание
//FullStopTPA();
Log(Data, TypeErrore, true);
}
/****************************** /Функции работы с ошибками **********************************/
/****************************** Функции работы с логом **********************************/
/// <summary>
/// Логирование
/// </summary>
void Log(String Data, int TypeLog, bool isErrore)
{
// TODO: Добавить описание
switch (connectionType)
{
case CT_Serial:
Serial.println(Data);
break;
}
}
/****************************** /Функции работы с логом **********************************/
Работа, еще не закончена. Осталось побороть 1 ошибку, сделать контроль номера пакета, оптимизировать и нормально закомментировать.
Ошибка, даже не в ардуине, а в принимающей программе на C#. Получаю, странные символы перед ответом
����. Если пользоваться монитором Сом порта ардуины, то все норм.
Вот настройки соединения порта в программе.
ArduinoSerialPort.PortName = "COM25";
ArduinoSerialPort.BaudRate = 115200;
ArduinoSerialPort.Parity = Parity.None;
ArduinoSerialPort.DataBits = 8;
ArduinoSerialPort.StopBits = StopBits.One;
//ArduinoSerialPort.ReadTimeout = 500;
//ArduinoSerialPort.WriteTimeout = 500;
ArduinoSerialPort.Encoding = Encoding.UTF8;
ArduinoSerialPort.NewLine = "\n";
ArduinoSerialPort.DataReceived += new SerialDataReceivedEventHandler(ArduinoSerialPort_DataReceived);
ArduinoSerialPort.Open();
Может кто-то сталкивался с такой проблемой.
Вообще возможно я изобретаю велосипед, сейчас буду читать Modbus-ASCII и Wake. Но в этом проекте свою наработку оставлю, зря, что ли трудился. Плюс это дополнительный опыт, даже если в дальнейших проектах буду использовать Modbus-ASCII или Wake.
Забыл, строка для проверки
?0;14;7;0;1;1!
Вернул версию с delayMicroseconds(100), работает без ошибки.
До 35 запросов в 1с. с возвратом посылаемому устройству. И отображение в программе.
Думаю, можно и больше, но надо оптимизировать код.
/****************************** Переменные перечисления **********************************/ /// <summary> /// Перечисление - Тип протокола передачи данных /// </summary> enum ConnectionTypeEnum { /// <summary> /// Не выбран /// </summary> CT_None = 0, /// <summary> /// Serial порт /// </summary> CT_Serial = 1, /// <summary> /// UDP /// </summary> CT_UDP = 2 }; /// <summary> /// Переменная определяющая тип связи с компьютером /// </summary> ConnectionTypeEnum connectionType = CT_Serial; /// <summary> /// Перечисление - Тип операции /// </summary> enum OperationTypeEnum { /// <summary> /// Не выбрана /// </summary> OT_None = 0, /// <summary> /// Отправка данных с Arduino /// </summary> OT_SendDataFromArduino = 1, /// <summary> /// Отправка данных с PC /// </summary> OT_SendDataFromPC = 2, /// <summary> /// Получение данных с Arduino /// </summary> OT_ReciveDataFromArduino = 3, /// <summary> /// Получение данных с PC /// </summary> OT_ReciveDataFromPC = 4, /// <summary> /// Проверка вернувшихся данных с Arduino /// </summary> OT_CheckReturnFromArduino = 5, /// <summary> /// Проверка вернувшихся данных с PC /// </summary> OT_CheckReturnFromPC = 6, /// <summary> /// Старт ТПА /// </summary> OT_StartTPA = 7, /// <summary> /// Начало инициализации Arduino /// </summary> OT_InitArduinoBegin = 8, /// <summary> /// Инициализация Arduino /// </summary> OT_InitArduino = 9, /// <summary> /// Конец инициализации Arduino /// </summary> OT_InitArduinoFinish = 10, /// <summary> /// Начало настройки Arduino /// </summary> OT_SetupArduinoBegin = 11, /// <summary> /// Настройка Arduino /// </summary> OT_SetupArduino = 12, /// <summary> /// Конец настройки Arduino /// </summary> OT_SetupArduinoFinish = 13, /// <summary> /// Проверка соединения с Arduino /// </summary> OT_CheckConnectionFromArduino = 14, /// <summary> /// Проверка соединения с PC /// </summary> OT_CheckConnectionFromPC = 15, /// <summary> /// Команда ручного режима /// </summary> OT_CommandManualMode = 16, /// <summary> /// Стоп ТПА /// </summary> OT_StopTPA = 17 }; /// <summary> /// Переменная хранящая тип операции /// </summary> OperationTypeEnum operationType = OT_None; /// <summary> /// Перечисление - Тип параметра /// </summary> enum ParametrEnum { /// <summary> /// Не установлен /// </summary> P_None = 0 }; /// <summary> /// Перечисление - Тип данных параметра /// </summary> enum DataTypeEnum { /// <summary> /// Не указан /// </summary> DT_None = 0, /// <summary> /// Int /// </summary> DT_Int = 1 }; /****************************** /Переменные перечисления **********************************/ /****************************** Переменные для работы с Serial портом **********************************/ /// <summary> /// Константа - Скорость Serial порта /// </summary> #define speedSerial 115200 /****************************** /Переменные для работы с Serial портом **********************************/ /****************************** Переменные для работы передачи данных **********************************/ /// <summary> /// Константа - Символ новой строки /// </summary> #define newLine '\n' /// <summary> /// Константа - Символ начала пакета /// </summary> #define beginChar '?' /// <summary> /// Константа - Символ разделитель параметров пакета /// </summary> #define breakChar ';' /// <summary> /// Константа - Символ конца пакета /// </summary> #define endChar '!' /// <summary> /// Константа - Символ для очистки значения char /// </summary> #define clearChar '\0' /// <summary> /// Переменная хранящая последний полученный char /// </summary> char incomingChar; /// <summary> /// Переменная хранящая индекс текущего char в массиве хранящего пакет данных /// </summary> int indexCharInArray; /// <summary> /// Переменная хранящая завершено ли получения данных /// </summary> boolean finishReceiveData = false; /// <summary> /// Константа хранящая мак. размер массива хранящего пакет данных /// </summary> #define receiveSizeArrayCOM 40 /// <summary> /// Массив хранящий пакет данных /// </summary> char receiveCharArrayCOM[receiveSizeArrayCOM]; /// <summary> /// Переменная хранящее номер пакета в системе /// </summary> int globalNumberPacket = 0; /// <summary> /// Переменная хранящее номер символа начала пакета в пакете /// </summary> int indexBeginCharInPackage; /// <summary> /// Переменная хранящее номер символа конца пакета в пакете /// </summary> int indexEndCharInPackage; /// <summary> /// Переменная кол-во символов разделителя блоков данных в пакете /// </summary> int breakCharCountInPackage; /// <summary> /// Переменная хранящее номер пакета /// </summary> String numberPacket; /// <summary> /// Переменная хранящее длину пакета /// </summary> String lenPacket; /// <summary> /// Переменная хранящее тип операции пакета /// </summary> String operationTypePacket; /// <summary> /// Переменная хранящее тип параметра пакета /// </summary> String parametrPacket; /// <summary> /// Переменная хранящее тип данных параметра пакета /// </summary> String dataTypePacket; /// <summary> /// Переменная хранящее значение параметра пакета /// </summary> String valuePacket; /// <summary> /// Переменная хранящее номер пакета /// </summary> int receiveNumberIntPacket = 0; /// <summary> /// Переменная хранящее тип операции пакета /// </summary> int receiveLenIntPacket; /// <summary> /// Переменная хранящее тип операции пакета /// </summary> int receiveOperationTypeIntPacket; /// <summary> /// Переменная хранящее реальную длину пакета, проверка после получения /// </summary> int receiveLength; /****************************** /Переменные для работы передачи данных **********************************/ /****************************** Переменные запуска ТПА **********************************/ /// <summary> /// Переменная хранящее состояние программы запущена или нет /// </summary> boolean beginWorkTPA = false; /// <summary> /// Переменная хранящее состояние программы инициализирован ли ТПА /// </summary> boolean beginInitializationTPA = false; /****************************** /Переменные запуска ТПА **********************************/ /****************************** Переменные отладки **********************************/ /// <summary> /// Переменная хранящее время старта цикла, для проверки скорости выполнения цикла /// </summary> unsigned long timeCycleWork = 0; #define IS_DEBUG true /****************************** /Переменные отладки **********************************/ /// <summary> /// Настройка программы /// </summary> void setup () { // TODO: Добавить описание switch (connectionType) { case CT_Serial: Serial.begin(speedSerial); break; } } /// <summary> /// Основной цикл /// </summary> void loop() { // Проверка времени обработки данных //timeCycleWork = millis(); // /Проверка времени обработки данных // Очистка переменных получения данных, после получения данных ClearReceiveDataVariable(); // Запущена или программа ТПА if (!beginWorkTPA) { // Программа ТПА не запущена // Выбираем протокол передачи данных switch (connectionType) { case CT_Serial: // Протокол передачи данных на основе Serial порта // Получение данных с Serial порта GetReceiveDataFromSerial(); break; } // Парсинг полученных данных ParseReceiveData(); // Обрываем выполнение loop return; } // Состояние программы инициализирован ли ТПА if (!beginInitializationTPA) { // ТПА не инициализирован // Устанавливаем состояние программы инициализирован beginInitializationTPA = true; } // Выбираем протокол передачи данных switch (connectionType) { case CT_Serial: // Протокол передачи данных на основе Serial порта // Получение данных с Serial порта GetReceiveDataFromSerial(); break; } // Парсинг полученных данных ParseReceiveData(); delay(25); SendDataFromSerial("Hello", OT_SendDataFromArduino, P_None, DT_Int); // Проверка времени обработки данных //unsigned long workTime = millis() - timeCycleWork; //Serial.println(workTime); // /Проверка времени обработки данных } /****************************** Функции работы с Serial портом **********************************/ /// <summary> /// Получение данных с Serial порта /// </summary> void GetReceiveDataFromSerial() { // TODO: Добавить описание while(Serial.available()) { incomingChar = (char)Serial.read(); if(incomingChar != newLine) { receiveCharArrayCOM[indexCharInArray++] = incomingChar; } else { finishReceiveData = true; } delayMicroseconds(100); } } /// <summary> /// Отправка данных по Serial порту /// </summary> void SendDataFromSerial(String SendValue, int OperationType, int Parametr, int DataType) { RepeatSendDataFromSerial(SendValue, OperationType, Parametr, DataType); } /// <summary> /// TODO: Добавить описание /// </summary> void SendConfirmationDataFromSerial(String SendValue, int Parametr, int DataType) { // TODO: Добавить описание RepeatSendDataFromSerial(SendValue, OT_CheckReturnFromArduino, Parametr, DataType); } /// <summary> /// Повтор отправки данных по Serial порту /// </summary> void RepeatSendDataFromSerial(String SendValue, int OperationType, int Parametr, int DataType) { // TODO: Добавить описание int sendValueLen = String(globalNumberPacket).length() + String(OperationType).length() + String(Parametr).length() + String(DataType).length() + SendValue.length() + 7; sendValueLen += String(sendValueLen).length(); String sendPacket = beginChar + String(globalNumberPacket) + breakChar + String(sendValueLen) + breakChar + OperationType + breakChar + Parametr + breakChar + DataType + breakChar + SendValue + endChar; SerialPrintLine(sendPacket); } void SerialPrintLine(String SendValue) { Serial.print(SendValue); Serial.print(newLine); } /****************************** /Функции работы с Serial портом **********************************/ /****************************** Функции работы с полученными данными **********************************/ /// <summary> /// Очистка переменных получения данных, после получения данный /// </summary> void ClearReceiveDataVariable() { // TODO: Добавить описание indexCharInArray = 0; memset(receiveCharArrayCOM,clearChar,receiveSizeArrayCOM); indexBeginCharInPackage = indexEndCharInPackage = breakCharCountInPackage = receiveLenIntPacket = receiveLength = receiveOperationTypeIntPacket = 0; numberPacket = lenPacket = operationTypePacket = parametrPacket = dataTypePacket = valuePacket = ""; finishReceiveData = false; } /// <summary> /// Парсинг полученных данных /// </summary> void ParseReceiveData() { // Завершено ли получения данных if (finishReceiveData) { // Получаем длину пакета receiveLength = strlen(receiveCharArrayCOM); // Пробегаемся по массиву хранящий пакет данных for (int i = 0; i < receiveLength; i++) { switch (receiveCharArrayCOM[i]) { case beginChar: // Получаем индекс символ начала пакета в пакете indexBeginCharInPackage = i; break; case endChar: // Получаем индекс символ конца пакета в пакете indexEndCharInPackage = i; break; case breakChar: // Получаем кол-во символов разделителя блоков данных в пакете breakCharCountInPackage++; break; default: // В зависимости от номера блока данных в пакете, заполняем переменные блоков switch (breakCharCountInPackage) { case 0: // Номер пакета в системе numberPacket += receiveCharArrayCOM[i]; break; case 1: // Длина пакета lenPacket += receiveCharArrayCOM[i]; break; case 2: // Тип операции пакета operationTypePacket += receiveCharArrayCOM[i]; break; case 3: // Тип операции пакета parametrPacket += receiveCharArrayCOM[i]; break; case 4: // Тип данных параметра пакета dataTypePacket += receiveCharArrayCOM[i]; break; case 5: // Значение параметра пакета valuePacket += receiveCharArrayCOM[i]; break; } break; } } if (breakCharCountInPackage != 5) { ErroreDataReceived("1",1); return; } // Номер пакета в системе, Int receiveNumberIntPacket = numberPacket.toInt(); // Номер пакета в системе, Int receiveLenIntPacket = lenPacket.toInt(); // Тип операции пакета, Int receiveOperationTypeIntPacket = operationTypePacket.toInt(); // Проверка корректности полученного пакета if (indexBeginCharInPackage == 0 && breakCharCountInPackage == 5 // Индекс символа начала пакета, должен быть равен 0, кол-во блоков данных должно быть 5 && indexEndCharInPackage == (receiveLength - 1) // Индекс символа конца пакета, должен быть равен значению блока длины пакета - 1 && receiveLenIntPacket == receiveLength // Реальная длина пакета должна быть равна значению блока длины пакета ) { // Определяем тип операции пакета switch(receiveOperationTypeIntPacket) { case OT_StartTPA: // Старт ТПА SendConfirmationDataFromSerial(valuePacket, parametrPacket.toInt(), dataTypePacket.toInt()); // Устанавливаем состояние программы запущена beginWorkTPA = true; break; default: int globalNumberPacketAndOne = globalNumberPacket;//globalNumberPacket + 1; if (receiveOperationTypeIntPacket != OT_CheckReturnFromPC && receiveNumberIntPacket == globalNumberPacketAndOne) { SendConfirmationDataFromSerial(valuePacket, parametrPacket.toInt(), dataTypePacket.toInt()); } else { if (receiveNumberIntPacket == globalNumberPacketAndOne) { switch(receiveOperationTypeIntPacket) { case OT_StopTPA: // Стоп ТПА SendConfirmationDataFromSerial(valuePacket, parametrPacket.toInt(), dataTypePacket.toInt()); //globalNumberPacket = 0; // Устанавливаем состояние программы остановлена beginWorkTPA = false; break; } } else { ErroreDataReceived("2",0); } } break; } } else { ErroreDataReceived("3",0); } } } /****************************** /Функции работы с полученными данными **********************************/ /****************************** Функции работы с ошибками **********************************/ /// <summary> /// Ошибка получения данных /// </summary> void ErroreDataReceived(String DataReceived, int TypeErrore) { // TODO: Добавить описание Errore(DataReceived, 0); } /// <summary> /// Ошибка получения данных /// </summary> void Errore(String Data, int TypeErrore) { // TODO: Добавить описание //FullStopTPA(); Log(Data, TypeErrore, true); } /****************************** /Функции работы с ошибками **********************************/ /****************************** Функции работы с логом **********************************/ /// <summary> /// Логирование /// </summary> void Log(String Data, int TypeLog, bool isErrore) { // TODO: Добавить описание switch (connectionType) { case CT_Serial: SerialPrintLine(Data); break; } } /****************************** /Функции работы с логом **********************************/Пока, протестирую и дополню номером пакет.
Сильно не вникал.... но не вижу нескольких нужных вещей:
1. Игнорирование символа \r (Вы вкурсе что Serial.println() шлет в конце строки \r\n?)
2. При обнаружении конца строки, в массив receiveCharArrayCOM нигде не записывается ноль. Все сишные функции работы со строками предполагают \0 в качестве символа конца строки). Без этого, все будет работать "как повезет".
3. Не видно проверок выхода за границы массива receiveCharArrayCOM . А вдруг прилезет строка длинней чем вы выделили память под буффер? Кстати не забудте, что принять мы можем не больше чем "размер буфера - 1". Так как под нулевой символ должен быть всегда.
Погодите, похоже что вы делаете
ClearReceiveDataVariable()
в начале каждого loop().
Ну, само собой, если в этому момент успела прилететь только часть строки.... то "готовой строки целиком" - еще нет.
Вот вам и приходится втыкать delay(). Что-бы строка успела накопится в буфере самого UART-та, и ее можно было "прочитать" за один проход loop().
Большое спасибо, за замечания.
leshak пишет:
1. Игнорирование символа \r (Вы вкурсе что Serial.println() шлет в конце строки \r\n?)
Во втором варианте, у меня нет Serial.println(). Но узнал, не давно, поэтому в 1 варианте без delay есть.
void SerialPrintLine(String SendValue) { Serial.print(SendValue); Serial.print(newLine); }2. При обнаружении конца строки, в массив receiveCharArrayCOM нигде не записывается ноль. Все сишные функции работы со строками предполагают \0 в качестве символа конца строки). Без этого, все будет работать "как повезет".
Не знал.
3. Не видно проверок выхода за границы массива receiveCharArrayCOM . А вдруг прилезет строка длинней чем вы выделили память под буффер? Кстати не забудте, что принять мы можем не больше чем "размер буфера - 1". Так как под нулевой символ должен быть всегда.
Да упустил из виду.
Погодите, похоже что вы делаете
ClearReceiveDataVariable()
в начале каждого loop().
Ну, само собой, если в этому момент успела прилететь только часть строки.... то "готовой строки целиком" - еще нет.
Вот вам и приходится втыкать delay(). Что-бы строка успела накопится в буфере самого UART-та, и ее можно было "прочитать" за один проход loop().
Да, во втором варианте, это есть.
Сейчас попробую в 1 вариант, без delay перенести, наработки второй версии и то, что Вы отметили.
Большое спасибо.
Переделал код
/****************************** Переменные перечисления **********************************/ /// <summary> /// Перечисление - Тип протокола передачи данных /// </summary> enum ConnectionTypeEnum { /// <summary> /// Не выбран /// </summary> CT_None = 0, /// <summary> /// Serial порт /// </summary> CT_Serial = 1, /// <summary> /// UDP /// </summary> CT_UDP = 2 }; /// <summary> /// Переменная определяющая тип связи с компьютером /// </summary> ConnectionTypeEnum connectionType = CT_Serial; /// <summary> /// Перечисление - Тип операции /// </summary> enum OperationTypeEnum { /// <summary> /// Не выбрана /// </summary> OT_None = 0, /// <summary> /// Отправка данных с Arduino /// </summary> OT_SendDataFromArduino = 1, /// <summary> /// Отправка данных с PC /// </summary> OT_SendDataFromPC = 2, /// <summary> /// Получение данных с Arduino /// </summary> OT_ReciveDataFromArduino = 3, /// <summary> /// Получение данных с PC /// </summary> OT_ReciveDataFromPC = 4, /// <summary> /// Проверка вернувшихся данных с Arduino /// </summary> OT_CheckReturnFromArduino = 5, /// <summary> /// Проверка вернувшихся данных с PC /// </summary> OT_CheckReturnFromPC = 6, /// <summary> /// Старт ТПА /// </summary> OT_StartTPA = 7, /// <summary> /// Начало инициализации Arduino /// </summary> OT_InitArduinoBegin = 8, /// <summary> /// Инициализация Arduino /// </summary> OT_InitArduino = 9, /// <summary> /// Конец инициализации Arduino /// </summary> OT_InitArduinoFinish = 10, /// <summary> /// Начало настройки Arduino /// </summary> OT_SetupArduinoBegin = 11, /// <summary> /// Настройка Arduino /// </summary> OT_SetupArduino = 12, /// <summary> /// Конец настройки Arduino /// </summary> OT_SetupArduinoFinish = 13, /// <summary> /// Проверка соединения с Arduino /// </summary> OT_CheckConnectionFromArduino = 14, /// <summary> /// Проверка соединения с PC /// </summary> OT_CheckConnectionFromPC = 15, /// <summary> /// Команда ручного режима /// </summary> OT_CommandManualMode = 16, /// <summary> /// Стоп ТПА /// </summary> OT_StopTPA = 17 }; /// <summary> /// Переменная хранящая тип операции /// </summary> OperationTypeEnum operationType = OT_None; /// <summary> /// Перечисление - Тип параметра /// </summary> enum ParametrEnum { /// <summary> /// Не установлен /// </summary> P_None = 0 }; /// <summary> /// Перечисление - Тип данных параметра /// </summary> enum DataTypeEnum { /// <summary> /// Не указан /// </summary> DT_None = 0, /// <summary> /// Int /// </summary> DT_Int = 1 }; /****************************** /Переменные перечисления **********************************/ /****************************** Переменные для работы с Serial портом **********************************/ /// <summary> /// Константа - Скорость Serial порта /// </summary> #define speedSerial 115200 /****************************** /Переменные для работы с Serial портом **********************************/ /****************************** Переменные для работы передачи данных **********************************/ /// <summary> /// Константа - Символ новой строки /// </summary> #define newLine '\n' /// <summary> /// Константа - Символ начала пакета /// </summary> #define beginChar '?' /// <summary> /// Константа - Символ разделитель параметров пакета /// </summary> #define breakChar ';' /// <summary> /// Константа - Символ конца пакета /// </summary> #define endChar '!' /// <summary> /// Константа - Символ для очистки значения char /// </summary> #define clearChar '\0' /// <summary> /// Переменная хранящая завершено ли получения данных /// </summary> boolean finishReceiveData = false; /// <summary> /// Переменная хранящее номер пакета в системе /// </summary> int globalNumberPacket = 0; /// <summary> /// Переменная хранящая индекс текущего char в массиве хранящего пакет данных /// </summary> int indexCharInArray; /// <summary> /// Константа хранящая мак. размер массива хранящего пакет данных /// </summary> #define receiveSizeArrayCOM 40 /// <summary> /// Массив хранящий пакет данных /// </summary> char receiveCharArrayCOM[receiveSizeArrayCOM]; /// <summary> /// Переменная хранящее номер символа начала пакета в пакете /// </summary> int indexBeginCharInPackage; /// <summary> /// Переменная хранящее номер символа конца пакета в пакете /// </summary> int indexEndCharInPackage; /// <summary> /// Переменная кол-во символов разделителя блоков данных в пакете /// </summary> int breakCharCountInPackage; /// <summary> /// Переменная хранящее номер пакета /// </summary> String numberPacket; /// <summary> /// Переменная хранящее длину пакета /// </summary> String lenPacket; /// <summary> /// Переменная хранящее тип операции пакета /// </summary> String operationTypePacket; /// <summary> /// Переменная хранящее тип параметра пакета /// </summary> String parametrPacket; /// <summary> /// Переменная хранящее тип данных параметра пакета /// </summary> String dataTypePacket; /// <summary> /// Переменная хранящее значение параметра пакета /// </summary> String valuePacket; /// <summary> /// Переменная хранящее номер пакета /// </summary> int receiveNumberIntPacket = 0; /// <summary> /// Переменная хранящее тип операции пакета /// </summary> int receiveLenIntPacket; /// <summary> /// Переменная хранящее тип операции пакета /// </summary> int receiveOperationTypeIntPacket; /// <summary> /// Переменная хранящее реальную длину пакета, проверка после получения /// </summary> int receiveLength; /****************************** /Переменные для работы передачи данных **********************************/ /****************************** Переменные запуска ТПА **********************************/ /// <summary> /// Переменная хранящее состояние программы запущена или нет /// </summary> boolean beginWorkTPA = false; /// <summary> /// Переменная хранящее состояние программы инициализирован ли ТПА /// </summary> boolean beginInitializationTPA = false; /****************************** /Переменные запуска ТПА **********************************/ /****************************** Переменные отладки **********************************/ /// <summary> /// Переменная хранящее время старта цикла, для проверки скорости выполнения цикла /// </summary> unsigned long timeCycleWork = 0; #define IS_DEBUG true /****************************** /Переменные отладки **********************************/ /// <summary> /// Настройка программы /// </summary> void setup () { // TODO: Добавить описание switch (connectionType) { case CT_Serial: Serial.begin(speedSerial); break; } } /// <summary> /// Основной цикл /// </summary> void loop() { // Проверка времени обработки данных //timeCycleWork = millis(); // /Проверка времени обработки данных // Выбираем протокол передачи данных switch (connectionType) { case CT_Serial: // Протокол передачи данных на основе Serial порта // Получение данных с Serial порта GetReceiveDataFromSerial(); break; } // Завершено ли получения данных if (finishReceiveData) { // Парсинг полученных данных ParseReceiveData(); ClearReceiveDataVariable(); } // Проверка времени обработки данных //unsigned long workTime = millis() - timeCycleWork; //SerialPrintLine(workTime); // /Проверка времени обработки данных } /****************************** Функции работы с Serial портом **********************************/ /// <summary> /// Получение данных с Serial порта /// </summary> void GetReceiveDataFromSerial() { // TODO: Добавить описание while(Serial.available()) { /// <summary> /// Переменная хранящая последний полученный char /// </summary> char incomingChar = (char)Serial.read(); if(incomingChar != newLine && incomingChar != clearChar) { receiveCharArrayCOM[indexCharInArray] = incomingChar; indexCharInArray++; } else { receiveCharArrayCOM[indexCharInArray] = '\0'; finishReceiveData = true; } } } /// <summary> /// Отправка данных по Serial порту /// </summary> void SendDataFromSerial(String SendValue, int OperationType, int Parametr, int DataType) { // TODO: Добавить описание RepeatSendDataFromSerial(SendValue, OperationType, Parametr, DataType); } /// <summary> /// TODO: Добавить описание /// </summary> void SendConfirmationDataFromSerial(String SendValue, int Parametr, int DataType) { // TODO: Добавить описание RepeatSendDataFromSerial(SendValue, OT_CheckReturnFromArduino, Parametr, DataType); ClearReceiveDataVariable(); } /// <summary> /// Повтор отправки данных по Serial порту /// </summary> void RepeatSendDataFromSerial(String SendValue, int OperationType, int Parametr, int DataType) { // TODO: Добавить описание int sendValueLen = String(globalNumberPacket).length() + String(OperationType).length() + String(Parametr).length() + String(DataType).length() + SendValue.length() + 7; sendValueLen += String(sendValueLen).length(); String sendPacket = beginChar + String(globalNumberPacket) + breakChar + String(sendValueLen) + breakChar + OperationType + breakChar + Parametr + breakChar + DataType + breakChar + SendValue + endChar; SerialPrintLine(sendPacket); } void SerialPrintLine(String SendValue) { SendValue += newLine; Serial.print(SendValue); } /****************************** /Функции работы с Serial портом **********************************/ /****************************** Функции работы с полученными данными **********************************/ /// <summary> /// Очистка переменных получения данных, после получения данный /// </summary> void ClearReceiveDataVariable() { // TODO: Добавить описание indexCharInArray = 0; memset(receiveCharArrayCOM,clearChar,receiveSizeArrayCOM); indexBeginCharInPackage = indexEndCharInPackage = breakCharCountInPackage = receiveLenIntPacket = receiveLength = receiveOperationTypeIntPacket = 0; numberPacket = lenPacket = operationTypePacket = parametrPacket = dataTypePacket = valuePacket = ""; finishReceiveData = false; } /// <summary> /// Парсинг полученных данных /// </summary> void ParseReceiveData() { // Завершено ли получения данных if (finishReceiveData) { // Получаем длину пакета receiveLength = strlen(receiveCharArrayCOM); // Пробегаемся по массиву хранящий пакет данных for (int i = 0; i < receiveLength; i++) { switch (receiveCharArrayCOM[i]) { case beginChar: // Получаем индекс символ начала пакета в пакете indexBeginCharInPackage = i; break; case endChar: // Получаем индекс символ конца пакета в пакете indexEndCharInPackage = i; break; case breakChar: // Получаем кол-во символов разделителя блоков данных в пакете breakCharCountInPackage++; break; default: // В зависимости от номера блока данных в пакете, заполняем переменные блоков switch (breakCharCountInPackage) { case 0: // Номер пакета в системе numberPacket += receiveCharArrayCOM[i]; break; case 1: // Длина пакета lenPacket += receiveCharArrayCOM[i]; break; case 2: // Тип операции пакета operationTypePacket += receiveCharArrayCOM[i]; break; case 3: // Тип операции пакета parametrPacket += receiveCharArrayCOM[i]; break; case 4: // Тип данных параметра пакета dataTypePacket += receiveCharArrayCOM[i]; break; case 5: // Значение параметра пакета valuePacket += receiveCharArrayCOM[i]; break; } break; } } if (breakCharCountInPackage != 5) { ErroreDataReceived("2",1); return; } // Номер пакета в системе, Int receiveNumberIntPacket = numberPacket.toInt(); // Номер пакета в системе, Int receiveLenIntPacket = lenPacket.toInt(); // Тип операции пакета, Int receiveOperationTypeIntPacket = operationTypePacket.toInt(); //SerialPrintLine(String(indexBeginCharInPackage)); //SerialPrintLine(String(breakCharCountInPackage)); //SerialPrintLine(String(indexEndCharInPackage)); //SerialPrintLine(String(receiveLength)); //SerialPrintLine(String(receiveLenIntPacket)); // Проверка корректности полученного пакета if (indexBeginCharInPackage == 0 && breakCharCountInPackage == 5 // Индекс символа начала пакета, должен быть равен 0, кол-во блоков данных должно быть 5 && indexEndCharInPackage == (receiveLength - 1) // Индекс символа конца пакета, должен быть равен значению блока длины пакета - 1 && receiveLenIntPacket == receiveLength // Реальная длина пакета должна быть равна значению блока длины пакета ) { // Определяем тип операции пакета switch(receiveOperationTypeIntPacket) { case OT_StartTPA: // Старт ТПА SendConfirmationDataFromSerial(valuePacket, parametrPacket.toInt(), dataTypePacket.toInt()); // Устанавливаем состояние программы запущена beginWorkTPA = true; break; default: int globalNumberPacketAndOne = globalNumberPacket;//globalNumberPacket + 1; if (receiveOperationTypeIntPacket != OT_CheckReturnFromPC && receiveNumberIntPacket == globalNumberPacketAndOne) { SendConfirmationDataFromSerial(valuePacket, parametrPacket.toInt(), dataTypePacket.toInt()); } else { if (receiveNumberIntPacket == globalNumberPacketAndOne) { switch(receiveOperationTypeIntPacket) { case OT_StopTPA: // Стоп ТПА SendConfirmationDataFromSerial(valuePacket, parametrPacket.toInt(), dataTypePacket.toInt()); //globalNumberPacket = 0; // Устанавливаем состояние программы остановлена beginWorkTPA = false; break; } } else { ErroreDataReceived("3",0); } } break; } } else { ErroreDataReceived("4",0); } } } /****************************** /Функции работы с полученными данными **********************************/ /****************************** Функции работы с ошибками **********************************/ /// <summary> /// Ошибка получения данных /// </summary> void ErroreDataReceived(String DataReceived, int TypeErrore) { // TODO: Добавить описание ClearReceiveDataVariable(); Errore(DataReceived, 0); } /// <summary> /// Ошибка получения данных /// </summary> void Errore(String Data, int TypeErrore) { // TODO: Добавить описание //FullStopTPA(); Log(Data, TypeErrore, true); } /****************************** /Функции работы с ошибками **********************************/ /****************************** Функции работы с логом **********************************/ /// <summary> /// Логирование /// </summary> void Log(String Data, int TypeLog, bool isErrore) { // TODO: Добавить описание switch (connectionType) { case CT_Serial: SerialPrintLine(Data); break; } } /****************************** /Функции работы с логом **********************************/Работает стабильно.
Пока не дополнял доп. проверками и не наводил порядок.
Но ошибка, есть, но на стороне программы C#.
После внимательного изучения, понял, что проблема в потоках и взаимодействии между ними на стороне программы C#.
Нашел ошибку у себя в C#, кроме потоков. Неправильная инициализация соединения.
Как должно быть.
ArduinoSerialPort.PortName = "COM25"; ArduinoSerialPort.BaudRate = 115200; ArduinoSerialPort.Parity = Parity.None; ArduinoSerialPort.DataBits = 8; ArduinoSerialPort.StopBits = StopBits.One; ArduinoSerialPort.Encoding = Encoding.UTF8; ArduinoSerialPort.NewLine = "\n"; ArduinoSerialPort.RtsEnable = false; ArduinoSerialPort.DtrEnable = true; ArduinoSerialPort.DataReceived += new SerialDataReceivedEventHandler(ArduinoSerialPort_DataReceived); ArduinoSerialPort.Open();Не хватало
ArduinoSerialPort.RtsEnable = false;
ArduinoSerialPort.DtrEnable = true;
Если не установить эти настройки, будут проблемы. При первой отправке на ардуину к запросу будет добавляться мусор, могут быть и другие проблемы.