uart протокол общения мк с пк - есть ли что готовое?

kalobyte

★★★✩✩✩✩
1 Янв 2020
724
148
я нашел только https://github.com/thijse/Arduino-CmdMessenger
есть биндинги на шарпе

а есть ли что подобное еще, чтобы был биндинг на шарпе

смотрел библиотеку arest, но на 328 меге она не будет работать со строками и флоатами, слишком много озу жрет
и вроде бы сам контроллер не может делать запросы, только к контроллеру можно и получать ответ в виде джейсона

не хочется выдумывать каждый раз велик, хочется быстро накидать протокол под конкретное устройство и софт для виндовса
 

kalobyte

★★★✩✩✩✩
1 Янв 2020
724
148
оно же для аппаратного управления только
строки и флоаты оно предает?
 

kalobyte

★★★✩✩✩✩
1 Янв 2020
724
148
@Эдуард Анисимов,

зацени, как красиво все сделано
не надо никаких структур и каждый раз изобретать парсер

C++:
// *** SendandReceiveArguments ***

// This example expands the previous SendandReceive example. The Arduino will now receive multiple
// and sent multiple float values.
// It adds a demonstration of how to:
// - Return multiple types status; It can return an Acknowlegde and Error command
// - Receive multiple parameters,
// - Send multiple parameters
// - Call a function periodically

#include <CmdMessenger.h>  // CmdMessenger

// Blinking led variables
unsigned long previousToggleLed = 0;   // Last time the led was toggled
bool ledState                   = 0;   // Current state of Led
const int kBlinkLed             = 13;  // Pin of internal Led

// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger = CmdMessenger(Serial);

// This is the list of recognized commands. These can be commands that can either be sent or received.
// In order to receive, attach a callback function to these events
enum
{
  // Commands
  kAcknowledge         , // Command to acknowledge that cmd was received
  kError               , // Command to report errors
  kFloatAddition       , // Command to request add two floats
  kFloatAdditionResult , // Command to report addition result
};

// Commands we send from the PC and want to receive on the Arduino.
// We must define a callback function in our Arduino program for each entry in the list below.

void attachCommandCallbacks()
{
  // Attach callback methods
  cmdMessenger.attach(OnUnknownCommand);
  cmdMessenger.attach(kFloatAddition, OnFloatAddition);
}

// ------------------  C A L L B A C K S -----------------------

// Called when a received command has no attached function
void OnUnknownCommand()
{
  cmdMessenger.sendCmd(kError,"Command without attached callback");
}

// Callback function that responds that Arduino is ready (has booted up)
void OnArduinoReady()
{
  cmdMessenger.sendCmd(kAcknowledge,"Arduino ready");
}

// Callback function calculates the sum of the two received float values
void OnFloatAddition()
{
  // Retreive first parameter as float
  float a = cmdMessenger.readFloatArg();
 
  // Retreive second parameter as float
  float b = cmdMessenger.readFloatArg();
 
  // Send back the result of the addition
  //cmdMessenger.sendCmd(kFloatAdditionResult,a + b);
  cmdMessenger.sendCmdStart(kFloatAdditionResult);
  cmdMessenger.sendCmdArg(a+b);
  cmdMessenger.sendCmdArg(a-b);
  cmdMessenger.sendCmdEnd();
}

// ------------------ M A I N  ----------------------

// Setup function
void setup()
{
  // Listen on serial connection for messages from the pc
  Serial.begin(115200);

  // Adds newline to every command
  cmdMessenger.printLfCr();   

  // Attach my application's user-defined callback methods
  attachCommandCallbacks();

  // Send the status to the PC that says the Arduino has booted
  cmdMessenger.sendCmd(kAcknowledge,"Arduino has started!");

  // set pin for blink LED
  pinMode(kBlinkLed, OUTPUT);
}

// Returns if it has been more than interval (in ms) ago. Used for periodic actions
bool hasExpired(unsigned long &prevTime, unsigned long interval) {
  if (  millis() - prevTime > interval ) {
    prevTime = millis();
    return true;
  } else     
    return false;
}

// Loop function
void loop()
{
  // Process incoming serial data, and perform callbacks
  cmdMessenger.feedinSerialData();

  // Toggle LED periodically. If the LED does not toggle every 2000 ms,
  // this means that cmdMessenger are taking a longer time than this 
  if (hasExpired(previousToggleLed,2000)) // Toggle every 2 secs
  {
    toggleLed(); 
  }
}

// Toggle led state
void toggleLed()
{ 
  ledState = !ledState;
  digitalWrite(kBlinkLed, ledState?HIGH:LOW);
}
 

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,407
976
58
Марий-Эл
Не очень красиво.
И при структуре парсер не нужен. Отправил структуру как массив байт. Принял структуру как массив байт. Обратился к структуре на стороне приёмника как с структуре, получил данные. И не ограничен никакими рамками протоколов.
 

kalobyte

★★★✩✩✩✩
1 Янв 2020
724
148
а есть где какой пример? я много времени потратил и не нашел ничего
только быдлокод как включить светодиод кнопкой на компутере
 

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,407
976
58
Марий-Эл
Самое простое на вскидку. Со стороны передатчика.
C++:
struct tSend
{
  int ValueInt;
  char ValueInt8;
  float ValueFloat;
};


void setup() {

  Serial.begin(9600);
  tSend SendBuff;
  tSend* p = &SendBuff;

  p->ValueInt = 8;
  p->ValueInt8 = 'B';
  p->ValueFloat = 25.234;

  Serial.write((uint8_t* ) p, sizeof(SendBuff));
}

void loop() {
  // put your main code here, to run repeatedly:

}
Со стороны приёмника нужно принять в такую же точно структуру. И можно спокойно вычитывать из неё принятые переменные.
С указателями я мог напутать. Но, вроде, отсылается правильно.
 

Kir

★✩✩✩✩✩✩
28 Мар 2020
69
16
Самое простое на вскидку. Со стороны передатчика.
C++:
struct tSend
{
  int ValueInt;
  char ValueInt8;
  float ValueFloat;
};


void setup() {

  Serial.begin(9600);
  tSend SendBuff;
  tSend* p = &SendBuff;

  p->ValueInt = 8;
  p->ValueInt8 = 'B';
  p->ValueFloat = 25.234;

  Serial.write((uint8_t* ) p, sizeof(SendBuff));
}

void loop() {
  // put your main code here, to run repeatedly:

}
Со стороны приёмника нужно принять в такую же точно структуру. И можно спокойно вычитывать из неё принятые переменные.
С указателями я мог напутать. Но, вроде, отсылается правильно.
Это пример сериализации, ничего более, а пример выше, который "не красивый", это механизм построение обработчиков реакции на события (похож на шаблон проектирования Command), в данном случае на последовательном порте, через строки. Вы предлагаете в бинарном виде предлагаете передавать и принимать данные, ну одно другому не мешает.
 

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,407
976
58
Марий-Эл
@Kir, Не бинарный. Побайтно.
Плюс этого метода, без разницы что передавать.
Минус. Можно легко запутаться в указателях.

И это только реализация самой передачи.
В компиляторе MikroElektronika есть библиотека RS485. Только она передаёт не более 3х байт.
Я такую библиотеку реализовал на C для PIC16 очень давно, когда ещё начинал вспоминать программирование.
Её можно расширить до нужного количества байт. А передавать через неё всё ту же структуру.
 

kalobyte

★★★✩✩✩✩
1 Янв 2020
724
148
ну ладно
а если в структуре есть текстовое поле, но не строка с нуль терминатором и она разной длины?
как приемник узнает ее длину?
а если 2-3 текстовых поля? нужно тогда обязательно передавать как строку?

в моем примере там передаются разделительные символы
 

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,407
976
58
Марий-Эл
@kalobyte,
Serial.write((uint8_t* ) p, sizeof(SendBuff))
В этой строке функции write передаётся не сама структура, а только её адрес расположения в памяти. Фунция sizeof() предварительно узнаёт какого размера будет передаваемый массив. Поэтому содержимое структуры, её размер и всё остальное не имеет значения, но при одном условии, что сама функция write() не имеет каких либо ограничений. Но я такой информации нигде не находил. Вот буфер UART ограничен 64 байтами, вроде, поэтому на стороне пиёмника нужно стараться считывать его вовремя.
 

Kir

★✩✩✩✩✩✩
28 Мар 2020
69
16
@Kir, Не бинарный. Побайтно.
Плюс этого метода, без разницы что передавать.
Минус. Можно легко запутаться в указателях.

