diff --git a/README.md b/README.md index 5a451753..c40406c0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,14 @@ +# TODO's for ESP32 + +- Implement handleFirmwarePost() and handleFirmwareUpload() in MiLightHttpServer.cpp +- Erase config in MiLightHttpServer.cpp +- Wifi set hostname in main.cpp +- Set Wifi physical (setPhyMode) in main.cpp +- SSDP (Service discovery) support in main.cpp +- LED callback in WiFiManager loop in main.cpp +- Implement other TODO's in main.cpp +- Reset reason in AboutHelper.cpp + # esp8266_milight_hub [![Build Status](https://travis-ci.org/sidoh/esp8266_milight_hub.svg?branch=master)](https://travis-ci.org/sidoh/esp8266_milight_hub) [![License][shield-license]][info-license] This is a replacement for a Milight/LimitlessLED remote/gateway hosted on an ESP8266. Leverages [Henryk Plötz's awesome reverse-engineering work](https://hackaday.io/project/5888-reverse-engineering-the-milight-on-air-protocol). diff --git a/lib/ESP/ESPId.cpp b/lib/ESP/ESPId.cpp new file mode 100644 index 00000000..51ff0bf2 --- /dev/null +++ b/lib/ESP/ESPId.cpp @@ -0,0 +1,17 @@ +#include + +#ifdef ESP8266 +uint32_t getESPId() +{ + return ESP.getChipId(); +} +#elif ESP32 +uint32_t getESPId() +{ + uint32_t id = 0; + for(int i=0; i<17; i=i+8) { + id |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i; + } + return id; +} +#endif \ No newline at end of file diff --git a/lib/ESP/ESPId.h b/lib/ESP/ESPId.h new file mode 100644 index 00000000..96e8c149 --- /dev/null +++ b/lib/ESP/ESPId.h @@ -0,0 +1,8 @@ +#ifndef _ESPID_H +#define _ESPID_H + +#include + +uint32_t getESPId(); + +#endif \ No newline at end of file diff --git a/lib/MQTT/HomeAssistantDiscoveryClient.cpp b/lib/MQTT/HomeAssistantDiscoveryClient.cpp index 5687a184..55a96048 100644 --- a/lib/MQTT/HomeAssistantDiscoveryClient.cpp +++ b/lib/MQTT/HomeAssistantDiscoveryClient.cpp @@ -37,7 +37,7 @@ void HomeAssistantDiscoveryClient::addConfig(const char* alias, const BulbId& bu DynamicJsonDocument config(1024); char uniqidBuffer[30]; - sprintf_P(uniqidBuffer, PSTR("%X-%s"), ESP.getChipId(), alias); + sprintf_P(uniqidBuffer, PSTR("%X-%s"), getESPId(), alias); config[F("schema")] = F("json"); config[F("name")] = alias; @@ -50,7 +50,8 @@ void HomeAssistantDiscoveryClient::addConfig(const char* alias, const BulbId& bu deviceMetadata[F("sw_version")] = QUOTE(MILIGHT_HUB_VERSION); JsonArray identifiers = deviceMetadata.createNestedArray(F("identifiers")); - identifiers.add(ESP.getChipId()); + identifiers.add(getESPId()); + bulbId.serialize(identifiers); // HomeAssistant only supports simple client availability @@ -142,7 +143,7 @@ String HomeAssistantDiscoveryClient::buildTopic(const BulbId& bulbId) { topic += "light/"; // Use a static ID that doesn't depend on configuration. - topic += "milight_hub_" + String(ESP.getChipId()); + topic += "milight_hub_" + String(getESPId()); // make the object ID based on the actual parameters rather than the alias. topic += "/"; diff --git a/lib/MQTT/HomeAssistantDiscoveryClient.h b/lib/MQTT/HomeAssistantDiscoveryClient.h index ee6fc798..17fef90e 100644 --- a/lib/MQTT/HomeAssistantDiscoveryClient.h +++ b/lib/MQTT/HomeAssistantDiscoveryClient.h @@ -2,6 +2,7 @@ #include #include +#include #include class HomeAssistantDiscoveryClient { diff --git a/lib/MQTT/MqttClient.cpp b/lib/MQTT/MqttClient.cpp index 095d1ebe..d9eed6f4 100644 --- a/lib/MQTT/MqttClient.cpp +++ b/lib/MQTT/MqttClient.cpp @@ -56,7 +56,8 @@ void MqttClient::begin() { bool MqttClient::connect() { char nameBuffer[30]; - sprintf_P(nameBuffer, PSTR("milight-hub-%u"), ESP.getChipId()); + sprintf_P(nameBuffer, PSTR("milight-hub-%u"), getESPId()); + #ifdef MQTT_DEBUG Serial.println(F("MqttClient - connecting using name")); diff --git a/lib/MQTT/MqttClient.h b/lib/MQTT/MqttClient.h index dc601c4b..b5a2bf20 100644 --- a/lib/MQTT/MqttClient.h +++ b/lib/MQTT/MqttClient.h @@ -3,6 +3,7 @@ #include #include #include +#include #ifndef MQTT_CONNECTION_ATTEMPT_FREQUENCY #define MQTT_CONNECTION_ATTEMPT_FREQUENCY 5000 diff --git a/lib/MiLightState/GroupStatePersistence.cpp b/lib/MiLightState/GroupStatePersistence.cpp index 14a1f718..75572ad1 100644 --- a/lib/MiLightState/GroupStatePersistence.cpp +++ b/lib/MiLightState/GroupStatePersistence.cpp @@ -1,6 +1,10 @@ #include #include +#ifdef ESP32 + #include +#endif + static const char FILE_PREFIX[] = "group_states/"; void GroupStatePersistence::get(const BulbId &id, GroupState& state) { diff --git a/lib/Settings/AboutHelper.cpp b/lib/Settings/AboutHelper.cpp index aac1233d..72e098a9 100644 --- a/lib/Settings/AboutHelper.cpp +++ b/lib/Settings/AboutHelper.cpp @@ -1,7 +1,12 @@ #include #include #include -#include + +#ifdef ESP8266 + #include +#elif ESP32 + #include +#endif String AboutHelper::generateAboutString(bool abbreviated) { DynamicJsonDocument buffer(1024); @@ -18,11 +23,19 @@ void AboutHelper::generateAboutObject(JsonDocument& obj, bool abbreviated) { obj["firmware"] = QUOTE(FIRMWARE_NAME); obj["version"] = QUOTE(MILIGHT_HUB_VERSION); obj["ip_address"] = WiFi.localIP().toString(); +#ifdef ESP8266 obj["reset_reason"] = ESP.getResetReason(); +#elif ESP32 + // TODO get reset reason +#endif if (! abbreviated) { obj["variant"] = QUOTE(FIRMWARE_VARIANT); obj["free_heap"] = ESP.getFreeHeap(); +#ifdef ESP8266 obj["arduino_version"] = ESP.getCoreVersion(); +#elif ESP32 + obj["arduino_version"] = ESP.getSdkVersion(); +#endif } } \ No newline at end of file diff --git a/lib/Settings/Settings.cpp b/lib/Settings/Settings.cpp index 04100395..5d01975e 100644 --- a/lib/Settings/Settings.cpp +++ b/lib/Settings/Settings.cpp @@ -5,6 +5,10 @@ #include #include +#ifdef ESP32 + #include +#endif + #define PORT_POSITION(s) ( s.indexOf(':') ) GatewayConfig::GatewayConfig(uint16_t deviceId, uint16_t port, uint8_t protocolVersion) @@ -208,10 +212,11 @@ void Settings::dumpGroupIdAliases(JsonObject json) { } void Settings::load(Settings& settings) { + if (SPIFFS.exists(SETTINGS_FILE)) { // Clear in-memory settings settings = Settings(); - + File f = SPIFFS.open(SETTINGS_FILE, "r"); DynamicJsonDocument json(MILIGHT_HUB_SETTINGS_BUFFER_SIZE); @@ -220,7 +225,7 @@ void Settings::load(Settings& settings) { if (! error) { JsonObject parsedSettings = json.as(); - settings.patch(parsedSettings); + settings.patch(parsedSettings); } else { Serial.print(F("Error parsing saved settings file: ")); Serial.println(error.c_str()); diff --git a/lib/Udp/MiLightDiscoveryServer.cpp b/lib/Udp/MiLightDiscoveryServer.cpp index 3e501b8b..9f869c5d 100644 --- a/lib/Udp/MiLightDiscoveryServer.cpp +++ b/lib/Udp/MiLightDiscoveryServer.cpp @@ -1,6 +1,11 @@ #include #include -#include + +#ifdef ESP8266 + #include +#elif ESP32 + #include +#endif const char V3_SEARCH_STRING[] = "Link_Wi-Fi"; const char V6_SEARCH_STRING[] = "HF-A11ASSISTHREAD"; @@ -85,6 +90,10 @@ void MiLightDiscoveryServer::sendResponse(char* buffer) { #endif socket.beginPacket(socket.remoteIP(), socket.remotePort()); +#ifdef ESP8266 socket.write(buffer); +#elif ESP32 + socket.write(*buffer); +#endif socket.endPacket(); } diff --git a/lib/Udp/MiLightUdpServer.cpp b/lib/Udp/MiLightUdpServer.cpp index 71fa931e..8a51bc96 100644 --- a/lib/Udp/MiLightUdpServer.cpp +++ b/lib/Udp/MiLightUdpServer.cpp @@ -1,7 +1,12 @@ #include #include #include -#include + +#ifdef ESP8266 + #include +#elif ESP32 + #include +#endif MiLightUdpServer::MiLightUdpServer(MiLightClient*& client, uint16_t port, uint16_t deviceId) : client(client), diff --git a/lib/Udp/V6MiLightUdpServer.cpp b/lib/Udp/V6MiLightUdpServer.cpp index a1b93fe8..b4d7403a 100644 --- a/lib/Udp/V6MiLightUdpServer.cpp +++ b/lib/Udp/V6MiLightUdpServer.cpp @@ -1,9 +1,14 @@ #include -#include #include #include #include +#ifdef ESP8266 + #include +#elif ESP32 + #include +#endif + #define MATCHES_PACKET(packet1) ( \ matchesPacket(packet1, size(packet1), packet, packetSize) \ ) diff --git a/lib/WebServer/MiLightHttpServer.cpp b/lib/WebServer/MiLightHttpServer.cpp index 6c84d328..f07eecb2 100644 --- a/lib/WebServer/MiLightHttpServer.cpp +++ b/lib/WebServer/MiLightHttpServer.cpp @@ -9,6 +9,10 @@ #include #include +#ifdef ESP32 + #include +#endif + using namespace std::placeholders; void MiLightHttpServer::begin() { @@ -101,7 +105,7 @@ WiFiClient MiLightHttpServer::client() { return server.client(); } -void MiLightHttpServer::on(const char* path, HTTPMethod method, ESP8266WebServer::THandlerFunction handler) { +void MiLightHttpServer::on(const char* path, HTTPMethod method, THandlerFunction handler) { server.on(path, method, handler); } @@ -125,7 +129,11 @@ void MiLightHttpServer::handleSystemPost(RequestContext& request) { server.send_P(200, TEXT_PLAIN, PSTR("true")); delay(100); +#ifdef ESP8266 ESP.eraseConfig(); +#elif ESP32 + // TODO erase config +#endif delay(100); ESP.restart(); @@ -225,6 +233,7 @@ void MiLightHttpServer::handleUpdateSettingsPost(RequestContext& request) { } void MiLightHttpServer::handleFirmwarePost() { +#ifdef ESP8266 server.sendHeader("Connection", "close"); server.sendHeader("Access-Control-Allow-Origin", "*"); @@ -245,9 +254,13 @@ void MiLightHttpServer::handleFirmwarePost() { delay(1000); ESP.restart(); +#elif ESP32 + // TODO implement firmware post +#endif } void MiLightHttpServer::handleFirmwareUpload() { +#ifdef ESP8266 HTTPUpload& upload = server.upload(); if(upload.status == UPLOAD_FILE_START){ WiFiUDP::stopAll(); @@ -266,6 +279,9 @@ void MiLightHttpServer::handleFirmwareUpload() { } } yield(); +#elif ESP32 + // TODO implement firmware upload +#endif } diff --git a/lib/WebServer/MiLightHttpServer.h b/lib/WebServer/MiLightHttpServer.h index ff272aa8..48ebfb32 100644 --- a/lib/WebServer/MiLightHttpServer.h +++ b/lib/WebServer/MiLightHttpServer.h @@ -14,6 +14,7 @@ typedef std::function SettingsSavedHandler; typedef std::function GroupDeletedHandler; +typedef std::function THandlerFunction; using RichHttpConfig = RichHttp::Generics::Configs::EspressifBuiltin; using RequestContext = RichHttpConfig::RequestContextType; @@ -47,7 +48,7 @@ class MiLightHttpServer { void handleClient(); void onSettingsSaved(SettingsSavedHandler handler); void onGroupDeleted(GroupDeletedHandler handler); - void on(const char* path, HTTPMethod method, ESP8266WebServer::THandlerFunction handler); + void on(const char* path, HTTPMethod method, THandlerFunction handler); void handlePacketSent(uint8_t* packet, const MiLightRemoteConfig& config); WiFiClient client(); @@ -101,7 +102,7 @@ class MiLightHttpServer { GroupStateStore*& stateStore; SettingsSavedHandler settingsSavedHandler; GroupDeletedHandler groupDeletedHandler; - ESP8266WebServer::THandlerFunction _handleRootPage; + THandlerFunction _handleRootPage; PacketSender*& packetSender; RadioSwitchboard*& radios; TransitionController& transitions; diff --git a/platformio.ini b/platformio.ini index c2da520d..35f43271 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,17 +8,20 @@ ; Please visit documentation for the other options and examples ; http://docs.platformio.org/page/projectconf.html +; [platformio] +; default_envs = lolin_d32_pro +; default_envs = nodemcuv2 + [common] framework = arduino -platform = espressif8266@~1.8 board_f_cpu = 160000000L lib_deps_builtin = -lib_deps_external = - WiFiManager=https://github.com/sidoh/WiFiManager.git#cmidgley + SPI +lib_deps_external = RF24@~1.3.2 ArduinoJson@~6.10.1 PubSubClient@~2.7 - https://github.com/ratkins/RGBConverter.git#07010f2 + https://github.com/ratkins/RGBConverter.git#07010f2605d9f787c6c55e62df10fec14e5894e4 WebSockets@~2.2.0 CircularBuffer@~1.2.0 PathVariableHandlers@~2.0.0 @@ -43,74 +46,86 @@ build_flags = # -D MILIGHT_UDP_DEBUG # -D STATE_DEBUG +[esp8266] +platform = espressif8266@~1.8 +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + https://github.com/sidoh/WiFiManager.git#cmidgley + +[esp32] +platform = espressif32 +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + https://github.com/tzapu/WiFiManager.git#2.0.3-alpha + [env:nodemcuv2] -platform = ${common.platform} +platform = ${esp8266.platform} framework = ${common.framework} upload_speed = ${common.upload_speed} board = nodemcuv2 build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.4m1m.ld -D FIRMWARE_VARIANT=nodemcuv2 extra_scripts = ${common.extra_scripts} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} +lib_deps = ${esp8266.lib_deps} test_ignore = ${common.test_ignore} [env:d1_mini] -platform = ${common.platform} +platform = ${esp8266.platform} framework = ${common.framework} upload_speed = ${common.upload_speed} board = d1_mini build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.4m1m.ld -D FIRMWARE_VARIANT=d1_mini extra_scripts = ${common.extra_scripts} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} +lib_deps = ${esp8266.lib_deps} test_ignore = ${common.test_ignore} [env:esp12] -platform = ${common.platform} +platform = ${esp8266.platform} framework = ${common.framework} upload_speed = ${common.upload_speed} board = esp12e build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.4m1m.ld -D FIRMWARE_VARIANT=esp12 extra_scripts = ${common.extra_scripts} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} +lib_deps = ${esp8266.lib_deps} test_ignore = ${common.test_ignore} [env:esp07] -platform = ${common.platform} +platform = ${esp8266.platform} framework = ${common.framework} upload_speed = ${common.upload_speed} board = esp07 build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m64.ld -D FIRMWARE_VARIANT=esp07 extra_scripts = ${common.extra_scripts} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} +lib_deps = ${esp8266.lib_deps} test_ignore = ${common.test_ignore} [env:huzzah] -platform = ${common.platform} +platform = ${esp8266.platform} framework = ${common.framework} upload_speed = ${common.upload_speed} board = huzzah build_flags = ${common.build_flags} -D FIRMWARE_VARIANT=huzzah extra_scripts = ${common.extra_scripts} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} +lib_deps = ${esp8266.lib_deps} test_ignore = ${common.test_ignore} [env:d1_mini_pro] -platform = ${common.platform} +platform = ${esp8266.platform} framework = ${common.framework} upload_speed = ${common.upload_speed} board = d1_mini_pro build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.4m1m.ld -D FIRMWARE_VARIANT=d1_mini_PRO extra_scripts = ${common.extra_scripts} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} +lib_deps = ${esp8266.lib_deps} test_ignore = ${common.test_ignore} + +[env:lolin_d32_pro] +platform = ${esp32.platform} +framework = ${common.framework} +upload_speed = ${common.upload_speed} +board = lolin_d32_pro +build_flags = ${common.build_flags} -Wl, -D FIRMWARE_VARIANT=lolin_d32_pro +extra_scripts = ${common.extra_scripts} +lib_deps = ${esp32.lib_deps} +test_ignore = ${common.test_ignore} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 1c76567d..73e1c0eb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,8 +16,6 @@ #include #include #include -#include -#include #include #include #include @@ -27,6 +25,15 @@ #include #include #include +#include + +#ifdef ESP8266 + #include + #include +#elif ESP32 + #include + #include +#endif #include #include @@ -267,6 +274,7 @@ void applySettings() { if (settings.discoveryPort != 0) { discoveryServer = new MiLightDiscoveryServer(settings); discoveryServer->begin(); + } // update LED pin and operating mode @@ -275,22 +283,22 @@ void applySettings() { ledStatus->continuous(settings.ledModeOperating); } - WiFi.hostname(settings.hostname); - - WiFiPhyMode_t wifiMode; - switch (settings.wifiMode) { - case WifiMode::B: - wifiMode = WIFI_PHY_MODE_11B; - break; - case WifiMode::G: - wifiMode = WIFI_PHY_MODE_11G; - break; - default: - case WifiMode::N: - wifiMode = WIFI_PHY_MODE_11N; - break; - } - WiFi.setPhyMode(wifiMode); + // WiFi.hostname(settings.hostname); + + // WiFiPhyMode_t wifiMode; + // switch (settings.wifiMode) { + // case WifiMode::B: + // wifiMode = WIFI_PHY_MODE_11B; + // break; + // case WifiMode::G: + // wifiMode = WIFI_PHY_MODE_11G; + // break; + // default: + // case WifiMode::N: + // wifiMode = WIFI_PHY_MODE_11N; + // break; + // } + // WiFi.setPhyMode(wifiMode); } /** @@ -331,12 +339,22 @@ void onGroupDeleted(const BulbId& id) { void setup() { Serial.begin(9600); - String ssid = "ESP" + String(ESP.getChipId()); + + String ssid = "ESP" + String(getESPId()); // load up our persistent settings from the file system +#ifdef ESP8266 SPIFFS.begin(); +#elif ESP32 + if(!SPIFFS.begin(true)){ + Serial.println(F("Error while mounting SPIFFS")); + } +#endif + Settings::load(settings); +#ifdef ESP8266 applySettings(); +#endif // set up the LED status for wifi configuration ledStatus = new LEDStatus(settings.ledPin); @@ -351,7 +369,11 @@ void setup() { // allows the "autoConnect" method to be non-blocking which can implement this same functionality. However, // that change is only on the development branch so we are going to continue to use this fork until // that is merged and ready. +#ifdef ESP8266 wifiManager.setSetupLoopCallback(handleLED); +#elif ESP32 + // TODO check if the non-blocking implementation can be used or create a version with setSetupLoopCallback +#endif // Allows us to have static IP config in the captive portal. Yucky pointers to pointers, just to have the settings carry through wifiManager.setSaveConfigCallback(wifiExtraSettingsChange); @@ -401,6 +423,10 @@ void setup() { // if the config portal was started, make sure to turn off the config AP WiFi.mode(WIFI_STA); + +#ifdef ESP32 + applySettings(); +#endif } else { // set LED mode for Wifi failed ledStatus->continuous(settings.ledModeWifiFailed); @@ -410,9 +436,9 @@ void setup() { ESP.restart(); } - MDNS.addService("http", "tcp", 80); +#ifdef ESP8266 SSDP.setSchemaURL("description.xml"); SSDP.setHTTPPort(80); SSDP.setName("ESP8266 MiLight Gateway"); @@ -420,11 +446,19 @@ void setup() { SSDP.setURL("/"); SSDP.setDeviceType("upnp:rootdevice"); SSDP.begin(); +#elif ESP32 + // TODO SSDP +#endif + httpServer = new MiLightHttpServer(settings, milightClient, stateStore, packetSender, radios, transitions); httpServer->onSettingsSaved(applySettings); httpServer->onGroupDeleted(onGroupDeleted); +#ifdef ESP8266 httpServer->on("/description.xml", HTTP_GET, []() { SSDP.schema(httpServer->client()); }); +#elif ESP32 + // TODO SSDP +#endif httpServer->begin(); transitions.addListener( @@ -443,6 +477,7 @@ void setup() { } void loop() { + httpServer->handleClient(); if (mqttClient) {