Помогите с отправкой команд по UART

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

Здравствуйте уважаемые. Надеюсь на вашу помощь ибо я дал себе правило, обращаться сюда на крайний случай. Для меня он настал. И не исключено что я просто где-то подтупливаю.

Есть 3 платы. Первая - пульт управления, передает команды второй. Вторая плата передает команды третей и отчитывается первой. 

Все используют программный и аппаратный UART в следующей связке: [DEBUG_PC]-HARDWARE-><-HARDWARE-[1]-SOFTWARE-><-HARDWARE-[2]-SOFTWARE-><-SOFTWARE-[3] 

Ранее использовал конкретно аппаратный uart но из-за проблем с отладкой и перекрестным подключением двух устройств, я подключил программный. Но вот кажется сам загнал себя в угол теперь думаю что делать.

Команды управления передаются в следующем виде TX0000XT где TX-XT - метки начала-конца команд. Где 0000 - команда.

Время от времени порт "хавает" первые два, а то и три символа! И я уже устал бороться с этой проблемой. Я собираю с помощью Serial.avaible и read данные из аппаратного буфера. в общую строковую переменную. Из длинной строки вырезаю 8 символов точно зная что там есть команда, а уж потом сверяю есть ли там метки. Если они есть и на месте, запускаю исполнение команды. И так пока строковая переменная - буфер не очиститься "откусыванием" по 8 смволов за каждый цикл. Этим я избавился от слипшихся команд. Но стоит потеряться хоть одному символу в начале - все! идет сдвиг и вся очередь пошла с рельс в речку.

Позже после смены кода (из-за которго поменялась скорость работы устройства) началась другая беда. Отпадали концы команд. Я понял что проверять буфер каждый цикл не есть правильно и сделал проверку в каждую секунду, надеясь что за секунду успеет дойти все что нужно. Обрадовался. Но не надолго. Через время выявил что команды все равно бьются. В основном в начале. Редко в конце.

Я не прошу вас придумывать мне код. Я хочу попросить вас подсказать мне как поступить чтобы избежать потерь на приемо-передаче. Алгоритм, на словах. 

Прерывание по UART - пишу сразу не вариант.

До этого пробовал:

Поднять baud до 115200 и опустить до 2400. Установить сигнал приема-передачи чтобы программа остановилась и слушала (типа прерывание). Замедление работы программы. Делать аккумулирующие символы пробелов в начале и конце чтобы не достало до команды. Все-равно режет и еще хуже.

Ни один из способов не подошел.

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

И serialEvent пробовал. Терялись целые команды.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Свой код приведите, плз. Потому как я ни разу не замечал, чтобы Atmega как-то самопроизвольно чего-то там кусала или откусывала: если байт пришёл в аппаратный UART и успел отработаться в обработчике прерывания - то он не растворяется в небытие. Тем более, что искаропки в ардуино есть буфер ажно на целых 64 байта для UART. Так что если что-то куда-то пропадает - то скорее всего, что проблемы надо искать прежде всего в написанном коде, а уже потом - думать дальше.

В общем, код - в студию, пожалуйста.

Logik
Offline
Зарегистрирован: 05.08.2014

UserDead пишет:

Из длинной строки вырезаю 8 символов точно зная что там есть команда, а уж потом сверяю есть ли там метки. Если они есть и на месте, запускаю исполнение команды.

А если нет? Надо начинать выбирать по 1 символу в поиске стартовой метки. Плохо что она совпадает с финишной. Все уже украдено до нас, выбирайте из стандартных https://ru.wikipedia.org/wiki/Управляющие_символы 

 

UserDead пишет:

 Но стоит потеряться хоть одному символу в начале - все! идет сдвиг и вся очередь пошла с рельс в речку.

Ну да. Рассинхронизация протокола.

Еще CRC штука полезная. У вас в каналах похоже весело.

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

Код 400 строк. Поэтому я приведу функцию которая отвечает за то самое чтение. Вызывалась ранее каждый цикл. Теперь каждую секунду с посмощью millis()

