Помогите в очередной раз разобрать строку

MihaNN52
Offline
Зарегистрирован: 22.01.2017

Прошу подсказать как разобрать строоку. Тем по этой теме на форуме много, разобратся не могу.

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

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

$&a29.72&b20.44&c748.35&d26.20#
// $ символ начала передачи
//# символ окончания передачи
//& символ для распознавания идентификатора
//  a b c d идентификаторы
//  цифры значения  с датчиков 

Вообще строка передачи может быть любого другого формата.

Нужно условие проверки начала и конца пакета по символам $ и #

Если они есть то разбираем строку.

Если  после '&' символ 'a'  то цифры которые идут после нее до следующего '&' это значение.

Значение из строки перевести в float  и присвоить переменной a.

Сделал через субстринг, но это ненадежно т.к длины значений могут быть разные.

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

Ну, покажите, как сделал-то?

И поподробнее. Количество переменных всегда одинаковое? Или может быть разным? Имена всегд одни и тже и в том же порядке? Поподробнее, в общем.

MihaNN52
Offline
Зарегистрирован: 22.01.2017

Переменных количество разное.

Имена произвольные из одной буквы. a b c d e f g.... +- около 10шт, без повторения.

while (Serial.available() > 0) {
  String data = Serial.readString();
 a = data.substring(3,7);
 BME_T =  a.toFloat();  
// и т.д

 

__Alexander
Offline
Зарегистрирован: 24.10.2012

найти символы и их адрес можно по strstr. разложить по разделителю strtok.

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

Нет, ну на фиксированную длиину забиваться нельзя, я правильно понял?

Как делать зависит от задачи опять же (подробности Вы от нас скрываете).

Например, строка приходит целиком и её нужно обрабатывать только после того, как придёт заключительный символ #? Или сразу по поступлении $ нужно обрабатывать числа по мере поступления, не дожидаясь конца строки?

Если первое, то можно просто разбить строку на лексемы (например, функцией strtok) (можно и String задействовать, если память девать некуда, конечно). На лексемы разбивать по символу &. Ну. а каждую лексему уже легко обработать, т.к. про неё известно, что там односимвольный код и число.

Кстати. а что с этими названиями и числами делать-то надо?

MihaNN52
Offline
Зарегистрирован: 22.01.2017

Фиксированной длины не получится.

Не знаю как еще подробней расписать.

Все равно по сути как будет лишь бы было надежно))

Для надежносости хотелось бы ввести начало и конец пакета$# 

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

Надо сначало получить потом разобрать т.е после#

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

 

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

MihaNN52 пишет:

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

Не понял, что значит "с любыми разделителями"? Вы же говорили, что разделитель всегда &. Если там "любой разделитель", то как Вы его выделять собираетесь.

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

MihaNN52 пишет:

Надо сначало получить потом разобрать т.е после#

Получать умеете? Если умееете, получайте, а дальше разбирайте strtok'ом. Пример есть по той ссылке, что я давал.

В чём именно у Вас проблема?

И главное, что Вы с этими переменными делать собираетесь? Раскладывать по местным переменным? Как именно. Вы это сейчас чётко себе представляете?

 

MihaNN52
Offline
Зарегистрирован: 22.01.2017

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

strtok разобьет строку на части получится что то вроде

&a29.72

&b20.44

&c748.35

&d26.20

как дальше это разбить не понимаю

MihaNN52
Offline
Зарегистрирован: 22.01.2017

ЕвгенийП пишет:
И главное, что Вы с этими переменными делать собираетесь? Раскладывать по местным переменным? Как именно. Вы это сейчас чётко себе представляете?

Да в моем примере идет присваивание значения переменной BME_T Это работает.

Но в случае с субстрингом длина должна быть фиксирована. 

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

 

