• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Где найти подробное описания по работе с pubsubclient.h

Bposter

New member
Где найти на русском подробное описание с библиотекой pubsubclient ? В яндексе не че не нахожу
 

Victor

Administrator
Команда форума
Популярных библиотек pubsubclient на сегодняшний момент 2
1. Устанавливается через стандартный менеджер библиотек GitHub - knolleary/pubsubclient: A client library for the Arduino Ethernet Shield that provides support for MQTT. (имеет ограничение на объем передаваемых данных)
2. Форк от imroy GitHub - Imroy/pubsubclient: A client library for the ESP8266 that provides support for MQTT (не имеющий вышеуказанного ограничения)

Русского описания я не встречал, но очень удобно смотреть доступные публичные функции в исходнике библиотеки pubsubclient/PubSubClient.h at master · Imroy/pubsubclient · GitHub
там понятно какие параметры и каких типов можно передавать
 

Bposter

New member
Нашел вот такую функцию
Код:
// Функция получения данных от сервера

void callback(const MQTT::Publish& pub)
{
Serial.print(pub.topic()); // выводим в сериал порт название топика
Serial.print(" => ");
Serial.print(pub.payload_string()); // выводим в сериал порт значение полученных данных

String payload = pub.payload_string();

if(String(pub.topic()) == "led/1") // проверяем из нужного ли нам топика пришли данные
{
int stled = payload.toInt(); // преобразуем полученные данные в тип integer
}
}
Может подскажете как обратится к этой функции из loop чтоб вытащить значение передаваемые приложением на андроид?
 

Сергей_Ф

Moderator
Команда форума
@Bposter обратите внимание, к этой функции не обращаются. Ее назначают в качестве обработчика сообщения. Почитайте про callback функции, что бы было понятнее.
 

Bposter

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

При условии что допустим все устройства питаются от 9 вольт и мощность блока хватит на все так правильно будет?
 

combasoft

New member
..callback функции..
Подскажите, откуда вызывается назначаемая callback функция? Я посмотрел код клиента и похоже, что колбэк в итоге дергаем из основного цикла(client.loop(), который как раз сидит в основном цикле ), т.е. в данной реализации это не какой-то асинхронный процесс, который навесили на прерывание.
Мне это почему интересно: я предполагаю, что могут быть ситуации, когда от брокера прилетит толпа сообщений и скорость их поступления окажется больше скорости их обработки на стороне esp и часть пакетов будет потеряна. При этом, если б колбек функция вызывалась асинхронно, то можно было б сначала все мессаджи складывать в программный FIFO, а в основном цикле (или в колбек функции + либа Ticker) с небольшой задержкой по времени их обрабатывать (каждые 0.5 с например).
Ну а если колбек будет вызываться из основного цикла, то нет смысла тратить время на складывание данных в буфер - их нужно тут же обрабатывать.
 

Victor

Administrator
Команда форума
Подскажите, откуда вызывается назначаемая callback функция?
как-бы ниоткуда, т.е. мы сами ее никогда не вызываем.
ее вызывает библиотека, когда приходит сообщение (в loop не забываем добавить[inline]client.loop()[/inline])
применяйте к ней те же правила, что и к прерываниям (минимум операций, никакого вывода в консоль и т.п.)
эту функцию желательно писать так, чтобы она была реентерабельна, т.е. один из вариантов так, как вы и описали, с FIFO.
Но у брокера тоже все не мгновенно происходит, поэтому можно просто сложить сообщение во временную переменную (FIFO=одно сообщение) - для большинства применений этого достаточно.
Есть еще AsyncMQTT (на AsyncIO) - можете посмотреть ее.
 

combasoft

New member
..[inline]client.loop()[/inline]..
можно просто сложить сообщение во временную переменную (FIFO=одно сообщение)..
..AsyncMQTT (на AsyncIO) ..
Ну вот вы тоже говорите про client.loop(), а эта функция сидит в основном цикле. Т.е. как такового асинхронного вызова колбэк функции не происходит(может я не верно это понимаю).
Я дальше pubSubClient не копал, есть надежда, что возможно в самой библиотеке MQTT есть асинхронные вызовы + некий буфер в который складываются сообщения.
Спасибо за наводку, гляну что есть такое AsyncMQTT.
А вот насчет организации работы с FIFO. Как обычно делают?
Мне видится пара вариантов:
  • - в колбэк функции pubSubClient пришедшее сообщение сразу складываем в буфер. Из этого буфера добывает сообщения другая колбэк функция - использующаяся совместно с либой Ticker. Из плюсов мне видится простота решения, из минусов - некоторая задержка реакции на сообщение, максимум - на заданные период в тикере.
  • - в колбек функции pubSubClient пришедшее сообщение не сразу кладется в буфер, сначала смотрим флажок, который говорит, что прежний вызов колбек функции еще не завершился.
    Если флаг сброшен - выставляем его и начинаем обрабатывать сообщение. После обработки сообщения смотрим - нет ли чего в буфере. Если есть - обрабатываем, если нет - флаг сбрасываем и завершаем работу функции. Если на момент вызова функции флаг был установлен - просто добавляем очередное сообщение в буфер. Из плюсов этого решения - быстрая реакция на сообщение, из минусов - конкурентный доступ к флагу "занятости", который непонятно как организовать и допущение того, что для каждого вызова функции рождается новый процесс, т.е. одна и та же функция может работать в нескольких экземплярах в один и тот же момент времени - а это может быть не так. Т.е. вопрос - что произойдет, если вызвать функцию еще раз до того, как она завершит свою работу с прежнего вызова?
 
