Определение формата пакета данных разных сенсоров для передачи по радио

d00m
Offline
Зарегистрирован: 21.02.2013

всем привет.

есть несколько девайсов на базе arduino + RFM95(LoRa)  - серверов -  которые по запросу снимают данные со своих сенсоров и передают эти данные на _одно_ клиентское устройство.

сейчас это сделано так - для сбора данных и передачи их на сервере используется array of float:

float data_buf[23];

//[0]-[14]: ext thermosensors, 

//[15]: int sensor, 
//[16]: batV, 
//[17]: RSSI, 
//[18]: Vcc, 
//[19]: light, 
//[20]: snow_depth, 
//[21]: LRF temp, 
//[22]: LRF Vcc

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

uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];

uint8_t len = sizeof(buf);
uint8_t from;
    
if (manager.recvfromAckTimeout(buf, &len, 5000, &from)) { 


Serial.print(F("T0:"));Serial.println((*(float *)&buf[0]));    // buf[0]
Serial.print(F("T1:"));Serial.println((*(float *)&buf[4]));    // buf[1]
..............................
etc

 

но некоторые серверы имеют разное кол-вол термосенсоров и, например, есть или нет лазерный сенсор и так далее..

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

например:

сервер1 - 5 термосенсоров:

typedef struct {
  float         temp1;
  float         temp2;
  float         temp3;
  float         temp4;
  float         temp5;
  float         light;
  float         vcc;
} Payload;
Payload srv_data;

сервер2 - 10 термосенсоров:

typedef struct {
  float         temp1;
  float         temp2;
  float         temp3;
  float         temp4;
  float         temp5;
.............................
  float         temp10;
  float         light;
  float         vcc;
  float         snow_depth;
} Payload;

Payload srv_data;

но вот какой должен быть код на клиенте, чтобы он одинаково обрабатывал полученные данные от 1 и 2 сервера, и выводил их корректно?

прошу совета, и если можно - пример кода.

или хотябы что читать на эту тему.

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

но массив или другой структурированый пакет данных вроде бы красивее выглядит.

спасибо заранее

b707
Онлайн
Зарегистрирован: 26.05.2017

простите, но какая-то хрень написана. Сначала описан массив флоат(почему флоат?), но потом принимается массив байт.

Вы разницу между флоатом и байтом понимаете? Который из двух вам реально нужен?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

d00m пишет:

сервер1 - 5 термосенсоров:

typedef struct {
  float         temp1;
  float         temp2;
  float         temp3;
  float         temp4;
  float         temp5;
  float         light;
  float         vcc;
} Payload;
Payload srv_data;

сервер2 - 10 термосенсоров:

typedef struct {
  float         temp1;
  float         temp2;
  float         temp3;
  float         temp4;
  float         temp5;
.............................
  float         temp10;
  float         light;
  float         vcc;
  float         snow_depth;
} Payload;

Payload srv_data;

В начало каждой структуры добавьте её длину. А при чтении, сначала читайте длину, потом запрашивайте нужный буфер и читайте остальное. так сделано в Windows API со многими структурами.

d00m
Offline
Зарегистрирован: 21.02.2013

ЕвгенийП пишет:

В начало каждой структуры добавьте её длину. А при чтении, сначала читайте длину, потом запрашивайте нужный буфер и читайте остальное. так сделано в Windows API со многими структурами.

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

эх..  примерчик бы не помешал) особенно как это потом читать на клиенте учитывая полученный length.

но вот что еще - как быть если в каком то сервере будет всего 10 термосенсоров == 40 байт

а в другом сервере будет тоже 40 байт, но данные будут распределены,  например 5 термо и 5 датчиков влажности?

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

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

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

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Непременно распухнет. А как иначе? Должна быть или жесткая структура метрик или каждая передаваемая метрика должна описываться типом и id.

b707
Онлайн
Зарегистрирован: 26.05.2017

d00m пишет:

тогда распухнет этот пакет данных из-за служебной инфы.

он у вас в первую очередь распух от неправильно выбранного типа данных. Вы не ответили на вопрос, зачем вам флоат, для температуры более чем достаточно типа int16_t, а для влажности - даже uint8_t. и сразу пакет похудеет в 2-3 раза

d00m
Offline
Зарегистрирован: 21.02.2013

b707 пишет:

d00m пишет:

тогда распухнет этот пакет данных из-за служебной инфы.

он у вас в первую очередь распух от неправильно выбранного типа данных. Вы не ответили на вопрос, зачем вам флоат, для температуры более чем достаточно типа int16_t, а для влажности - даже uint8_t. и сразу пакет похудеет в 2-3 раза

 

int16_t - это ведь целочисленный формат, не так ли?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

d00m пишет:

но вот что еще - как быть если в каком то сервере будет всего 10 термосенсоров == 40 байт

а в другом сервере будет тоже 40 байт, но данные будут распределены,  например 5 термо и 5 датчиков влажности?

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

В таком случае я бы задался вопросом: а сколько у меня реально различных пакетов данных. Допустим, 20. Я бы описал 20 структур (это ничего не стоит по ресурсам исполнения) и первым полем передавал бы не длину, а ID структуры. По этому ID создавал бы нужную структуру и вычитывал бы её.

