Initial OTA implementation

master
informatic 2016-08-30 21:24:34 +02:00
parent 5499270903
commit aff284f570
8 changed files with 383 additions and 79 deletions

View File

@ -13,6 +13,8 @@
#define TOPIC_PREFIX "iot/"
#define OTA_URL "http://" MQTT_BROKER "/"
#define BTN_PIN 0
#define LED_PIN 2

View File

@ -2,3 +2,10 @@ mosquitto:
image: toke/mosquitto
ports:
- "1883:1883"
nginx:
image: nginx
volumes:
- ota:/usr/share/nginx/html
ports:
- "80:80"

30
master/ota.py Normal file
View File

@ -0,0 +1,30 @@
import paho.mqtt.client as mqtt
import time
import random
import sys
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, rc):
print("Connected with result code "+str(rc))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("light/status")
client.subscribe("#")
client.publish("iot/" + sys.argv[1] + "/" + sys.argv[2], sys.argv[3])
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(str(time.time())+" "+msg.topic+" "+str(msg.payload))
exit()
client = mqtt.Client("test-client-%d" % random.randint(100, 999))
client.on_connect = on_connect
client.on_message = on_message
client.connect("localhost", 1883, 60)
# Blocking call that processes network traffic, dispatches callbacks and
# handles reconnecting.
# Other loop*() functions are available that give a threaded interface and a
# manual interface.
client.loop_forever()

View File

