Помогите разобраться с приемом по UART

elfrock
Offline
Зарегистрирован: 03.04.2016

Добрый день!

помогите пожалуйста разобраться с приемом данных по UART на Arduino Nano

суть такая - по UART идет прием данных (например с GPS, но его использую для примера), когда принимаем /CR, всю собранную в буфер ерунду передаем по радио (передача по радио занимает довольно много времени, секунд 10)

обработка (в немного обрезанном виде) выглядит вот так:

unsigned  char     track[654];
unsigned char serial_buffer[622];

void setup(){
  Serial.begin(9600);
}

void loop(){
 if (Read_UART(Serial.read(), serial_buffer, sizeof(serial_buffer)) >0) { 
    memcpy(track +31, serial_buffer, sizeof(serial_buffer)); //данные добавляем к заголовку пакета
    radio();  //Функция передачи по радио
    memset(serial_buffer,0,sizeof(serial_buffer));
    }
}

int Read_UART(int source, unsigned char *buffer, unsigned int len)
{
  static unsigned int pos = 0;
  unsigned int rpos;
  
  if (source > 0) {
    switch (source) {
      case '\n':
        break;
      case '\r':
        rpos = pos;
        pos = 0;
        return rpos;
      default:
         if (pos < len-1) { 
         buffer[pos++] = source; 
         buffer[pos] = 0; 
         }
    }
  }
  return -1;
}

Проблема вот в чем.

если я подцепляю usb-com и передаю данные на ардуинку вручную, то все отрабатывает хорошо, передается ровно та строка данных которую я туда и загоняю, завершая ее CR-ом.

но вот если прицепить модуль gps, которые генерирует строку $GPGGA вида:

$GPGGA,,,,,,0,00,99.99,,,,,,*48

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

$GPGGA,,,,,,0,00,99$GPGGA,,,,,,0,00,99.99,,,,,,*48 

по какой-то причине не обнаруживается завершение первой строки и в буфер попадает часть первой и вторая строки.

Подскажите пожалуйста почему такое может происходить? Может кто сталкивался или это очевидно, а я не понимаю?

Спасибо!

Andy
Andy аватар
Offline
Зарегистрирован: 01.01.2016

serial_buffer - это указатель на массив.

sizeof(serial_buffer) это размер указателя.

Объяви #define SIZE_OF_SERIAL_BUFFER 622 и используй его везде, в том числе и при объявлении самого массива.

elfrock
Offline
Зарегистрирован: 03.04.2016

сделал, но дело ведь не в этом.. 

elfrock
Offline
Зарегистрирован: 03.04.2016

я так понимаю что дело какраз в том, что передавая данные в UART вручную, буфер UART успевает и наполниться данными и опустошиться при чтении оттуда. А при передаче в UART с модуля GPS т.к. он отправляет туда строку каждую секунду, буфер захлебывается и отдельные строки "слипаются".

Но я всегда считал, что буфер UART кольцевой и я в любом случае наткнусь на CR и сформирую свою телеграмму.

а тут выходит, что CR кудато теряется, если GPS грузит в буфер постоянно, а считывание из него (в момент работы функции radio()) не происходит.

Подскажите пожалуста как можно решить это проблему?

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

Предлагаю подумать, что будет, если:

1. В момент вызова radio() во внутренний буфер UART будут поступать данные;

2. Что произойдёт, когда пойдут дальнейшие вызовы Read_UART.

Код - неправильный: во-первых, вы обнуляете содержимое массива неправильно. Во-вторых, поскольку вы отбрасываете \n - то по причине неправильного обнуления массива после первой отработки radio() вы получите слипшиеся две строки в буфере.

Вопросы?

elfrock
Offline
Зарегистрирован: 03.04.2016

Мне казалось, что в момент вызова radio(), поскольку буфер кольцевой новые данные будут как им и следует приходить в него.

а в момент очередного чтения из него, это чтение произойдет в некий произвольный момент, не обязательно попадающий на начало необходимой мне строки, массив будет заполняться снова.

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

вы имеете ввиду что неправильно обнулять массив memset-ом? 

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

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

