Arduino, ESP8266 и MQTT
- Войдите на сайт для отправки комментариев
Добрый день!
Подскажите пожалуйста, как правильно нужно разбить скетч, чтобы было в стиле C, основной файл и файлы .h .c? Так как довольно трудно понять, как Arduino IDE собирает и компилирует исходник. Используется ESP8266 с прошивкой ESP-link, датчик BME280 и Arduino UNO с библиотекой ELclient. Два примеры объединил в один скетч. Один пример вывод данных с датчика на web-страницу и второй передача данных датчика MQTT-брокеру
// подключим необходимые библиотеки #include <ELClient.h> #include <ELClientWebServer.h> #include <ELClientCmd.h> #include <ELClientMqtt.h> #include <Wire.h> #include <SPI.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> //void bmeLoop(); //void bmeInit(); #define SEALEVELPRESSURE_HPA (1013.25) //// создадим объект для работы с библиотекой BME280 Adafruit_BME280 bme; // I2C float temperature = 0; // измеренная температура float pressure = 0; //измеренное давление float humidity = 0; //измеренная влажность bool connected; static int count; static uint32_t last; // Инициализация подключения к esp-link с использованием обычного последовательного порта // DEBUG отключен как // - ведение журнала пакетов происходит медленно, и буфер приема UART может переполняться (отправка HTML-формы) ELClient esp(&Serial, &Serial); // Инициализируем клиента Web-сервера ELClientWebServer webServer(&esp); // Инициализация клиента CMD (для GetTime) ELClientCmd cmd(&esp); // Инициализация клиента MQTT ELClientMqtt mqtt(&esp); void ftoa(float f, char *str, uint8_t precision) { uint8_t i, j, divisor = 1; int8_t log_f; int32_t int_digits = (int)f; //store the integer digits float decimals; char s1[12]; memset(str, 0, sizeof(s1)); memset(s1, 0, 10); if (f < 0) { //if a negative number str[0] = '-'; //start the char array with '-' f = abs(f); //store its positive absolute value } log_f = ceil(log10(f)); //get number of digits before the decimal if (log_f > 0) { //log value > 0 indicates a number > 1 if (log_f == precision) { //if number of digits = significant figures f += 0.5; //add 0.5 to round up decimals >= 0.5 itoa(f, s1, 10); //itoa converts the number to a char array strcat(str, s1); //add to the number string } else if ((log_f - precision) > 0) { //if more integer digits than significant digits i = log_f - precision; //count digits to discard divisor = 10; for (j = 0; j < i; j++) divisor *= 10; //divisor isolates our desired integer digits f /= divisor; //divide f += 0.5; //round when converting to int int_digits = (int)f; int_digits *= divisor; //and multiply back to the adjusted value itoa(int_digits, s1, 10); strcat(str, s1); } else { //if more precision specified than integer digits, itoa(int_digits, s1, 10); //convert strcat(str, s1); //and append } } else { //decimal fractions between 0 and 1: leading 0 s1[0] = '0'; strcat(str, s1); } if (log_f < precision) { //if precision exceeds number of integer digits, decimals = f - (int)f; //get decimal value as float strcat(str, "."); //append decimal point to char array i = precision - log_f; //number of decimals to read for (j = 0; j < i; j++) { //for each, decimals *= 10; //multiply decimals by 10 if (j == (i-1)) decimals += 0.5; //and if it's the last, add 0.5 to round it itoa((int)decimals, s1, 10); //convert as integer to character array strcat(str, s1); //append to string decimals -= (int)decimals; //and remove, moving to the next } } } // Обратный вызов из esp-link для уведомления об изменениях статуса wifi // Здесь мы просто что-то распечатываем void wifiCb(void* response) { ELClientResponse *res = (ELClientResponse*)response; if (res->argc() == 1) { uint8_t status; res->popArg(&status, 1); if (status == STATION_GOT_IP) { Serial.println("WIFI CONNECTED"); } else { Serial.print("WIFI NOT READY: "); Serial.println(status); } } } // Обратный вызов при подключении MQTT void mqttConnected(void* response) { Serial.println("MQTT connected!"); mqtt.subscribe("/bme280/#"); connected = true; } // обратный вызов, когда MQTT отключен void mqttDisconnected(void* response) { Serial.println("MQTT disconnected"); connected = false; } // Обратный вызов при получении сообщения MQTT для одной из наших подписок void mqttData(void* response) { ELClientResponse *res = (ELClientResponse *)response; Serial.print("Received: topic="); String topic = res->popString(); Serial.println(topic); Serial.print("data="); String data = res->popString(); Serial.println(data); } void mqttPublished(void* response) { Serial.println("MQTT published"); } // Обратный вызов из esp-link для уведомления о том, что он только что вышел из сброса. Это означает, что мы // должны инициализировать веб-сервер! void resetCb(void) { Serial.println("EL-Client (re-)starting!"); bool ok = false; do { ok = esp.Sync(); // синхронизировать с esp-link, блокировать до 2 секунд if (!ok) Serial.println("EL-Client sync failed!"); } while (!ok); Serial.println("EL-Client synced!"); webServer.setup(); } // цикл измерения void bmeLoop() { // присваиваем переменной temperature текущее значение температуры в градусах Цельсия temperature = bme.readTemperature(); // присваиваем переменной pressure текущее значение давления pressure = bme.readPressure()/133.3; // присваиваем переменной humidity текущее значение влажности humidity = bme.readHumidity(); } // sprintf %f не поддерживается в Arduino... String floatToString(float f) { char buf[20]; char str_temp[7]; dtostrf(f, 5, 2, str_temp); sprintf(buf, "%s", str_temp); return String(buf); } String arrayToString(byte array[]) { unsigned int len = 8; char buf[20]; for (unsigned int i = 0; i < len; i++) { byte nib1 = (array[i] >> 4) & 0x0F; byte nib2 = (array[i] >> 0) & 0x0F; buf[i*2+0] = nib1 < 0xA ? '0' + nib1 : 'A' + nib1 - 0xA; buf[i*2+1] = nib2 < 0xA ? '0' + nib2 : 'A' + nib2 - 0xA; } buf[len*2] = '\0'; return String(buf); } void bmeRefreshCb(char * url) { // вычисляем значение температуры String t = floatToString(temperature); webServer.setArgString(F("temp"), t.begin()); // вычисляем значение давления String p = floatToString(pressure); webServer.setArgString(F("press"), p.begin()); // вычисляем значение влажности String h = floatToString(humidity); webServer.setArgString(F("humi"), h.begin()); } // настройка страницы void bmeInit() { unsigned status; // инициализируем работу с датчиком status = bme.begin(); if (!status) { // сделаем запрос на получение адреса датчика // Serial.println(bme.sensorID(),16); while (1); } URLHandler *bmeHandler = webServer.createURLHandler(F("/bme280.html.json")); bmeHandler->loadCb.attach(bmeRefreshCb); bmeHandler->refreshCb.attach(bmeRefreshCb); } void setup() { Serial.begin(115200); Serial.println("EL-Client starting!"); esp.resetCb = resetCb; //инициализируем датчик bme280 bmeInit(); resetCb(); // Синхронизация с esp-link, это требуется в начале любого скетча и инициализирует // обратные вызовы к обратному вызову изменения статуса Wi-Fi. Обратный вызов вызывается с начальным // статусом сразу после завершения Sync () ниже. esp.wifiCb.attach(wifiCb); // обратный вызов изменения статуса Wi-Fi, необязательно (удалить, если не требуется) bool ok; do { ok = esp.Sync(); // sync up with esp-link, blocks for up to 2 seconds if (!ok) Serial.println("EL-Client sync failed!"); } while (!ok); Serial.println("EL-Client synced!"); // Set-up callbacks for events and initialize with es-link. mqtt.connectedCb.attach(mqttConnected); mqtt.disconnectedCb.attach(mqttDisconnected); mqtt.publishedCb.attach(mqttPublished); mqtt.dataCb.attach(mqttData); mqtt.setup(); Serial.println("EL-MQTT ready"); } void loop() { esp.Process(); //функция измерения датчиком bme280 bmeLoop(); if (connected && (millis() - last) > 4000) { Serial.println("publishing"); char buf[12]; ftoa(temperature, buf, 4); mqtt.publish("/bme280/temp", buf); ftoa(pressure, buf, 4); mqtt.publish("/bme280/pressure", buf); ftoa(humidity, buf, 4); mqtt.publish("/bme280/humidity", buf); uint32_t t = cmd.GetTime(); Serial.print("Time: "); Serial.println(t); last = millis(); } }
Хочется выделить функции работы с bme280 и mqtt в отдельные файлы. Также непонятно, в какую часть следует поместить функции преобразования переменных в другие типы. Так как получаю данные с датчика в виде float, вэб-странице следует передавать String, а MQTT-брокер должен получить данные в формате char[]
https://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%B3%D0%BE%D0%BB%D0%BE%D0%B2...
http://cppstudio.com/cat/309/
http://it.mmcs.sfedu.ru/wiki/%D0%97%D0%B0%D0%B3%D0%BE%D0%BB%D0%BE%D0%B2%...
ну и пример
А как правильно сделать, если в функции, которую я хочу вынести в другой файл используется метод экземпляра класса, который объявлен в основном файле? Пробовал использовать extern, но может из-за того, что делал это в проекте с несколькими файлами ino, компиляция не проходила.
Вот ниже указан код функции, в которой используется webServer.createURLHandler
Саму функцию я хочу вынести в заголовочный файл, относящийся к bme 280. Но объект webServer создается в основном файле
Как это сделать правильно?
Как это сделать правильно?
например, передавать функции указатель на обьект webServer в качестве параметра
Сделал файл bme280.h
файл bme280.c
Добавил в главный ino файл объявление
Выдает ошибку
Назовите файл bme280.cpp
Спасибо, после изменения расширения дело сдвинулось с мертвой точки.
А не подскажите, почему ArduinoIDE не получалось с файлом СИ компилировать. Заголовочный файл в формате СИ среда разработки проглотила.
Это надо спросить у любителей покопаться в кишках Arduino IDE.
Я же только знаю, что .c передаются компилятору C, а .cpp - компилятору CPP. А так, как в pure-C вроде как нет функционала "передача аргумента по ссылке", то и понять запись Adafruit_BME280& компилятор не в состоянии.
Заголовочный файл в формате СИ среда разработки проглотила.
Не смущает, что у тебя ошибка именно в заголовочном файле? Разберись что такое эти самые файлы и как они работают.
Меня смущает, когда говорят ни о чем. Раз Вы все знаете, просветите что эта ошибка означает. В заголовочном файле описаны прототипы функций, что в нем не правильно? Можете продолжить свою мысль. А то как-то интересно получается, вроде человек знает и понимает, но сказать толком ничего не может. Компилятор может выдавать разные ошибки и не всегда эти ошибки указывают на проблему.
Я сказал всё, что нужно знать. Я указал, где ошибка в твоем суждении. Я сказал в каком направлении нужно двигаться. Я дал понять, что в данном случае компилятор указывает на ошибку правильно. Если этого не достаточно чтобы исправиться, то это значит, что ты исправляться просто не хочешь.