void reaqdSerialDatax () {
  rxCheck = ""; //пре-буфферная переменная string
  rxData = ""; //переменная с с которой идет выполнение. в нее копируются нормальные команды прошедшие проверку.
  netDatax = ""; //содержимое для команды
  netComm = ""; //содержимое для команды
  netCommdigit = 0; //содержимое для команды
  delay(50);
  while (Serial.available())    //if we have something on serial buffer
  {
    char buff = Serial.read();     //read that
    rxCheck += buff;  //add to string
  }
  while (softSerial.available())    //if we have something on serial buffer
  {
    char buff = softSerial.read();     //read that
    rxCheck += buff;  //add to string
  }
if(rxCheck.startsWith("TX") == false && rxCheck.length() != 0){ //bad packet filter - фильтр плохих пакетов
  aSerial.level(Level::v).println("bad packet - removed").p(rxCheck);
  rxCheck = "";
  delay(50);
  errorCounter++; //счетчик ошибок
}
bufferCommL1 += rxCheck; // копируем в общий буфер если прошло проверку.
if(bufferCommL1 != "") aSerial.level(Level::vv).p("bufferCommL1 = ").p(bufferCommL1);
if(bufferCommL1.length() >= 8){  //меряем длину
if(bufferCommL1.endsWith("XT") == true){
bufferCommL2 = bufferCommL1.substring(0, 8);  //копируем кусок команды из общего буферного потока
bufferCommL1.remove(0, 8); удаляем скопированное
if(bufferCommL1.length() < 8 && bufferCommL1.length() != 0){ //если остаток менее 8 и не равен 0 то там остатки которые не подлежат исполнению
  aSerial.level(Level::v).p(bufferCommL1.length()).pln(bufferCommL1).pln(bufferCommL1).pln("bufferCommL1 has short corrupted command");
  bufferCommL1 = "";
  rxCheck = "";    
  errorCounter++;
}
rxData = bufferCommL2; //присваиваем в исполнительную переменную буфер с целой командой
bufferCommL2 = "";
}
}
  compare = 70;
  if (rxData.startsWith("TX") == true) compare = 0;
  else if (rxData.startsWith("TX") == false) compare = 69;
  if (rxData.endsWith("XT") == true) compare = 0;
  else if (rxData.startsWith("XT") == false) compare = 69;
  if (compare == 0) //if compare success
  {
// проверили чтометки на местах (ранее использовал substring и compareTo но отказался. Переменная compare осталась как флаг
    rxCheck = rxData.substring(6); //copy a ner end mark string portion to another variable
    compare = rxCheck.compareTo("XT"); //cpmpare to
    if (compare == 0) //if start and end marks are ok
    {
      //packet valid, continue...
      if (rxData.substring(2, 3) == "2") //Read device address
      {
        netComm = rxData.substring(3, 5); //current number or command
        netCommdigit = netComm.toInt();
        netCommdigitconv = netCommdigit - 20;
        netDatax = rxData.substring(5, 6); //this is define command or req
        if (netCommdigit >= 20 && netCommdigit <= 40)
        {
          if (netDatax == "1" || netDatax == "0") {
            //this is a command
            SRead[netCommdigitconv] = netDatax.toInt();
            if (netDatax == "1") {
              SwitchHandler(netCommdigitconv, 0);
              delay(50);
              SwitchHandler(netCommdigitconv, 1);
              DevTime[netCommdigitconv] = millis();
              zeroTime[netCommdigitconv] = millis();
            }
            if (netDatax == "0") {
              SwitchHandler(netCommdigitconv, 0);
            }
// дальше идет распознание комманд и их выполнение.

 

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

Да я вот уже подумал слать по ответу ОК. Тоесть если мк отправил он ждет подтверждения. Если пришло что-то непонятное или error то переслать команду до тех пор пока не прийдет OK. Это пока единственное что пришло мне в голову сегодня.

Проблема в том что если команды сильно будут биться, то канал просто загрузнет в ошибках передачи как и в случае с crc

Logik
Offline
Зарегистрирован: 05.08.2014

То не проблема. То обнаружится и полечится. Проблема если команда "поворот налево" исказится и восприймется как "уничтожить цивилизацию"  ;))

Ну в принципе подтверждение получения - вариант. Но CRC тоже. В общем добро пожаловать в чарующий мир протоколов ))) 

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

Спасибо. И Спасибо что уделили время.

Мысль вот вот пришла. А как вот вы представляете обмен сообщениями с вычислением crc если его тоже передавать. Тем же самым каналом. Ох...

Logik
Offline
Зарегистрирован: 05.08.2014

Ну я обычно делаю типа такого: <стартовый байт> <сообщение> <crc>. Если сообщение прерменной длины то <стартовый байт> <длина> <сообщение> <crc включая длину>.

Алгоритм приема так.

1.Если не в синхронизации, например при запуске - прием по байту в ожидании стартового. Полезно изредка чегото отвечать, типа WAIT. Чтоб понимать что на том конце вобще еще шото живо.

2.После появления стартового - прием всего сообщения + crc по фиксированой длинне или прием длины а затем  прием всего сообщения + crc 

3. Проверка crc. Если совпало - считаем что теперь протокол в синхронизации и отвечаем OK, иначе отвечаем ERROR_CRC и на п.1

4. Прием стартовый байт+сообщение+crc или для переменной длины  стартовый байт+длинна, а затем сообщение+crc. Стартовый байт проверяем сразу, если не совпал -  ответить типа ERROR_SYN и п.1

5.  на п.3

Если передатчик получил в ответ: ОК - считаем что команда прошла, если ERROR_CRC- не прошла и надо отправить повторно, если ERROR_SYN - все плохо, сразу отправляем пустые команды до получения ОК.

То, что CRC идет по ненадежному каналу - естественно и приемлемо. Возможна конечно ситуация сообщение верное а ошибка в самой CRC. Ну ниче, повторно отправит. 

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

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

Заинтриговали вы меня с контрольной суммой.

Буду пробовать на свежую голову. Посмотрим сколько коррекций будет на канале за час работы))

