Change naming convention to that of homie

master
informatic 2016-12-05 00:17:51 +01:00
parent 41edfc0953
commit 401b6a8108
9 changed files with 124 additions and 328 deletions

View File

@ -1,24 +0,0 @@
#####################################################################
#### Please don't change this file. Use Makefile-user.mk instead ####
#####################################################################
# Including user Makefile.
# Should be used to set project-specific parameters
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
ifndef ESP_HOME
$(error ESP_HOME is not set. Please configure it in Makefile-user.mk)
endif
# Include main Sming Makefile
ifeq ($(RBOOT_ENABLED), 1)
include $(SMING_HOME)/Makefile-rboot.mk
else
include $(SMING_HOME)/Makefile-project.mk
endif

View File

@ -1,31 +0,0 @@
## Local build configuration
## Parameters configured here will override default and ENV values.
## Uncomment and change examples:
#Add your source directories here separated by space
MODULES = app
DISABLE_SPIFFS = 1
USER_CFLAGS = -I../common
## ESP_HOME sets the path where ESP tools and SDK are located.
## Windows:
# ESP_HOME = c:/Espressif
## MacOS / Linux:
#ESP_HOME = /opt/esp-open-sdk
## SMING_HOME sets the path where Sming framework is located.
## Windows:
# SMING_HOME = c:/tools/sming/Sming
# MacOS / Linux
# SMING_HOME = /opt/sming/Sming
## COM port parameter is reqruied to flash firmware correctly.
## Windows:
# COM_PORT = COM3
# MacOS / Linux:
# COM_PORT = /dev/tty.usbserial
# Com port speed
# COM_SPEED = 115200

View File

@ -1,62 +0,0 @@
#include <user_config.h>
#include <common_config.h>
#include <SmingCore/SmingCore.h>
bool state = false;
MqttClient mqtt(MQTT_BROKER, MQTT_PORT, *[](String topic, String message) {
Serial.printf("*** message received @ %s:\n\t%s\n***\n", topic.c_str(), message.c_str());
if(message == "on" || message == "1") {
state = true;
} else if(message == "off" || message == "0") {
state = false;
} else if(message == "toggle") {
state = !state;
}
digitalWrite(LED_PIN, !state);
});
Timer keepaliveTimer;
String deviceName;
void startMqttClient()
{
Serial.println("*** Connecting to MQTT as " + deviceName);
mqtt.setWill("main/status/" + deviceName, "offline", 1, true);
mqtt.connect(deviceName);
mqtt.publish("main/status/" + deviceName, "online");
keepaliveTimer.initializeMs(5000, *[] {
mqtt.publish("main/status/" + deviceName, "alive " + String(millis()));
}).start();
mqtt.subscribe("light/status");
}
void init()
{
deviceName = "light-" + WifiStation.getMAC().substring(6, 12);
Serial.begin(SERIAL_BAUD_RATE); // 115200 by default
Serial.systemDebugOutput(true); // Debug output to serial
Serial.println("*** Starting " + deviceName + " ...");
WifiStation.config(WIFI_SSID, WIFI_PWD);
WifiStation.enable(true);
WifiAccessPoint.enable(false);
WifiStation.waitConnection(*[] {
Serial.println("*** Connection succeeded");
startMqttClient();
}, 20, *[] {
Serial.println("*** Connection failed");
});
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
}

View File

