Skip to content

Commit 5b34a62

Browse files
authored
Develop v3 esp32 (#572)
* update file permissions (#525) * update file permissions * Update CI Build * Add option for ota_updater script to connect to TLS-protected brokers +Send when online only (#512) * Add option for ota_updater script to connect to TLS-protected brokers * Fix ota_updater not working when device is in deep sleep mode * Fix CI PK algorithm used * Add 'name' field to HomieNode * Update Homie version (3.0.0) * Add 'init' state to advertise progress * Add 'stats' state to advertise progress * Add 'ready' state to advertise progress * Add 'PUB_NAME' state to advertise progress * Add 'name', 'unit', 'datatype' and 'format' fields to 'Property' class * Initialize 'name', 'unit', 'datatype' and 'format' to null * Set global step to 'PUB_INIT' on disconnect * Publish properties attributes on setup * Fix 'NULL' spelling * Fix typo * Fix typos * Remove property attribute setters and overload 'advertise' function * Rework the property attribute announcement mechanism * Remove ':settable' indicator from properties advertisement * Change 'will' topic and message to '$state'->'lost' * Set '$state' attribute to "sleeping' * Update all examples * Update library version * Implement node arrays functionality * Adapt examples * Update README file * Fix cpplint complains * Introduce individual set functions for optional attributes of properties * Update examples to the new advertise syntax * Fix cpplint errors * Fix typo in TemperatureSensor example * Add $retained attribute to properties (Homie 3.0.1) and button example * Address review comments * -Ported to esp32. homie-esp8266 now supports architectures esp32 and esp8266. -Successfully tested with M5Stick/Environment Sensor (https://docs.m5stack.com/#/en/core/m5stick, https://docs.m5stack.com/#/en/unit/env) on openHAB 2.4 / mosquitto 1.4.15. -For increased openhab2.4 compatibility $stats/interval is now set correctly and published on every stats update (see eclipse-archived/smarthome#6495). -On esp32 OTA and LED support are currently not working (future work needs to be done, see #FIXME comments). * Increased WiFi robustness: Explicit connects/reconnects for proper handling of sockets/connections. * Increased WiFi robustness: Explicit connects/reconnects for proper handling of sockets/connections. * lint: Fixed indent. * Fix "SPIFFS: mount failed" by setting formatOnFail to true (see espressif/arduino-esp32#638) * Use LED if defined.
1 parent b4735c3 commit 5b34a62

15 files changed

+196
-10
lines changed

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ An Arduino for ESP8266 implementation of [Homie](https://github.com/homieiot/con
1212

1313
This branch of Homie for ESP8266 implements [Homie 3.0.1](https://github.com/homieiot/convention/releases/tag/v3.0.1)
1414

15+
1516
## Download
1617

1718
The Git repository contains the development version of Homie for ESP8266. Stable releases are available [on the releases page](https://github.com/homieiot/homie-esp8266/releases).
@@ -78,7 +79,7 @@ Happy coding with PlatformIO!
7879

7980
const int PIN_RELAY = 5;
8081

81-
HomieNode lightNode("light", "switch");
82+
HomieNode lightNode("light", "Light", "switch");
8283

8384
bool lightOnHandler(const HomieRange& range, const String& value) {
8485
if (value != "true" && value != "false") return false;
@@ -99,7 +100,7 @@ void setup() {
99100

100101
Homie_setFirmware("awesome-relay", "1.0.0");
101102

102-
lightNode.advertise("on").settable(lightOnHandler);
103+
lightNode.advertise("on", "On", "boolean").settable(lightOnHandler);
103104

104105
Homie.setup();
105106
}

library.properties

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ name=Homie
22
version=3.0.0
33
author=Marvin Roger
44
maintainer=Marvin Roger
5-
sentence=ESP8266 framework for Homie, a lightweight MQTT convention for the IoT
5+
sentence=ESP32/ESP8266 framework for Homie, a lightweight MQTT convention for the IoT
66
paragraph=Like this project? Please star it on GitHub!
77
category=Device Control
88
url=https://github.com/marvinroger/homie-esp8266
9-
architectures=esp8266
9+
architectures=esp32,esp8266

src/Homie.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,17 @@ HomieClass::HomieClass()
99
strlcpy(Interface::get().brand, DEFAULT_BRAND, MAX_BRAND_LENGTH);
1010
Interface::get().bootMode = HomieBootMode::UNDEFINED;
1111
Interface::get().configurationAp.secured = false;
12+
#ifdef ESP32
13+
Interface::get().led.enabled = false;
14+
#ifdef LED_BUILTIN
15+
Interface::get().led.pin = LED_BUILTIN;
16+
#endif // LED_BUILTIN
17+
Interface::get().led.on = LOW;
18+
#elif defined(ESP8266)
1219
Interface::get().led.enabled = true;
1320
Interface::get().led.pin = LED_BUILTIN;
1421
Interface::get().led.on = LOW;
22+
#endif // ESP32
1523
Interface::get().reset.idle = true;
1624
Interface::get().reset.enabled = true;
1725
Interface::get().reset.triggerPin = DEFAULT_RESET_PIN;
@@ -332,6 +340,9 @@ Logger& HomieClass::getLogger() {
332340
return _logger;
333341
}
334342

343+
#ifdef ESP32
344+
//FIXME: implement for ESP32
345+
#elif defined(ESP8266)
335346
void HomieClass::prepareToSleep() {
336347
Interface::get().getLogger() << F("Flagged for sleep by sketch") << endl;
337348
if (Interface::get().ready) {
@@ -350,5 +361,7 @@ void HomieClass::doDeepSleep(uint32_t time_us, RFMode mode) {
350361
Serial.flush();
351362
ESP.deepSleep(time_us, mode);
352363
}
364+
#endif // ESP32
365+
353366

354367
HomieClass Homie;

src/Homie.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,12 @@ class HomieClass {
6464
static const ConfigStruct& getConfiguration();
6565
AsyncMqttClient& getMqttClient();
6666
Logger& getLogger();
67+
#ifdef ESP32
68+
//FIXME implement on ESP32
69+
#elif defined(ESP8266)
6770
static void prepareToSleep();
6871
static void doDeepSleep(uint32_t time_us = 0, RFMode mode = RF_DEFAULT);
72+
#endif // ESP32
6973

7074
private:
7175
bool _setupCalled;

src/Homie/Boot/Boot.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
#include "Arduino.h"
44

5+
#ifdef ESP32
6+
#include <WiFi.h>
7+
#elif defined(ESP8266)
58
#include <ESP8266WiFi.h>
9+
#endif // ESP32
610
#include "../../StreamingOperator.hpp"
711
#include "../Datatypes/Interface.hpp"
812
#include "../Constants.hpp"

src/Homie/Boot/BootConfig.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,28 @@ void BootConfig::_generateNetworksJson() {
208208
JsonObject& jsonNetwork = generatedJsonBuffer.createObject();
209209
jsonNetwork["ssid"] = WiFi.SSID(network);
210210
jsonNetwork["rssi"] = WiFi.RSSI(network);
211+
#ifdef ESP32
212+
switch (WiFi.encryptionType(network)) {
213+
case (WIFI_AUTH_OPEN):
214+
jsonNetwork["encryption"] = "none";
215+
break;
216+
case (WIFI_AUTH_WEP):
217+
jsonNetwork["encryption"] = "wep";
218+
break;
219+
case (WIFI_AUTH_WPA_PSK):
220+
jsonNetwork["encryption"] = "wpa";
221+
break;
222+
case (WIFI_AUTH_WPA2_PSK):
223+
jsonNetwork["encryption"] = "wpa2";
224+
break;
225+
case (WIFI_AUTH_WPA_WPA2_PSK):
226+
//FIXME
227+
break;
228+
case (WIFI_AUTH_WPA2_ENTERPRISE):
229+
//FIXME
230+
break;
231+
}
232+
#elif defined(ESP8266)
211233
switch (WiFi.encryptionType(network)) {
212234
case ENC_TYPE_WEP:
213235
jsonNetwork["encryption"] = "wep";
@@ -225,6 +247,7 @@ void BootConfig::_generateNetworksJson() {
225247
jsonNetwork["encryption"] = "auto";
226248
break;
227249
}
250+
#endif // ESP32
228251

229252
networks.add(jsonNetwork);
230253
}

src/Homie/Boot/BootConfig.hpp

+7
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,16 @@
33
#include "Arduino.h"
44

55
#include <functional>
6+
#ifdef ESP32
7+
#include <WiFi.h>
8+
#include <HTTPClient.h>
9+
#include <AsyncTCP.h>
10+
#include <SPIFFS.h>
11+
#elif defined(ESP8266)
612
#include <ESP8266WiFi.h>
713
#include <ESP8266HTTPClient.h>
814
#include <ESPAsyncTCP.h>
15+
#endif // ESP32
916
#include <ESPAsyncWebServer.h>
1017
#include <DNSServer.h>
1118
#include <ArduinoJson.h>

src/Homie/Boot/BootNormal.cpp

+95-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,32 @@
22

33
using namespace HomieInternals;
44

5+
#ifdef ESP32
6+
BootNormal::BootNormal()
7+
: Boot("normal")
8+
, _mqttReconnectTimer(MQTT_RECONNECT_INITIAL_INTERVAL, MQTT_RECONNECT_MAX_BACKOFF)
9+
, _setupFunctionCalled(false)
10+
, _mqttConnectNotified(false)
11+
, _mqttDisconnectNotified(true)
12+
, _otaOngoing(false)
13+
, _flaggedForReboot(false)
14+
, _mqttOfflineMessageId(0)
15+
, _otaIsBase64(false)
16+
, _otaBase64Pads(0)
17+
, _otaSizeTotal(0)
18+
, _otaSizeDone(0)
19+
, _mqttTopic(nullptr)
20+
, _mqttClientId(nullptr)
21+
, _mqttWillTopic(nullptr)
22+
, _mqttPayloadBuffer(nullptr)
23+
, _mqttTopicLevels(nullptr)
24+
, _mqttTopicLevelsCount(0) {
25+
//FIXME: getSketchMD5 not implemented / boot loop on ESP32
26+
//Remove Update.h from BootNormal.cpp, change status codes to https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/ota.html
27+
strcpy(_fwChecksum, "00000000000000000000000000000000");
28+
_fwChecksum[sizeof(_fwChecksum) - 1] = '\0';
29+
}
30+
#elif defined(ESP8266)
531
BootNormal::BootNormal()
632
: Boot("normal")
733
, _mqttReconnectTimer(MQTT_RECONNECT_INITIAL_INTERVAL, MQTT_RECONNECT_MAX_BACKOFF)
@@ -24,14 +50,20 @@ BootNormal::BootNormal()
2450
strlcpy(_fwChecksum, ESP.getSketchMD5().c_str(), sizeof(_fwChecksum));
2551
_fwChecksum[sizeof(_fwChecksum) - 1] = '\0';
2652
}
53+
#endif // ESP32
54+
2755

2856
BootNormal::~BootNormal() {
2957
}
3058

3159
void BootNormal::setup() {
3260
Boot::setup();
3361

62+
#ifdef ESP32
63+
//FIXME
64+
#elif defined(ESP8266)
3465
Update.runAsync(true);
66+
#endif // ESP32
3567

3668
_statsTimer.setInterval(Interface::get().getConfig().get().deviceStatsInterval * 1000);
3769

@@ -53,8 +85,13 @@ void BootNormal::setup() {
5385
}
5486
_mqttTopic = std::unique_ptr<char[]>(new char[baseTopicLength + longestSubtopicLength]);
5587

88+
#ifdef ESP32
89+
_wifiGotIpHandler = WiFi.onEvent(std::bind(&BootNormal::_onWifiGotIp, this, std::placeholders::_1, std::placeholders::_2), WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
90+
_wifiDisconnectedHandler = WiFi.onEvent(std::bind(&BootNormal::_onWifiDisconnected, this, std::placeholders::_1, std::placeholders::_2), WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
91+
#elif defined(ESP8266)
5692
_wifiGotIpHandler = WiFi.onStationModeGotIP(std::bind(&BootNormal::_onWifiGotIp, this, std::placeholders::_1));
5793
_wifiDisconnectedHandler = WiFi.onStationModeDisconnected(std::bind(&BootNormal::_onWifiDisconnected, this, std::placeholders::_1));
94+
#endif // ESP32
5895

5996
Interface::get().getMqttClient().onConnect(std::bind(&BootNormal::_onMqttConnected, this));
6097
Interface::get().getMqttClient().onDisconnect(std::bind(&BootNormal::_onMqttDisconnected, this, std::placeholders::_1));
@@ -144,10 +181,15 @@ void BootNormal::loop() {
144181
}
145182

146183
if (_statsTimer.check()) {
184+
char statsIntervalStr[3 + 1];
185+
itoa(Interface::get().getConfig().get().deviceStatsInterval+5, statsIntervalStr, 10);
186+
Interface::get().getLogger() << F("〽 Sending statistics...") << endl;
187+
Interface::get().getLogger() << F(" • Interval: ") << statsIntervalStr << F("s (") << Interface::get().getConfig().get().deviceStatsInterval << F("s including 5s grace time)") << endl;
188+
uint16_t intervalPacketId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$stats/interval")), 1, true, statsIntervalStr);
189+
147190
uint8_t quality = Helpers::rssiToPercentage(WiFi.RSSI());
148191
char qualityStr[3 + 1];
149192
itoa(quality, qualityStr, 10);
150-
Interface::get().getLogger() << F("〽 Sending statistics...") << endl;
151193
Interface::get().getLogger() << F(" • Wi-Fi signal quality: ") << qualityStr << F("%") << endl;
152194
uint16_t signalPacketId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$stats/signal")), 1, true, qualityStr);
153195

@@ -157,7 +199,7 @@ void BootNormal::loop() {
157199
Interface::get().getLogger() << F(" • Uptime: ") << uptimeStr << F("s") << endl;
158200
uint16_t uptimePacketId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$stats/uptime")), 1, true, uptimeStr);
159201

160-
if (signalPacketId != 0 && uptimePacketId != 0) _statsTimer.tick();
202+
if (intervalPacketId != 0 && signalPacketId != 0 && uptimePacketId != 0) _statsTimer.tick();
161203
}
162204

163205
Interface::get().loopFunction();
@@ -204,10 +246,14 @@ void BootNormal::_endOtaUpdate(bool success, uint8_t update_error) {
204246
switch (update_error) {
205247
case UPDATE_ERROR_SIZE: // new firmware size is zero
206248
case UPDATE_ERROR_MAGIC_BYTE: // new firmware does not have 0xE9 in first byte
249+
#ifdef ESP32
250+
//FIXME
251+
#elif defined(ESP8266)
207252
case UPDATE_ERROR_NEW_FLASH_CONFIG: // bad new flash config (does not match flash ID)
208253
code = 400; // 400 Bad Request
209254
info.concat(F("BAD_FIRMWARE"));
210255
break;
256+
#endif //ESP32
211257
case UPDATE_ERROR_MD5:
212258
code = 400; // 400 Bad Request
213259
info.concat(F("BAD_CHECKSUM"));
@@ -246,7 +292,11 @@ void BootNormal::_wifiConnect() {
246292

247293
if (WiFi.getMode() != WIFI_STA) WiFi.mode(WIFI_STA);
248294

295+
#ifdef ESP32
296+
WiFi.setHostname(Interface::get().getConfig().get().deviceId);
297+
#elif defined(ESP8266)
249298
WiFi.hostname(Interface::get().getConfig().get().deviceId);
299+
#endif // ESP32
250300
if (strcmp_P(Interface::get().getConfig().get().wifi.ip, PSTR("")) != 0) { // on _validateConfigWifi there is a requirement for mask and gateway
251301
IPAddress convertedIp;
252302
convertedIp.fromString(Interface::get().getConfig().get().wifi.ip);
@@ -278,11 +328,31 @@ void BootNormal::_wifiConnect() {
278328
WiFi.begin(Interface::get().getConfig().get().wifi.ssid, Interface::get().getConfig().get().wifi.password);
279329
}
280330

331+
#ifdef ESP32
332+
WiFi.setAutoConnect(false);
333+
WiFi.setAutoReconnect(false);
334+
#elif defined(ESP8266)
281335
WiFi.setAutoConnect(true);
282336
WiFi.setAutoReconnect(true);
337+
#endif // ESP32
283338
}
284339
}
285340

341+
#ifdef ESP32
342+
void BootNormal::_onWifiGotIp(WiFiEvent_t event, WiFiEventInfo_t info) {
343+
if (Interface::get().led.enabled) Interface::get().getBlinker().stop();
344+
Interface::get().getLogger() << F("✔ Wi-Fi connected, IP: ") << IPAddress(info.got_ip.ip_info.ip.addr) << endl;
345+
Interface::get().getLogger() << F("Triggering WIFI_CONNECTED event...") << endl;
346+
Interface::get().event.type = HomieEventType::WIFI_CONNECTED;
347+
Interface::get().event.ip = IPAddress(info.got_ip.ip_info.ip.addr);
348+
Interface::get().event.mask = IPAddress(info.got_ip.ip_info.netmask.addr);
349+
Interface::get().event.gateway = IPAddress(info.got_ip.ip_info.gw.addr);
350+
Interface::get().eventHandler(Interface::get().event);
351+
MDNS.begin(Interface::get().getConfig().get().deviceId);
352+
353+
_mqttConnect();
354+
}
355+
#elif defined(ESP8266)
286356
void BootNormal::_onWifiGotIp(const WiFiEventStationModeGotIP& event) {
287357
if (Interface::get().led.enabled) Interface::get().getBlinker().stop();
288358
Interface::get().getLogger() << F("✔ Wi-Fi connected, IP: ") << event.ip << endl;
@@ -296,19 +366,35 @@ void BootNormal::_onWifiGotIp(const WiFiEventStationModeGotIP& event) {
296366

297367
_mqttConnect();
298368
}
369+
#endif // ESP32
370+
371+
#ifdef ESP32
372+
void BootNormal::_onWifiDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) {
373+
Interface::get().ready = false;
374+
if (Interface::get().led.enabled) Interface::get().getBlinker().start(LED_WIFI_DELAY);
375+
_statsTimer.reset();
376+
Interface::get().getLogger() << F("✖ Wi-Fi disconnected, reason: ") << info.disconnected.reason << endl;
377+
Interface::get().getLogger() << F("Triggering WIFI_DISCONNECTED event...") << endl;
378+
Interface::get().event.type = HomieEventType::WIFI_DISCONNECTED;
379+
Interface::get().event.wifiReason = info.disconnected.reason;
380+
Interface::get().eventHandler(Interface::get().event);
299381

382+
_wifiConnect();
383+
}
384+
#elif defined(ESP8266)
300385
void BootNormal::_onWifiDisconnected(const WiFiEventStationModeDisconnected& event) {
301386
Interface::get().ready = false;
302387
if (Interface::get().led.enabled) Interface::get().getBlinker().start(LED_WIFI_DELAY);
303388
_statsTimer.reset();
304-
Interface::get().getLogger() << F("✖ Wi-Fi disconnected") << endl;
389+
Interface::get().getLogger() << F("✖ Wi-Fi disconnected, reason: ") << event.reason << endl;
305390
Interface::get().getLogger() << F("Triggering WIFI_DISCONNECTED event...") << endl;
306391
Interface::get().event.type = HomieEventType::WIFI_DISCONNECTED;
307392
Interface::get().event.wifiReason = event.reason;
308393
Interface::get().eventHandler(Interface::get().event);
309394

310395
_wifiConnect();
311396
}
397+
#endif // ESP32
312398

313399
void BootNormal::_mqttConnect() {
314400
if (!Interface::get().disable) {
@@ -367,7 +453,7 @@ void BootNormal::_advertise() {
367453
break;
368454
case AdvertisementProgress::GlobalStep::PUB_STATS_INTERVAL:
369455
char statsIntervalStr[3 + 1];
370-
itoa(STATS_SEND_INTERVAL_SEC / 1000, statsIntervalStr, 10);
456+
itoa(Interface::get().getConfig().get().deviceStatsInterval+5, statsIntervalStr, 10);
371457
packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$stats/interval")), 1, true, statsIntervalStr);
372458
if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_FW_NAME;
373459
break;
@@ -384,7 +470,11 @@ void BootNormal::_advertise() {
384470
if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION;
385471
break;
386472
case AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION:
473+
#ifdef ESP32
474+
packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$implementation")), 1, true, "esp32");
475+
#elif defined(ESP8266)
387476
packetId = Interface::get().getMqttClient().publish(_prefixMqttTopic(PSTR("/$implementation")), 1, true, "esp8266");
477+
#endif // ESP32
388478
if (packetId != 0) _advertisementProgress.globalStep = AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_CONFIG;
389479
break;
390480
case AdvertisementProgress::GlobalStep::PUB_IMPLEMENTATION_CONFIG:
@@ -672,7 +762,7 @@ void BootNormal::_onMqttDisconnected(AsyncMqttClientDisconnectReason reason) {
672762
_advertisementProgress.currentPropertyIndex = 0;
673763
if (!_mqttDisconnectNotified) {
674764
_statsTimer.reset();
675-
Interface::get().getLogger() << F("✖ MQTT disconnected") << endl;
765+
Interface::get().getLogger() << F("✖ MQTT disconnected, reason: ") << (int8_t)reason << endl;
676766
Interface::get().getLogger() << F("Triggering MQTT_DISCONNECTED event...") << endl;
677767
Interface::get().event.type = HomieEventType::MQTT_DISCONNECTED;
678768
Interface::get().event.mqttReason = reason;

0 commit comments

Comments
 (0)