Нужно ускорить процедуру чтения UART

vde69
Offline
Зарегистрирован: 10.01.2016

тут ниже тема чужая, не буду в нее влезать...

есть модуль ESP-01 он по WiFi  раздает контент, все работает относительно быстро пока его не подключаю к MEGA по UART,

 

Мега шлет относительно маленькие пакеты 1 раз в секунду, ESP их разбирает и все работает, но уже сильно медленнее... 

Да я читал, что String медленный, но не понимаю на сколько он медленный, может кто подскажет что-то конкретное по ускрению этого кусочка?

 

// эти ID используются в HTML и передаются от контроллера по UART
const char out_id [PARAM_COUNT][21] = {
  {"main\0"},		// - контроль того, что данные меняются и основной контроллер работает
  {"data\0"},		// - текущая дата
  {"time\0"},		// - текущее время
  {"sd\0"}, 		// - модуль SD карты
  {"display\0"}, 	// - модуль дисплея
  {"keybord\0"}, 	// - модуль клавиатуры
  {"term\0"},		// - установленнаяя температура в доме
  {"t_home_1\0"},	// - датчик 1 в доме
  {"t_home_2\0"},	// - датчик 2 в доме
  {"t_out_1\0"},	// - датчик 1 на улице
  {"t_out_2\0"},	// - датчик 2 на улице
  {"t_heat_in\0"},	// - датчик температуры возвращаемого теплоносителя 	
  {"t_heat_out\0"},	// - датчик температуры исходящего теплоносителя 
  {"m_heat\0"},		// - насос циркуляции для отопления
  {"t_heat\0"},		// - датчик температуры котла 
  {"t_water_in\0"},	// - датчик температуры подпитки в контур горячей воды  	
  {"t_water_out\0"},// - датчик температуры исходящей горячей воды  
  {"m_water\0"},	// - насос циркуляции для горячей воды 
  {"v_water\0"},	// - датчик скорости циркуляции горячей воды в контуре
  {"pressure\0"},	// - давление воздуха на улице
  {"light\0"}		// - освещенность на улице
};

char out_value [PARAM_COUNT][11];
char out_status [PARAM_COUNT][6];

void loop() {

  LoopReadData();

}



void LoopReadData() {
	//
    // формат: <id=value;status>
	//
	char c;
	int16_t ii;
	String inString = "";
	String s_out_id = "";
	String s_out_value = "";
	String s_out_status = "";
	while(Serial.available()) { 
		c = Serial.read();    // принять байт как символ
		if (c == '<') {		// начало параметра
			clear_buf(&(buf_UART[0]), 0);
		} else if (c == '>') {		// окончание параметра
			ii = find_byte_buf(&(buf_UART[0]), '=');
			if (ii != 0) {
				inString = buf_UART;
				s_out_id = inString.substring(0, ii);
				inString = inString.substring(ii+1);
				ii = inString.indexOf(';');
				if (ii < 0 ) {
					s_out_value = inString;
					s_out_status = "";					
				} else {
					s_out_value = inString.substring(0, ii);
					s_out_status = inString.substring(ii+1);
				}
				for (uint8_t i = 0; i < PARAM_COUNT; i++) {
					if (s_out_id == (String)(out_id[i])) {
						s_out_value.getBytes((uint8_t*)(&(out_value[i][0])), s_out_value.length()+1);
						s_out_status.getBytes((uint8_t*)(&(out_status[i][0])), s_out_status.length()+1);
					}
				}

			}
			clear_buf(&(buf_UART[0]), 0);
			
		} else if ((uint8_t)c < 32) {  // эти символы пропускаем

		} else {					// добавляем в буфер
			ii = find_byte_buf(&(buf_UART[0]), 0);
			*((uint8_t*)(buf_UART+ii)) = c;
		}
		
	}	
}

 

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

тормозит find_byte_buf

vde69
Offline
Зарегистрирован: 10.01.2016

Logik пишет:

тормозит find_byte_buf

там особо тормозить нечему


uint8_t find_byte_buf(char* buf, uint8_t b){
	for (uint8_t i = 0; i < 250; i++) {
		if (*((uint8_t*)(buf+i))==b) {return i;}
	}
	return 0;
}

 

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

vde69 пишет:

там особо тормозить нечему


uint8_t find_byte_buf(char* buf, uint8_t b){
	for (uint8_t i = 0; i < 250; i++) {
		if (*((uint8_t*)(buf+i))==b) {return i;}
	}
	return 0;
}

 

Фига-се, нечему! линейный поиск на 250 элементов. К тому же он неправильно написан.

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

 

vde69
Offline
Зарегистрирован: 10.01.2016

допустим поиск можно немного оптимизировать, но не думаю, что это много даст

uint8_t find_byte_buf(char* buf, uint8_t b){
	for (uint8_t i = 0; i < 250; i++) {
		uint8_t a = *((uint8_t*)(buf+i));
		if ( a == 0 ) {return 0;}
		if ( a == b ) {return i;}
	}
	return 0;
}