Поторопился я - с обнулением массива всё норм. Но проблему склеивания строк это не снимает, как понимаете. Попробуйте, во-первых, переместить memset выше radio(), и, во-вторых - проанализируйте, как ведёт себя переменная pos - чой-то подозрения у меня на неё :)

elfrock
Offline
Зарегистрирован: 03.04.2016

Убрал все до минимума, решил поиграться без лишних функций, оставив вместо radio() просто задержку.

#include <SoftwareSerial.h> 
#define RXD 2
#define TXD 3

SoftwareSerial soft_serial(RXD, TXD);
unsigned char serial_buffer[200];

void setup(){
  Serial.begin(9600);
  soft_serial.begin(9600);
}

void loop(){
 if (Read_UART(soft_serial.read(), serial_buffer, 200) >0) {  
    Serial.write(serial_buffer,200);
    Serial.println();
    memset(serial_buffer,0,200); 
    delay(1000);    
}
}

int Read_UART(int source, unsigned char *buffer, unsigned int len)
{
  static unsigned int pos = 0;
  unsigned int rpos;
  
  if (source > 0) {
    switch (source) {
      case '\n': 
        break;
      case '\r': 
        rpos = pos;
        pos = 0;
        return rpos;
      default:  
         if (pos < len-1) { 
         buffer[pos++] = source; 
         buffer[pos] = 0; 
         }
    }
  }
  return -1; 
}

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

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

soft_serial.end();
delay(7000);    
soft_serial.begin(9600);

Или если опустошать буфер вот так:

for(;soft_serial.available();){soft_serial.read();}

Но если честно, я всеравно не понимаю почему так происходит :(

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

Эммм. Смотрите, что увидел: if(Read_UART, когда вы возвращаете -1 - тоже true, т.к. всё, что отлично от нуля - интерпретируется булевой логикой как true. Ферштейн?

elfrock
Offline
Зарегистрирован: 03.04.2016

ферштейн то ферштейн, но Read_UART имеет тип integer, а условие if проверяется на >0...

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

elfrock пишет:

ферштейн то ферштейн, но Read_UART имеет тип integer, а условие if проверяется на >0...

Млять, старею, не заметил > 0 :))

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

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

 

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

Вывод: попробовать переписать код, читая Read_UART до тех пор, пока available().

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Помогите и мне пожалуйста.

К ардуино uno будет подключено много много датчиков. Nodemcu будет эти датчики считывать по uart.

С чтением/записью в Ардуино мне помогли разобраться, спасибо.

http://arduino.ru/forum/programmirovanie/kak-sobrat-string-ot-com-porta#comment-293939

Для ардуино я написала следующий код:

//Device Address: 281398D70200005D
//Device Address: 28FF57A3811603C0
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress Thermometer1 = {0x28, 0x13, 0x98, 0xD7, 0x02, 0x00, 0x00, 0x5D }; 
DeviceAddress Thermometer2 = {0x28, 0xFF, 0x57, 0xA3, 0x81, 0x16, 0x03, 0xC0 };
String inString;

void setup() {
sensors.begin();
sensors.setResolution(Thermometer1, 10);
sensors.setResolution(Thermometer2, 10);
Serial.begin(115200);
}
 
void printTemperature(DeviceAddress deviceAddress) {
float tempC = sensors.getTempC(deviceAddress);
Serial.println(tempC);
}

void loop() {
  while (Serial.available()) {
    char inChar = Serial.read();
    inString += inChar;
    if (inChar == '#') {

   if (inString.indexOf("sensor1#")>0) {
 sensors.requestTemperatures();
 Serial.print("Sensor1  ");
printTemperature(Thermometer1);
      }
      else if (inString.indexOf("sensor2#")>0) {
 sensors.requestTemperatures();
 Serial.print("Sensor2  ");
printTemperature(Thermometer2);
      }
      else
      {
        Serial.println("Net komandy");
      }
    inString="";
    }
  }
}

Т.е. будут считываться два датчика температуры.

Теперь что качается Nodemcu

Т.е. отправить команду я умею.