Еще раз Спасибо.

nik182
Offline
Зарегистрирован: 04.05.2015

В аналогичной ситуации, устав бороться с протоколами обмена на RS232 я попробовал MODBUS. Сейчас перевёл на него все обмены. Даже мелкие. И на RS485.  Рекомендую.

diakin
diakin аватар
Offline
Зарегистрирован: 04.06.2016

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

зы. у меня засада была в том, что помехи тупо вырубали USB и обмен с компом останавливался.

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

Остаток - я уверен на 60% что он битый. Я сделал очистку сразу как подумал об этом методе обработке с буферизацией команд. Вот в чем дело:

Устройство [3] является управляющим, а устройство [2] командующим.

[2] Выполняет свою программу и решает что когда переключить отдавая команды в [3].

Он шлет все попакетно. Отдельно. К примеру TX3231XT (прошло 2 секунды) TX3221XT (прошло пол секунды) TX3251XT (прошло 0,8 секунды) TX3211XT (Прошло 10 секунд) TX3331XT И так далее. Устройсво [2] замеряет время на контактных датчиках и делавет вывод когда и что переключить по получению сигнала а работает все в проверке через for. Поэтому паузы между командами вальируются от 0,5 до 20 секунд.

Теоритически если в буфере осталось TX32 после извлечения из всего потока. То команда никак не дополниться. Позже я подвердил свою теорию. И начиналось нечто вроде

TX3333XTTX22TX3211XT

            ^^^^

Мне важно не терять пакеты даже если отгрызать символы до следующей метки. Ибо есть спец команда которая если не учтется - начнет рушить все вокруг))) 

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

Попытался переписать алгоритм приема-передачи. После отправки команды ожидается цифра длины полученной строки. Если она не равна восьми то переслать ее заново пока не будет 8. Проблема не заставила себя ждать. Я сделал цикл ожидания ответа в функции отправки команды. И получил бесконечное количество ошибок с переполнением буфера.

Кусок отвечающий за передачу:

void writeSerialDatax(String addr, String comm, String datax) {
  netOutbuffer = "";     //reset data in buffer before re-assembling new params
  int tS2 = comm.toInt();
  netOutbuffer = "TX" + addr + tS2 + datax + "XT";  //собираем команду
  softSerial.flush();
  softSerial.print(netOutbuffer);
while(netOutbuffer.length() != 0)
{
  delay(10);
  while (Serial.avaible())    //if we have something on serial buffer
  {
    char buff = softSerial.read();     //read that
    rxCheck += buff;  //add to string
  }
  aSerial.level(Level::v).p("rxCheck - ").pln(rxCheck).p("sentDatax_length - ").pln(rxCheck.length());
  if(rxCheck.toInt() == netOutbuffer.length()) netOutbuffer = "";
  if(rxCheck.toInt() != netOutbuffer.length()) softSerial.print(netOutbuffer);  

}
}

И то что принимает:

while (softSerial.available())    //if we have something on serial buffer
  {
    char buff = softSerial.read();     //read that
    rxControl += buff;  //add to string
  }
  if (rxControl.length() != 0){
if(rxControl.startsWith("TX") == true && rxControl.endsWith("XT") == true){ rxCheck = rxControl; softSerial.print(rxControl.length());}    //if command valid
if(rxControl.startsWith("TX") == false || rxControl.endsWith("XT") == false) {softSerial.print(rxControl.length()), aSerial.level(Level::v).p("command receive - ").pln(rxControl), rxControl = "";} //if command corrupted
bufferCommL1 += rxCheck;

Немного "быдлокодно" но оптимизация чуть позже.

И вот как дальше, думать еще километров 10 пешком)))

Про ModBus посмотрел, почитал. Не в ник что-то. Понял что даные передаются регистрами которые как я понял и есть содержимое. Данные нужно преобразовывать. В общем для меня стало сложно и я пока отложил этот вопрос.

Вычислять crc пока не буду. 

Пришла мысль что команды кусает именно while. Мол МК замялся в передаче, сделал передышку, а тот принял за конец передачи. Если бы постоянно снимать данные с uart тогда стало бы что-то ясно. Но пока не придумаю как именно постоянно опрашивать буфер uart не тормозя сильно основную программу.

diakin
diakin аватар
Offline
Зарегистрирован: 04.06.2016

Ну то есть команды передаются в виде TX0000XT, по одной команде за раз. Минимальный интервал 0.5 сек.
loop видимо крутится быстрее, чем 0.5 сек. Поэтому в loop 1 раз делается проверка буфера порта. Если в буфере что-то есть - то начинается чтение буфера до победного конца, то есть пока не будет считана целиком последовательность TX0000XT.
Поэтому условие должно быть не while (softSerial.available()) ,  а

if (softSerial.available()>0)

{
while (softSerial.available() < 8 байт)
{ delay 100; } // ждем пока в буфере не накопится 8 байт

Потом считываем из буфера порта 8 байт и проверяем, что последние 2 - это "XT".

}

ну как-то так. А то действительно байты в порт поступают медленнее, чем считываются и оно вываливается из while (softSerial.available()) не дождавшись конца передачи.

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

diakin, Благодарю за подсказку. Я чего-то не додумался дожидаться накопления команды в буфере. 

Я как-то выводил значение Serial.available и там было то 1 то 2. Двойка чаще. такое ощущение что оно так кусочками и переадает. Получается байты разбирались как горячие пирожки и работало все чисто на совпадении времени.

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

Помниться когда замедлял передатчик [2] то команды шли исправно. Когда я понял что он торопиться сделал буфер. А потом начали откусываться куски.

 

 

Logik
Offline
Зарегистрирован: 05.08.2014

Т.е. приемник просто недожидался поступления всех байт команды?! Все оказалось так просто )))

Пишите в лопе сразу без циклов и ожиданий

if (softSerial.available()>=8)

{

//считываем из буфера порта 8 байт и запускаем проверки и т.д.

.....

}

// делаем остальные дела

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

Хрень какая-то.

Погонял тесты. Сначала в отдельной функции через интервал, потом в loop пнапрямую. Получих такое:

 