по факту мне нужно разобрать строку вида 

<id=value;status>

на 3 строки

Upper
Offline
Зарегистрирован: 23.06.2020

Можно расписать кто какие задержки вносит, тогда и принимать решение.

Данные по UART передаются за время X и скорость обработки на это время не влияет. Какую ДОПОЛНИТЕЛЬНУЮ задержку вносит разбор строки?

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

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

vde69 пишет:

по факту мне нужно разобрать строку вида 

<id=value;status>

на 3 строки

По символу '<' очищаете все три строки и принимаете символы в первую пока не поступит '=' Тогда начинаете прием во вторую до символа ';', по нему начинаете прием в третью строку до >. Только очищать - это не совсем забивать все нулями, лучше просто указатель устанавливать на начало строки. Можно 3 указателя держать, но лучше один и переменную состояния приема. А завершать прием в каждую строку - не забывать добавлять 0 в конец. Ну и контроль длинны, чтоб за буфера не вылетать.

Кстати на String  этот Ваш код не сильно то и завязан, выкиньте его и забудьте.

Можно еще и совместить с поиском id в таблице out_id. Тогда по завершению приема id сразу будет известен есть ли в таблице и с каким индексом. Нет предела совершенству... 

rkit
Offline
Зарегистрирован: 23.11.2016

На esp есть sscanf, std::map, и прочие удобства не на улице. Не надо писать код как инвалид.

gfo
Offline
Зарегистрирован: 20.03.2021

rkit пишет:

На esp есть sscanf, std::map, и прочие удобства не на улице. Не надо писать код как инвалид.

 

regex?

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

vde69 пишет:

по факту мне нужно разобрать строку вида 

<id=value;status>

на 3 строки

На какие три строки. Я же Вам написал, что могу попробовать Вам помочь, но для этого мне нужно чёткое описание задачи и несколько примеров типа "входящая строка->результат". А не фраза "на три строки" про текст в котором всего два значения.

Нужна помощь? Не ленитесь объяснить задачу.

gfo
Offline
Зарегистрирован: 20.03.2021

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

Очень неаккуратное регулярное выражение. Такие можно применять на мощных процессорах, где вопрос ресурсов вообще не стоит.

gfo
Offline
Зарегистрирован: 20.03.2021

на столько я понял то вживую входящие данные будут выглятеть как то вот так: <45=333;ОК>

жаль нет под рукой железки, интересно замерить... 

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

vde69
Offline
Зарегистрирован: 10.01.2016

<t_home_1=099;E1>

<t_home_1=-02;OFF>

<t_home_1=015;OK>

 

 

vde69
Offline
Зарегистрирован: 10.01.2016

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

добавлю в out_id колонку "количество" и заведу новый байтовый массив для отсева (что-то вроде индексного дерева будет), и параметры value и status буду считывать сразу по назначению, то есть вообще обойдусь без буферизации buf_UART

vde69
Offline
Зарегистрирован: 10.01.2016

кстати вопрос:

0.15 сек на получение ответа в браузере на JSON запрос - это нормально? ответ примерно 1200 символов

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

vde69 пишет:

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

добавлю в out_id колонку "количество" и заведу новый байтовый массив для отсева (что-то вроде индексного дерева будет), и параметры value и status буду считывать сразу по назначению, то есть вообще обойдусь без буферизации buf_UART

Та не нужен никакой байтовый массив. Вы постоянно усложняете ))) Приняли первый символ id - пробежались по out_id до элемента, у которого он такой же. Запомнили его индекс. Приняли второй символ - сравнили его с вторым символом у запомненного. Совпало - продолжаем прием, после приема третего символа аналогично. Не совпало - бежим по out_id и ищем элемент, у которого первый символ совпадает с аналогичным у ранее найденного элемента, а второй с принятым - запомним теперь его.

Пример. Допустим принимаем посимвольно "t_heat\0"

1. Приняли t, нашли в элемент "time\0", т.к. первый символ совпал.

2. Приняли _, второй символ не совпал, значить движемся по out_id дальше, ищем элемент, у которого первый символ как у "time\0", а второй _. нашли в элемент "t_home_1\0".

3. Приняли h, совпал с третим символо в "t_home_1\0". продолжаем

4. Приняли e, четвертый символ не совпал с таковым в "t_home_1\0", значить движемся по out_id дальше, ищем элемент, у которого первые три символ как у "t_home_1\0", а четвертый e. нашли в элемент "t_heat_in\0".

5. Прием a и t - совпадают.
 
6.  пришло =. На этом прием id закончится . Проверяем что у последнего  найденного элемента очередной символ \0,  У "t_heat_in\0" это не так.  Значить движемся по out_id дальше, ищем элемент, у которого первые шесть символ как у "t_heat_in\0", а седьмой \0. Нашли в элемент "t_heat\0".
 