И это только реализация самой передачи.
В компиляторе MikroElektronika есть библиотека RS485. Только она передаёт не более 3х байт.
Я такую библиотеку реализовал на C для PIC16 очень давно, когда ещё начинал вспоминать программирование.
Её можно расширить до нужного количества байт. А передавать через неё всё ту же структуру.
Бинарное - это значит чистые данные из памяти, без преобразования (дамп, байты - называйте как хотите). Строки, кстати, тоже из байт состоят, и сама передача строк от массива или структур принципиально не отличается. Так что, боюсь вы не поняли, что я имел ввиду.

а если в структуре есть текстовое поле, но не строка с нуль терминатором и она разной длины?
как приемник узнает ее длину?
а если 2-3 текстовых поля? нужно тогда обязательно передавать как строку?
Структура должна быть четко определена и конечна по размеру. Приемник должен "знать" структуру (т.е. в приемнике определена точно такая же структура данных как и в передатчике). Но, тут есть один момент, если приемник и передатчик на разных процессорах и имеют разный порядок байт, то будут проблемы, хотя в вашем случае это мало вероятно, но, пример может быть следующий, если обработкой данных занимается программа выполняющаяся на Java машине, то будут проблемы, так Java-машина имеет другой порядок байт нежели AVR или arm, а значит придется дополнительные манипуляции производить.
 

Эдуард Анисимов

★★★★★★✩
23 Сен 2019
2,407
976
58
Марий-Эл
Бинарное - это значит чистые данные из памяти, без преобразования
Здесь непонятки идут от разных школ. Со временем понятия трансформируются, не будем придираться к словам.
Структура должна быть четко определена и конечна по размеру. Приемник должен "знать" структуру
Я не заострял на этом внимание. Каюсь.
Да изначально подразумевается, что структура конечна и приёмник с передатчиком должны пользоваться одинаковой структурой.
И Вы правильно подметили, что таким способом можно передавать данные только между контроллерами с одинаковой разрядностью. Т.К. действительно на разной разрядности разные переменные имеют разную длину в байтах, кроме char или byte.
Если учитывать и это, может проще переводить данные в строковый формат, а потом передавать?
 

kalobyte

★★★✩✩✩✩
1 Янв 2020
724
148
@Эдуард Анисимов,
я понял твой код, ты не понял мой вопрос
меня интересует, как приемная сторона узнает размер принятых данных? допустим буфер 64 и я это знаю или переопределил буфер побольше
но у меня размер всей структуры может быть 50 байт, а в другой раз 55

допустим на приемной стороне заполнился буфер 64
как теперь от туда достать данные, если приемник ничего не знает о конце данных
ведь можно считать 64 байта и в конце будут нули

как из этого набора данных теперь получить 2 строки, которые всегда разной длины могут быть?
для этого же есть признак конца строки, но в ардуине есть целый класс для работы со строками и там непонятна структура этого класса
а можно просто сделать массив чаров, но там не будет вроде \0?

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

у тебя на стороне приемника просто есть функции чтение инт, байт, стринг и оно там как-то работает
я вчера так проглядел быстренько, но там используются шаблоны с++, а я уже не помню, что это и как работает
 

kalobyte

★★★✩✩✩✩
1 Янв 2020
724
148
Clipboard01.png

чтобы было понятно, о чем я говорю про разные длины строкового массива
это имя и фамилия в скриншоте
в структуре 4 поля
байт
стринг
лонг инт
бул или байт
 

Kir

★✩✩✩✩✩✩
28 Мар 2020
69
16
Посмотреть вложение 11986

чтобы было понятно, о чем я говорю про разные длины строкового массива
это имя и фамилия в скриншоте
в структуре 4 поля
байт
стринг
лонг инт
бул или байт
Вариант первый: выделяй под каждую запись (структуру) для строковых полей достаточное коли - во байт, скажем 32 или 64. Да, избыточно, но будет работать.
Вариант 2, в начало структуры (самое первое поле) указывай размер структуры, вместо строковых полей передавай смещение от начала массива строк (типа указатели).

C++:
struct {
    uint32_t total_size;
    uint32_t id;
    uint16_t first_name;
    uint16_t second_name;
    uint8_t age;
    char strings[0];
};
Думаю все понятно, кроме строк. Итак, допустим у тебя что массив strings[] = "Ivan\0Petrov\0", тогда поле first_name = 0, а second_name = 5. Так как по смещению 5 от начала strings будет идти "Petrov". А total_size = 25 байт.