//Стандартный способ while(softSerial.available()) и уменьшение интервала для ускорения передачи
buffer = TEST-test-TEST-1234-TEST-976 // Пробная строка
available out while: 0 //есть ли что-то в буфере после чтения
buffer = TEST-test-TEST-1234-TEST-973
available out while: 0
buffer = TEST-test-TEST-1234-TEST-970
available out while: 0 
//----- прошло время и понеслась
buffer = t-TEST-1234-TEST-142TEST-test-TEST-1234-TEST-139TEST-test-TEST-
available out while: 0
buffer = TEST-test-TEST-1234-TEST-118TEST-test-TEST-1234-TEST-115TEST-teTEST-
available out while: 0
buffer = 1234-TEST-88TEST-test-TEST-1234-TEST-85TEST-test-TEST-1234-TEST4-TES
available out while: 0
//причем в буфере ноль, но строка хз пойми какая Потерь не видно. Интервал уменьшился до 100мс дальше - хуже. Чисто сдвиг. 
//Буфер чистится после приема.
//---------------------------------------
//Начал фильтровать по вашему совету while (softSerial.avaible() >= 8) и убрал отображение интервала.
//Началось такое
buffer = 
available out while: 0
buffer = 
available out while: 5
buffer = TX1234XTT
available out while: 7
buffer = X1234XTT
available out while: 7
buffer = X1234XTT
available out while: 7
buffer = X1234XTT
available out while: 7
//снова сдвиг после первой команды. Если поставить в loop то там происходит тоже самое только вот:
buffer = 
available out while: 7
buffer = 
available out while: 7
buffer = 
available out while: 7
buffer = 
available out while: 7
buffer = X1234XTT
available out while: 7
buffer = 
available out while: 7
buffer = 
available out while: 7
buffer = 
// видно же available 7 символов, а пишется что буфер пустой. И если пришли команды, снова сдвиг.
//будто восьмой символ доходит с задержкой. Сделал отображдение из while а очистку за ним:
available out while: 7
buffer = X // первый как всегда посеяли
available out while: 7
buffer = X1
available out while: 12
buffer = X12
available out while: 12
buffer = X123
available out while: 11
buffer = X1234
available out while: 10 //чего-то больше чем (8) O_o
buffer = X1234X
available out while: 9
buffer = X1234XT
available out while: 8
buffer = X1234XTT
available out while: 7
buffer = X   //и так далее
//решил не чистить буферную переменную и:
Write_module_started///
buffer = T
available out while: 7
buffer = TX
available out while: 7
buffer = TX1
available out while: 12
buffer = TX12
available out while: 12
buffer = TX123
available out while: 11
buffer = TX1234
available out while: 10
buffer = TX1234X
available out while: 9
buffer = TX1234XT
available out while: 8
buffer = TX1234XTT
available out while: 7
buffer = TX1234XTTX
available out while: 7
buffer = TX1234XTTX1
available out while: 13
buffer = TX1234XTTX12
available out while: 12
buffer = TX1234XTTX123
available out while: 11
buffer = TX1234XTTX1234
available out while: 10
buffer = TX1234XTTX1234X
available out while: 9
buffer = TX1234XTTX1234XT
available out while: 8
buffer = TX1234XTTX1234XTT
available out while: 7
buffer = TX1234XTTX1234XTTX
available out while: 7
buffer = TX1234XTTX1234XTTX1
available out while: 13
buffer = TX1234XTTX1234XTTX12
available out while: 12
buffer = TX1234XTTX1234XTTX123
available out while: 11
buffer = TX1234XTTX1234XTTX1234
available out while: 10
buffer = TX1234XTTX1234XTTX1234X
available out while: 9
buffer = TX1234XTTX1234XTTX1234XT
available out while: 8
//отсюда видно что все исправно передаеться. Потерь не заметил даже в самой длиной строке
//но неясно почему available out while:  больше 8 если ...понял потому что стоит больше-равно 8
//попробовал фильтровать по конкретному числу и увидел только
buffer = T
available out while: 7
buffer = TX
available out while: 7
//И тишина. Значить надо учитывать управляющие символы поставил конкретно 10-12-13 но ловил только один символ. ВОт тут ничего не понятно. Получается только ловить с условием" >="

Пока в ступоре. Одно ясно - ОНО не успеватет или торопиться.

Попытался сделать такое: откусывать полезные команды стараясь не кусать важное.