Для работы алгоритма нужно всего две переменные - индекс по out_id и номер принимаемого символа. Все просто..

 

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

Если out_id был бы большой по размеру (или есть перспектива что выростит), имело бы смысл его записать сортированным и искать более интелектуально. Но при том размере, что в примере это без толку.

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

gfo пишет:

rkit пишет:

На esp есть sscanf, std::map, и прочие удобства не на улице. Не надо писать код как инвалид.

 

regex?

Не. rkit просто тупой.

vde69
Offline
Зарегистрирован: 10.01.2016

Logik пишет:

Если out_id был бы большой по размеру (или есть перспектива что выростит), имело бы смысл его записать сортированным и искать более интелектуально. Но при том размере, что в примере это без толку.

можно и сортировано, для этого нужны 2 переменные,

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

единствено не удобно будет придется забить 0 до конца элемента и хранить в сортированном виде

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

Не нужно. Выиграша по времени на небольшом наборе данных не будет. А сложней  всеж.

vde69
Offline
Зарегистрирован: 10.01.2016

зато не нужно хранить все символы которые уже были проверены и не нужно по ним бегать

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

Зачем их хранит? Они и в out_id хорошо хранятся. Это первые символы в элементе из out_id , найденном на предыдущем этапе.  Пишу же, хранить достаточно " индекс по out_id и номер принимаемого символа". Больше ничего для поиска по мере приема не нужно.

MaksVV
Offline
Зарегистрирован: 06.08.2015

ппц лютая борьба за каждый байт , у вас что проект на тиньке? Написали ведь, что пакеты маленькие. Что нельзя на буфер байт 20  выделить? и строко_сишными процедурами парсить. 

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

А толку выделять ненужное. Выделитель, ты код пишешь или стену мажешь? ТС уже навыделял был, с чем сюда и обратился. Так что иди се, с миром, выделяй из себя в другом месте, можешь даже сразу фреймворками откладывать ;)

vde69
Offline
Зарегистрирован: 10.01.2016

переписал без использования String, постарался написать относительно быстрый код, стало лучше но все равно очень странное поведение.

когда ESP подсоединена к USB (через переходник/программатор) и с консоли я туда кидаю строки типа "<t_home_1=099;E1>" и кидаю часто то все равно сайт грузится ровно, скорость получения ответа 120....150 мсек,

как только я подключаю к МЕГЕ то скорость получения JSON в браузере становится очень сильно не стабильная, от 200 до 1500 мсек, плавает и на первый взгляд не зависит от того много или мало туда МЕГА отдает данных

 

собственно сам код, но я уже не думаю, что дело в нем...

// -----------------------------------------------------------
// описание данных для json и получения от главного контроллера

// эти ID используются в HTML и передаются от контроллера по UART
// ВНИМАНИЕ !!! данный список должен быть отсортирован по ID
// идентификатор не более 20 символов
const char out_id [PARAM_COUNT][21] = {
  {"data\0"},		// - текущая дата
  {"display\0"}, 	// - модуль дисплея	
  {"keybord\0"}, 	// - модуль клавиатуры	
  {"light\0"},		// - освещенность на улице
  {"m_heat\0"},		// - насос циркуляции для отопления
  {"m_water\0"},	// - насос циркуляции для горячей воды 	
  {"main\0"},		// - контроль того, что данные меняются и основной контроллер работает
  {"pressure\0"},	// - давление воздуха на улице	
  {"sd\0"}, 		// - модуль SD карты
  {"t_heat_in\0"},	// - датчик температуры возвращаемого теплоносителя 	
  {"t_heat_out\0"},	// - датчик температуры исходящего теплоносителя 	
  {"t_heat\0"},		// - датчик температуры котла 
  {"t_home_1\0"},	// - датчик 1 в доме	
  {"t_home_2\0"},	// - датчик 2 в доме	
  {"t_out_1\0"},	// - датчик 1 на улице	
  {"t_out_2\0"},	// - датчик 2 на улице	
  {"t_water_in\0"},	// - датчик температуры подпитки в контур горячей воды  	
  {"t_water_out\0"},// - датчик температуры исходящей горячей воды  		
  {"term\0"},		// - установленнаяя температура в доме
  {"time\0"},		// - текущее время
  {"v_water\0"}		// - датчик скорости циркуляции горячей воды в контуре	
};

// массивы для хранения данных полученых от контролера и которые передаются в HTML
uint8_t out_id_length [PARAM_COUNT];
char out_value [PARAM_COUNT][11];
char out_status [PARAM_COUNT][6];

