Продолжаем работу с протоколом MQTT.
На данном уроке мы попробуем передать (опубликовать) с помощью данного протокола осознанные данные, а именно значение температуры, снятое с датчика DS18B20.
С протоколом MQTT мы разобрались в прошлом уроке, а с датчиком DS18B20 мы также умеем работать.
В качестве брокера MQTT у нас будет также Mosquitto, установленный на плате Raspberry PI, поэтому давайте её включим
В качестве клиента у нас также будет плата с контроллером ESP32, к которой мы подключи датчик DS18B20, так же как мы делали в последних уроках по данному датчику. Логический анализатор мы подключать не будем. Подключим мы пока один датчик
А проект мы сделаем из проекта прошлого урока с именем WIFI_STA_TCP_MQTT и присвоим ему новое имя MQTT_RMT_DS18B20.
Прежде чем открыть наш новый проект в среде, скопируем в его каталог в подкаталог main файлы owb.h, owb.c, ds18b20.h и ds18b20.c из проекта урока 41 с именем RMT_DS18B20.
Откроем наш проект в Espressif IDE и в файле Kconfig.projbuild немного исправим главное меню
menu "TCP MQTT DS18B20 Configuration"
После пункта config LED_GPIO добавим пункт с настройкой ножки для датчика
config ONE_WIRE_GPIO
int "ONE WIRE GPIO number"
range 0 48
default 4
help
GPIO number to ONE WIRE.
В файле CMakeLists.txt добавим наши модули для работы с датчиком
set(COMPONENT_SRCS "main.c wifi.c mqtt.c owb.c ds18b20.c")
Теперь у нас скорей всего соберётся проект.
Зайдём в конфигуратор и настроим адрес брокера MQTT, если он изменился, также в случае изменения настроим ножку датчика и учётные данные сети WiFi.
В файле main.h удалим подключение SPIFFS
#include "esp_spiffs.h"
и
#include "spiffs_config.h"
Подключим библиотеку для работы с датчиком температуры
1 2 3 |
#include "wifi.h" #include "owb.h" #include "ds18b20.h" |
В файле ds118b20.h объявим макрос максимального количества устройств
1 2 3 4 |
#include "owb.h" //--------------------------------------------------------------------- #define MAX_DEVICES (8) //--------------------------------------------------------------------- |
В файле main.c объявим макрос разрешающей способности датчика
1 2 3 4 |
static const char *TAG = "main"; //------------------------------------------------------------- #define DS18B20_RESOLUTION (DS18B20_RESOLUTION_12_BIT) //------------------------------------------------------------- |
В функции app_main удалим запуск клиента MQTT
mqtt_start();
Применим задержку, чтобы датчик запустился
1 2 |
ESP_LOGI(TAG, "wifi_init_sta: %d", ret); vTaskDelay(2000 / portTICK_PERIOD_MS); |
Дальнейший код инициализации 1-wire для работы с датчиками взят практически полностью из проекта урока 41 за исключением непосредственно измерения температуры. Вместо этого в коде будет запуск сервера MQTT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
vTaskDelay(2000 / portTICK_PERIOD_MS); ESP_LOGI(TAG, "OWB Start"); // Create a 1-Wire bus, using the RMT timeslot driver OneWireBus * owb; owb_rmt_driver_info rmt_driver_info; owb = owb_rmt_initialize(&rmt_driver_info, CONFIG_ONE_WIRE_GPIO, RMT_CHANNEL_1, RMT_CHANNEL_0); owb_use_crc(owb, true); // enable CRC check for ROM code // Find all connected devices printf("Find devices:\n"); OneWireBus_ROMCode device_rom_codes[MAX_DEVICES] = {0}; int num_devices = 0; OneWireBus_SearchState search_state = {0}; bool found = false; owb_search_first(owb, &search_state, &found); while (found) { char rom_code_s[17]; owb_string_from_rom_code(search_state.rom_code, rom_code_s, sizeof(rom_code_s)); printf(" %d : %s\n", num_devices, rom_code_s); device_rom_codes[num_devices] = search_state.rom_code; ++num_devices; owb_search_next(owb, &search_state, &found); } printf("Found %d device%s\n", num_devices, num_devices == 1 ? "" : "s"); if (num_devices == 1) { OneWireBus_ROMCode rom_code; owb_status status = owb_read_rom(owb, &rom_code); if (status == OWB_STATUS_OK) { char rom_code_s[OWB_ROM_CODE_STRING_LENGTH]; owb_string_from_rom_code(rom_code, rom_code_s, sizeof(rom_code_s)); printf("Single device %s present\n", rom_code_s); } else { printf("An error occurred reading ROM code: %d", status); } } else { // Search for a known ROM code (LSB first): // 0xb60416847630ff28 OneWireBus_ROMCode known_device1 = { .fields.family = { 0x28 }, .fields.serial_number = { 0xff, 0x30, 0x76, 0x84, 0x16, 0x04 }, .fields.crc = { 0xb6 }, }; char rom_code_s1[OWB_ROM_CODE_STRING_LENGTH]; owb_string_from_rom_code(known_device1, rom_code_s1, sizeof(rom_code_s1)); bool is_present = false; owb_status search_status = owb_verify_rom(owb, known_device1, &is_present); if (search_status == OWB_STATUS_OK) { printf("Device %s is %s\n", rom_code_s1, is_present ? "present" : "not present"); } else { printf("An error occurred searching for known device: %d", search_status); } // Search for a known ROM code (LSB first): // 0x625d53c70664ff28 OneWireBus_ROMCode known_device2 = { .fields.family = { 0x28 }, .fields.serial_number = { 0xff, 0x64, 0x06, 0xc7, 0x53, 0x5d }, .fields.crc = { 0x62 }, }; char rom_code_s2[OWB_ROM_CODE_STRING_LENGTH]; owb_string_from_rom_code(known_device2, rom_code_s2, sizeof(rom_code_s2)); is_present = false; search_status = owb_verify_rom(owb, known_device2, &is_present); if (search_status == OWB_STATUS_OK) { printf("Device %s is %s\n", rom_code_s2, is_present ? "present" : "not present"); } else { printf("An error occurred searching for known device: %d", search_status); } // Search for a known ROM code (LSB first): // 0xad6629c70664ff28 OneWireBus_ROMCode known_device3 = { .fields.family = { 0x28 }, .fields.serial_number = { 0xff, 0x64, 0x06, 0xc7, 0x29, 0x66 }, .fields.crc = { 0xad }, }; char rom_code_s3[OWB_ROM_CODE_STRING_LENGTH]; is_present = false; owb_string_from_rom_code(known_device3, rom_code_s3, sizeof(rom_code_s3)); search_status = owb_verify_rom(owb, known_device3, &is_present); if (search_status == OWB_STATUS_OK) { printf("Device %s is %s\n", rom_code_s3, is_present ? "present" : "not present"); } else { printf("An error occurred searching for known device: %d", search_status); } } // Create DS18B20 devices on the 1-Wire bus DS18B20_Info * devices[MAX_DEVICES] = {0}; for (int i = 0; i < num_devices; ++i) { DS18B20_Info * ds18b20_info = ds18b20_malloc(); // heap allocation devices[i] = ds18b20_info; if (num_devices == 1) { printf("Single device optimisations enabled\n"); ds18b20_init_solo(ds18b20_info, owb); // only one device on bus } else { ds18b20_init(ds18b20_info, owb, device_rom_codes[i]); // associate with bus and device } ds18b20_use_crc(ds18b20_info, true); // enable CRC check on all reads ds18b20_set_resolution(ds18b20_info, DS18B20_RESOLUTION); } // Check for parasitic-powered devices bool parasitic_power = false; ds18b20_check_for_parasite_power(owb, ¶sitic_power); if (parasitic_power) { printf("Parasitic-powered devices detected"); } owb_use_parasitic_power(owb, parasitic_power); if (num_devices > 0) { mqtt_start(owb, devices, num_devices); } else { printf("\nNo DS18B20 devices detected!\n"); } for (int i = 0; i < num_devices; ++i) { ds18b20_free(&devices[i]); } owb_uninitialize(owb); printf("Restarting now.\n"); fflush(stdout); vTaskDelay(1000 / portTICK_PERIOD_MS); esp_restart(); |
По желанию вы можете удалить здесь некоторую часть кода, отвечающего за различную диагностику устройств на датчике. Я решил пока всё оставить, не зря же это мы писали.
Также нам надо при запуске клиента MQTT передать кое-какие параметры. Поэтому перейдём в файл mqtt.c и для начала в функции удалим подписки на топики
msg_id = esp_mqtt_client_subscribe(client, "house2/room2/cmd0", 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_subscribe(client, "house2/room2/cmd1", 1);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
msg_id = esp_mqtt_client_subscribe(client, "house2/room2/cmd2", 2);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
В заголовке функции добавим эти самые параметры
void mqtt_start(const OneWireBus * bus, const DS18B20_Info * ds18b20_info[MAX_DEVICES], int num_devices)
Не забываем проделать то же самое и с прототипом в заголовочном файле mqtt.h.
Также в данном файле подключим библиотеку для работы с датчиком
1 2 3 |
#include "lwip/netdb.h" #include "owb.h" #include "ds18b20.h" |
Вернёмся в функцию app_main файла main.c и при вызове функции старта клиента MQTT передадим наши параметры
mqtt_start(owb, devices, num_devices);
Вернёмся в файл mqtt.c и в функции объявим символьный массив, а также целочисленный массив для подсчёта количества ошибок для каждого устройства
1 2 3 4 5 |
.username = "mosquitto", }; char str0[15] = {0}; int errors_count[MAX_DEVICES] = {0}; |
Вот эта переменная нам будет не нужна, удалим её
uint16_t i = 0;
Зафиксируем время
1 2 3 |
while(1) { TickType_t last_wake_time = xTaskGetTickCount(); |
Удалим объявление переменной идентификатора сообщения
int msg_id;
Получим показания температуры от самого первого датчика и объявим парочку массивов
1 2 3 4 5 6 |
if (bits & MQTT_EVT_CONNECTED) { ds18b20_convert_all(bus); ds18b20_wait_for_conversion(ds18b20_info[0]); float readings[MAX_DEVICES] = { 0 }; DS18B20_ERROR errors[MAX_DEVICES] = { 0 }; |
Дальнейший код в данном блоке удалим.
Получим температуру с остальных датчиков
1 2 3 4 5 |
DS18B20_ERROR errors[MAX_DEVICES] = { 0 }; for (int i = 0; i < num_devices; ++i) { errors[i] = ds18b20_read_temp(ds18b20_info[i], &readings[i]); } |
Подсчитаем ошибки
1 2 3 4 5 6 7 8 9 |
errors[i] = ds18b20_read_temp(ds18b20_info[i], &readings[i]); } for (int i = 0; i < num_devices; ++i) { if (errors[i] != DS18B20_OK) { ++errors_count[i]; } } |
Соберём показания в строковое значения и отобразим их в терминале
1 2 3 4 |
++errors_count[i]; } sprintf(str0, "%.1f", readings[i]); printf("%d: %s, %d errors\n\n", i, str0, errors_count[i]); |
Передадим показания с разным уровнем качества обслуживания (для эксперимента, потом можно будет передавать с одинаковым)
1 2 3 4 |
printf("%d: %s, %d errors\n\n", i, str0, errors_count[i]); if(i==0) esp_mqtt_client_publish(client, "house2/room1/temp0", str0, 0, 0, 0); if(i==1) esp_mqtt_client_publish(client, "house2/room1/temp1", str0, 0, 1, 0); if(i==2) esp_mqtt_client_publish(client, "house2/room1/temp2", str0, 0, 2, 0); |
Выйдем из цикла и в случае подключения не всех трёх датчиков передадим нулевые показания
1 2 3 4 |
if(i==2) esp_mqtt_client_publish(client, "house2/room1/temp2", str0, 0, 2, 0); } if(num_devices < 3) esp_mqtt_client_publish(client, "house2/room1/temp2", "0.0", 0, 2, 0); if(num_devices < 2) esp_mqtt_client_publish(client, "house2/room1/temp1", "0.0", 0, 1, 0); |
Вместо обычной задержки
vTaskDelay(2000 / portTICK_PERIOD_MS);
будем использовать продвинутую
vTaskDelayUntil(&last_wake_time, 2000 / portTICK_PERIOD_MS);
Запустим MQTT Explorer, удалим там старые подписки и подпишемся на новые топики одной строкой сразу с максимальным уровнем QoS
Соединимся там с сервером, соберём наш код и прошьём контроллер.
Через некоторое время мы увидим, как начнут приходить наши данные клиенту 2
Подключим ещё два датчика
Перезагрузим контроллер и посмотрим результат в MQTT Explorer
Отлично, все показания поступают.
Попробуем нагреть рукой датчики и посмотреть изменения температур в графиках
Отлично! Всё передаётся, изменения фиксируются.
Итак, на данном уроке нам удалось передать по беспроводной сети показания температур с трёх датчиков с помощью протокола MQTT.
Всем спасибо за внимание!
Предыдущий урок Программирование МК ESP32 Следующий урок
Недорогие отладочные платы ESP32 можно купить здесь Недорогие отладочные платы ESP32
Недорогие отладочные платы ESP32/ESP32-C3/ESP32-S3 можно купить здесь Недорогие отладочные платы ESP32
Логический анализатор 16 каналов можно приобрести здесь
Недорогую плату Raspberry PI 4B можно купить здесь Raspberry Pi 4 Model B 4b
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в Дзен (нажмите на картинку)
Добавить комментарий