Последнее редактирование:

combasoft

New member
И еще пока не понятно как организовывать длительные процессы на которые пришел запрос. Я о публикации данных в топик. Т.е. вот допустим пришел запрос вида "дай мне последнее считанное значение с датчика Х". В ответ нужно будет начать что-то там отвечать, но если делать это тут же, в колбэк функции, есть шанс потерять часть следующих входных данных. Я думаю организовать пару FIFO - один для входных данных (наполняется в колбэк pubSubClient, это все, что там делается), второй - для данных, которые нужно будет опубликовать. А работать с ними так: создать пару колбэк функций для тикера - А и Б. колбэк А читает данные из буфера входных данных, как-то там их обрабатывает: это может быть прямое управление пинами или там изменение каких-то настроечных праметров, а может быть обработка запроса на передачу данных - состояния пинов, значений настроечных параметров, датчиков. В последнем случае запрашиваемые значения добавляются в буфер для публикации. колбэк Б занимается тем, что смотрит буфер выходных данных и если там что-то есть - извлекает данные и публикует. По одному элементу буфера за каждый вызов.
 

combasoft

New member
Поигрался с колбэк функциями.
О грустном: если одна и та же колбэк функция будет запущена до того, как успеет отработать ее предыдущая запущенная копия, то ловим рестарт системы по WDT. Т.е. нельзя давать функции "наступать самой себе на пятки". Для этого она должна вызываться с заведомо бОльшим интервалом времени, чем она сама отрабатывает.
Что до совместной работы нескольких колбэк функций, то вроде б все норм.
В аттаче я прикрепил наброски того как мне видится обработка входящих сообщений от брокера. Если увидите какие-либо недочеты или возможные узкие моменты - пожалуйста подскажите их.
Издалека же) решение выглядит рабочим вариантом.
В первом приближении:
  • - в колбэк функцию для pubSubClient будет добавлен парсер (наверное XML) выходные данные которого будут положены во входной буфер,
  • - в колбэк А -некоторые рулы, которые в зависимости от данных с датчиков и настроечных параметров меняют состояние пинов (либо значение некоторых настроечных параметров - так одни рулы смогут влиять на поведение других рулов).
  • - почти нетронутым останется колбэк Б - там просто вынимаем что есть в выходном буфере и публикуем.
  • - в качестве буферов будут кольцевые FIFO.
  • - чтение датчиков хочу делать в основном цикле.
  • - так же хочу завести еще одну колбэк функцию для запуска рулов - там период будет побольше, что-то вроде раз в 5 минут, т.к. предполгается рулить вялотекущими процессами.

Т.е. запуск рулов и последующее обновление состояний выходных пинов будет осуществляться как "по требованию" со стороны брокера, так и штатно.
 

Вложения

combasoft

New member
@nikolz, верно, наплодив колбэки я не получу несколько независимых тредов. В итоге они всё равно будут выстроены друг за другом. Пожалуй, я их использовал для упрощения разграничения зон ответственности. А вот интересно - если в данный момент времени будет работать колбэк А, который вызывается по тикеру и в это же время прилетит сообщение от брокера - колбек для pubSubClient вызовется, прервав работу колбэк А? Или мы просто ждем завершения работы очередной функции и только потом управление передается следующей? Хочется в колбэке для pubSubClient делать какое-то не длительно действия, типа перекидывания сообщения в буфер и быть при этом уверенным, что это произойдет в любом случае. Хотя.. колбэк - это обычная функция, и что б одна колбэк функция прервала выполнение другой колбэк функции, она должна быть запущена от функции, которая вызывается для обработки прерывания.
 
Последнее редактирование:

Сергей_Ф

Moderator
Команда форума
@combasoft колбэк функция должна выполняться полностью, без прерываний. Вы сколько mqtt событий в секунду собрались обрабатывать, что такие вопросы Вас волнуют?
 

combasoft