while (Serial.available() > 0) {
  String data = Serial.readString(); //заносим все что приняли в строку
 a = data.substring(3,7);// то что в строке на 3 начинается на 7 заканчивается... пусть будет а
 BME_T =  a.toFloat();  // это ранее обьявленная float BME_T; которой мы подставляем цифирки который получили шагом ранее
// и т.д

мне такой вариант не подходит т.к тут жесткая привязка к позициям данных в строке.

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

Так а дальше бить не надо. Каждая из этих частей имеет фиксированный формат, а именно 

str + 0 - символ &
str + 1 - имя переменной
str + 2 - начало числа.

Поэтому, так и делаете. Допустим, такой кусочек у Вас сидит в переменной str (тип у неё char *, разумеется). Тогда, Вы тупо пишете

char imyaPeremennoy = * (str + 1);
float znacheniePeremennoy = atof(str + 2);

И всё.

Как получить эту str типа char * при помощи strtok там в примере показано.

Если остались вопросы - не стесняйтесь.

MihaNN52
Offline
Зарегистрирован: 22.01.2017

спасибо, попробую переварить.

MihaNN52
Offline
Зарегистрирован: 22.01.2017

чет запутался окончательно)

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

char str[] = "Особенности национальной рыбалки - художественный, комедийный фильм.";

у меня то его нет.

делаю так) но компилятор говорит что что то тут не так)

  while (Serial1.available() > 0) { 
    char data = Serial1.read();
    char *pch = strtok (data,"&");
    while (pch != NULL){
    Serial.println(pch);
    pch = strtok (NULL, "&");
      }
   }  

 

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

Так я ж Вас спрашивал, "Вы умеете получать строку из сериала?". Её прочитать сначала надо.

Ну, я могу помочь, но хоть скажите ограничение на её длину. Только не надо, что "может быть любой" - у Вас же не любое количество датчиков. Итак, какова максимальная длина строки, которая будет приходить из Сериала?

MihaNN52
Offline
Зарегистрирован: 22.01.2017

на данный момент максимально 69 символов

$&a31.52&b19.85&c753.45&d27.40&e19.50&f22.10&g22.31&h1671.62&i32.50#

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

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

Хорошо, я завтра вечером (после работы) напишу для Вас пример (не бойтесь - не полный, Вам оставлю что-нибудь вкусное для самостоятельной работы -:))) сейчас не могу, простите. Потерпит до завтра, я надеюсь?

MihaNN52
Offline
Зарегистрирован: 22.01.2017

Конечно))) ни кто не умрет) Спасибо.

Максимальная длина сообщения которая может придти в контроллер 62 символа, дальше обрезает начало сообщения. Наверное если буфер увеличить то и больше зайдет, но мне не это не надо, два раза отправит, где то это даже лучше.

__Alexander
Offline
Зарегистрирован: 24.10.2012

Если Вы можете и на слейве и на мастере менять протокол, то в данном случае Вы страдаете херней. Вам ASCII ваще тут нахрен не надо, нахера передавать число с пл. запятой в виде аски?

MihaNN52
Offline
Зарегистрирован: 22.01.2017

наверное для удобства, много выиграю от передачи в ином формате?

__Alexander
Offline
Зарегистрирован: 24.10.2012

какого нафиг удобства? вы с датчиков принимаете в бинарном виде? да. так вот в том виде и передавайте дальше, а уже на мастере переводите в удобочитаемый вид.  У Вас вот эта хрень &a27.72 занимает 7 байт. в семь байт можно уложить информацию минимум  с трех датчиков. Зачем между машинами обмениваться человеческой информацией, если нет такой потребности?

MihaNN52
Offline
Зарегистрирован: 22.01.2017

Вроде понял о чем имете ввиду. Сейчас разберусь с тем что более менее понятно. 

 

 

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

Коллега прав.

Вам нужен пример разбора строки?

MihaNN52
Offline
Зарегистрирован: 22.01.2017

Да прав... я чемпион по обновлению этой страницы сегодня))

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

Так пример со строкой больше не актуален? Мне-то написать нетрудно, но если не нужно, так зачем? Ответьте.

MihaNN52
Offline
Зарегистрирован: 22.01.2017

Не правильно выразился. Мне очень нужен ваш пример и я его целый день жду) 