astwo
Offline
Зарегистрирован: 10.07.2019

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

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

sadman41 пишет:

Непременно распухнет. А как иначе? Должна быть или жесткая структура метрик или каждая передаваемая метрика должна описываться типом и id.


а упаковать все ID в байтик не вариант? но проще конечно побитово, 4 байта - 32 датчика

b707
Онлайн
Зарегистрирован: 26.05.2017

d00m пишет:

int16_t - это ведь целочисленный формат, не так ли?

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

b707
Онлайн
Зарегистрирован: 26.05.2017

astwo пишет:
Ну здесь вы не правы. Сначала передаётся длина, а потом ID пакета.

почему обязательно в этом порядке? если длина однозначно определяется ID - а часто это так и есть - длину можно и не передавать вовсе

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

d00m пишет:

но некоторые серверы имеют разное кол-вол термосенсоров и, например, есть или нет лазерный сенсор и так далее..

Значить, передавай с каждого сервера строку типа

ID T1NN T5NN H2NN L3NN

и разбирай ее на своем устройстве

ID это идентификатор сервера с которого пришла информация

T1NN, T5NN  -  показания температуры (Т) с даччика номер 1 и 5, NN - сопсно температура

H2NN - показания влажности (H) с даччика номер 2 NN - сама влажность

L3NN показания с лазерного даччика (L) номер 3, значение NN

b707
Онлайн
Зарегистрирован: 26.05.2017

DetSimen пишет:

Значить, передавай с каждого сервера строку типа

зачем же ты ему каку советуешь? ну нафига строки? пусть передает 1 байт - ID датчика, и дальше за ним - числовое значение.

astwo
Offline
Зарегистрирован: 10.07.2019

Конечно пакеты распухают. А то будет как со слоненком. Нёс обезьяне привет и не донес. Чем больше бумаги, тем чище попа. Это и здесь работает. Если на прикладной уровне на длину можно забить, то на траспортном уже забиваю на содержание. Там важны длина данных, от кого, кому, id посылки, время отправки.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

b707 пишет:

зачем же ты ему каку советуешь? ну нафига строки? 

их передавать можно разной длины и потом разбирать легче. 

b707
Онлайн
Зарегистрирован: 26.05.2017

astwo пишет:
Если на прикладной уровне на длину можно забить, то на траспортном уже забиваю на содержание. Там важны длина данных, от кого, кому, id посылки, время отправки.

вы это про ардуино пишете или так, вообще?

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

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

b707 пишет:

зачем же ты ему каку советуешь? ну нафига строки? пусть передает 1 байт - ID датчика, и дальше за ним - числовое значение.

Вот смари.  Если формат пакета жестко задан и длина его известна, то при потере/неисправности одного/нескольких даччиков нам все равно придется передавать хоть какие-то значения, даже неверные,но при этом еще как-то информировать сборщика, что некоторые данные неверны, а при переменной длине пакета никто не мешает передаччику, если он обнаружил сбой даччика, информацию с него не передавать.  Один раз не передаст, второй, а потом есть повод задумаца, а чойта у нас на сервере информации с одного/нескольких даччиков уже час/сутки нету?  Не пора ли отправить туда юнгу/огнемётчиков, чтоб посмотрели/приняли меры.  

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

В любом случае, я не настаиваю, это ж просто один из вариантов.  Лично мне проще строку разобрать. :) 

b707
Онлайн
Зарегистрирован: 26.05.2017

DetSimen пишет:

Лично мне проще строку разобрать. :) 

так это тебе :) а у новичков при разборе строк вечно ТАКОЕ получается... как у вчерашнего кадра, у которого код для разбора 78 параметров занимал 2500 строк :)

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

В любом случае, я не настаиваю, это ж просто один из вариантов.  Лично мне проще строку разобрать. :) 

небось тоже свой интерпретатор писал? )))

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ну акей, убедили. 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ua6em пишет:

небось тоже свой интерпретатор писал? )))

какрас, в процессе.  Скоро выложу, для Леонарды. 

Этот меня занозил 

http://arduino.ru/forum/ishchu-ispolnitelya/simulyator-myshiatmega32u4-leonardo-due-nuzhen-sketch

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

ну акей, убедили. 

ты Дед колись, написал интерпретатор али нет

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ua6em пишет:

DetSimen пишет:

ну акей, убедили. 

ты Дед колись, написал интерпретатор али нет

ты #22 читал?

astwo
Offline
Зарегистрирован: 10.07.2019

b707 пишет:

вы это про ардуино пишете или так, вообще?


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

astwo
Offline
Зарегистрирован: 10.07.2019

Ммм

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

ua6em пишет:

DetSimen пишет:

ну акей, убедили. 

ты Дед колись, написал интерпретатор али нет

ты #22 читал?

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

d00m
Offline
Зарегистрирован: 21.02.2013

b707 пишет:

d00m пишет:

int16_t - это ведь целочисленный формат, не так ли?

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

нет, не вкурсе. поэтому использую float.

 

короче пока вот прикрутил такую структуру:

 