New member
Про приоритеты - этого я еще не знал. Можно пример?
А вообще, похоже зря я всё так усложняю. Смотрите: в основном цикле дергаем client.loop(), внутри которого вызывается указанный юзером колбэк. В этой функции проверяем - не пришло ли нам сообщение. Если б все это выполнялось в один поток, то эта проверка сработала, если б мы стали проверять приход сообщения прям в момент его прихода. Что конечно крайне маловероятно. Зато если предположить(тут надо мануалы по esp смотреть), что процессом приема-передачи сообщений занимается отдельный wifi модуль, да еще с буфером в несколько сообщений, то самому МК остается только проверить не заполнен ли этот буфер и если он не пуст - прочитать из него очередное сообщение.
Иначе говоря, можно особо не заморачиваться (если только случай не исключительный) с тем как много времени займет выполнение пользовательской колбэк функции, ну разве что датчики внутри этой функции не читать.
 

combasoft

New member
Если не сложно, расскажите в кратце что происходит с пакетом данных, когда он долетает до ESP c MQTT клиентом на ней, в плоть до попадания в определенный пользователем колбэк.
Почитать "про процессоры" это довольно абстрактно - могу копать много, но не в ту сторону, в итоге без результата. Да и как вникание в архитектуру МК поможет с колбеками, которые (если я правильно понимаю) специфичны именно для ЯП, а не архитектуры МК?
 

Victor

Administrator
Команда форума
расскажите в кратце что происходит с пакетом данных, когда он долетает до ESP c MQTT клиентом на ней, в плоть до попадания в определенный пользователем колбэк.
таких мануалов я не встречал, вам поможет изучение исходных кодов клиента (не важно - для микроконтроллеров или нет). Выберите язык в котором больше разбираетесь и найдите open source MQTT клиент, написанный на этом языке и изучайте.
P.S. Я, вообще, поддерживаю дотошность, но в данном случае, по моему мнению, вы перебарщиваете. Не надо сильно в дебри залезать - по упрощенной схеме, как я вам уже сказал все работает - проверено. Решайте проблемы по мере поступления.
 

combasoft

New member
@nikolz, вы меня не поняли. Я примерно понимаю идею MQTT. Спрашивал я за то, что происходит на стороне ESP. Вот например: "прилетел пакет". Как ESP об этом узнает - что ей что-то пришло? Если знаете и не сложно написать - напишите, мне это сэкономит время. Может еще кому-нибудь кроме меня. Ярлыки про дилетантов оставье при себе.
механизм обработки событий без ожидания т е по появлению
если ничего нового не придумали, то это прерывания. Т.е. с таблицей векторов прерываний, то, что среди прерываний есть приоритеты - я в курсе, но я не слышал, чтоб эти функции обзывали как колбэк функции. Я только недавно с этим видом функций ознакомился,
могу конечно ошибаться, но в кратце такую функцию можно описать так: колбэк функция - это функция, которая передается в качестве аргумента для другой функции.
@Victor, да, я усложнил без надобности. Попробую сделать по приведенному вами примеру, спасибо. Будут сложности - буду копать.
 

Victor

Administrator
Команда форума
Будут сложности - буду копать.
ок, только в нашем случае несколько "слоев реальности"
  • самый верхний, видимый слой - обмен по протоколу MQTT в вашем скетче
  • средний слой - обертка функций Espressif SDK - это Arduino IDE
  • следующий слой - стек TCP/IP в Espressif SDK (почти полностью разобран @pvvx)
  • далее WiFi стек, который, в принципе, известен, но реализация в ESP8266 практически закрыта для нас (она частично в SDK и частично в BIOS чипа)
Все это как-то работает на одноядерном процессоре, Arduino волшебным образом переключает контексты между SDK и вашим скетчем в свободное время. Поэтому говорить об истиной асинхронности не очень верно, тут @nikolz прав.

Я все это к тому, что полный ответ на ваш вопрос достаточно объемный и имеет смысл только при наличии определенных знаний (не малых) у того, кто спрашивает.
Ответить со всеми деталями на него могут пара тысяч человек в мире, но я не из их числа. А понять ответ смогут лишь несколько сотен тысяч человек в мире. Я лишь отношу себя к тем, кто примерно понимает, что там происходит внутри.

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

combasoft

New member
ок, только в нашем случае несколько "слоев реальности"
  • самый верхний, видимый слой - обмен по протоколу MQTT в вашем скетче
  • средний слой - обертка функций Espressif SDK - это Arduino IDE
  • следующий слой - стек TCP/IP в Espressif SDK (почти полностью разобран @pvvx)
  • далее WiFi стек, который, в принципе, известен, но реализация в ESP8266 практически закрыта для нас (она частично в SDK и частично в BIOS чипа)
Отлично! Спасибо)
 
Сверху Снизу