// глобальные переменные которые используются для получения и разбора данных от контроллера
uint8_t out_min_pointer=0;  // указатель на перый элемент списка out_id который нам подходит
uint8_t out_max_pointer=0;  // указатель на последний элемент списка out_id который нам подходит
uint8_t out_tec_pointer=0;  // указатель на найденный элемент списка out_id, по которомы мы ччитаем value и status
char out_value_tec[11];	    // буфер для хранения считываемого параметра value
char out_status_tec[6];     // буфер для хранения считываемого параметра status
uint8_t out_num_char=0;     // номер символа в читаемом параметре ID, value,status
uint8_t out_step=0; 		// шаг на котором мы при получении данных.
						    //	0 - ждем начала блока "<"
						    //	1 - читаем ID, и ждем "="
						    //	2 - читаем value, и ждем ";"
						    //	3 - читаем status, и ждем ">"
						    //	4 - все верно прочитано, можно копировать в память

// -----------------------------------------------------------

void setup() {
	out_value_tec[0] = 0;
	out_status_tec[0] = 0;
	
	// инициализация массивов
	for (uint8_t i = 0; i < PARAM_COUNT; i++) {
		for (uint8_t ii = 0; ii < 20; ii++) {
			if (out_id[i][ii] == 0) {
				out_id_length[i] = ii;
				break;
			}
		}
		
		out_value[i][0] = 0;
		out_status[i][0] = 0;
	}
 
}

void loop() {

  LoopReadData();

}


void LoopReadData() {
	//
    // формат: <id=value;status>
	//
	char c;
	while(Serial.available()) { 
		c = Serial.read();    // принять байт как символ
#ifdef ON_DEBUG_ECHO
		Serial.write(c);
#endif
		if ((uint8_t)c < 32) {  // эти символы пропускаем и считаем сначала
			out_step = 0;
#ifdef ON_DEBUG_ECHO
//			Serial.write('#');  // сообщаем, что пришло не то, чего ждем
#endif
		} else if (out_step == 0) { //	0 - ждем начала блока "<"
			if (c == '<') {  // начало параметра
				out_step = 1; 
				out_min_pointer=0; 
				out_max_pointer=(uint8_t)(PARAM_COUNT)-1;
				out_tec_pointer=0;
				out_num_char=0;
				out_value_tec[0] = 0;
				out_status_tec[0] = 0;
				
			} else {
#ifdef ON_DEBUG_ECHO
				Serial.write('#');  // сообщаем, что пришло не то, чего ждем
#endif
			}
		} else if ((out_step == 1) && (out_num_char < 20)) {
			// ищем идентификатор
			uint8_t fl = 0;
			uint8_t fl_end = out_max_pointer;
			for (uint8_t i = out_min_pointer; i <= out_max_pointer; i++) {
				if ((out_num_char > 0) && (out_id[i][out_num_char] == 0) && (c == '=')) {
					// мы нашли нужный id 
					out_tec_pointer = i;
					out_num_char=0;
					out_step = 2;
					break;
				} else if (out_id_length[i] < out_num_char) {
					// слишком короткий ключ
					
				} else if (out_id[i][out_num_char] == c) {
					// пока вроде подходит
					fl_end = i; 
					if (fl == 0) {	out_min_pointer = i; fl = 1; } 
				} else if (out_id[i][out_num_char] > c) {
					// уже стало больше
					break;
				} 
			}			
			if (out_step == 1) {
				if (fl == 0) { // ни один вариант не сработал
					out_step = 0;
#ifdef ON_DEBUG_ECHO
					Serial.write('#');  // сообщаем, что пришло не то, что ждем
#endif
				} else {
					out_max_pointer = fl_end;
					out_num_char++;				
				}
			}
			
		} else if (out_step == 2) {
			if (out_num_char > 10) { 
				out_num_char = 10; 
#ifdef ON_DEBUG_ECHO
				Serial.write('#');  // сообщаем, что пришло не то, что ждем
#endif
			} else { out_value_tec[out_num_char] = c; }
			
			if (c == ';') { 
				out_value_tec[out_num_char] = 0; 
				out_step = 3;
				out_num_char = 0; 
			} else {
				out_num_char++;
			}
			
		} else if (out_step == 3) {
			if (out_num_char > 5) { 
				out_num_char = 5; 
#ifdef ON_DEBUG_ECHO
				Serial.write('#');  // сообщаем, что пришло не то, что ждем
#endif
			} else { out_status_tec[out_num_char] = c; }
			
			if (c == '>') { 
				out_status_tec[out_num_char] = 0; 
				out_step = 4;	
			} else {
				out_num_char++;
			}
		}
		
		if (out_step == 4) {
			memcpy (&(out_value [out_tec_pointer][0]), &(out_value_tec[0]), 11);     
			memcpy (&(out_status [out_tec_pointer][0]), &(out_status_tec[0]), 5);     
			out_step = 0;
#ifdef ON_DEBUG_ECHO	
			// сообщаем, что пакет загружен
			Serial.println();  
			Serial.print("load:");  
			Serial.print(out_tec_pointer, DEC);  
			Serial.println(":");  
			Serial.print(out_id [out_tec_pointer]);
			Serial.print('-');
			Serial.print(out_value [out_tec_pointer]);
			Serial.print('-');
			Serial.println(out_status [out_tec_pointer]);
#endif
			
			
		}

	}	
}





 

