#include #include #include #include #define CONFIG_FILE "config.json" void SpejsNode::init() { deviceID = WifiStation.getMAC().substring(6, 12); currentSlot = 0; if(!rboot_get_last_boot_rom(¤tSlot)) { currentSlot = rboot_get_current_rom(); } Serial.begin(115200); Serial.systemDebugOutput(false); // Debug output to serial Serial.print("*** SpejsNode init, running on: "); Serial.print(deviceID); Serial.printf(", current rom: %d\r\n", currentSlot); WifiAccessPoint.enable(false); WifiStation.enable(true); WifiStation.config(WIFI_SSID, WIFI_PWD); WifiEvents.onStationGotIP(StationGotIPDelegate(&SpejsNode::gotIP, this)); registerEndpoint("$implementation", new ImplementationEndpoint()); // Keepalive Timer initialization keepaliveTimer.initializeMs(10000, TimerDelegate(&SpejsNode::keepAliveHandler, this)).start(); statusLED.high(); } void SpejsNode::loadJSON(std::vector initializers) { #ifdef RBOOT_SPIFFS_0 debugf("trying to mount spiffs at 0x%08x, length %d", RBOOT_SPIFFS_0, SPIFF_SIZE); spiffs_mount_manual(RBOOT_SPIFFS_0, SPIFF_SIZE); #else debugf("trying to mount spiffs at 0x%08x, length %d", 0x100000, SPIFF_SIZE); spiffs_mount_manual(0x100000, SPIFF_SIZE); #endif DynamicJsonBuffer jsonBuffer; if (fileExist(CONFIG_FILE)) { Serial.println("Found config file"); int size = fileGetSize(CONFIG_FILE); Serial.printf("%d bytes\n", size); char* jsonString = new char[size + 1]; fileGetContent(CONFIG_FILE, jsonString, size + 1); JsonObject& root = jsonBuffer.parseObject(jsonString); if (root.containsKey("name")) deviceType = (root["name"]).asString(); JsonObject& data = root["endpoints"].asObject(); for (auto it: data) { bool found = false; for(auto init: initializers) { Endpoint* ep = init(it.value); if (ep != NULL) { Serial.printf("%s: got object\n", it.key); registerEndpoint(it.key, ep); found = true; break; } } if (!found) { Serial.printf("%s: nothing found\n", it.key); } } } else { Serial.println("No configuration"); } } void SpejsNode::keepAliveHandler() { static int failureCounter = 0; if(!WifiStation.isConnected()) { statusLED.high(); Serial.println("keepalive: Network reconnect"); if(failureCounter++ < 5) WifiStation.connect(); else System.restart(); } else if(mqtt.getConnectionState() != eTCS_Connected) { statusLED.high(); Serial.println("keepalive: MQTT reconnect"); if(failureCounter++ < 5) onConnected(); else System.restart(); } else { statusLED.idle(); failureCounter = 0; uint8_t mode; if(rboot_get_last_boot_mode(&mode) && mode == MODE_TEMP_ROM) { rboot_set_current_rom(currentSlot); Serial.println("Successfuly connected, accepting temp rom"); } } } inline String SpejsNode::DEV_TOPIC(String t) { return TOPIC_PREFIX + deviceID + "/" + t; } void SpejsNode::httpIndex(HttpRequest &request, HttpResponse &response) { response.sendString("This is spejsiot device, take a look at: https://wiki.hackerspace.pl/projects:spejsiot\n" "\nDevice type: " + deviceType + "\nFirmware version: " + String(BUILD_ID) + "\nMAC: " + WifiStation.getMAC()); } /* * Successful network connection handler */ void SpejsNode::gotIP(IPAddress ip, IPAddress netmask, IPAddress gateway) { onConnected(); } void SpejsNode::onConnected() { statusLED.idle(); Serial.println("Connection successful"); // MQTT initialization mqtt.setWill(DEV_TOPIC("$online"), "false", 1, true); #ifdef ENABLE_SSL const uint8_t sha1Fingerprint[] = SSL_FINGERPRINT; mqtt.connect("iot-" + deviceID, "", "", true); mqtt.addSslOptions(SSL_SERVER_VERIFY_LATER); mqtt.setSslFingerprint(sha1Fingerprint, 20); #else mqtt.connect("iot-" + deviceID); #endif for(unsigned int i = 0 ; i < endpoints.count() ; i++) { endpoints.valueAt(i)->onConnected(); } subscribe("$implementation/+"); // Say hello notify("$online", "true"); notify("$homie", "2"); notify("$name", deviceType); notify("$localip", WifiStation.getIP().toString()); notify("$mac", WifiStation.getMAC()); notify("$fw/name", "spejsiot"); notify("$fw/version", BUILD_ID); notify("$fw/slot", String(currentSlot)); // HTTP initialization http.listen(80); http.addPath("/", HttpPathDelegate(&SpejsNode::httpIndex, this)); http.setDefaultHandler(HttpPathDelegate(&SpejsNode::httpFile, this)); // mDNS initialization initializeMDNS(); } 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 || !endpoints.contains(key)) { response.code = 400; } else { EndpointResult result = endpoints[key]->onValue(key, value); JsonObjectStream* stream = new JsonObjectStream(); JsonObject& json = stream->getRoot(); json["status"] = result.status; response.sendJsonObject(stream); } } } void SpejsNode::initializeMDNS() { /* static struct mdns_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; char tmp_version[32] = "version="; int prefix_len = strlen(tmp_version); strncat(tmp_version, BUILD_ID, 32); info->txt_data[0] = tmp_version; char tmp_type[32]; ("type=" + deviceType).toCharArray(tmp_type, 32); info->txt_data[1] = tmp_type; espconn_mdns_init(info); */ } /* * Publish on device-specific topic */ bool SpejsNode::notify(String key, String value) { mqtt.publish(DEV_TOPIC(key), value, true); return mqtt.getConnectionState() == eTCS_Connected; } /* * Subsribe to device-specific topic */ bool SpejsNode::subscribe(String topic) { mqtt.subscribe(DEV_TOPIC(topic)); return mqtt.getConnectionState() == eTCS_Connected; } /* * Register new endpoint */ void SpejsNode::registerEndpoint(String key, Endpoint* endpoint) { endpoints[key] = endpoint; endpoint->bind(key, this); } void SpejsNode::mqttCallback(String origtopic, String value) { /*int propPos = origtopic.indexOf("/", devicePrefix.length()); String endpoint = origtopic.substring(devicePrefix.length(), propPos); String property = origtopic.substring(propPos+1, origtopic.indexOf("/", propPos+1)); if(endpoints.contains(endpoint)) { Serial.printf("%s - %s response: %d\n", endpoint.c_str(), property.c_str(), endpoints[endpoint]->onValue(property, value).status); } else { Serial.println("unknown topic? " + endpoint); }*/ for(unsigned int i = 0 ; i < endpoints.count() ; i++) { endpoints.valueAt(i)->onMessage(origtopic, value); } }