void loop() {
    while (softSerial.available() >= 8)    //if we have something on serial buffer
  {
    //aSerial.level(Level::vv).p("available before: ").pln(softSerial.available());
    char buff = softSerial.read();     //read that
    rxCheck += buff;  //add to string
    //aSerial.level(Level::vv).p("available after: ").pln(softSerial.available());
  }
   rxData += rxCheck;
   aSerial.level(Level::vv).p("buffer = ").pln(rxData).p("available out while: ").pln(softSerial.available());
   if(rxData.length() > 16 && rxData.endsWith("XT") == true) rxCheck = "'";
   if(rxData.startsWith("TX") == true) {netDatax = rxData.substring(0, 8); rxData.remove(0, 8);}
    Serial.println(netDatax);

А получил почемуто

buffer = TTTTTTTTTTTTTTTTTTTTTX1234XTTTX1234XTTTX1234XTTTX1234XTTTX1234XTTTX1234XTTTX1234XTTTX1234XTTTX1234XTTTX1234XTTTX1234XTTX1234XTTTX1234XTTX1234XTTTX1234XTTX1234XTT
available out while: 7

Я где-то явно туплю((

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

А буфер действительно не успевает. Сделал разгон по времени начиная от 1 секунды и уменьшая по 5 мс.

Подцепил два шнурка на софт и на хард. После интервала менее 100мс данные льються рекой, а на входе [3] ничего нет!

Но когда 1000-600мс данные идут стройным шагом. Дальше есть подозрение что теряються полными пакетами. Добавил счетчик и как-то оно странно себя ведет.

Еще чуток погоняю, до стабильности и я стану чуточку счастливее =)

diakin
diakin аватар
Offline
Зарегистрирован: 04.06.2016

Так неправильно!


while (softSerial.available() >= 8)    //if we have something on serial buffer
      {
        char buff = softSerial.read();     //read that
        rxCheck += buff;  //add to string    
      }

В этом случае, как только в буфере останется меньше 8 байт - чтение прекращается. Правильно так (как писали выше)


if (softSerial.available()>=8) // если в буфере 8 или больше байт
{

for (i=0 to 7){ //считываем из буфера порта 8 байт и запускаем проверки и т.д.
        char buff = softSerial.read();     //read that
        rxCheck += buff;  //add to string    
}

}
UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

Чтение прекращается. Ну никудаже ничего не девается. По идее должно просто дописываться. Я когда мониторил софтсериал там поток был без единой ошибки.

Хотя попробую. Я понял суть. 

Сегодня я попробовал сделать так:

void loop() {

  unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    if(softSerial.available() > 0){
        while(softSerial.available() < 8){ delay(100); aSerial.level(Level::vv).println("Delayed");}
    while (softSerial.available() > 8)    //if we have something on serial buffer
  {
    char buff = softSerial.read();     //read that
    rxCheck += buff;  //add to string
  }
}

Интересно то что сообщение delayed - которое мне рассказало бы о том что была задержка при поступлении в буфер, не было отображено. Но все исправно приходило. Однако досконально не проверял так как пакеты однотипные отправлял. И почему-то между командами появился знак апострофа. Откуда - не знаю. Строки выглядели так: TX1234XT'TX1234XT'TX1234XT 

Но меня это не смутило, просто вопрос откуда он появился? А увидел я его как добавил здержку при получении чего-то в буфер.

А вот цикл for с числом 7... просто я мониторил softSerial.available и там были (на этот раз) цифры больше 8-ми. Максимум 13. Я предположил что это управляющие символы... хотя разве они учитываются функцией при счете. Не знаю.

Спасибо diakin, попробую твой вариант. Может доведу до ума и буду вспоминать это, как страшненький сон.

Logik
Offline
Зарегистрирован: 05.08.2014

По коду из 17 поста. А  rxCheck если softSerial.available()>=8 не выполнено очищаете? Или оно к rxData добавляется при каждом проходе снова и снова? У вас слишком много буферов, что способствует путанице в логике.

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

Там суть такая, мы считываем все что есть в "аппаратном" буфере в rxCheck а потом, теоритически там должна быть одна команда или кусок команды который потом должен прийти. Все разом они собираються в rxData. А потом проверяется если в rxData у нас собралось более двух команд, мы дополнительно смотрим есть ли метка XT (конечная метка) (вообще надо было сделать >= 16) Если метка есть, значит все целое и мы чистим rxCheck.

Позже проверяем первая команда начинается с начально метки?! Тогда отрезаем наш кусочек в netDatax и исполняем.

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

С буферами я разберусь. Просто сейчас чтобы мне было ясно как все работает в отладке, с создаю уровневые "мертвые переменные" которые мониторю через порт.

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

У меня две странности, может вы подскажите почему так.

1 - не пойму откуда в порте на приеме появляется знак одиночной кавычки. 

У меня на входе такая строка выходит:

'TX1237XTTX1238XT'TX1239XTTX1240XT'TX1241XTTX1242XT'TX1243XTTX1244XT'TX1245XTTX1246XT'

А вот что в начале

Write_module_started///
Delayed
Delayed
Delayed
Delayed
Delayed
Delayed
Delayed
Delayed
Delayed
Delayed
Delayed
rxCheck - clear
rxData - cutOut
rxData in buffer = TX1236XT
rxCheck - '
netDatax - TX1235XT

Получается эта ЗАкавычка остается вконце. Зачем, почему откуда?

Я-то могу их отфильровать. Но откуда они? передача идет строго по тому что я делал (мониторил) Потерь кстати не заметно.

2 - В мониторе видно когда работает задержка на прием (50мс) но она отрабатывает 13 раз. Я ранее писал что почему-то при передачи 8-ми символьной команды в serial.available () фигурируют числа больше 8-ми. было 10-11-12и13 максимум.

Но у нас 8 символов. Почему 13 раз отрабатывает задержка на прием?!

Алгоритм приема и разбора такой:

void loop() {

  unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {  //важно считывать ежесекундно чтобы основная программа не замедлялась.
    previousMillis = currentMillis;
    if(softSerial.available() >= 8){
        while(softSerial.available() <= 8){ delay(50); aSerial.level(Level::vv).println("Delayed");} //если что-то есть в буфере, даем время быть принятым.
    while (softSerial.available())    // именно этот цикл работает лучше цикла for.
  {
    char buff = softSerial.read();     //read that
    rxCheck += buff;  //add to string1
  }
}
if(rxCheck.startsWith("'")) rxCheck.remove(0,1); //ну да, фильтр кавычки
if(rxCheck.endsWith("'")) rxCheck.remove(8,1);
  if(rxCheck.length() >= 16 && rxCheck.endsWith("XT") == true) {rxData += rxCheck; rxCheck = "'"; aSerial.level(Level::vv).println("rxCheck - clear");} //Если в буфере больше 16-ти символов то ждем когда там появиться закрывающая метка для копирования данных.
  if(rxData.startsWith("TX") == true) {netDatax = rxData.substring(0, 8); rxData.remove(0, 8); aSerial.level(Level::vv).pln("rxData - cutOut");} //вырезаем полезную команду на выполнение.
  if(rxData.length() != 0) aSerial.level(Level::vv).p("rxData in buffer = ").pln(rxData).p("rxCheck - ").pln(rxCheck).p("netDatax - ").pln(netDatax); //конечно же отчитываемся что да как и где.
     //readSerialDatax();
  }
}

 

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

закоменти это if (currentMillis - previousMillis >= interval) {

if(softSerial.available() >= 8){ должно происходить максимально часто, т.е. крутиться в лупе постоянно.

и это delay(50); - зачем это и кому это нужно?

//если что-то есть в буфере, даем время быть принятым. - если в буфере что-то есть, то нужно максимально быстро принять и обработать потому, что в буфере сериала уже что-то есть и ждёт обработки.

кавычка ' может быть числом 39 http://book.itep.ru/10/ascii.htm

>= 8 почему 8-мь, а не иное число?

 

 

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

8 потому что длина команды 8 символов и нам надо не меньше

Вот почему именно этот символ прорывается так и не ясно.

Но. Убрал посекундную проверку. По вашему совету воступил в общем убрал задержку.

И вернулся к тому с чего начал. Тестированием интервала отправки понял что от 1000 до 50 мс паузы достаточно для быстрой передачи. НО если меньше, идут страшные потери. Так что 50мс - минимальная задержка для успешной передачи.

ВОт только теперь думаю, Этот код в модуле [3] и он только принимает команды. А вот как быть с модулем [2] если он выполняет достаточно сложную программу (считыает входы с мультиплексора, контролирует время и отправляет команды управления) ему помимо этого тоже слушать надо порт от пульта управления [1] успеет ли он?!

А то получается работал над чем-то и пришел к тому с чего начинал. Да, я понял что проблема был в том что МК не успевал хватать. 

Получился шум на ровном месте?!

Сейчас сделаю все на рабочий лад, и посмотрим что сейчас будет между всеми тремя платками.

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

ХА! Я в начале функции чтения и разбора команд вычищал все переменные в том числе и rxCheck думал от глюков избавлюсь, а получается сам себе гемор создал. Вычищать rxCheck надо когда в нем полноценная команда. Отследил монитором с выполнением основной команды:

we have on rxCheck - TX1363XTTX1364XTTX1365XTTX1366XTTX1367XTTX1368XTTX1369XTTX1370X
we have on rxCheck - TX1363XTTX1364XTTX1365XTTX1366XTTX1367XTTX1368XTTX1369XTTX1370X
we have on rxCheck - TX1363XTTX1364XTTX1365XTTX1366XTTX1367XTTX1368XTTX1369XTTX1370X
we have on rxCheck - TX1363XTTX1364XTTX1365XTTX1366XTTX1367XTTX1368XTTX1369XTTX1370XTX1372XT
rxCheck - clear - execute
//и пошло поехало

Видно же что толко на четвертом цикле дописался конец, а когда есть конец XT мы можем очистить переменную переведя содержимое rxCheck в другую надежную строковую переменную где ничего не побито.

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

UserDead пишет:

Так что 50мс - минимальная задержка для успешной передачи.

слушай, ну ты же тестируешь код не передачи, а приёмки - о какой неуспешной передаче ты постоянно пишешь?

ты не можешь принять, а не передать.

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015

Ну да. С терминами запарился)

Правильнее написать: 50мс - минимальное время интервала между командами для успешного приема. (учитывая размер команды)

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

ок. тогда измени алгоритм приёма - собирай с сериала по одному символу в строку и по достижению условия:чего там у тебя - длина строки, метка начала нужной тебе инфы, метка конца конфы/строки, что то делай со строкой.

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

посмотри, как здесь сделано #76 - там digiusb, но суть сериала - когда в буфере появляется символ, с ним что-то делается - собирается в строку, а строка, затем сравнивается для выполнения нужной команды, затем строка обнуляется и начинает строиться с начала.

 

UserDead
UserDead аватар
Offline
Зарегистрирован: 29.11.2015
В общем в некоторых местах сам создал себе проблему.
Проштудировав варианты кода переведя приемный алгоритм в loop и вернув его обратно в фукнцию но при этом постоянно вызывая его из loop (сделано для улучшения организации кода)
Я пришел к выводу что всего навсего не стоило очищать переменную rxCheck в начале функции. В начале функции я обнулял все переменные отвечающие за прием и разбор команды. Очищал все кроме буфферной переменной где хранились их стеки.
Отныне убрав сброс переменных приема, и немного сменив алгоритм я добился более менее стабильного приема.
Описание алгоритма:
Передача: Тут ничего мудренного нет. Собираем данные в выходной буфер и отправляем
netOutBuffer = "TX" + addr + numb + comm + "XT"; //собираем строку с метками, адресом, номером и командой с закрывающей меткой
softSerial.print(netOutBuffer) // в другом случае просто Serial.print(netOutBuffer);
Прием:
Не особо важно делать это функцией или частью loop но второе предпочтительней.
  if(Serial.available() < 8 && Serial.available() != 0) while(Serial.available() < 8) delay(50);  //Не кидаться! Это условие и не обязательно ставить.
  while (Serial.available())    //цикл пока что-то есть в аппаратном буфере
  {
    char buff = Serial.read();     //Читаем. А что там?
    rxCheck += buff;  //записываем букафку за букафкой)) в rxCheck
  }
if(rxCheck.length() >= 8 && rxCheck.startsWith("TX") == true && rxCheck.endsWith("XT") == true) {bufferCommL1 += rxCheck; rxCheck = "";} //тут должно быть понятно. Три условия. Если в буфере больше 8 символов и метки TX-XT на месте то сливаем целую строку в bufferCommL1, а затем вычищаем rxCheck. В любом случае независимо от приема и длины строки если у нас началось на TX и закончилось XT то с большей долей вероятности у нас все целое, только тогда мы переливаем целую строку в другой уровень обработки и только тогда вычищаем rxCheck
while(bufferCommL1.length() != 0){ //и конечно пока не закончиться перечень команд в буфере первого уровня, мы никуда не уйдем.
  if(bufferCommL1.startsWith("TX") == true) {bufferCommL2 = bufferCommL1.substring(0, 8); bufferCommL1.remove(0, 8);} //Если у нас есть стартовая метка то мы вырезаем из строки одну команду в bufferCommL2 буфер второго уровня.
  compare = 2; //задаем флаг для определения ошибки
  if (bufferCommL2.startsWith("TX") == true) compare = 0;
  else if (bufferCommL2.startsWith("TX") == false) compare = 69;
  if (bufferCommL2.endsWith("XT") == true) compare = 0;
  else if (bufferCommL2.endsWith("XT") == false) compare = 69;
if(compare != 0){
//ошибка
}
  if (compare == 0) //если проверки пройдены
  {
      if (bufferCommL2.substring(2, 3) == "2") //Читаем адрес устройства
      {
//дальше исполнение в подобном роде. Копируем цифры из строки сравниваем их и выполняем команду. Для этого пойдет и If и Switch case. Конечно не забываем что все они String типные, для этго есть замечательная функция stringVariable.toInt();

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

Все остльные мелочи вроде страннных символов в приеме, я создал себе сам по невнимательности.

----

Благодарю всех кто отозвался и подсказал как стоит сделать.