vde69
Offline
Зарегистрирован: 10.01.2016

 

пока вот так https://ibb.co/McxQ5p9

MaksVV
Offline
Зарегистрирован: 06.08.2015

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

vde69
Offline
Зарегистрирован: 10.01.2016

MaksVV пишет:

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

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

vde69
Offline
Зарегистрирован: 10.01.2016

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

MaksVV
Offline
Зарегистрирован: 06.08.2015

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

MaksVV
Offline
Зарегистрирован: 06.08.2015

был час на работе свободный, предлагаю такой говнокод

#define UART Serial

const byte MAX_CHAR_ID    = 20; // максимальное количество символов ID
const byte MAX_CHAR_VALUE = 10; // максимальное количество символов VALUE
const byte MAX_CHAR_STATUS=  5; // максимальное количество символов STATUS

#define DEBUG // раскоментировать для отладки

const byte MAX_BUFFER = MAX_CHAR_ID+MAX_CHAR_VALUE+MAX_CHAR_STATUS+8; // размер буфера парсилки

char currStr[MAX_BUFFER]; // буфер парсилки  

 
struct parameter 
   {                            
const char out_id[MAX_CHAR_ID+1];
char out_value [MAX_CHAR_VALUE+1];
char out_status[MAX_CHAR_STATUS+1];
   };

parameter Parameter[] = 
  {
"data",         "", "", // - текущая дата
"display",      "", "", // - модуль дисплея
"keybord",      "", "", // - модуль клавиатуры 
"light",        "", "", // - освещенность на улице
"m_heat",       "", "", // - насос циркуляции для отопления
"m_water",      "", "", // - насос циркуляции для горячей воды 
"main",         "", "", // - контроль того, что данные меняются и основной контроллер работает
"pressure",     "", "", // - давление воздуха на улице 
"sd",           "", "", // - модуль SD карты
"t_heat_in",    "", "", // - датчик температуры возвращаемого теплоносителя    
"t_heat_out",   "", "", // - датчик температуры исходящего теплоносителя   
"t_heat",       "", "", // - датчик температуры котла
"t_home_1",     "", "", // - датчик 1 в доме   
"t_home_2",     "", "", // - датчик 2 в доме   
"t_out_1",      "", "", // - датчик 1 на улице 
"t_out_2",      "", "", // - датчик 2 на улице 
"t_water_in",   "", "", // - датчик температуры подпитки в контур горячей воды     
"t_water_out",  "", "", // - датчик температуры исходящей горячей воды         
"term",         "", "", // - установленнаяя температура в доме
"time",         "", "", // - текущее время
"v_water",      "", "", // - датчик скорости циркуляции горячей воды в контуре   
  
  };
byte parameter_quantity = sizeof(Parameter)/(MAX_CHAR_ID+MAX_CHAR_STATUS+MAX_CHAR_VALUE+3); // количество параметров 


//------------------------функция чтения информации из уарт
void UART_read()
{
  static bool timer = 0; static uint32_t prevtime  = 0;  // таймер выхода из сбора строки по таймауту 
  static bool stringStat = 0;    // статус строки 0 - ждем начала, 1 - принимаем,
  if (timer && millis() - prevtime>500) // если данные оборвались
  {
    timer = 0 ; currStr[0] = 0; stringStat=0;  // заканчиваем набор строки
  #ifdef DEBUG 
    Serial.println("reset parse by timeout!");
  #endif
  } 

  if (!UART.available()) return;// если на уарте ниче нет, выходим 
    
  char currSymb[2] = {0};      // символьный кэш
  currSymb[0] = UART.read();   // читаем очередной символ
     if (currSymb[0] == '>')   // если полностью получили строку - парсим:
 {if (stringStat){
  bool IDfind = 0;   // флаг совпадает ли ID с одним из списка
  // парсим строку:
  for (int i=0; i<parameter_quantity; i++) 
     {
      if (strstr(currStr, Parameter[i].out_id)>0) // если нашли знакомый ID
         {
          IDfind = 1; 
          char*ravno=0, *tzz=0;  
          ravno = strchr(currStr, '='), tzz = strchr(currStr, ';');   // ищем '=' ';' 
          if (!ravno || !tzz) 
          {
 #ifdef DEBUG   
          Serial.println ("Not find '=' & ';'");  // не нашли ругнемся
 #endif 
          }
          else{                                                       // нашли - действуем
          strncpy (Parameter[i].out_value,  ravno+1,  tzz - ravno-1); //парсим value
          Parameter[i].out_value[tzz - ravno-1] =NULL;                //NULL в конце value 
          strcpy (Parameter[i].out_status,  tzz+1);                   //парсим status 
 #ifdef DEBUG         
          Serial.print("id:     "); Serial.println (Parameter[i].out_id);
          Serial.print("value:  "); Serial.println (Parameter[i].out_value);
          Serial.print("status: "); Serial.println (Parameter[i].out_status);
 #endif         
          break;
              }//end find '=' ';' is OK
         } //end find ID is OK
     }
if (!IDfind)
 #ifdef DEBUG   
       Serial.println ("ID not find"); // поругаемся, если нет такого IDв списке
 #endif  
 timer = 0 ; currStr[0] = 0; stringStat=0; // в конце парсинга нулим буфер
 }} 
      
      
 else if (currSymb[0] == '<') {currStr[0] = 0; stringStat = 1; timer = 1; prevtime = millis();} // принято начало строки
 else if (stringStat)  {strcat (currStr,currSymb); prevtime = millis();} //набираем строку
      if (strlen(currStr)>=MAX_BUFFER-3)  // если буфер закончился 
      {
        timer = 0 ; currStr[0] = 0; stringStat=0; // сброс сбора строки
 #ifdef DEBUG        
        Serial.println ("reset parse by over buffer");
 #endif
      }
  }