Совет коллеги  принял  и уверен что так было бы правильно, но если с "человеческим языком" пока справится не могу, то в цифрах точно потону) По этому, пока мне надо сделать, именно так как я планировал и понять как это делается. 

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

Ага, ну сейчас покурю и займусь.

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

Ну вот как-то так, если нигде ничего не ляпнул.

Любые символы между пакетами она игнорирует. Попробуйте, например,

qwerty$&a123.32&b0.12&c3.14#lalalalalal$&a0.1&b0.2&c999#zxcv

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

// пример пакета
// $&a123.32&b0.12&c3.14#lalalalalal$&a0.1&b0.2&c999#
//

template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; }

enum AUTOMATA_STATE { // Возможные состояния разборщика
	WAITING,	// ожидание начала пакета
	PARSING 	//	разбор пакета
};

#define	PACKET_BEGIN	'$'
#define	PACKET_END	'#'
#define	VALUE_BEGIN	'&'


void setup(void) {
	Serial.begin(115200);
	Serial << "Enter packet:\n";
}

void parseString(char * szBuffer) {
	Serial << "GOT Packet: " << szBuffer << '\n';
	for (char *ptr = szBuffer; ptr; ptr = strchr(ptr, VALUE_BEGIN))  {
		if (*ptr++ != VALUE_BEGIN) {
			Serial << "*** ERROR: packet malformed\n";
			return;
		}
		char variableName = *ptr++;
		float variableValue = atof(ptr);
		Serial << "Name=" << variableName << "; Value=" << variableValue << '\n';
	}
	Serial << "All Done!\n";
}


void loop(void) {
	// Изначально автомат в состоянии ожидания
	static AUTOMATA_STATE state = WAITING;
	static char szBuffer[64]; // буфер для накапливания строки
	static int8_t bufPtr = 0;
	
	if (! Serial.available()) return;	// не пришёл символ? Ну, и нечего тут делать
	//
	const char ch = Serial.read();	// прочитали сивол 
	//
	// Что делать дальше пределяется состоянием автомата
	switch (state) {
		case WAITING:
			// В состоянии ожидания мы ждём символа PACKET_BEGIN и игнорируем любые другие
			if (ch != PACKET_BEGIN) return;
			// 
			// Пришёл символ начала пакета. Переходим в состояние PARSING
			state = PARSING;
			break;
		case PARSING:
			// В состоянии ожидания мы ждём символа PACKET_END. 
			// Любые другие символы - часть пакета. Их складываем в буфер.
			if (ch == PACKET_END) { // пришёл символ конца пакета
				szBuffer[bufPtr++] = 0;
				parseString(szBuffer);
				state = WAITING; // переходим в состояние ожидания нового пакета
				bufPtr = 0;
			} else {
				szBuffer[bufPtr++] = ch;
				if (bufPtr == sizeof(szBuffer)) {
					Serial << "*** ERROR *** Buffer overflow!!!\n";
					bufPtr--;
				}
			}
	}
}

 

MihaNN52
Offline
Зарегистрирован: 22.01.2017
GOT Packet: &a31.60&b19.38&c756.17&d27.30&e19.90&f0.00
Name=a; Value=31.60
Name=b; Value=19.38
Name=c; Value=756.17
Name=d; Value=27.30
Name=e; Value=19.90
Name=f; Value=0.00

Отправляю и получаю. Супер. 

Разбираюсь дальше.

GOT Packet: &a32.09&b19.33&c756.06&d27.40&e19.20&f29.00
Name=a; Value=32.09
Name=b; Value=19.33
Name=c; Value=756.06
Name=d; Value=27.40
Name=e; Value=19.20
Name=f; Value=29.00
All Done!
GOT Packet: &a32.06&b19.57&c756.05&d27.40&e19.20&f29.00
Name=a; Value=32.06
Name=b; Value=19.57
Name=c; Value=756.05
Name=d; Value=27.40
Name=e; Value=19.20
Name=f; Value=29.00
All Done!
GOT Packet: &a32.05&b18.70&c756.08&d27.4e19.10&f29.00
Name=a; Value=32.05
Name=b; Value=18.70
Name=c; Value=756.08
Name=d; Value=<large double>
Name=f; Value=29.00
All Done!

 