@ -1,45 +0,0 @@
#ifndef __USER_CONFIG_H__
#define __USER_CONFIG_H__
#ifdef __cplusplus
extern "C" {
#endif
// UART config
#define SERIAL_BAUD_RATE 115200
// ESP SDK config
#define LWIP_OPEN_SRC
#define USE_US_TIMER
// Default types
#define __CORRECT_ISO_CPP_STDLIB_H_PROTO
#include <limits.h>
#include <stdint.h>
// Override c_types.h include and remove buggy espconn
#define _C_TYPES_H_
#define _NO_ESPCON_
// Updated, compatible version of c_types.h
// Just removed types declared in <stdint.h>
#include <espinc/c_types_compatible.h>
// System API declarations
#include <esp_systemapi.h>
// C++ Support
#include <esp_cplusplus.h>
// Extended string conversion for compatibility
#include <stringconversion.h>
// Network base API
#include <espinc/lwip_includes.h>
// Beta boards
#define BOARD_ESP01
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,37 +1,60 @@
#include <SpejsNode.h>
#include <Endpoint.h>
void Endpoint::bind(String _key, SpejsNode* _parent) {
void Endpoint::bind(String _name, SpejsNode* _parent) {
parent = _parent;
key = _key;
name = _name;
}
void Endpoint::notify(String value) {
void Endpoint::notify(String property, String value) {
if(parent)
parent->notify(key, value);
parent->notify(name + "/" + property, value);
}
template <class T> void ValueEndpoint<T>::updateValue(T newValue) {
value = newValue;
notify(String(value));
}
template <class T> void ValueEndpoint<T>::fillValue(JsonObject& obj) {
obj["value"] = value;
}
EndpointResult ControlEndpoint::onValue(String key, String value) {
if (value == "ota") {
return startOTA();
} else if(value == "restart") {
System.restart();
return 200;
EndpointResult OutputEndpoint::onValue(String property, String value) {
if (value == "1" or value == "on" or value == "true") {
currentValue = 1;
} else if (value == "0" or value == "off" or value == "false") {
currentValue = 0;
} else {
return 400;
}
digitalWrite(pin, inverted ^ currentValue);
notify("on", currentValue ? "true" : "false");
return 200;
}
EndpointResult ControlEndpoint::startOTA() {
void DHTEndpoint::bind(String _name, SpejsNode* _parent) {
Endpoint::bind(_name, _parent);
sensor.begin();
samplingTimer.initializeMs(samplingRate, TimerDelegate(&DHTEndpoint::sample, this)).start();
}
void DHTEndpoint::sample() {
TempAndHumidity th;
if(sensor.readTempAndHumidity(th))
{
notify("degree", String(th.temp));
notify("humidity", String(th.humid));
}
else
{
Serial.print("Failed to read from DHT: ");
Serial.print(sensor.getLastError());
}
}
EndpointResult ImplementationEndpoint::onValue(String property, String value) {
if (property == "ota" and value == "true") {
startOTA();
return 200;
}
return 400;
}
void ImplementationEndpoint::startOTA() {
uint8_t slot;
rboot_config bootconf;
String romURL = OTA_URL + parent->deviceID + "/rom0.bin";
@ -64,18 +87,16 @@ EndpointResult ControlEndpoint::startOTA() {
}
#endif
otaUpdater->setCallback(otaUpdateDelegate(&ControlEndpoint::otaUpdateCallback, this));
otaUpdater->setCallback(otaUpdateDelegate(&ImplementationEndpoint::otaUpdateCallback, this));
otaUpdater->start();
//notify("ota", "started");
return 200;
notify("ota", "started");
}
void ControlEndpoint::otaUpdateCallback(bool result) {
void ImplementationEndpoint::otaUpdateCallback(bool result) {
if(result == true) {
// success
//notify("ota", "finished");
Serial.println("ota finished");
notify("ota", "finished");
uint8 slot;
@ -88,49 +109,9 @@ void ControlEndpoint::otaUpdateCallback(bool result) {
Serial.printf("Firmware updated, rebooting to rom %d...\r\n", slot);
rboot_set_temp_rom(slot);
System.restart();
} else {
Serial.println("ota failed");
//notify("ota", "failed");
}
}
void OutputEndpoint::fillValue(JsonObject& obj) {
obj["value"] = currentValue;
}
EndpointResult OutputEndpoint::onValue(String key, String value) {
if (value == "1" or value == "on") {
currentValue = 1;
} else if (value == "0" or value == "off") {
currentValue = 0;
//} else if (value == "toggle") {
// currentValue = !currentValue;
} else {
return 400;
}
digitalWrite(pin, inverted ^ currentValue);
return 200;
}
void DHTEndpoint::bind(String _key, SpejsNode* _parent) {
parent = _parent;
key = _key;
sensor.begin();
samplingTimer.initializeMs(samplingRate, TimerDelegate(&DHTEndpoint::sample, this)).start();
}
void DHTEndpoint::sample() {
TempAndHumidity th;
if(sensor.readTempAndHumidity(th))
{
updateValue(th.temp);
}
else
{
Serial.print("Failed to read from DHT: ");
Serial.print(sensor.getLastError());
notify("ota", "failed");
}
}

View File

@ -18,33 +18,19 @@ public:
class Endpoint {
protected:
SpejsNode* parent;
String key;
String name;
public:
String type;
Endpoint(String _type = "unknown") : type(_type) { }
virtual void bind(String _key, SpejsNode* _parent);
void notify(String value);
virtual void bind(String _name, SpejsNode* _parent);
void notify(String property, String value);
virtual EndpointResult onValue(String key, String value) {
virtual EndpointResult onValue(String property, String value) {
return 400;
}
virtual void fillValue(JsonObject& obj) { }
};
class ControlEndpoint : public Endpoint {
public:
ControlEndpoint() : Endpoint("control") {}
EndpointResult onValue(String key, String value);
void otaUpdateCallback(bool result);
protected:
rBootHttpUpdate* otaUpdater = 0;
EndpointResult startOTA();
};
class OutputEndpoint : public Endpoint {
@ -60,8 +46,7 @@ public:
digitalWrite(pin, currentValue);
}
EndpointResult onValue(String key, String value);
void fillValue(JsonObject& obj);
EndpointResult onValue(String property, String value);
};
template <class T> class ValueEndpoint : public Endpoint {
@ -71,8 +56,6 @@ protected:
public:
ValueEndpoint(String _type) : Endpoint(_type) {}
virtual void fillValue(JsonObject& obj);
};
#include <Libraries/DHT/DHT.h>
@ -90,7 +73,19 @@ public:
DHTEndpoint(int _pin, int _samplingRate = 10000) :
ValueEndpoint("dht"), sensor(_pin, DHT11), samplingRate(_samplingRate) {}
void bind(String key, SpejsNode* _parent);
void bind(String name, SpejsNode* _parent);
};
class ImplementationEndpoint : public Endpoint {
protected:
rBootHttpUpdate* otaUpdater = 0;
public:
ImplementationEndpoint() : Endpoint("$implementation") {}
EndpointResult onValue(String property, String value);
void otaUpdateCallback(bool result);
void startOTA();
};
#endif

View File

@ -25,7 +25,7 @@ void SpejsNode::init() {
Serial.println("Connection failed");
});
registerEndpoint("control", new ControlEndpoint());
registerEndpoint("$implementation", new ImplementationEndpoint());
}
void SpejsNode::keepAliveHandler() {
@ -46,45 +46,67 @@ void SpejsNode::keepAliveHandler() {
rboot_set_current_rom(currentSlot);
Serial.println("Successfuly connected, accepting temp rom");
} else {
Serial.printf("Not a TEMP ROM boot: %d\r\n", mode);
//Serial.printf("Not a TEMP ROM boot: %d\r\n", mode);
}
} else {
Serial.println("No boot mode info");
//Serial.println("No boot mode info");
}
}
}
#define DEV_TOPIC(t) (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");
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());
}
void SpejsNode::buildMetadata(JsonObjectStream* stream)
{
JsonObject& json = stream->getRoot();
json["version"] = 1;
json["device_id"] = deviceID;
json["device_type"] = deviceType;
json["rom_slot"] = currentSlot;
json["rom_rev"] = BUILD_ID;
void SpejsNode::onConnected() {
Serial.println("Connection successful");
JsonArray& endpoints_list = json.createNestedArray("endpoints");
for(unsigned int i = 0; i < endpoints.count(); i++) {
JsonObject& obj = endpoints_list.createNestedObject();
obj["name"] = endpoints.keyAt(i);
obj["type"] = endpoints.valueAt(i)->type;
// MQTT initialization
mqtt.setWill(TOPIC_PREFIX + deviceID + "/$online", "false", 1, true);
endpoints.valueAt(i)->fillValue(obj);
#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++) {
mqtt.subscribe(TOPIC_PREFIX + deviceID + "/" + endpoints.keyAt(i) + "/+/set");
}
}
mqtt.subscribe(TOPIC_PREFIX + deviceID + "/$implementation/+");
void SpejsNode::httpMetadata(HttpRequest &request, HttpResponse &response)
{
JsonObjectStream* stream = new JsonObjectStream();
buildMetadata(stream);
stream->getRoot()["uptime"] = millis();
// Say hello
mqtt.publish(DEV_TOPIC("$online"), "true", true);
mqtt.publish(DEV_TOPIC("$homie"), "2", true);
mqtt.publish(DEV_TOPIC("$name"), deviceType, true);
mqtt.publish(DEV_TOPIC("$localip"), WifiStation.getIP().toString(), true);
mqtt.publish(DEV_TOPIC("$mac"), WifiStation.getMAC(), true);
mqtt.publish(DEV_TOPIC("$fw/name"), "spejsiot", true);
mqtt.publish(DEV_TOPIC("$fw/version"), BUILD_ID, true);
response.sendJsonObject(stream);
for(unsigned int i = 0; i < endpoints.count(); i++) {
mqtt.publish(DEV_TOPIC(endpoints.keyAt(i) + "/$type"), endpoints.valueAt(i)->type, true);
}
// HTTP initialization
http.listen(80);
http.addPath("/", HttpPathDelegate(&SpejsNode::httpIndex, this));
http.setDefaultHandler(HttpPathDelegate(&SpejsNode::httpFile, this));
// mDNS initialization
initializeMDNS();
// Keepalive Timer initialization
keepaliveTimer.initializeMs(10000, TimerDelegate(&SpejsNode::keepAliveHandler, this)).start();
}
void SpejsNode::httpFile(HttpRequest &request, HttpResponse &response)
@ -108,51 +130,11 @@ void SpejsNode::httpFile(HttpRequest &request, HttpResponse &response)
json["status"] = result.status;
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);
}
}
void SpejsNode::onConnected() {
Serial.println("Connection successful");
// MQTT initialization
mqtt.setWill(TOPIC_PREFIX + deviceID + "/state", "offline", 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++) {
mqtt.subscribe(TOPIC_PREFIX + deviceID + "/" + endpoints.keyAt(i));
}
// Say hello
mqtt.publish(TOPIC_PREFIX + deviceID + "/state", "online", true);
JsonObjectStream stream;
buildMetadata(&stream);
char* metadataStr = new char[stream.getRoot().measureLength() + 10]();
stream.getRoot().printTo(metadataStr, stream.getRoot().measureLength() + 10);
mqtt.publish(TOPIC_PREFIX + deviceID + "/metadata", metadataStr, true);
delete metadataStr;
// HTTP initialization
http.listen(80);
http.addPath("/", HttpPathDelegate(&SpejsNode::httpIndex, this));
http.addPath("/metadata.json", HttpPathDelegate(&SpejsNode::httpMetadata, this));
http.setDefaultHandler(HttpPathDelegate(&SpejsNode::httpFile, this));
// mDNS initialization
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);
@ -171,14 +153,11 @@ void SpejsNode::onConnected() {
info->txt_data[1] = tmp_type;
espconn_mdns_init(info);
// Keepalive Timer initialization
keepaliveTimer.initializeMs(10000, TimerDelegate(&SpejsNode::keepAliveHandler, this)).start();
}
bool SpejsNode::notify(String key, String value) {
if(mqtt.getConnectionState() == eTCS_Connected) {
mqtt.publish(TOPIC_PREFIX + deviceID + "/" + key + "/state", value, true);
mqtt.publish(TOPIC_PREFIX + deviceID + "/" + key, value, true);
return true;
} else {
Serial.println("MQTT Not Connected!!!");
@ -200,11 +179,13 @@ void SpejsNode::mqttCallback(String origtopic, String value) {
return;
}
String topic = origtopic.substring(devicePrefix.length() + 1);
int propPos = origtopic.indexOf("/", devicePrefix.length() + 1);
String endpoint = origtopic.substring(devicePrefix.length() + 1, propPos);
String property = origtopic.substring(propPos+1, origtopic.indexOf("/", propPos+1));
if(endpoints.contains(topic)) {
endpoints[topic]->onValue(origtopic, value);
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?");
Serial.println("unknown topic? " + endpoint);
}
}

View File

@ -27,6 +27,7 @@ protected:
void onConnected();
void keepAliveHandler();
void initializeMDNS();
void buildMetadata(JsonObjectStream* stream);

View File

@ -4,5 +4,5 @@ SpejsNode node("switch");
void init() {
node.init();
node.registerEndpoint("relay", new OutputEndpoint(5));
node.registerEndpoint("relay", new OutputEndpoint(2));
}