typedef struct {

  int           srvId; 
  unsigned long uptime;
  int           light;
  int           lastRSSI;
  float         MCU_batV;
  float         MCU_Vcc;
  float         snow_depth;
  float         LRF_temp;
  float         LRF_Vcc;
  float         t_int;
  float         ts0; // 0cm
  float         ts1; // 20cm
  float         ts2; // 40cm
  float         ts3; // 60cm
  float         ts4; // 80cm
  float         ts5; // 100cm
  float         ts6; // 120cm
  float         ts7; // 140cm
  float         ts8; // 160cm
  float         ts9; // 180cm

} Payload;
Payload srv_data;
/**/

 

вроде передаю-принимаю норм..

только вот, конечно, не очень то оно отличается от простого array of float...

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

а может просто добавить еще одно поле - кол-во термосенсоров? и на клиенте проверять его и соответственно выводить только данные из этого кол-ва?

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

d00m пишет:

а может просто добавить еще одно поле - кол-во термосенсоров? и на клиенте проверять его и соответственно выводить только данные из этого кол-ва?

Ну, Вам же уже говорили, не только не выводить лишнее, но и не передавать и не принимать!

Только Вы это как-то "через зад" сделали. Действительно - у Вас просто массив, нахрена какая-то структура?

b707
Онлайн
Зарегистрирован: 26.05.2017

d00m пишет:

b707 пишет:

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

нет, не вкурсе. поэтому использую float.

ну неужели сами не догадались

34.56 градуса домножаете на 100 = получаете целое 3456. Формат int16_t позволит хранить температуры от -325 до +325 градусов

Ровно точно таже храните свои напряжения VCC и Vbat

Такой подход позволит не только уменьшить размер данных при передаче - если применить его по всей программе, вы существенно уменьшите размер кода и сделаете его быстрее, потому что тип float на микроконтроллерах требует больших библиотек и очень медленный.

d00m
Offline
Зарегистрирован: 21.02.2013

ЕвгенийП пишет:

Ну, Вам же уже говорили, не только не выводить лишнее, но и не передавать и не принимать!

Только Вы это как-то "через зад" сделали. Действительно - у Вас просто массив, нахрена какая-то структура?

ну а как мне на одном и том же клиенте принимать данные от сервера с 5 и от сервера с 10 сенсорами?

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

и тогда если получу от сервера с 5, просто в начале проверю поле с кол-вом сенсором и не буду выводить

t6 =0

t7 =0

както так..

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

наверное дальнейшая оптимизация такого пакета данных уже подразумевает гораздо лучшее знание языка и опыт.

d00m
Offline
Зарегистрирован: 21.02.2013

b707 пишет:

не догадались

34.56 градуса домножаете на 100 = получаете целое 3456. Формат int16_t позволит хранить температуры от -325 до +325 градусов

Ровно точно таже храните свои напряжения VCC и Vbat

Такой подход позволит не только уменьшить размер данных при передаче - если применить его по всей программе, вы существенно уменьшите размер кода и сделаете его быстрее, потому что тип float на микроконтроллерах требует больших библиотек и очень медленный.

хм

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

b707
Онлайн
Зарегистрирован: 26.05.2017

d00m пишет:

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

не надо делить, а то вы снова получите тип float. Если вы используете данные в расчетах -так и используйте умноженные на 100, сделав нужные поправки в формуле. А если выводите на экран - то выводите сначала ЦЕЛОЕ T/100 - для этого случая это будет 34. а потом опять же ЦЕЛОЕ T%100 - это будет 56

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

d00m пишет:

ну а как мне на одном и том же клиенте принимать данные от сервера с 5 и от сервера с 10 сенсорами?

Так и принимать Сколько пришло, столько и принимаете.

d00m пишет:

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

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

Я уже говорил Вам, что нужно расписать разные структуры для разных источников. Если хотите статическую память, то сложить их друг на друга физически. Прочитали id поняли о какой структуре речь и её полностью вычитываете и показываете.

 
d00m
Offline
Зарегистрирован: 21.02.2013

спасибо всем. 

сделал struct, все пашет как надо - 100 байт летает по радио

 

typedef struct {

  int           srvId; 
  unsigned long uptime;
  int           light;
  int           lastRSSI;
  float         MCU_batV;
  float         MCU_Vcc;
  float         snow_depth;
  float         LRF_temp;
  float         LRF_Vcc;
  int           num_of_ts;
  float         ts_int;
  float         ts[NUM_OF_TS]; // specified the size

 

и с помощью NUM_OF_TS могу указать на сервере нужное кол-во сенсоров

от float пока не ушел, думаю позже доделаю)

sadman41
Offline
Зарегистрирован: 19.10.2016

Не хватает ещё одного поля.

d00m
Offline
Зарегистрирован: 21.02.2013

sadman41 пишет:

Не хватает ещё одного поля.

 

все поля на месте

sadman41
Offline
Зарегистрирован: 19.10.2016

CRC нет.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Неприличными словами не выражацца!

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

sadman41 пишет:

Не хватает ещё одного поля.

А может и больше. https://www.youtube.com/watch?v=0LB3ejdGut0