бывает так

GOT Packet: &a31.96&b19.62&c756.08&.50&e20.20&f29.50
Name=a; Value=31.96
Name=b; Value=19.62
Name=c; Value=756.08
Name=.; Value=50.00
Name=e; Value=20.20
Name=f; Value=29.50
All Done!

теряет символы почему то

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

MihaNN52 пишет:

GOT Packet: &a32.05&b18.70&c756.08&d27.4e19.10&f29.00
Name=a; Value=32.05
Name=b; Value=18.70
Name=c; Value=756.08
Name=d; Value=<large double>
Name=f; Value=29.00
All Done!


Ну, задания контролировать формат не было, предполагалось, что строки программа формирует. тут просто ошибка в формате строки. Смотрите, предпоследний слот: d27.4e19.10 Это разве допустимо?

MihaNN52 пишет:

GOT Packet: &a31.96&b19.62&c756.08&.50&e20.20&f29.50
Name=a; Value=31.96
Name=b; Value=19.62
Name=c; Value=756.08
Name=.; Value=50.00
Name=e; Value=20.20
Name=f; Value=29.50
All Done!

теряет символы почему то

Ничего не теряет. Это Вы потеряли имя переменной в третьем слоте справа. Он у Вас выглядит: &.50 И где тут имя переменной?

Если надо контролировать формат и отлавливать такие ошибки, так надо говорить об этом при постановке задачи.

Или Вы случайно ошиблись при вводе?

Т.е. при правильных строках она работает правильно.

 

 

MihaNN52
Offline
Зарегистрирован: 22.01.2017

так точно если все правильно то все разбирает отлично

щас надо посидеть потлавливать почему не доходит.

привык что обычно если не влазит теряется начало.

сейчас видно что если  убрать дну переменную то все приходит целое.

MihaNN52
Offline
Зарегистрирован: 22.01.2017

Все нормально, наблюдаю.

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

Надо конечно глянуть от куда они идут, или не доходят или не успевают улетать в буфер.

Большое человеческое спасибо.

С вами можно будет как то связаться по мимо форума? Если не сложно черканите мне на почту mmc1@bk.ru

GOT Packet: &a32.25&b18.93&c755.67&d27.90&e19.00
Name=a; Value=32.25
Name=b; Value=18.93
Name=c; Value=755.67
Name=d; Value=27.90
Name=e; Value=19.00
All Done!
GOT Packet: &a32.35&b18.91&c755.69&d28.00&e19.00
Name=a; Value=32.35
Name=b; Value=18.91
Name=c; Value=755.69
Name=d; Value=28.00
Name=e; Value=19.00
All Done!
GOT Packet: &f29.00&g0.00&h958.95&i0.00
Name=f; Value=29.00
Name=g; Value=0.00
Name=h; Value=958.95
Name=i; Value=0.00
All Done!
GOT Packet: &a32.38&b18.88&c755.71&d28.00&e19.10
Name=a; Value=32.38
Name=b; Value=18.88
Name=c; Value=755.71
Name=d; Value=28.00
Name=e; Value=19.10
All Done!

Оставлю на пару дней чтоб логи записать.

Проект будет интересный 24 контролера STM32, 12 шт ESP12, одна малина пи3 и просто целая гора датчиков.

 

 

 

Дописал присваивание вот так.