Вариант 3: не привязываться к строкам, а к ID на 32 или 64 бита (чтобы прям на пару веков хватило). А имена сопоставляй по базе, где бы она не была.
 

kalobyte

★★★✩✩✩✩
1 Янв 2020
724
148
Вариант 2, в начало структуры (самое первое поле) указывай размер структуры,
ну да, я думал, что где-то придется указывать размер
А имена сопоставляй по базе, где бы она не была.
я отказался от этого как раз, все хранится в епром контроллера или отдельной мс и контроллер автономный полностью

Итак, допустим у тебя что массив strings[] = "Ivan\0Petrov\0"
на самом деле там имя и фамилия это одно поле
strings[] = "Ivan Petrov\0"

предлогаеш добавлять конец строки руками и на приемнике потом считывать в цикле до конца терминатора?

неуж то до сих пор ничего не придумали универсального? ну стандартная же вещь - передача данных между мк и пк
 

Kir

★✩✩✩✩✩✩
28 Мар 2020
69
16
на самом деле там имя и фамилия это одно поле
strings[] = "Ivan Petrov\0"
Я просто привел пример как в таком случае с 2-мя или более строковыми полями работать.

предлогаеш добавлять конец строки руками и на приемнике потом считывать в цикле до конца терминатора?
Если у тебя в структуре 2 и более строковых поля, как в моем примере, разделенные \0-м, тогда нельзя на него ориентироваться, иначе потеряешь остальные строки.

неуж то до сих пор ничего не придумали универсального? ну стандартная же вещь - передача данных между мк и пк
Есть стандартизированные протоколы уровнем выше, но такие вещи, как правило, под конкретные задачи делаются.
 

kostyamat

★★★★★★✩
29 Окт 2019
1,098
632
@kalobyte, ок. А почему бы все данные, кроме ФИО, не передать структурой, а ФИО отдельно следующей строкой, или даже в той же, поставив какой-то разделитель перед строковых выводом?

И ещё одно, просто как ремарка, в свое время игрался классом String в структурах, тоже это дело в ЕЕПРОМ писал. Получал очень странные результаты. Лучше String там не использовать, а использовать стандартные С строки, фиксированной длины. Иначе потом концов не найдешь.
 
Изменено:

Kir

★✩✩✩✩✩✩
28 Мар 2020
69
16
@kalobyte, ок. А почему бы все данные, кроме ФИО, не передать структурой, а ФИО отдельно следующей строкой, или даже в той же, поставив какой-то разделитель перед строковых выводом?

И ещё одно, просто как ремарка, в свое время игрался классом String в структурах, тоже это дело в ЕЕПРОМ писал. Получал очень странные результаты. Лучше String там не использовать, а использовать стандартные С строки, фиксированной длины. Иначе потом концов не найдешь.
Так никто и не предлагал использовать класс String.

в свое время игрался классом String в структурах, тоже это дело в ЕЕПРОМ писал. Получал очень странные результаты
Оно и не удивительно, так как класс работает через аллокаторы malloc. И сохраняя структуру объекта в EEPROM при сбросе питания строка терялась, а указатель восстановленный из EEPROM указывал на какую-нибудь дичь.
 

kostyamat

★★★★★★✩
29 Окт 2019
1,098
632
@Kir, так и я не предлагаю. Написано же "как ремарка". У топик-стартера пробегало про String в комментариях, вот я и написал, что с ними вообще возится не стоит в принципе.
 

kalobyte

★★★✩✩✩✩
1 Янв 2020
724
148
А почему бы все данные, кроме ФИО, не передать структурой, а ФИО отдельно следующей строкой, или даже в той же, поставив какой-то разделитель перед строковых выводом?
нарушается структура данных
проще говоря - это быдлокод

Так никто и не предлагал использовать класс String.
просто массив чар что ли?
а завершение строки компилятор сам добавляет?

для хранения таких данных есть хорошая библиотека с апи, как будто ты работаеш с базой данных
можно извлекать целый массив данных по номеру записи

правда я не в курсе, вроде бы она поддерживает разную длину строк, хотя я вроде как решил сделать одинаковую длину
наверное так проще будет