//--------------------------------

void setup() 
{
  UART.begin(38400);
}

void loop() 
{
UART_read(); // функция чтения из UART
// тут остальной код
}

 

Upper
Offline
Зарегистрирован: 23.06.2020

MaksVV пишет:

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

По моему while(Serial.available()) скорее полезен, чем вреден.
Мертвое зависание в данном цикле невозможно (если темп обработки быстрее темпа поступления новых).
(Дополнено - для перестраховки можно добавить выход из цикла при завершении разбора.)

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

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

А вы попробуйте обработку посимвольно, при нормальной реализации все отлично работает.

Upper
Offline
Зарегистрирован: 23.06.2020

andycat пишет:
... при нормальной реализации все отлично работает.

при нормальной реализации все нормально работает.
при отличной реализации все отлично работает.
MaksVV
Offline
Зарегистрирован: 06.08.2015

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

vde69
Offline
Зарегистрирован: 10.01.2016

У меня вообще думка, что код тут вообще не при чем.... 

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

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

что-то мне кажется проблема в том, что соединенные напрямую ttl 5в и 3.3в могут что-то выкидывать, сейчас поищу схемки, соберу на коленке и проверю. Я делаю ставку не на мой код а на код который обрабатывает непосредственно сигнал UART и заполняет кольцевой буфер, он сидит на внутреннем прерывании и вполне может давать такую картину

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

vde69, 16 МГц мк это достаточно мощная сила, уж обработать строку точно может. В loop проверяете serial.available, если символ есть не нулевой - отправляете его в отдельную процедуру, там уже флагами определяете / запоминаете статус / позиции / логику и обрабатываете. Если по логике необходимо производить какие либо действия - отправляете в функцию флаг - пришёл ли реальный символ.

vde69
Offline
Зарегистрирован: 10.01.2016

ха... ха... ха...

подключил вот с таким делителем https://i.stack.imgur.com/GOb3K.jpg и все шалтай балтай пропали, все стало ровненько и красиво....

вывод - ттл5 ->>> ттл3.3 даже если работает не значит, что правильно:)

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

 

зы

резисторы 4.7к и 2.2к

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

К сведению - нормальный терминал даёт тайм-код.

Под линуксом я пользуюсь minicom

vde69
Offline
Зарегистрирован: 10.01.2016

что-то я рано обрадовался...

короче ситуация

1. на плате с которой сняты все модули кроме ESP-01 - НЕ работает

2. на переходнике/програматоре (от USB) - работает

 

промерял все контакты, на програматоре 3.34вт, на плате 3.26вт. UART ни там ни там не используется вообще.

Тут меня осенило, надо затестить осцилограф (который в общем купил "поигратся"), промерил напряжения на плате (их 3шт)

+12в идет от основного БП (идет на мегу) - небольшие импульсы, напряжение "играет" в диапазоне 0.1в

+5в идет от +12 через понижайку (должно идти на перефирийку, экран и т.д.) - небольшие импульсы, напряжение "играет" в диапазоне 0.1в, то есть от 5 до 5.1 вт

+3.3в  идет от +12 через понижайку (идет на модули которые питаются от +3,3) - вот тут меня ждала неожиданность, очень сильно "играет", в диапазоне от 2.8в до 3.7в, а вольтметр показывает ровне 3.3

 

понижайки одинаковые, но та которая на 3.3 греется (даже если убрать нагрузку), короче пока основная версия - надо питание "выпрямлять", или понижайку менять или что-то навешивать типа кондеров, фиритов и т.д.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

ВТ - ВАТТЫ, ВОЛЬТЫ не ватты.

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

ESP довольно требовательны по питанию, не так даже по стабильности напряжения, как по току в импульсе. По памяти до 300мА надо. Потому хороший конденсатор заметно помогает. А средний ток не так уж и велик. 

