diff --git a/README.md b/README.md index 4bc8191..1be2909 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,10 @@ Oh my, that's slow.^W^Wquite fast, when patched properly. **WARNING!** `Sming.reset();` jumps to (serial) bootloader right after flashing. This causes OTA to fail with `wdt reset`. External RESET assert is required after flashing. + +TODO +---- + * Refactor endpoint handling + * Fix mDNS + * Store configuration (credentials, broker IP, maybe endpoints?) in + flash memory diff --git a/common/common_config.h b/common/common_config.h index 686dd85..cb8b0ed 100644 --- a/common/common_config.h +++ b/common/common_config.h @@ -18,7 +18,7 @@ #define TOPIC_PREFIX "iot/" -#define OTA_URL "http://" MQTT_BROKER "/" +#define OTA_URL "http://" MQTT_BROKER "/api/1/ota/" #define BTN_PIN 0 #define LED_PIN 2 diff --git a/spejsiot/SpejsNode.cpp b/spejsiot/SpejsNode.cpp index e383548..01e6b1c 100644 --- a/spejsiot/SpejsNode.cpp +++ b/spejsiot/SpejsNode.cpp @@ -1,16 +1,17 @@ #include +#include void SpejsNode::init() { deviceID = WifiStation.getMAC().substring(6, 12); currentSlot = 0; if(!rboot_get_last_boot_rom(¤tSlot)) { - currentSlot = rboot_get_current_rom(); + currentSlot = rboot_get_current_rom(); } Serial.begin(115200); Serial.systemDebugOutput(false); // Debug output to serial - Serial.print("*** SpejsNode init, running on:"); + Serial.print("*** SpejsNode init, running on: "); Serial.print(deviceID); Serial.printf(", current rom: %d\r\n", currentSlot); @@ -20,10 +21,10 @@ void SpejsNode::init() { WifiAccessPoint.enable(false); WifiStation.waitConnection( - ConnectionDelegate(&SpejsNode::onConnected, this), - 20, *[] { - Serial.println("Connection failed"); - }); + ConnectionDelegate(&SpejsNode::onConnected, this), + 20, *[] { + Serial.println("Connection failed"); + }); inputs["control"] = MqttStringSubscriptionCallback(&SpejsNode::controlHandler, this); } @@ -31,19 +32,69 @@ void SpejsNode::init() { void SpejsNode::keepAliveHandler() { if(mqtt.getConnectionState() != eTCS_Connected) { Serial.println("Reconnecting"); - onConnected(); + onConnected(); } else { - uint8_t mode; - if(rboot_get_last_boot_mode(&mode)) { - if(mode == MODE_TEMP_ROM) { - rboot_set_current_rom(currentSlot); - Serial.println("Successfuly connected, accepting temp rom"); - } else { - Serial.printf("Not a TEMP ROM boot: %d\r\n", mode); - } - } else { - Serial.println("No boot mode info"); - } + uint8_t mode; + if(rboot_get_last_boot_mode(&mode)) { + if(mode == MODE_TEMP_ROM) { + rboot_set_current_rom(currentSlot); + Serial.println("Successfuly connected, accepting temp rom"); + } else { + Serial.printf("Not a TEMP ROM boot: %d\r\n", mode); + } + } else { + Serial.println("No boot mode info"); + } + } +} + +void SpejsNode::httpIndex(HttpRequest &request, HttpResponse &response) +{ +} + +void SpejsNode::httpMetadata(HttpRequest &request, HttpResponse &response) +{ + JsonObjectStream* stream = new JsonObjectStream(); + JsonObject& json = stream->getRoot(); + json["version"] = 1; + json["device_id"] = deviceID; + json["device_type"] = deviceType; + json["rom_slot"] = currentSlot; + json["rom_rev"] = BUILD_ID; + + JsonArray& endpoints = json.createNestedArray("endpoints"); + for(unsigned int i = 0; i < inputs.count(); i++) { + endpoints.add(inputs.keyAt(i)); + } + response.sendJsonObject(stream); +} + +void SpejsNode::httpFile(HttpRequest &request, HttpResponse &response) +{ + String file = request.getPath(); + + if (file[0] == '/') + file = file.substring(1); + + if (file.startsWith("api/1/")) { + String req = file.substring(6); + String key = req.substring(0, req.indexOf("/")); + String value = req.substring(req.indexOf("/") + 1); + + if(key.length() == 0 || value.length() == 0 || !inputs.contains(key)) { + response.badRequest(); + } else { + inputs[key](key, value); + JsonObjectStream* stream = new JsonObjectStream(); + JsonObject& json = stream->getRoot(); + json["status"] = (bool)true; + response.sendJsonObject(stream); + } + } else if (file[0] == '.') { + response.forbidden(); + } else { + response.setCache(86400, true); // It's important to use cache for better performance. + response.sendFile(file); } } @@ -71,6 +122,28 @@ void SpejsNode::onConnected() { mqtt.publish(TOPIC_PREFIX + deviceID + "/type", deviceType); keepaliveTimer.initializeMs(10000, TimerDelegate(&SpejsNode::keepAliveHandler, this)).start(); + + http.listen(80); + + http.addPath("/", HttpPathDelegate(&SpejsNode::httpIndex, this)); + http.addPath("/metadata.json", HttpPathDelegate(&SpejsNode::httpMetadata, this)); + + http.setDefaultHandler(HttpPathDelegate(&SpejsNode::httpFile, this)); + + static struct mdns_info info;// *info = (struct mdns_info *)os_zalloc(sizeof(struct mdns_info)); + char tmp_name[32]; + ("iot-" + deviceID).toCharArray(tmp_name, 32); + info.host_name = tmp_name; // You can replace test with your own host name + info.ipAddr = WifiStation.getIP(); + info.server_name = (char *) "spejsiot"; + info.server_port = 80; + info.txt_data[0] = (char *) "version = now"; + + char tmp_type[32] = "type = "; + deviceType.toCharArray(tmp_type + 7, 32-7); + info.txt_data[1] = tmp_type; + + espconn_mdns_init(&info); } bool SpejsNode::notify(String key, String value) { @@ -144,9 +217,9 @@ void SpejsNode::startOTA() { #ifndef DISABLE_SPIFFS // use user supplied values (defaults for 4mb flash in makefile) if (slot == 0) { - otaUpdater->addItem(RBOOT_SPIFFS_0, spiffsURL); + otaUpdater->addItem(RBOOT_SPIFFS_0, spiffsURL); } else { - otaUpdater->addItem(RBOOT_SPIFFS_1, spiffsURL); + otaUpdater->addItem(RBOOT_SPIFFS_1, spiffsURL); } #endif @@ -158,23 +231,23 @@ void SpejsNode::startOTA() { void SpejsNode::otaUpdateCallback(rBootHttpUpdate& updater, bool result) { if(result == true) { - // success - notify("ota", "finished"); + // success + notify("ota", "finished"); - uint8 slot; + uint8 slot; - if (currentSlot == 0) + if (currentSlot == 0) slot = 1; - else + else slot = 0; - // set to boot new rom and then reboot - Serial.printf("Firmware updated, rebooting to rom %d...\r\n", slot); + // set to boot new rom and then reboot + Serial.printf("Firmware updated, rebooting to rom %d...\r\n", slot); - rboot_set_temp_rom(slot); + rboot_set_temp_rom(slot); - System.restart(); + System.restart(); } else { - notify("ota", "failed"); + notify("ota", "failed"); } } diff --git a/spejsiot/SpejsNode.h b/spejsiot/SpejsNode.h index 4242e42..4a7f7c3 100644 --- a/spejsiot/SpejsNode.h +++ b/spejsiot/SpejsNode.h @@ -8,6 +8,7 @@ protected: String deviceType; MqttClient mqtt; + HttpServer http; Timer keepaliveTimer; @@ -33,4 +34,7 @@ public: void mqttCallback(String, String); void controlHandler(String, String); void otaUpdateCallback(rBootHttpUpdate& updater, bool result); + void httpFile(HttpRequest &request, HttpResponse &response); + void httpIndex(HttpRequest &request, HttpResponse &response); + void httpMetadata(HttpRequest &request, HttpResponse &response); }; diff --git a/spejsiot/ver.h b/spejsiot/ver.h new file mode 100644 index 0000000..bf32d9f --- /dev/null +++ b/spejsiot/ver.h @@ -0,0 +1 @@ +extern char* BUILD_ID; diff --git a/switch/Makefile b/switch/Makefile index e5af1e5..1213c3e 100644 --- a/switch/Makefile +++ b/switch/Makefile @@ -8,7 +8,7 @@ include ./Makefile-user.mk # Important parameters check. # We need to make sure SMING_HOME and ESP_HOME variables are set. # You can use Makefile-user.mk in each project or use enviromental variables to set it globally. - + ifndef SMING_HOME $(error SMING_HOME is not set. Please configure it in Makefile-user.mk) endif @@ -23,7 +23,16 @@ else include $(SMING_HOME)/Makefile-project.mk endif +$(APP_AR): $(OBJ) + $(vecho) "AR $@" + + @echo "char* BUILD_ID = \"$(shell git rev-parse --short HEAD)-$(shell TZ=UTC date +%Y%m%d-%H%M%S)\";" > $(BUILD_BASE)/ver.c + cat $(BUILD_BASE)/ver.c + $(CC) -c $(BUILD_BASE)/ver.c -o $(BUILD_BASE)/ver.o + + $(Q) $(AR) cru $@ $^ $(BUILD_BASE)/ver.o + ota: all -mkdir ../master/ota/$(DEVICE) >/dev/null cp -r out/firmware/* ../master/ota/$(DEVICE) - python ../master/ota.py $(DEVICE) control ota + python ../master/client.py $(DEVICE) control ota diff --git a/switch/Makefile-user.mk b/switch/Makefile-user.mk index 5735625..0f34e50 100644 --- a/switch/Makefile-user.mk +++ b/switch/Makefile-user.mk @@ -1,7 +1,7 @@ MODULES = app ../spejsiot DISABLE_SPIFFS = 1 USER_CFLAGS = -I../common -ENABLE_SSL = 1 +ENABLE_SSL = 0 RBOOT_ENABLED = 1 RBOOT_BIG_FLASH = 1 RBOOT_RTC_ENABLED = 1