void parseString(char * szBuffer) {
  Serial << "GOT Packet: " << szBuffer << '\n';
  for (char *ptr = szBuffer; ptr; ptr = strchr(ptr, VALUE_BEGIN))  {
    if (*ptr++ != VALUE_BEGIN) {
      //Serial << "*** ERROR: packet malformed\n";
      return;
    }
    char variableName = *ptr++;
    float variableValue = atof(ptr);
    Serial << "Name=" << variableName << "; Value=" << variableValue << '\n';
    if(variableName == 'a'){
      BME_T = variableValue;
    }
    if(variableName == 'b'){
      BME_H = variableValue;
    }
     if(variableName == 'c'){
      BME_P = variableValue;
    }
      if(variableName == 'd'){
      B_DEF = variableValue;
    }
      if(variableName == 'e'){
      H_DEF = variableValue;
    }
      if(variableName == 'f'){
      B_B1 = variableValue;
    }
      if(variableName == 'g'){
      B_B2 = variableValue;
    }
      if(variableName == 'h'){
      CO2 = variableValue;
    }
      if(variableName == 'i'){
      B_TEPL = variableValue;
    }
    

    
  }
  Serial << "All Done!\n";
}

 

 

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

Если нужно контролировать пришедшие пакеты, это тоже делается, просто Вы не просили. Можно доделать. Другими способами не надо, в реале меня нет (в смысле - в реале живёт другой человек, а такой, какой я здесь, толь здесь и существует) просто при случае апните тему, она у меня и вылезет в списке отслеживания.

MihaNN52
Offline
Зарегистрирован: 22.01.2017

Понял.

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

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

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

Кстати, Вы уверены, что разбор надо начинать, когда всё пришло? Если разбирать по мере прихода, то можно отказаться от того 64-байтного буфера - он тогда не нужен.

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

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

Если нужно контролировать пришедшие пакеты, это тоже делается, просто Вы не просили. Можно доделать. Другими способами не надо, в реале меня нет (в смысле - в реале живёт другой человек, а такой, какой я здесь, толь здесь и существует) просто при случае апните тему, она у меня и вылезет в списке отслеживания.

" ...не скажу, что я идеальный, я живой, я просто реальный..."

 

MihaNN52
Offline
Зарегистрирован: 22.01.2017

Давайте попробуем без буфера. Если не затруднит.

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

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

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

MihaNN52
Offline
Зарегистрирован: 22.01.2017

Я правильно понимаю что нужно перейти в этом случае на Serial.write ? Не совсем понимаю... Serial.write не передаст float, надо множить на 100 потом делить то что придет. Верно?

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

Serial.write передаст всё, что угодно. Просто передайте ему адрес начала Вашего float и количество байтов.

float value = 3.1459;
Serial.write((byte *) & value, sizeof(value));

Вот и все дела. Передаст за милую душу.

Принимать также

float value;
Serial.readBytes((byte *) & value, sizeof(value));

 

MihaNN52
Offline
Зарегистрирован: 22.01.2017

ух ты..

Вот это можно более подробно (byte *) & value,

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

Можно. По шагам:

value - кусок памяти в котором лежит Ваше плавающее число

& value - адрес этого куска памяти (т.е. номер байта с которого начинается Ваше число).

Собственно по сути это всё, но формально функция readBytes ожидает не адреса плавающего числа, а адреса массива байтов. Технически это одно и тоже - адрес он и есть адрес - это просто номер байта в памяти, считая с нуля, но синтаксис требует, чтобы мы передавали readBytes именно  адрес массива байтов. Для этого мы используем операция преобразования типа для адреса

(byte *) & value

эта операция не делает вообще ничего с адресом - никак его не изменяет, просто говорит компилятору, чтобы он счиал &value не адресом плавающего числа, а адресом массива байтов (повторяю, это важно - никаких физических манипуляций с адресом не делается - от остаётся точно таким же).

Т.е. обобщая. Выражение (byte *) & value означает "адрес в памяти начального байта плавающего числа value").

Значит, когда мы пишем

Serial.write((byte *) & value, sizeof(value));

мы передаём write адрес начала и длину в байтах того, что мы хотим, чтобы она передала.

MihaNN52
Offline
Зарегистрирован: 22.01.2017

Понадобится идентификатор передачи, его отправляем отдельно, перед сообщением или все одной командой можно? или тут не нужен ни какой идентификатор?

Или можно одновременно передать пакет переменных не вызывая каждый раз Serial.write()

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

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

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

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

__Alexander
Offline
Зарегистрирован: 24.10.2012

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

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

__Alexander пишет:

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

Это как?

