Merge remote-tracking branch 'origin/settings' into sming-4

sming-4
vuko 2020-10-06 14:53:49 +02:00
commit ca839a93a9
8 changed files with 175 additions and 41 deletions

View File

@ -1,2 +1,3 @@
SPIFF_SIZE = 196600
DISABLE_SPIFFS = 0
ENABLE_SSL = 1

View File

@ -5,7 +5,7 @@
SpejsNode node("unconfigured-generic-device");
void init() {
node.init();
node.init(true);
node.loadJSON({
&OutputEndpoint::fromJSON,
&DHTEndpoint::fromJSON,

View File

@ -1,5 +1,5 @@
{
"name": "testdev",
"name": "unprovisioned",
"extras": {
"owner": "informatic",
@ -10,5 +10,11 @@
"out": {"type": "output", "gpio": 2},
"testInput": {"type": "input", "gpio": 3},
"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
}

View File

@ -6,4 +6,7 @@ openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out s
# Extract SHA-1
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'
```

View File

@ -13,7 +13,9 @@ void Endpoint::notify(String property, String value) {
void Endpoint::onConnected() {
parent->subscribe(name + "/+/set");
parent->notify(name + "/$type", type);
if (type.length()) {
parent->notify(name + "/$type", type);
}
}
void Endpoint::onMessage(String topic, String payload) {

View File

@ -7,11 +7,18 @@
#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) {
Serial.begin(115200);
Serial.systemDebugOutput(debug); // Debug output to serial
deviceID = WifiStation.getMAC().substring(6, 12);
brokerClient = "iot-" + deviceID;
currentSlot = 0;
if(!rboot_get_last_boot_rom(&currentSlot)) {
@ -20,16 +27,21 @@ void SpejsNode::init(bool debug) {
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);
WifiStation.enable(true);
WifiStation.config(WIFI_SSID, WIFI_PWD);
WifiStation.config(wifiSSID, wifiPassword);
WifiStation.connect();
WifiEvents.onStationGotIP(StationGotIPDelegate(&SpejsNode::gotIP, this));
registerEndpoint("$implementation", new ImplementationEndpoint());
// Keepalive Timer initialization
keepaliveTimer.initializeMs(10000, TimerDelegate(&SpejsNode::keepAliveHandler, this)).start();
keepaliveTimer.initializeMs(10000, [=]() { keepAliveHandler(); }).start();
statusLED.high();
}
@ -47,15 +59,72 @@ void SpejsNode::loadJSON(std::vector<EndpointInitializer> initializers) {
if (fileExist(CONFIG_FILE)) {
int size = fileGetSize(CONFIG_FILE);
debugf("Found config file, %d bytes", size);
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);
deserializeJson(root, jsonString);
debugf("loaded %s", jsonString);
DeserializationError err = deserializeJson(root, jsonString);
debugf("parsed: %d", err);
if ( err ) {
debugf("invalid config");
return;
}
if (root.containsKey("name"))
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>();
for (auto it: data) {
bool found = false;
@ -108,7 +177,7 @@ void SpejsNode::keepAliveHandler() {
}
}
inline String SpejsNode::DEV_TOPIC(String t) {
String SpejsNode::DEV_TOPIC(String t) {
return TOPIC_PREFIX + deviceID + "/" + t;
}
@ -132,42 +201,79 @@ void SpejsNode::onConnected() {
debugf("Connection successful");
// MQTT initialization
mqtt.setWill(DEV_TOPIC("$online"), "false", 1, true);
#ifdef ENABLE_SSL
const uint8_t sha1Fingerprint[] = SSL_FINGERPRINT;
mqtt.connect(
"mqtts://" MQTT_BROKER ":" MQTT_PORT,
"iot-" + deviceID, "", "", true);
mqtt.addSslOptions(SSL_SERVER_VERIFY_LATER);
mqtt.setSslFingerprint(sha1Fingerprint, 20);
#else
mqtt.connect(
"mqtt://" MQTT_BROKER ":" MQTT_PORT, "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));
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.setWill(DEV_TOPIC("$online"), "false", 1, true);
#ifdef ENABLE_SSL
if (brokerUseTLS) {
debugf("Using TLS");
mqtt.addSslOptions(SSL_SERVER_VERIFY_LATER);
//mqtt.pinCertificate(fingerprints);
}
#endif
debugf("Connecting: %s as %s", brokerURL.c_str(), brokerClient.c_str());
mqtt.setConnectedHandler([=](MqttClient& client, mqtt_message_t* message) {
debugf("Initializing endpoints");
for(unsigned int i = 0 ; i < endpoints.count() ; i++) {
endpoints.valueAt(i)->onConnected();
}
debugf("subscribing");
subscribe("$implementation/+");
debugf("say hello");
// 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)
@ -198,6 +304,7 @@ void SpejsNode::httpFile(HttpRequest &request, HttpResponse &response)
* Publish on device-specific topic
*/
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);
return mqtt.getConnectionState() == eTCS_Connected;
}

View File

@ -43,7 +43,7 @@ public:
highState = highState_;
pinMode(pin, OUTPUT);
animateTimer.initializeMs(50, TimerDelegate(&LED::animate, this)).start();
animateTimer.initializeMs(50, [=]() { animate(); }).start();
}
void idle() {
@ -69,7 +69,22 @@ protected:
void mqttCallback(String, String);
void otaUpdateCallback(bool result);
void httpFile(HttpRequest &request, HttpResponse &response);
void httpConfig(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:
MqttClient mqtt;
HttpServer http;

View File

@ -4,7 +4,7 @@ void DHTEndpoint::bind(String _name, SpejsNode* _parent) {
Endpoint::bind(_name, _parent);
sensor.setup(pin, sensor_type);
samplingTimer.initializeMs(samplingRate, TimerDelegate(&DHTEndpoint::sample, this)).start();
samplingTimer.initializeMs(samplingRate, [=]() { sample(); }).start();
}
void DHTEndpoint::sample() {