@ -3,10 +3,16 @@
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.println(deviceID);
Serial.print("*** SpejsNode init, running on:");
Serial.print(deviceID);
Serial.printf(", current rom: %d\r\n", currentSlot);
WifiStation.config(WIFI_SSID, WIFI_PWD);
WifiStation.enable(true);
@ -18,12 +24,26 @@ void SpejsNode::init() {
20, *[] {
Serial.println("Connection failed");
});
inputs["control"] = MqttStringSubscriptionCallback(&SpejsNode::controlHandler, this);
}
void SpejsNode::keepAliveHandler() {
if(mqtt.getConnectionState() != eTCS_Connected) {
Serial.println("Reconnecting");
onConnected();
} else {
uint8_t mode;
if(rboot_get_last_boot_mode(&mode)) {
if(mode == MODE_TEMP_ROM) {
rboot_set_current_rom(currentSlot);
Serial.println("Successfuly connected, accepting temp rom");
} else {
Serial.printf("Not a TEMP ROM boot: %d\r\n", mode);
}
} else {
Serial.println("No boot mode info");
}
}
}
@ -54,26 +74,104 @@ bool SpejsNode::notify(String key, String value) {
return false;
}
void SpejsNode::registerInput(String key, InputCallback callback) {
void SpejsNode::registerInput(String key, MqttStringSubscriptionCallback callback) {
inputs[key] = callback;
}
void SpejsNode::mqttCallback(String topic, String value) {
void SpejsNode::mqttCallback(String origtopic, String value) {
String devicePrefix = TOPIC_PREFIX + deviceID;
if(!topic.startsWith(devicePrefix)) {
if(!origtopic.startsWith(devicePrefix)) {
Serial.println("ignoring");
return;
}
topic = topic.substring(devicePrefix.length() + 1);
String topic = origtopic.substring(devicePrefix.length() + 1);
Serial.println(topic);
Serial.println(value);
if(inputs.contains(topic)) {
Serial.println("dupa");
inputs[topic](value);
inputs[topic](origtopic, value);
} else {
Serial.println("default");
Serial.println("unknown topic?");
}
}
void SpejsNode::controlHandler(String key, String value) {
Serial.println("Control command: " + value);
if(value == "ota") {
startOTA();
} else if(value == "restart") {
//System.restart();
keepaliveTimer.initializeMs(500, *[] {
System.restart();
}).start();
} else {
Serial.println("Invalid command");
}
}
void SpejsNode::startOTA() {
uint8_t slot;
rboot_config bootconf;
String romURL = OTA_URL + deviceID + "/rom0.bin";
String spiffsURL = OTA_URL + deviceID + "/spiff_rom.bin";
Serial.println("Updating...");
// need a clean object, otherwise if run before and failed will not run again
if (otaUpdater) delete otaUpdater;
otaUpdater = new rBootHttpUpdate();
bootconf = rboot_get_config();
if (currentSlot == 0)
slot = 1;
else
slot = 0;
Serial.printf("Updating to rom %d.\r\n", slot);
// flash rom to position indicated in the rBoot config rom table
otaUpdater->addItem(bootconf.roms[slot], romURL);
#ifndef DISABLE_SPIFFS
// use user supplied values (defaults for 4mb flash in makefile)
if (slot == 0) {
otaUpdater->addItem(RBOOT_SPIFFS_0, spiffsURL);
} else {
otaUpdater->addItem(RBOOT_SPIFFS_1, spiffsURL);
}
#endif
otaUpdater->setCallback(otaUpdateDelegate(&SpejsNode::otaUpdateCallback, this));
otaUpdater->start();
notify("ota", "started");
}
void SpejsNode::otaUpdateCallback(bool result) {
Serial.println("In callback...");
if(result == true) {
// success
notify("ota", "finished");
uint8 slot;
if (currentSlot == 0)
slot = 1;
else
slot = 0;
// set to boot new rom and then reboot
Serial.printf("Firmware updated, rebooting to rom %d...\r\n", slot);
rboot_set_temp_rom(slot);
keepaliveTimer.initializeMs(500, *[] {
System.restart();
}).start();
} else {
notify("ota", "failed");
}
}

View File

@ -2,96 +2,35 @@
#include <common_config.h>
#include <SmingCore/SmingCore.h>
typedef void (*InputCallback)(String);
class SpejsNode {
protected:
String deviceID;
String deviceType;
MqttClient mqtt; // (MQTT_BROKER, MQTT_PORT);
MqttClient mqtt;
Timer keepaliveTimer;
HashMap<String, InputCallback> inputs;
rBootHttpUpdate* otaUpdater = 0;
HashMap<String, MqttStringSubscriptionCallback> inputs;
void onConnected();
void startOTA();
void keepAliveHandler();
uint8_t currentSlot;
public:
SpejsNode(String _deviceType) :
mqtt(MQTT_BROKER, MQTT_PORT, MqttStringSubscriptionCallback(&SpejsNode::mqttCallback, this)),
//*[](String topic, String message) {
// Serial.printf("*** message received @ %s:\n\t%s\n***\n", topic.c_str(), message.c_str());
//}),
deviceType(_deviceType)
{};
void init();
//void registerInput(uint32_t gpio);
//void registerOutput(uint32_t gpio);
bool notify(String key, String value);
void registerInput(String key, InputCallback cb);
void registerInput(String key, MqttStringSubscriptionCallback cb);
void mqttCallback(String, String);
void controlHandler(String, String);
void otaUpdateCallback(bool result);
};
/*
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());
});
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();
}
void init()
{
deviceName = "switch-" + WifiStation.getMAC().substring(6, 12);
Serial.begin(SERIAL_BAUD_RATE); // 115200 by default
Serial.systemDebugOutput(false); // Debug output to serial
Serial.println("*** Starting " + deviceName + " ...");
WifiStation.config(WIFI_SSID, WIFI_PWD);
WifiStation.setIP(IPAddress(10, 5, 0, 39), IPAddress(255, 255, 255, 0), IPAddress(10, 5, 0, 1));
WifiStation.enable(true);
WifiAccessPoint.enable(false);
WifiStation.waitConnection(*[] {
Serial.println("*** Connection succeeded");
startMqttClient();
}, 20, *[] {
Serial.println("*** Connection failed");
});
attachInterrupt(BTN_PIN, *[] {
static int lastSwitch = 0;
// Debouncing
if(lastSwitch + 150 > millis()) {
Serial.println("--- debouncing");
return;
}
lastSwitch = millis();
Serial.println("*** Button pressed");
mqtt.publish("light/status", "toggle");
}, FALLING);
}
*/

View File

@ -22,3 +22,8 @@ include $(SMING_HOME)/Makefile-rboot.mk
else
include $(SMING_HOME)/Makefile-project.mk
endif
ota: all
-mkdir ../master/ota/$(DEVICE) >/dev/null
cp -r out/firmware/* ../master/ota/$(DEVICE)
python ../master/ota.py $(DEVICE) control ota

View File

@ -1,3 +1,7 @@
MODULES = app ../spejsiot
DISABLE_SPIFFS = 1
USER_CFLAGS = -I../common
RBOOT_ENABLED ?= 1
RBOOT_BIG_FLASH ?= 1
SPI_SIZE ?= 4M

219
switch/rom0.ld Normal file
View File

@ -0,0 +1,219 @@
/* This linker script generated from xt-genldscripts.tpp for LSP . */
/* Linker Script for ld -N */
MEMORY
{
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x8000
irom0_0_seg : org = 0x40202010, len = 0x42000
}
PHDRS
{
dport0_0_phdr PT_LOAD;
dram0_0_phdr PT_LOAD;
dram0_0_bss_phdr PT_LOAD;
iram1_0_phdr PT_LOAD;
irom0_0_phdr PT_LOAD;
}
/* Default entry point: */
ENTRY(call_user_start)
EXTERN(_DebugExceptionVector)
EXTERN(_DoubleExceptionVector)
EXTERN(_KernelExceptionVector)
EXTERN(_NMIExceptionVector)
EXTERN(_UserExceptionVector)
PROVIDE(_memmap_vecbase_reset = 0x40000000);
/* Various memory-map dependent cache attribute settings: */
_memmap_cacheattr_wb_base = 0x00000110;
_memmap_cacheattr_wt_base = 0x00000110;
_memmap_cacheattr_bp_base = 0x00000220;
_memmap_cacheattr_unused_mask = 0xFFFFF00F;
_memmap_cacheattr_wb_trapnull = 0x2222211F;
_memmap_cacheattr_wba_trapnull = 0x2222211F;
_memmap_cacheattr_wbna_trapnull = 0x2222211F;
_memmap_cacheattr_wt_trapnull = 0x2222211F;
_memmap_cacheattr_bp_trapnull = 0x2222222F;
_memmap_cacheattr_wb_strict = 0xFFFFF11F;
_memmap_cacheattr_wt_strict = 0xFFFFF11F;
_memmap_cacheattr_bp_strict = 0xFFFFF22F;
_memmap_cacheattr_wb_allvalid = 0x22222112;
_memmap_cacheattr_wt_allvalid = 0x22222112;
_memmap_cacheattr_bp_allvalid = 0x22222222;
PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull);
SECTIONS
{
.dport0.rodata : ALIGN(4)
{
_dport0_rodata_start = ABSOLUTE(.);
*(.dport0.rodata)
*(.dport.rodata)
_dport0_rodata_end = ABSOLUTE(.);
} >dport0_0_seg :dport0_0_phdr
.dport0.literal : ALIGN(4)
{
_dport0_literal_start = ABSOLUTE(.);
*(.dport0.literal)
*(.dport.literal)
_dport0_literal_end = ABSOLUTE(.);
} >dport0_0_seg :dport0_0_phdr
.dport0.data : ALIGN(4)
{
_dport0_data_start = ABSOLUTE(.);
*(.dport0.data)
*(.dport.data)
_dport0_data_end = ABSOLUTE(.);
} >dport0_0_seg :dport0_0_phdr
.data : ALIGN(4)
{
_data_start = ABSOLUTE(.);
*(.data)
*(.data.*)
*(.gnu.linkonce.d.*)
*(.data1)
*(.sdata)
*(.sdata.*)
*(.gnu.linkonce.s.*)
*(.sdata2)
*(.sdata2.*)
*(.gnu.linkonce.s2.*)
*(.jcr)
_data_end = ABSOLUTE(.);
} >dram0_0_seg :dram0_0_phdr
.rodata : ALIGN(4)
{
_rodata_start = ABSOLUTE(.);
*(.sdk.version)
*(.rodata)
*(.rodata.*)
*(.gnu.linkonce.r.*)
*(.rodata1)
__XT_EXCEPTION_TABLE__ = ABSOLUTE(.);
*(.xt_except_table)
*(.gcc_except_table)
*(.gnu.linkonce.e.*)
*(.gnu.version_r)
*(.eh_frame)
. = (. + 3) & ~ 3;
/* C++ constructor and destructor tables, properly ordered: */
__dso_handle = ABSOLUTE(.);
__init_array_start = ABSOLUTE(.);
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
__init_array_end = ABSOLUTE(.);
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
/* C++ exception handlers table: */
__XT_EXCEPTION_DESCS__ = ABSOLUTE(.);
*(.xt_except_desc)
*(.gnu.linkonce.h.*)
__XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
*(.xt_except_desc_end)
*(.dynamic)
*(.gnu.version_d)
. = ALIGN(4); /* this table MUST be 4-byte aligned */
_bss_table_start = ABSOLUTE(.);
LONG(_bss_start)
LONG(_bss_end)
_bss_table_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
} >dram0_0_seg :dram0_0_phdr
.bss ALIGN(8) (NOLOAD) : ALIGN(4)
{
. = ALIGN (8);
_bss_start = ABSOLUTE(.);
*(.dynsbss)
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
*(.scommon)
*(.sbss2)
*(.sbss2.*)
*(.gnu.linkonce.sb2.*)
*(.dynbss)
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN (8);
_bss_end = ABSOLUTE(.);
_heap_start = ABSOLUTE(.);
/* _stack_sentry = ALIGN(0x8); */
} >dram0_0_seg :dram0_0_bss_phdr
/* __stack = 0x3ffc8000; */
.irom0.text : ALIGN(4)
{
_irom0_text_start = ABSOLUTE(.);
*(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text)
out/build/app_app.a:*(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
*libsming.a:*(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
_irom0_text_end = ABSOLUTE(.);
_flash_code_end = ABSOLUTE(.);
} >irom0_0_seg :irom0_0_phdr
.text : ALIGN(4)
{
_stext = .;
_text_start = ABSOLUTE(.);
*(.UserEnter.text)
. = ALIGN(16);
*(.DebugExceptionVector.text)
. = ALIGN(16);
*(.NMIExceptionVector.text)
. = ALIGN(16);
*(.KernelExceptionVector.text)
LONG(0)
LONG(0)
LONG(0)
LONG(0)
. = ALIGN(16);
*(.UserExceptionVector.text)
LONG(0)
LONG(0)
LONG(0)
LONG(0)
. = ALIGN(16);
*(.DoubleExceptionVector.text)
LONG(0)
LONG(0)
LONG(0)
LONG(0)
. = ALIGN (16);
*(.entry.text)
*(.init.literal)
*(.init)
*(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
*(.iram.literal .iram.text.literal .iram.text)
*(.fini.literal)
*(.fini)
*(.gnu.version)
_text_end = ABSOLUTE(.);
_etext = .;
} >iram1_0_seg :iram1_0_phdr
.lit4 : ALIGN(4)
{
_lit4_start = ABSOLUTE(.);
*(*.lit4)
*(.lit4.*)
*(.gnu.linkonce.lit4.*)
_lit4_end = ABSOLUTE(.);
} >iram1_0_seg :iram1_0_phdr
}
/* get ROM code address */
INCLUDE "../ld/eagle.rom.addr.v6.ld"