Нужно ускорить процедуру чтения UART
- Войдите на сайт для отправки комментариев
Ср, 24/03/2021 - 22:33
тут ниже тема чужая, не буду в нее влезать...
есть модуль 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; } } }
тормозит find_byte_buf
тормозит find_byte_buf
там особо тормозить нечему
там особо тормозить нечему
Фига-се, нечему! линейный поиск на 250 элементов. К тому же он неправильно написан.
А что Вы так хитро разбираете, почему на написать простой автомат разбора? Можете внятно сформулировать задачу? Тогда я попробовал бы подсказать как автомат написать.
допустим поиск можно немного оптимизировать, но не думаю, что это много даст
по факту мне нужно разобрать строку вида
на 3 строки
Можно расписать кто какие задержки вносит, тогда и принимать решение.
Данные по UART передаются за время X и скорость обработки на это время не влияет. Какую ДОПОЛНИТЕЛЬНУЮ задержку вносит разбор строки?
Дополнение. И может не стоит каждый раз искать хвост буфера, а хранить в глобальной переменной указатель на него.
по факту мне нужно разобрать строку вида
на 3 строки
По символу '<' очищаете все три строки и принимаете символы в первую пока не поступит '=' Тогда начинаете прием во вторую до символа ';', по нему начинаете прием в третью строку до >. Только очищать - это не совсем забивать все нулями, лучше просто указатель устанавливать на начало строки. Можно 3 указателя держать, но лучше один и переменную состояния приема. А завершать прием в каждую строку - не забывать добавлять 0 в конец. Ну и контроль длинны, чтоб за буфера не вылетать.
Кстати на String этот Ваш код не сильно то и завязан, выкиньте его и забудьте.
Можно еще и совместить с поиском id в таблице out_id. Тогда по завершению приема id сразу будет известен есть ли в таблице и с каким индексом. Нет предела совершенству...
На esp есть sscanf, std::map, и прочие удобства не на улице. Не надо писать код как инвалид.
На esp есть sscanf, std::map, и прочие удобства не на улице. Не надо писать код как инвалид.
regex?
по факту мне нужно разобрать строку вида
на 3 строки
На какие три строки. Я же Вам написал, что могу попробовать Вам помочь, но для этого мне нужно чёткое описание задачи и несколько примеров типа "входящая строка->результат". А не фраза "на три строки" про текст в котором всего два значения.
Нужна помощь? Не ленитесь объяснить задачу.
Очень неаккуратное регулярное выражение. Такие можно применять на мощных процессорах, где вопрос ресурсов вообще не стоит.
на столько я понял то вживую входящие данные будут выглятеть как то вот так: <45=333;ОК>
жаль нет под рукой железки, интересно замерить...
хотя че гадать, давайте дождемся пока чел выложит пример входящих данных, сравню тогда ваш и свой вариант. самому интересно.
<t_home_1=099;E1>
<t_home_1=-02;OFF>
<t_home_1=015;OK>
Вообще я пойду путем одновременного поиска ID в списке с чтением данных.
добавлю в out_id колонку "количество" и заведу новый байтовый массив для отсева (что-то вроде индексного дерева будет), и параметры value и status буду считывать сразу по назначению, то есть вообще обойдусь без буферизации buf_UART
кстати вопрос:
0.15 сек на получение ответа в браузере на JSON запрос - это нормально? ответ примерно 1200 символов
Вообще я пойду путем одновременного поиска 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".
Если out_id был бы большой по размеру (или есть перспектива что выростит), имело бы смысл его записать сортированным и искать более интелектуально. Но при том размере, что в примере это без толку.
На esp есть sscanf, std::map, и прочие удобства не на улице. Не надо писать код как инвалид.
regex?
Не. rkit просто тупой.
Если out_id был бы большой по размеру (или есть перспектива что выростит), имело бы смысл его записать сортированным и искать более интелектуально. Но при том размере, что в примере это без толку.
можно и сортировано, для этого нужны 2 переменные,
изначально эти переменные устанавливаем в начало и конец массива, получили первый знак, пробежались от первой ко второй переменной по первой букве, где совпало туда сдвигаем переменные.
единствено не удобно будет придется забить 0 до конца элемента и хранить в сортированном виде
Не нужно. Выиграша по времени на небольшом наборе данных не будет. А сложней всеж.
зато не нужно хранить все символы которые уже были проверены и не нужно по ним бегать
Зачем их хранит? Они и в out_id хорошо хранятся. Это первые символы в элементе из out_id , найденном на предыдущем этапе. Пишу же, хранить достаточно " индекс по out_id и номер принимаемого символа". Больше ничего для поиска по мере приема не нужно.
ппц лютая борьба за каждый байт , у вас что проект на тиньке? Написали ведь, что пакеты маленькие. Что нельзя на буфер байт 20 выделить? и строко_сишными процедурами парсить.
А толку выделять ненужное. Выделитель, ты код пишешь или стену мажешь? ТС уже навыделял был, с чем сюда и обратился. Так что иди се, с миром, выделяй из себя в другом месте, можешь даже сразу фреймворками откладывать ;)
переписал без использования String, постарался написать относительно быстрый код, стало лучше но все равно очень странное поведение.
когда ESP подсоединена к USB (через переходник/программатор) и с консоли я туда кидаю строки типа "<t_home_1=099;E1>" и кидаю часто то все равно сайт грузится ровно, скорость получения ответа 120....150 мсек,
как только я подключаю к МЕГЕ то скорость получения JSON в браузере становится очень сильно не стабильная, от 200 до 1500 мсек, плавает и на первый взгляд не зависит от того много или мало туда МЕГА отдает данных
собственно сам код, но я уже не думаю, что дело в нем...
пока вот так https://ibb.co/McxQ5p9
нафига блокирующий код с while ? ведь еще что то в лупе висит, может поэтому тормозит не?
нафига блокирующий код с while ? ведь еще что то в лупе висит, может поэтому тормозит не?
что именно Вы называете "блокирующий код" ? у меня нет ни одной паузы, и код не ждет когда весь пакет придет, именно по этому у меня глобальные а не локальные переменные которые отвечают за разбор данных.
хотя может и стоит поставить выход из цикла по таймеру, например 0.1 сек
я имел ввиду, почему бы не читать по одному символу за каждый проход луп?
был час на работе свободный, предлагаю такой говнокод
я имел ввиду, почему бы не читать по одному символу за каждый проход луп?
По моему while(Serial.available()) скорее полезен, чем вреден.
Мертвое зависание в данном цикле невозможно (если темп обработки быстрее темпа поступления новых).
(Дополнено - для перестраховки можно добавить выход из цикла при завершении разбора.)
А если в буфере скопилось несколько символов, то значит внешний цикл работает слишком медленно, и обработка по одному символу может только ухудшить ситуацию.
А вы попробуйте обработку посимвольно, при нормальной реализации все отлично работает.
если проблему ТС решит код, в котором за проход луп по символу добавляется, то говорить не о чем.
У меня вообще думка, что код тут вообще не при чем....
буду пробовать мерить чего там с UART происходит, просто все отличие схемы когда все стабилно от плавающей - внешнее воздействие.
размеру и частоте передачи я повторил уровень соединения с мегой. и все равно все стабильно...
что-то мне кажется проблема в том, что соединенные напрямую ttl 5в и 3.3в могут что-то выкидывать, сейчас поищу схемки, соберу на коленке и проверю. Я делаю ставку не на мой код а на код который обрабатывает непосредственно сигнал UART и заполняет кольцевой буфер, он сидит на внутреннем прерывании и вполне может давать такую картину
vde69, 16 МГц мк это достаточно мощная сила, уж обработать строку точно может. В loop проверяете serial.available, если символ есть не нулевой - отправляете его в отдельную процедуру, там уже флагами определяете / запоминаете статус / позиции / логику и обрабатываете. Если по логике необходимо производить какие либо действия - отправляете в функцию флаг - пришёл ли реальный символ.
ха... ха... ха...
подключил вот с таким делителем https://i.stack.imgur.com/GOb3K.jpg и все шалтай балтай пропали, все стало ровненько и красиво....
вывод - ттл5 ->>> ттл3.3 даже если работает не значит, что правильно:)
теперь придется на плате резисторы мостырить....
зы
резисторы 4.7к и 2.2к
К сведению - нормальный терминал даёт тайм-код.
Под линуксом я пользуюсь minicom
что-то я рано обрадовался...
короче ситуация
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 греется (даже если убрать нагрузку), короче пока основная версия - надо питание "выпрямлять", или понижайку менять или что-то навешивать типа кондеров, фиритов и т.д.
ВТ - ВАТТЫ, ВОЛЬТЫ не ватты.
ESP довольно требовательны по питанию, не так даже по стабильности напряжения, как по току в импульсе. По памяти до 300мА надо. Потому хороший конденсатор заметно помогает. А средний ток не так уж и велик.
Вообще внутренности ESP - дело довольно темное, официальной науке не известное;) Но некоторые особенности архитектуры сами по себе способны генерить нестабильность времени ответа. Дело в том, что код хранится в флеше, а по ходу работы , перед исполнением очередного "куска" кода, как минимум некоторая часть его, иногда, считывается из флеша в ОЗУ. Это не быстрый процесс. Следовательно если вдруг вызывается функция, кода которой которой нет в ОЗУ, то он начинает считываться, затирая ранее существовавший, и возможно скоро потребующийся снова. Так возникает тормозня. Напоминает свопинг на ПК.
В некоторой степени этим можно управлять атрибутом ICACHE_RAM_ATTR. Но насколько он будет не проигнорен - ХЗ. Но от
void
ICACHE_RAM_ATTRLoopReadData() {...
. Хуже не будет.Еще замечено, ESP любит хороший WiFi. С его антеной - нифига не удивительно. Но помехи, повторный отправки, сбор фрагментов при нарушенном порядке их прихода и пр. прелести плохого канала, наложенные на отот механизм подгрузки в ОЗУ кусков кода существенно могут тормозить работу. Плохой WiFi также возникнет при недостатке тока. Просто мощность излучения понизится и с слабенькой антенны до роутера сигнал задавят помехи. Так что в ESP все завязано жестко.
поставил на выход понижайки кондер на 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
<t_home_1=099;E1>
<t_home_1=-02;OFF>
<t_home_1=015;OK>
В общем дошли руки и я все таки затестил разбор строки при помощи регулярных выражений )) мне понравилось. Использовал вот эту библиотеку
Кусок кода который ее использует вот
Ну и собственно результат разбиения на подстроки
Отрабатывает в пределах 1мс. Насколько я понимаю оч даже не плохо. Может кому пригодится
Засек в микросекундах
С выводом в консоль 512мкс
Без вывода в консоль 342мкс.
Мне кажется регулярки тоже имеют право на жизнь. Хотя бы за то что они читабельнее и гибче. и писанины меньше. Хотя, "на вскус и цвет" все фломастеры разные ))
Отрабатывает в пределах 1мс.
На чём? На ESP? На малине?
Если на UNO, то что-то сомневаюсь. И очень сильно. Сдаётся мне, Вы нас обманываете.
Я добавил необходимое к Вашему скетчу и, заодно, заменил millis на micros, чтобы поточнее было. Запустил его. Вот получившийся код
а вот - результат
Как-то больше похоже на 7,2мс, чем на "в пределах одной"? Нет? Я что-то напутал?
Вы, кстати, очень неаккуратно замеряете время (параллельно выполнению работает Serial). Если это исключить, то получается 2,5мс, но всё равно никак не "в пределах одной".
Без вывода в консоль 342мкс.
Блин, на чём Вы это запускаете? У меня получается совсем не так (см. мой пост выше).
Может приведёте полный скетч, чтобы я Ваш мог запустить?
Блин, на чём Вы это запускаете? У меня получается совсем не так (см. мой пост выше).
Может приведёте полный скетч, чтобы я Ваш мог запустить?
Гыг.. та там кусок тестового скетча... пробую с вебсервером поигратся...
Запускаю на ESP32 Ai-Thinker
Замерил по вашей рекомендации особо ниче не поменялось
Это на mega2560
Прискорбно однако.
Евгений, чесно не обманываю. ... но вот что прям такая разительная разница между есп и мегой меня немного повегла в шок. Порядок цен ведь тот же...