__Alexander
Offline
Зарегистрирован: 24.10.2012

ну в разделе про уарт можно почитать.

 

или вот

http://we.easyelectronics.ru/AVR/vremya-govorit-s-kamnyami-ili-usart-mul...

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

Работает на камнях, где есть полноценный USART (это меги)

Не заморачиваться, да свой протокол написать, заготовку Евгений уже дал

__Alexander
Offline
Зарегистрирован: 24.10.2012

а на ардуино не мега?

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

MihaNN52
Offline
Зарегистрирован: 22.01.2017

яб в принципе модбас бы использовал, контроллеры уже подключены по RS485

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

 

MihaNN52
Offline
Зарегистрирован: 22.01.2017

Евгений привет.

Давай попробуем продолжить.

Предстоит отправлять в таком формате:

Признак начала, имя переменной, число.

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

Признак начала & и имена переменных анг алфавит по одной букве.

Разбирать надо будет сразу.

Отправка получается типа того


Serial.write('&')
Serial.write('a')
Serial.write((byte *) & value1, sizeof(value1));
Serial.write('&')
Serial.write('b')
Serial.write((byte *) & value2, sizeof(value2));//  и т.д

верно?

 

 

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

Ну, можно и так. 

Ну, а приём тоже проще некуда. Вы бы могли и сами по образцу, что был. 

Первым делом надо понимать, что любой разборщик - конечный автомат. Вот и давайте выделять его состояния. Читайте внимательно и Вы всё поймёте. 

1. Ждёт символа '&' при этом игнорирует все остальные символы. Получив символ '&' переходит в состояние 2
2. Ждёт имени переменной, получив байт проверяется яляется ли он латинской буквой. Если да, переходит в состояние 3, иначе в состояние 1
3. Ждёт начала числа, получив что-то, пытается прочитать как число и после этого выдаёт результат (имя - число) и переходит в состояние 1.

Изначально автомат находится в состоянии 1.

Вот и всё, осталось это написать. Но тут всё просто - пишем буквально как есть, прямо по этим пунктам.

template <typename T> inline Print & operator << (Print &s, T n) { s.print(n); return s; }

enum AUTOMATA_STATE { // Возможные состояния разборщика
	WAITING_AMPERSAND,	// ожидание начала пакета
	WAITING_NAME, 	//	ожидание имени
	WAITING_NUMBER	//	ожидание числа
};

#define	PACKET_BEGIN	'&'

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

void loop(void) {
	// Изначально автомат в состоянии ожидания
	static AUTOMATA_STATE state = WAITING_AMPERSAND;
	static char variableName;
	
	if (! Serial.available()) return;	// не пришёл символ? Ну, и нечего тут делать
	//
	// Что делать дальше определяется состоянием автомата
	switch (state) {
		case WAITING_AMPERSAND:
			// В состоянии ожидания мы ждём символа PACKET_BEGIN и игнорируем любые другие
			if (Serial.read() != PACKET_BEGIN) return;
			// Пришёл символ начала пакета. Переходим в состояние PARSING
			state = WAITING_NAME;
			break;
		case WAITING_NAME:
			// В состоянии ожидания имени мы мы должны получить латинскую букву. 
			// Если получили - переходим в состояние ввода числа
			// Если пришло что-то другое - ошибка, переходим обратно в ожидание &
			variableName = Serial.read();
			state = isalpha(variableName) ? WAITING_NUMBER : WAITING_AMPERSAND;
			break;
		case WAITING_NUMBER:
			// читаем число
			float value;
			Serial.readBytes((byte *) & value, sizeof(value));
			///////////////////////////
			//	В этом месте мы имеем результат !!!!
			//	Имя находится в переменной variableName, а значение - в переменной value
			//	Что с ними делать решайте сам - сериал-то у нас занят другим устройством
			//	так что печатать результат некуда
			///////////////////////////
			state = WAITING_AMPERSAND;
			break;
	}
}

Сравните текст с написанными выше правилами - всё переписано 1 к 1. Текст коротенький - комментариев больше.

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