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%...
ну и пример
//dmfmod.h #ifndef DMFMOD_H #define DMFMOD_H #define periodWaitModemBegin 3000UL #define periodWaitShortCmdModem 4000UL enum typeModemInitStep {modemBegin, modemPowerON, modemFirst, modemREG, modemRestart, modemStop, modemReady}; extern enum typeModemInitStep currentInitModem; void processInitModem(unsigned char inByte, _Bool inExist); void modemReset(void); #endif /* DMFMOD_H */А как правильно сделать, если в функции, которую я хочу вынести в другой файл используется метод экземпляра класса, который объявлен в основном файле? Пробовал использовать extern, но может из-за того, что делал это в проекте с несколькими файлами ino, компиляция не проходила.
Вот ниже указан код функции, в которой используется webServer.createURLHandler
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); }Саму функцию я хочу вынести в заголовочный файл, относящийся к bme 280. Но объект webServer создается в основном файле
Как это сделать правильно?
Как это сделать правильно?
например, передавать функции указатель на обьект webServer в качестве параметра
Сделал файл bme280.h
файл bme280.c
#include "bme280.h" // цикл измерения void bmeLoop(Adafruit_BME280 &bme280, float &tempOUT, float &pressOUT, float &humyOUT) { // присваиваем переменной tempOUT текущее значение температуры в градусах Цельсия tempOUT = bme280.readTemperature(); // присваиваем переменной pressOUT текущее значение давления pressOUT = bme280.readPressure()/133.3; // присваиваем переменной humyOUT текущее значение влажности humyOUT = bme280.readHumidity(); }Добавил в главный ino файл объявление
Выдает ошибку
Назовите файл bme280.cpp
Спасибо, после изменения расширения дело сдвинулось с мертвой точки.
А не подскажите, почему ArduinoIDE не получалось с файлом СИ компилировать. Заголовочный файл в формате СИ среда разработки проглотила.
Это надо спросить у любителей покопаться в кишках Arduino IDE.
Я же только знаю, что .c передаются компилятору C, а .cpp - компилятору CPP. А так, как в pure-C вроде как нет функционала "передача аргумента по ссылке", то и понять запись Adafruit_BME280& компилятор не в состоянии.
Заголовочный файл в формате СИ среда разработки проглотила.
Не смущает, что у тебя ошибка именно в заголовочном файле? Разберись что такое эти самые файлы и как они работают.
Меня смущает, когда говорят ни о чем. Раз Вы все знаете, просветите что эта ошибка означает. В заголовочном файле описаны прототипы функций, что в нем не правильно? Можете продолжить свою мысль. А то как-то интересно получается, вроде человек знает и понимает, но сказать толком ничего не может. Компилятор может выдавать разные ошибки и не всегда эти ошибки указывают на проблему.
Я сказал всё, что нужно знать. Я указал, где ошибка в твоем суждении. Я сказал в каком направлении нужно двигаться. Я дал понять, что в данном случае компилятор указывает на ошибку правильно. Если этого не достаточно чтобы исправиться, то это значит, что ты исправляться просто не хочешь.