Чтобы считать датчики нужно выполнить следующее:

print("sensor1#")

print("sensor2#")

Соответственно в ответ я получаю:

Sensor1  27.50

Sensor2  25.50
 
Как мне считать в переменную эти самые ответы

Sensor1  27.50

Sensor2  25.50
 
 
 

 

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Почему данные такие? РАзбитые

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017
pin=4
gpio.mode(pin, gpio.OUTPUT)



tmr.alarm(0, 3000, 1, function() print("sensor2#") 
gpio.write(pin, gpio.HIGH)
end )
  uart.on("data", &, 
      function(data)
print("Text: "..data)
     gpio.write(pin, gpio.LOW)
    end, 0)

Разобралась, добавила символ после которого получаю температуру

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Скажите,

RX -D9

TX -D10

Есть ещё,

RXD2 -D7

TXD2 -D8

Как использовать вторые? Чтобы не мешать процессу заливки lua кода

Irinka
Irinka аватар
Offline
Зарегистрирован: 28.06.2017

Перелопатив форум вот что у меня получилось.

Ввожу в браузер адрес:

192.168.0.11/sensor1

Получаю:

Sensor1=25.33&

Всё как мне и надо

Далее ввожу 192.168.0.11/sensor2

А получаю Sensor1=25.33&

А если повторно нажать 192.168.0.11/sensor2

Тогда получаю то что надо Sensor2=26.00&

Почему мне нужно делать два запроса из браузера чтобы обновить текст

(в мониторе порта ардуино и ноде команды получаю как в запросе, так же ардуино отвечает правильно)

 

Код ардуино:

//Device Address: 281398D70200005D
//Device Address: 28FF57A3811603C0
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress Thermometer1 = {0x28, 0x13, 0x98, 0xD7, 0x02, 0x00, 0x00, 0x5D }; 
DeviceAddress Thermometer2 = {0x28, 0xFF, 0x57, 0xA3, 0x81, 0x16, 0x03, 0xC0 };
String inString;
String b;
void setup() {
sensors.begin();
sensors.setResolution(Thermometer1, 10);
sensors.setResolution(Thermometer2, 10);
Serial.begin(115200);
}
 
void printTemperature(DeviceAddress deviceAddress) {
float tempC = sensors.getTempC(deviceAddress);
b = String(tempC)+"&";
Serial.println(b);
}

void loop() {
  while (Serial.available()) {
    char inChar = Serial.read();
    inString += inChar;
    if (inChar == '#') {

   if (inString.indexOf("sensor1#")>0) {
 sensors.requestTemperatures();
 Serial.print("Sensor1=");
printTemperature(Thermometer1);
      }
      else if (inString.indexOf("sensor2#")>0) {
 sensors.requestTemperatures();
 Serial.print("Sensor2=");
printTemperature(Thermometer2);
      }
      else
      {
        Serial.println("Net komandy&");
      }
    inString="";
    }
  }
}

 

Код ноде:

-- main.lua --
tmr.alarm(0, 1000, 1, function()
if wifi.sta.getip() == nil then
print("Connecting to AP...\n")
else
ip, nm, gw=wifi.sta.getip()
print("IP Info: \nIP Address: ",ip)
print("Netmask: ",nm)
print("Gateway Addr: ",gw,'\n')
tmr.stop(0)
end
end)
otvet='pusto'
-- a simple http server
srv = net.createServer(net.TCP, 30)
srv:listen(80,function(conn) 
conn:on("receive",function(conn,get) 
nac=string.find(get,"/");


uart.on("data",'&', function(data) otvet=data end, 0)

function zapr()
kon=string.find(get,"HTTP");
zap=string.sub(get, nac,kon-2);
if zap == "/sensor1" then  print("sensor1#")
elseif zap== "/sensor2" then  print("sensor2#") 
else otvet="error" end
end
if nac~=nil then zapr() end

conn:send('HTTP/1.1 200 OK\n')
conn:send('Access-Control-Allow-Origin:*\n\n')
conn:send(otvet)
conn:on("sent", function(conn) conn:close() end)
end) 
end)