Вообще внутренности ESP - дело довольно темное, официальной науке не известное;) Но некоторые особенности архитектуры сами по себе способны генерить нестабильность времени ответа. Дело в том, что код хранится в флеше, а по ходу работы ,  перед исполнением очередного "куска" кода, как минимум некоторая часть его, иногда,  считывается из флеша в ОЗУ. Это не быстрый процесс. Следовательно если вдруг вызывается функция, кода которой которой нет в ОЗУ, то он начинает считываться, затирая ранее существовавший, и возможно скоро потребующийся снова. Так возникает тормозня. Напоминает свопинг на  ПК.

В некоторой степени этим можно управлять атрибутом  ICACHE_RAM_ATTR. Но насколько он будет не проигнорен - ХЗ. Но от void ICACHE_RAM_ATTR LoopReadData() {.... Хуже не будет.

Еще замечено, ESP любит хороший WiFi. С его антеной - нифига не удивительно. Но помехи, повторный отправки, сбор фрагментов при нарушенном порядке их прихода и пр. прелести плохого канала, наложенные на отот механизм подгрузки в ОЗУ кусков кода существенно могут тормозить работу. Плохой WiFi также возникнет при недостатке тока. Просто мощность излучения понизится и с слабенькой антенны до роутера сигнал задавят помехи. Так что в ESP все завязано жестко. 

vde69
Offline
Зарегистрирован: 10.01.2016

поставил на выход понижайки кондер на 470мф, стало заметно лучше.

с точки зрения скорости возврата json в браузер стало от 150 до 250 при чем в не зависимости где стоит ESP на плате или на програматоре.

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

На второй понижайке таких провалов нет. там вроде все нормально.

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

 

зы

и кстати после того как поставил кондер, понижайка перестала грется !!! Кто объяснит почему? понижайка nfrfz https://aliexpress.ru/item/32725286642.html?spm=a2g0o.search0302.0.0.39d36b4brnWUoa&algo_pvid=e13921ac-e57b-41ca-80a0-b0991d77b4b6&algo_expid=e13921ac-e57b-41ca-80a0-b0991d77b4b6-6&btsid=0b8b036a16170390028723113e6e8a&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_&sku_id=61230666072

gfo
Offline
Зарегистрирован: 20.03.2021

vde69 пишет:

<t_home_1=099;E1>

<t_home_1=-02;OFF>

<t_home_1=015;OK>

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

Кусок кода который ее использует вот

////////////////////
Serial.print("Start at: ");
Serial.println(millis());
MatchState ms;
  char *str = "GET /?atname=MIXXMANN&atpin=9999&atpin1=8888&atpin2=7777&atpin3=6666&atpin4=555 HTTP/1.1v";
  ms.Target (str);
  unsigned int index = 0;
  char buf [100];
  while (true){
    char result = ms.Match ("(%w+)=(%w+)", index);
    if (result == REGEXP_MATCHED)    {
      //Serial.println ("-----");
      //Serial.print ("Matched on: ");
      //Serial.println (ms.GetMatch (buf));
      //Serial.println ("Captures:");
      for (int j = 0; j < ms.level; j++)
         Serial.println (ms.GetCapture (buf, j));
         index = ms.MatchStart + ms.MatchLength;
    }
    else
      break;
  }
  Serial.print("End at: ");
  Serial.println(millis());
////////////////////

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

--- Available filters and text transformations: colorize, debug, default, direct, esp32_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at http://bit.ly/pio-monitor-filters
--- Miniterm on COM4  115200,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
Start at: 159
atname       
MIXXMANN     
atpin        
9999
atpin1       
8888
atpin2       
7777
atpin3       
6666
atpin4       
555
End at: 159 

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

MaksVV
Offline
Зарегистрирован: 06.08.2015

gfo пишет:
Отрабатывает в пределах 1мс. Насколько я понимаю оч даже не плохо.  Может кому пригодится

скетч #31 ищет ID и разбирает строку за 250 мкс (при плохом раскладе, когда ID попался последний в списке; если ID был выше, то быстрее. 

gfo
Offline
Зарегистрирован: 20.03.2021

MaksVV пишет:

gfo пишет:
Отрабатывает в пределах 1мс. Насколько я понимаю оч даже не плохо.  Может кому пригодится

скетч #31 ищет ID и разбирает строку за 250 мкс (при плохом раскладе, когда ID попался последний в списке; если ID был выше, то быстрее. 

Засек в микросекундах

С выводом в консоль 512мкс

Start at: 159287
atname
MIXXMANN
atpin
9999
atpin1
8888
atpin2
7777
atpin3
6666
atpin4
555
End at: 159799

Без вывода в консоль 342мкс.

158187
158529

Мне кажется регулярки тоже имеют право на жизнь. Хотя бы за то что они читабельнее и гибче. и писанины меньше. Хотя, "на вскус и цвет" все фломастеры разные ))

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

gfo пишет:

Отрабатывает в пределах 1мс. 

На чём? На ESP? На малине?

Если на UNO, то что-то сомневаюсь. И очень сильно. Сдаётся мне, Вы нас обманываете.

Я добавил необходимое к Вашему скетчу и, заодно, заменил millis на micros, чтобы поточнее было. Запустил его. Вот получившийся код

