Compare commits
8 Commits
Author | SHA1 | Date |
---|---|---|
vuko | ca839a93a9 | |
vuko | b39215364d | |
informatic | 87ce4d473d | |
informatic | 19a58cf28f | |
informatic | 517ceea15c | |
informatic | 24924811b2 | |
informatic | 5879a80f48 | |
informatic | 012bdd0735 |
57
Makefile
57
Makefile
|
@ -1,57 +0,0 @@
|
||||||
#####################################################################
|
|
||||||
#### Please don't change this file. Use Makefile-user.mk instead ####
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
# SpejsIoT base configuration
|
|
||||||
MODULES = app $(realpath ../spejsiot ../spejsiot/endpoints)
|
|
||||||
EXTRA_INCDIR = include $(realpath ../common)
|
|
||||||
#ENABLE_SSL = 1
|
|
||||||
DISABLE_SPIFFS ?= 1
|
|
||||||
RBOOT_ENABLED ?= 1
|
|
||||||
RBOOT_BIG_FLASH ?= 1
|
|
||||||
RBOOT_RTC_ENABLED ?= 1
|
|
||||||
SPI_SIZE ?= 4M
|
|
||||||
|
|
||||||
RBOOT_ROM_0 ?= rom0
|
|
||||||
RBOOT_LD_0 ?= rboot.rom0.ld
|
|
||||||
RBOOT_ROM_1 ?= rom1
|
|
||||||
RBOOT_LD_1 ?= rboot.rom1.ld
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# Add BUILD_ID versioning
|
|
||||||
$(APP_AR): $(BUILD_BASE)/ver.o
|
|
||||||
OBJ += $(BUILD_BASE)/ver.o
|
|
||||||
$(BUILD_BASE)/ver.o:
|
|
||||||
@echo "char* BUILD_ID = \"$(shell git rev-parse --short HEAD)-$(shell TZ=UTC date +%Y%m%d-%H%M%S)\";" > $(BUILD_BASE)/ver.c
|
|
||||||
$(Q) $(CC) -c $(BUILD_BASE)/ver.c -o $(BUILD_BASE)/ver.o
|
|
||||||
|
|
||||||
|
|
||||||
ota: all
|
|
||||||
-mkdir ../master/ota/$(DEVICE) >/dev/null
|
|
||||||
cp -r out/firmware/* ../master/ota/$(DEVICE)
|
|
||||||
python ../master/client.py $(DEVICE) control ota
|
|
||||||
|
|
||||||
remoteota: all
|
|
||||||
scp -r out/firmware/rom*.bin root@sound:/var/www/api/1/ota/$(DEVICE)/
|
|
||||||
python ../master/remoteclient.py $(DEVICE) \$$implementation/ota true
|
|
|
@ -1,2 +1,3 @@
|
||||||
SPIFF_SIZE = 196600
|
SPIFF_SIZE = 196600
|
||||||
DISABLE_SPIFFS = 0
|
DISABLE_SPIFFS = 0
|
||||||
|
ENABLE_SSL = 1
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
SpejsNode node("unconfigured-generic-device");
|
SpejsNode node("unconfigured-generic-device");
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
node.init();
|
node.init(true);
|
||||||
node.loadJSON({
|
node.loadJSON({
|
||||||
&OutputEndpoint::fromJSON,
|
&OutputEndpoint::fromJSON,
|
||||||
&DHTEndpoint::fromJSON,
|
&DHTEndpoint::fromJSON,
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
SPIFF_SIZE := 196600
|
||||||
|
DISABLE_SPIFFS := 0
|
||||||
|
RBOOT_RTC_ENABLED := 1
|
||||||
|
|
||||||
|
COMPONENT_SEARCH_DIRS := ../../
|
||||||
|
COMPONENT_DEPENDS := spejsiot
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "testdev",
|
"name": "unprovisioned",
|
||||||
|
|
||||||
"extras": {
|
"extras": {
|
||||||
"owner": "informatic",
|
"owner": "informatic",
|
||||||
|
@ -10,5 +10,11 @@
|
||||||
"out": {"type": "output", "gpio": 2},
|
"out": {"type": "output", "gpio": 2},
|
||||||
"testInput": {"type": "input", "gpio": 3},
|
"testInput": {"type": "input", "gpio": 3},
|
||||||
"testtemp": {"type": "dht"}
|
"testtemp": {"type": "dht"}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
"broker": "mqtts://10.8.0.136:8883",
|
||||||
|
"brokerSHA1": "E7:FA:91:2B:E9:D6:7E:55:83:16:8D:09:68:BF:DD:4F:5D:F4:AC:DA",
|
||||||
|
"brokerSHA256": "29:10:5e:29:27:d6:2f:05:21:cc:8b:0e:f2:ca:e9:aa:1d:f4:1a:63:58:ed:4e:c8:92:b2:cb:9e:d7:71:a9:1d",
|
||||||
|
"brokerClient": "testing",
|
||||||
|
"brokerUseTLS": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
#ifndef MQTT_BROKER
|
#ifndef MQTT_BROKER
|
||||||
#define MQTT_BROKER "192.168.0.100"
|
#define MQTT_BROKER "192.168.0.100"
|
||||||
#ifdef ENABLE_SSL
|
#ifdef ENABLE_SSL
|
||||||
#define MQTT_PORT 8883
|
#define MQTT_PORT "8883"
|
||||||
#define SSL_FINGERPRINT { } // TODO
|
#define SSL_FINGERPRINT { } // TODO
|
||||||
#else
|
#else
|
||||||
#define MQTT_PORT 1883
|
#define MQTT_PORT "1883"
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,10 @@ extern "C" {
|
||||||
|
|
||||||
// ESP SDK config
|
// ESP SDK config
|
||||||
#define LWIP_OPEN_SRC
|
#define LWIP_OPEN_SRC
|
||||||
#define USE_US_TIMER
|
|
||||||
|
#ifndef USE_US_TIMER
|
||||||
|
#define USE_US_TIMER
|
||||||
|
#endif
|
||||||
|
|
||||||
// Default types
|
// Default types
|
||||||
#define __CORRECT_ISO_CPP_STDLIB_H_PROTO
|
#define __CORRECT_ISO_CPP_STDLIB_H_PROTO
|
||||||
|
@ -18,12 +21,13 @@ extern "C" {
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// Override c_types.h include and remove buggy espconn
|
// Override c_types.h include and remove buggy espconn
|
||||||
#define _C_TYPES_H_
|
//#define _C_TYPES_H_
|
||||||
#define _NO_ESPCON_
|
//#define _NO_ESPCON_
|
||||||
|
|
||||||
// Updated, compatible version of c_types.h
|
// Updated, compatible version of c_types.h
|
||||||
// Just removed types declared in <stdint.h>
|
// Just removed types declared in <stdint.h>
|
||||||
#include <espinc/c_types_compatible.h>
|
// #include <espinc/c_types_compatible.h>
|
||||||
|
//#include <c_types.h>
|
||||||
|
|
||||||
// System API declarations
|
// System API declarations
|
||||||
#include <esp_systemapi.h>
|
#include <esp_systemapi.h>
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
COMPONENT_NAME := spejsiot
|
||||||
|
COMPONENT_SRCDIRS := spejsiot spejsiot/endpoints/
|
||||||
|
COMPONENT_INCDIRS := spejsiot common/
|
||||||
|
COMPONENT_DEPENDS := ArduinoJson6 rboot spiffs
|
||||||
|
|
||||||
|
COMPONENT_CXXFLAGS := -DSPIFF_SIZE=196600 -DBOOT_RTC_ENABLED=1 -DRBOOT_SPIFFS_0=0x100000 -DRBOOT_SPIFFS_1=0x300000
|
|
@ -0,0 +1,39 @@
|
||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
let
|
||||||
|
esp = import (pkgs.fetchFromGitHub {
|
||||||
|
owner = "xvuko";
|
||||||
|
repo = "esp-nix";
|
||||||
|
rev = "1a100e3dce4fd72532ef16d3ec60e7eb2d98387a";
|
||||||
|
sha256 = "1yihw5p1n5s6l9zds9n566iai63zy3zhdlw70735fj504zdqjxv6";
|
||||||
|
}) {inherit pkgs; };
|
||||||
|
|
||||||
|
in
|
||||||
|
pkgs.stdenvNoCC.mkDerivation {
|
||||||
|
|
||||||
|
name = "spejsiot";
|
||||||
|
phases = ["unpackPhase" "buildPhase" "installPhase"];
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
ESP_HOME = esp.esp-open-sdk;
|
||||||
|
SMING_RELEASE = "1";
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
cp -r ${esp.sming} $NIX_BUILD_TOP/Sming
|
||||||
|
chmod +w -R $NIX_BUILD_TOP/Sming
|
||||||
|
export SMING_HOME=$(readlink -f $NIX_BUILD_TOP/Sming/Sming)
|
||||||
|
|
||||||
|
cd base-firmware
|
||||||
|
echo "const char * BUILD_ID=\"$out\";" > app/ver.c
|
||||||
|
make
|
||||||
|
'';
|
||||||
|
|
||||||
|
buildInputs = [ pkgs.python3 ];
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out
|
||||||
|
cp -r out/Esp8266/release/firmware $out/firmware
|
||||||
|
mkdir -p $out/bin/
|
||||||
|
ln -s ${esp.flash}/bin/flash $out/bin/flash
|
||||||
|
#make flash V=1
|
||||||
|
'';
|
||||||
|
}
|
|
@ -6,4 +6,7 @@ openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out s
|
||||||
|
|
||||||
# Extract SHA-1
|
# Extract SHA-1
|
||||||
openssl x509 -in server.crt -sha1 -noout -fingerprint | sed 's/.*=/0x/g; s/:/, 0x/g'
|
openssl x509 -in server.crt -sha1 -noout -fingerprint | sed 's/.*=/0x/g; s/:/, 0x/g'
|
||||||
|
|
||||||
|
# extract sha256
|
||||||
|
openssl x509 -in mqtt/certs/server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 | sed -r 's/.*= //g; s/([0-9a-z]{2})/\1:/g; s/:$//g'
|
||||||
```
|
```
|
||||||
|
|
|
@ -13,7 +13,9 @@ void Endpoint::notify(String property, String value) {
|
||||||
|
|
||||||
void Endpoint::onConnected() {
|
void Endpoint::onConnected() {
|
||||||
parent->subscribe(name + "/+/set");
|
parent->subscribe(name + "/+/set");
|
||||||
parent->notify(name + "/$type", type);
|
if (type.length()) {
|
||||||
|
parent->notify(name + "/$type", type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Endpoint::onMessage(String topic, String payload) {
|
void Endpoint::onMessage(String topic, String payload) {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#ifndef ENDPOINT_H
|
#ifndef ENDPOINT_H
|
||||||
#define ENDPOINT_H
|
#define ENDPOINT_H
|
||||||
|
|
||||||
#include <SmingCore/SmingCore.h>
|
#include <SmingCore.h>
|
||||||
|
#include <JsonObjectStream.h>
|
||||||
|
|
||||||
class SpejsNode;
|
class SpejsNode;
|
||||||
|
|
||||||
|
@ -33,11 +34,12 @@ public:
|
||||||
return 400;
|
return 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void onConnected();
|
|
||||||
|
|
||||||
static Endpoint* fromJSON(JsonObject& obj) {
|
static Endpoint* fromJSON(JsonObject& obj) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void onConnected();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T> class ValueEndpoint : public Endpoint {
|
template <class T> class ValueEndpoint : public Endpoint {
|
||||||
|
|
|
@ -1,34 +1,47 @@
|
||||||
#include <SpejsNode.h>
|
#include <SpejsNode.h>
|
||||||
#include <Endpoint.h>
|
#include <Endpoint.h>
|
||||||
#include <ver.h>
|
#include <ver.h>
|
||||||
|
#include <rboot-api.h>
|
||||||
|
|
||||||
#include <endpoints/ImplementationEndpoint.h>
|
#include <endpoints/ImplementationEndpoint.h>
|
||||||
|
|
||||||
#define CONFIG_FILE "config.json"
|
#define CONFIG_FILE "config.json"
|
||||||
|
|
||||||
|
uint8_t hexToInt(char c) {
|
||||||
|
if (c >= '0' && c <= '9') return c - '0';
|
||||||
|
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||||
|
return c - 'a' + 10;
|
||||||
|
}
|
||||||
|
|
||||||
void SpejsNode::init(bool debug) {
|
void SpejsNode::init(bool debug) {
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.systemDebugOutput(debug); // Debug output to serial
|
||||||
|
|
||||||
deviceID = WifiStation.getMAC().substring(6, 12);
|
deviceID = WifiStation.getMAC().substring(6, 12);
|
||||||
|
brokerClient = "iot-" + deviceID;
|
||||||
|
|
||||||
currentSlot = 0;
|
currentSlot = 0;
|
||||||
if(!rboot_get_last_boot_rom(¤tSlot)) {
|
if(!rboot_get_last_boot_rom(¤tSlot)) {
|
||||||
currentSlot = rboot_get_current_rom();
|
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);
|
debugf("*** SpejsNode init, runnning on: %s, current rom: %d", deviceID.c_str(), currentSlot);
|
||||||
|
|
||||||
|
System.onReady(SystemReadyDelegate(&SpejsNode::systemReady, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpejsNode::systemReady() {
|
||||||
WifiAccessPoint.enable(false);
|
WifiAccessPoint.enable(false);
|
||||||
WifiStation.enable(true);
|
WifiStation.enable(true);
|
||||||
WifiStation.config(WIFI_SSID, WIFI_PWD);
|
WifiStation.config(wifiSSID, wifiPassword);
|
||||||
|
WifiStation.connect();
|
||||||
|
|
||||||
WifiEvents.onStationGotIP(StationGotIPDelegate(&SpejsNode::gotIP, this));
|
WifiEvents.onStationGotIP(StationGotIPDelegate(&SpejsNode::gotIP, this));
|
||||||
|
|
||||||
registerEndpoint("$implementation", new ImplementationEndpoint());
|
registerEndpoint("$implementation", new ImplementationEndpoint());
|
||||||
|
|
||||||
// Keepalive Timer initialization
|
// Keepalive Timer initialization
|
||||||
keepaliveTimer.initializeMs(10000, TimerDelegate(&SpejsNode::keepAliveHandler, this)).start();
|
keepaliveTimer.initializeMs(10000, [=]() { keepAliveHandler(); }).start();
|
||||||
|
|
||||||
statusLED.high();
|
statusLED.high();
|
||||||
}
|
}
|
||||||
|
@ -42,33 +55,93 @@ void SpejsNode::loadJSON(std::vector<EndpointInitializer> initializers) {
|
||||||
spiffs_mount_manual(0x100000, SPIFF_SIZE);
|
spiffs_mount_manual(0x100000, SPIFF_SIZE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DynamicJsonBuffer jsonBuffer;
|
DynamicJsonDocument root(2048);
|
||||||
if (fileExist(CONFIG_FILE)) {
|
if (fileExist(CONFIG_FILE)) {
|
||||||
int size = fileGetSize(CONFIG_FILE);
|
int size = fileGetSize(CONFIG_FILE);
|
||||||
debugf("Found config file, %d bytes", size);
|
debugf("Found config file, %d bytes", size);
|
||||||
|
|
||||||
char* jsonString = new char[size + 1];
|
char* jsonString = new char[size + 1];
|
||||||
|
if (jsonString == nullptr) {
|
||||||
|
debugf("CAN'T CALLOCATE JSONSTRING");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
debugf("allocated %08x",jsonString);
|
||||||
|
|
||||||
fileGetContent(CONFIG_FILE, jsonString, size + 1);
|
fileGetContent(CONFIG_FILE, jsonString, size + 1);
|
||||||
JsonObject& root = jsonBuffer.parseObject(jsonString);
|
|
||||||
|
|
||||||
if (root.containsKey("name"))
|
debugf("loaded %s", jsonString);
|
||||||
deviceType = (root["name"]).asString();
|
DeserializationError err = deserializeJson(root, jsonString);
|
||||||
|
debugf("parsed: %d", err);
|
||||||
|
if ( err ) {
|
||||||
|
debugf("invalid config");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.containsKey("name")) {
|
||||||
|
deviceType = (root["name"]).as<String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
debugf("Loading networks...");
|
||||||
|
JsonArray networks = root["networks"].as<JsonArray>();
|
||||||
|
if (networks.size()) {
|
||||||
|
debugf("got networks");
|
||||||
|
JsonObject network = networks[0].as<JsonObject>();
|
||||||
|
|
||||||
|
if (network.containsKey("ssid")) {
|
||||||
|
debugf("Loaded JSON network configuration");
|
||||||
|
wifiSSID = network["ssid"].as<String>();
|
||||||
|
wifiPassword = network["psk"].as<String>();
|
||||||
|
} else {
|
||||||
|
debugf("invalid network configuration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broker configuration
|
||||||
|
debugf("Loading broker configuration...");
|
||||||
|
if (root.containsKey("broker")) {
|
||||||
|
auto brokerPort = root.containsKey("brokerPort") ? root["brokerPort"] : 1883;
|
||||||
|
brokerUseTLS = root["brokerUseTLS"];
|
||||||
|
brokerURL = root["broker"].as<String>();
|
||||||
|
|
||||||
|
String hash = root["brokerSHA256"].as<String>();
|
||||||
|
if ((hash.length() + 1) % 3 == 0) {
|
||||||
|
debugf("Loading sha256 certificate/pubkey hash");
|
||||||
|
int hashLength = (hash.length() + 1) / 3;
|
||||||
|
uint8_t* hashBlob = new uint8_t[hashLength];
|
||||||
|
|
||||||
|
for (int i = 0; i < hashLength; i++) {
|
||||||
|
hashBlob[i] = hexToInt(hash[3*i]) << 4 | hexToInt(hash[3*i+1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO use onSslInit callback
|
||||||
|
//mqtt.pinCertificate(hashBlob, eSFT_PkSha256);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debugf("Loading endpoints...");
|
||||||
|
if (!root.containsKey("endpoints")) {
|
||||||
|
debugf("No endpoints found...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JsonObject data = root["endpoints"].as<JsonObject>();
|
||||||
|
|
||||||
JsonObject& data = root["endpoints"].asObject();
|
|
||||||
for (auto it: data) {
|
for (auto it: data) {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
for(auto init: initializers) {
|
for(auto init: initializers) {
|
||||||
Endpoint* ep = init(it.value);
|
|
||||||
|
JsonObject object = it.value().as<JsonObject>();
|
||||||
|
Endpoint* ep = init(object);
|
||||||
if (ep != NULL) {
|
if (ep != NULL) {
|
||||||
debugf("%s: got object", it.key);
|
debugf("%s: got object", it.key());
|
||||||
registerEndpoint(it.key, ep);
|
registerEndpoint(String(it.key().c_str()), ep);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
debugf("%s: nothing found", it.key);
|
debugf("%s: nothing found", it.key());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -104,7 +177,7 @@ void SpejsNode::keepAliveHandler() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline String SpejsNode::DEV_TOPIC(String t) {
|
String SpejsNode::DEV_TOPIC(String t) {
|
||||||
return TOPIC_PREFIX + deviceID + "/" + t;
|
return TOPIC_PREFIX + deviceID + "/" + t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +192,7 @@ void SpejsNode::httpIndex(HttpRequest &request, HttpResponse &response)
|
||||||
/*
|
/*
|
||||||
* Successful network connection handler
|
* Successful network connection handler
|
||||||
*/
|
*/
|
||||||
void SpejsNode::gotIP(IPAddress ip, IPAddress netmask, IPAddress gateway) {
|
void SpejsNode::gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) {
|
||||||
onConnected();
|
onConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,44 +201,84 @@ void SpejsNode::onConnected() {
|
||||||
|
|
||||||
debugf("Connection successful");
|
debugf("Connection successful");
|
||||||
|
|
||||||
|
debugf("http init");
|
||||||
|
|
||||||
|
// HTTP initialization
|
||||||
|
http.listen(80);
|
||||||
|
http.paths.set("/", HttpPathDelegate(&SpejsNode::httpIndex, this));
|
||||||
|
http.paths.set("/config.json", HttpPathDelegate(&SpejsNode::httpConfig, this));
|
||||||
|
http.paths.setDefault(HttpPathDelegate(&SpejsNode::httpFile, this));
|
||||||
|
http.setBodyParser("application/json", bodyToStringParser);
|
||||||
|
|
||||||
|
debugf("mqtt init");
|
||||||
|
mqtt.setMessageHandler([=](MqttClient& client, mqtt_message_t* message) -> int {
|
||||||
|
if (message == nullptr) return -1;
|
||||||
|
|
||||||
|
String topic = String((const char*)message->publish.topic_name.data, message->publish.topic_name.length);
|
||||||
|
String content;
|
||||||
|
if(message->publish.content.data) {
|
||||||
|
content.concat((const char*)message->publish.content.data, message->publish.content.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
mqttCallback(topic, content);
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
// MQTT initialization
|
// MQTT initialization
|
||||||
mqtt.setWill(DEV_TOPIC("$online"), "false", 1, true);
|
mqtt.setWill(DEV_TOPIC("$online"), "false", 1, true);
|
||||||
|
|
||||||
#ifdef ENABLE_SSL
|
#ifdef ENABLE_SSL
|
||||||
const uint8_t sha1Fingerprint[] = SSL_FINGERPRINT;
|
if (brokerUseTLS) {
|
||||||
mqtt.connect("iot-" + deviceID, "", "", true);
|
debugf("Using TLS");
|
||||||
mqtt.addSslOptions(SSL_SERVER_VERIFY_LATER);
|
mqtt.addSslOptions(SSL_SERVER_VERIFY_LATER);
|
||||||
mqtt.setSslFingerprint(sha1Fingerprint, 20);
|
//mqtt.pinCertificate(fingerprints);
|
||||||
#else
|
|
||||||
mqtt.connect("iot-" + deviceID);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for(unsigned int i = 0 ; i < endpoints.count() ; i++) {
|
|
||||||
endpoints.valueAt(i)->onConnected();
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
debugf("Connecting: %s as %s", brokerURL.c_str(), brokerClient.c_str());
|
||||||
|
|
||||||
subscribe("$implementation/+");
|
mqtt.setConnectedHandler([=](MqttClient& client, mqtt_message_t* message) {
|
||||||
|
debugf("Initializing endpoints");
|
||||||
|
for(unsigned int i = 0 ; i < endpoints.count() ; i++) {
|
||||||
|
endpoints.valueAt(i)->onConnected();
|
||||||
|
}
|
||||||
|
|
||||||
// Say hello
|
debugf("subscribing");
|
||||||
notify("$online", "true");
|
subscribe("$implementation/+");
|
||||||
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
|
debugf("say hello");
|
||||||
http.listen(80);
|
|
||||||
http.addPath("/", HttpPathDelegate(&SpejsNode::httpIndex, this));
|
|
||||||
http.setDefaultHandler(HttpPathDelegate(&SpejsNode::httpFile, this));
|
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
mqtt.connect(brokerURL, brokerClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpejsNode::httpConfig(HttpRequest &request, HttpResponse &response)
|
||||||
|
{
|
||||||
|
if (request.method == HTTP_POST) {
|
||||||
|
debugf("settings data");
|
||||||
|
String body = request.getBody();
|
||||||
|
Serial.println(body);
|
||||||
|
fileSetContent("config.json", body);
|
||||||
|
response.sendString("{\"status\": 200}");
|
||||||
|
} else {
|
||||||
|
response.sendFile("config.json");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpejsNode::httpFile(HttpRequest &request, HttpResponse &response)
|
void SpejsNode::httpFile(HttpRequest &request, HttpResponse &response)
|
||||||
{
|
{
|
||||||
String file = request.getPath();
|
String file = request.uri.Path;
|
||||||
|
|
||||||
if (file[0] == '/')
|
if (file[0] == '/')
|
||||||
file = file.substring(1);
|
file = file.substring(1);
|
||||||
|
@ -180,9 +293,9 @@ void SpejsNode::httpFile(HttpRequest &request, HttpResponse &response)
|
||||||
} else {
|
} else {
|
||||||
EndpointResult result = endpoints[key]->onValue(key, value);
|
EndpointResult result = endpoints[key]->onValue(key, value);
|
||||||
JsonObjectStream* stream = new JsonObjectStream();
|
JsonObjectStream* stream = new JsonObjectStream();
|
||||||
JsonObject& json = stream->getRoot();
|
JsonObject json = stream->getRoot();
|
||||||
json["status"] = result.status;
|
json["status"] = result.status;
|
||||||
response.sendJsonObject(stream);
|
response.sendDataStream(stream, MIME_JSON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,6 +304,7 @@ void SpejsNode::httpFile(HttpRequest &request, HttpResponse &response)
|
||||||
* Publish on device-specific topic
|
* Publish on device-specific topic
|
||||||
*/
|
*/
|
||||||
bool SpejsNode::notify(String key, String value) {
|
bool SpejsNode::notify(String key, String value) {
|
||||||
|
debugf("%s [%d] = %s [%d]", key.c_str(), key.length(), value.c_str(), value.length());
|
||||||
mqtt.publish(DEV_TOPIC(key), value, true);
|
mqtt.publish(DEV_TOPIC(key), value, true);
|
||||||
return mqtt.getConnectionState() == eTCS_Connected;
|
return mqtt.getConnectionState() == eTCS_Connected;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
|
|
||||||
#include <user_config.h>
|
#include <user_config.h>
|
||||||
#include <common_config.h>
|
#include <common_config.h>
|
||||||
#include <SmingCore/SmingCore.h>
|
#include <SmingCore.h>
|
||||||
#include <Endpoint.h>
|
#include <Endpoint.h>
|
||||||
|
#include <JsonObjectStream.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -32,8 +33,8 @@ class LED {
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int blinkRate = 4000;
|
unsigned blinkRate = 4000;
|
||||||
int blinkOn = 100;
|
unsigned blinkOn = 100;
|
||||||
|
|
||||||
LED() { }
|
LED() { }
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ public:
|
||||||
highState = highState_;
|
highState = highState_;
|
||||||
|
|
||||||
pinMode(pin, OUTPUT);
|
pinMode(pin, OUTPUT);
|
||||||
animateTimer.initializeMs(50, TimerDelegate(&LED::animate, this)).start();
|
animateTimer.initializeMs(50, [=]() { animate(); }).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void idle() {
|
void idle() {
|
||||||
|
@ -68,7 +69,22 @@ protected:
|
||||||
void mqttCallback(String, String);
|
void mqttCallback(String, String);
|
||||||
void otaUpdateCallback(bool result);
|
void otaUpdateCallback(bool result);
|
||||||
void httpFile(HttpRequest &request, HttpResponse &response);
|
void httpFile(HttpRequest &request, HttpResponse &response);
|
||||||
|
void httpConfig(HttpRequest &request, HttpResponse &response);
|
||||||
void httpIndex(HttpRequest &request, HttpResponse &response);
|
void httpIndex(HttpRequest &request, HttpResponse &response);
|
||||||
|
|
||||||
|
void systemReady(void);
|
||||||
|
|
||||||
|
String wifiSSID = WIFI_SSID;
|
||||||
|
String wifiPassword = WIFI_PWD;
|
||||||
|
|
||||||
|
String brokerURL = "mqtt://" MQTT_BROKER;
|
||||||
|
String brokerClient = "";
|
||||||
|
|
||||||
|
bool brokerUseTLS = false;
|
||||||
|
|
||||||
|
#ifdef ENABLE_SSL
|
||||||
|
SslFingerprints fingerprints;
|
||||||
|
#endif
|
||||||
public:
|
public:
|
||||||
MqttClient mqtt;
|
MqttClient mqtt;
|
||||||
HttpServer http;
|
HttpServer http;
|
||||||
|
@ -78,14 +94,19 @@ public:
|
||||||
|
|
||||||
LED statusLED;
|
LED statusLED;
|
||||||
|
|
||||||
|
void (*onConnectedCallback)(void);
|
||||||
|
void (*onDisconnectedCallback)(void);
|
||||||
|
|
||||||
uint8_t currentSlot;
|
uint8_t currentSlot;
|
||||||
|
|
||||||
SpejsNode(String _deviceType) :
|
SpejsNode(String _deviceType) :
|
||||||
mqtt(MQTT_BROKER, MQTT_PORT, MqttStringSubscriptionCallback(&SpejsNode::mqttCallback, this)),
|
mqtt(),
|
||||||
deviceType(_deviceType) {};
|
deviceType(_deviceType), onConnectedCallback(NULL) {
|
||||||
|
mqtt.setCallback(MqttStringSubscriptionCallback(&SpejsNode::mqttCallback, this));
|
||||||
|
};
|
||||||
|
|
||||||
void onConnected();
|
void onConnected();
|
||||||
void gotIP(IPAddress ip, IPAddress netmask, IPAddress gateway);
|
void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway);
|
||||||
|
|
||||||
void init(bool debug=false);
|
void init(bool debug=false);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ void DHTEndpoint::bind(String _name, SpejsNode* _parent) {
|
||||||
Endpoint::bind(_name, _parent);
|
Endpoint::bind(_name, _parent);
|
||||||
|
|
||||||
sensor.setup(pin, sensor_type);
|
sensor.setup(pin, sensor_type);
|
||||||
samplingTimer.initializeMs(samplingRate, TimerDelegate(&DHTEndpoint::sample, this)).start();
|
samplingTimer.initializeMs(samplingRate, [=]() { sample(); }).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DHTEndpoint::sample() {
|
void DHTEndpoint::sample() {
|
||||||
|
|
|
@ -18,7 +18,7 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DHTEndpoint(int _pin, int _samplingRate = 10000, DHTesp::DHT_MODEL_t _sensor_type=DHTesp::DHT11) :
|
DHTEndpoint(int _pin, int _samplingRate = 10000, DHTesp::DHT_MODEL_t _sensor_type=DHTesp::DHT11) :
|
||||||
ValueEndpoint("dht"), pin(_pin), sensor_type(_sensor_type),
|
ValueEndpoint("dht"), sensor_type(_sensor_type), pin(_pin),
|
||||||
samplingRate(_samplingRate) {}
|
samplingRate(_samplingRate) {}
|
||||||
|
|
||||||
void bind(String name, SpejsNode* _parent);
|
void bind(String name, SpejsNode* _parent);
|
||||||
|
|
|
@ -23,7 +23,7 @@ void ImplementationEndpoint::startOTA() {
|
||||||
|
|
||||||
// need a clean object, otherwise if run before and failed will not run again
|
// need a clean object, otherwise if run before and failed will not run again
|
||||||
if (otaUpdater) delete otaUpdater;
|
if (otaUpdater) delete otaUpdater;
|
||||||
otaUpdater = new rBootHttpUpdate();
|
otaUpdater = new RbootHttpUpdater();
|
||||||
|
|
||||||
bootconf = rboot_get_config();
|
bootconf = rboot_get_config();
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ void ImplementationEndpoint::startOTA() {
|
||||||
parent->statusLED.high();
|
parent->statusLED.high();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImplementationEndpoint::otaUpdateCallback(rBootHttpUpdate& updater, bool result) {
|
void ImplementationEndpoint::otaUpdateCallback(RbootHttpUpdater& updater, bool result) {
|
||||||
parent->statusLED.idle();
|
parent->statusLED.idle();
|
||||||
|
|
||||||
if(result == true) {
|
if(result == true) {
|
||||||
|
|
|
@ -3,17 +3,18 @@
|
||||||
|
|
||||||
#include <Endpoint.h>
|
#include <Endpoint.h>
|
||||||
#include <SpejsNode.h>
|
#include <SpejsNode.h>
|
||||||
|
#include <Network/RbootHttpUpdater.h>
|
||||||
|
|
||||||
class ImplementationEndpoint : public Endpoint {
|
class ImplementationEndpoint : public Endpoint {
|
||||||
protected:
|
protected:
|
||||||
rBootHttpUpdate* otaUpdater = 0;
|
RbootHttpUpdater* otaUpdater = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Use empty endpoint type to just keep it unpublished
|
// Use empty endpoint type to just keep it unpublished
|
||||||
ImplementationEndpoint() : Endpoint("") {}
|
ImplementationEndpoint() : Endpoint("") {}
|
||||||
|
|
||||||
EndpointResult onValue(String property, String value);
|
EndpointResult onValue(String property, String value);
|
||||||
void otaUpdateCallback(rBootHttpUpdate& updater, bool result);
|
void otaUpdateCallback(RbootHttpUpdater& updater, bool result);
|
||||||
void startOTA();
|
void startOTA();
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -19,7 +19,7 @@ public:
|
||||||
EndpointResult onValue(String property, String value);
|
EndpointResult onValue(String property, String value);
|
||||||
|
|
||||||
static Endpoint* fromJSON(JsonObject& obj) {
|
static Endpoint* fromJSON(JsonObject& obj) {
|
||||||
if (String(obj["type"].asString()) == "output" && obj.containsKey("gpio")) {
|
if (String(obj["type"].as<String>()) == "output" && obj.containsKey("gpio")) {
|
||||||
return new OutputEndpoint(obj["gpio"], obj["inverted"]);
|
return new OutputEndpoint(obj["gpio"], obj["inverted"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue