spejsiot/spejsiot/SpejsNode.cpp

257 lines
7.6 KiB
C++

#include <SpejsNode.h>
#include <Endpoint.h>
#include <ver.h>
#include <endpoints/ImplementationEndpoint.h>
#define CONFIG_FILE "config.json"
void SpejsNode::init() {
deviceID = WifiStation.getMAC().substring(6, 12);
currentSlot = 0;
if(!rboot_get_last_boot_rom(&currentSlot)) {
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<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)) {
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);
}
}