#include <Regexp.h>

void setup(void) {
  Serial.begin(57600);
  Serial.print("Start at: ");
  Serial.println(micros());
  MatchState ms;
  char *str = (char *)"GET /?atname=MIXXMANN&atpin=9999&atpin1=8888&atpin2=7777&atpin3=6666&atpin4=555 HTTP/1.1v";
  ms.Target (str);
  unsigned int index = 0;
  char buf [100];
  while (true) {
    char result = ms.Match ("(%w+)=(%w+)", index);
    if (result == REGEXP_MATCHED)    {
      for (int j = 0; j < ms.level; j++)
        Serial.println (ms.GetCapture (buf, j));
      index = ms.MatchStart + ms.MatchLength;
    }
    else
      break;
  }
  Serial.print("End at: ");
  Serial.println(micros());
}

void loop(void) {}

а вот - результат

Start at: 80
atname
MIXXMANN
atpin
9999
atpin1
8888
atpin2
7777
atpin3
6666
atpin4
555
End at: 7340

Как-то больше похоже на 7,2мс, чем на "в пределах одной"? Нет? Я что-то напутал?

Вы, кстати, очень неаккуратно замеряете время (параллельно выполнению работает Serial). Если это исключить, то получается 2,5мс, но всё равно никак не "в пределах одной".

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

gfo пишет:

Без вывода в консоль 342мкс.

Блин, на чём Вы это запускаете? У меня получается совсем не так (см. мой пост выше).

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

gfo
Offline
Зарегистрирован: 20.03.2021

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

Блин, на чём Вы это запускаете? У меня получается совсем не так (см. мой пост выше).

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

Гыг.. та там кусок тестового скетча... пробую с вебсервером поигратся...

#include <Arduino.h>
#include "EEPROM.h"
#include <WiFi.h>
#include <Regexp.h>

#define EEPROM_SIZE 512


const char *ssid = "MIXXMANM";
const char *password = "31011983";
int connections = 0;

String atname;
String atpin;

WiFiServer server(80);
String responseHTML = "<!DOCTYPE html><html>"
                      "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
                      "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}"
                      "</style></head>"
                      "<body><h1>MIXXMANN SETUP</h1>"
                      "<form action=\"\" method=\"GET\">"
                      "<p>AT name: <input type=\"text\" name=\"atname\" size=\"15\" maxlength=\"13\" value=\"%atname%\"></p>"
                      "<p>AT pin: <input type=\"text\" name=\"atpin\" size=\"15\" maxlength=\"4\" value=\"%atpin%\"></p>"
                      "<input type=\"submit\" value=\"SET\">"
                      "</form>"
                      "</body></html>";
String header;
void showConnectionsCount() {
  Serial.print("connections coun:");
  Serial.println(connections);
}
void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info){
  connections += 1;
  showConnectionsCount();
}

void setup() {
  
  WiFi.mode(WIFI_AP);                   
  WiFi.softAP(ssid, password);
  WiFi.onEvent(WiFiStationConnected, SYSTEM_EVENT_AP_STACONNECTED);
  IPAddress ip_address = WiFi.softAPIP();
  server.begin();
  
  Serial.print("AP IP address: ");
  Serial.println(ip_address);

  Serial.begin(115200);
  EEPROM.begin(EEPROM_SIZE);
  
  
  atname=EEPROM.readString(0);
  atpin=EEPROM.readString(16);

  if (atname.isEmpty()){atname="MIXXMANN";}
  if (atpin.isEmpty()){atpin="9999";}
  if (atname.length()>13) {atname=atname.substring(0,13);}
  if (atpin.length()>4) {atpin=atpin.substring(0,4);}

////////////////////

Serial.println(micros());
MatchState ms;
  char *str = "GET /?atname=MIXXMANN&atpin=9999&atpin1=8888&atpin2=7777&atpin3=6666&atpin4=555 HTTP/1.1v";
  ms.Target (str);
  unsigned int index = 0;
  char buf [100];
  while (true){
    char result = ms.Match ("(%w+)=(%w+)", index);
    if (result == REGEXP_MATCHED)    {
      //Serial.println ("-----");
      //Serial.print ("Matched on: ");
      //Serial.println (ms.GetMatch (buf));
      //Serial.println ("Captures:");
      for (int j = 0; j < ms.level; j++)
        // Serial.println (ms.GetCapture (buf, j));
         index = ms.MatchStart + ms.MatchLength;
    }
    else
      break;
  }
  
  Serial.println(micros());
////////////////////
}
void loop()
{

}

Запускаю на ESP32 Ai-Thinker

Замерил по вашей рекомендации особо ниче не поменялось

 

gfo
Offline
Зарегистрирован: 20.03.2021
Start at: 84
atname
MIXXMANN
atpin
9999
atpin1
8888
atpin2
7777
atpin3
6666
atpin4
555
End at: 7344

Это на mega2560

Прискорбно однако.

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