219 lines
6.3 KiB
C++
219 lines
6.3 KiB
C++
#include <SpejsNode.h>
|
|
#include <Endpoint.h>
|
|
#include <ver.h>
|
|
|
|
#include <endpoints/ImplementationEndpoint.h>
|
|
|
|
#define CONFIG_FILE "config.json"
|
|
|
|
void SpejsNode::init(bool debug) {
|
|
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(debug); // Debug output to serial
|
|
|
|
debugf("*** SpejsNode init, runnning on: %s, current rom: %d", deviceID.c_str(), 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<EndpointInitializer> 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)) {
|
|
int size = fileGetSize(CONFIG_FILE);
|
|
debugf("Found config file, %d bytes", 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) {
|
|
debugf("%s: got object", it.key);
|
|
registerEndpoint(it.key, ep);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
debugf("%s: nothing found", it.key);
|
|
}
|
|
}
|
|
} else {
|
|
debugf("No configuration");
|
|
}
|
|
}
|
|
|
|
void SpejsNode::keepAliveHandler() {
|
|
static int failureCounter = 0;
|
|
if(!WifiStation.isConnected()) {
|
|
statusLED.high();
|
|
debugf("keepalive: Network reconnect");
|
|
if(failureCounter++ < 5)
|
|
WifiStation.connect();
|
|
else
|
|
System.restart();
|
|
} else if(mqtt.getConnectionState() != eTCS_Connected) {
|
|
statusLED.high();
|
|
debugf("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);
|
|
debugf("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();
|
|
|
|
debugf("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("$implementation/slot", String(currentSlot));
|
|
|
|
// HTTP initialization
|
|
http.listen(80);
|
|
http.addPath("/", HttpPathDelegate(&SpejsNode::httpIndex, this));
|
|
http.setDefaultHandler(HttpPathDelegate(&SpejsNode::httpFile, this));
|
|
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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) {
|
|
for(unsigned int i = 0 ; i < endpoints.count() ; i++) {
|
|
endpoints.valueAt(i)->onMessage(origtopic, value);
|
|
}
|
|
}
|