Initial OTA implementation
parent
5499270903
commit
aff284f570
|
@ -13,6 +13,8 @@
|
|||
|
||||
#define TOPIC_PREFIX "iot/"
|
||||
|
||||
#define OTA_URL "http://" MQTT_BROKER "/"
|
||||
|
||||
#define BTN_PIN 0
|
||||
#define LED_PIN 2
|
||||
|
||||
|
|
|
@ -2,3 +2,10 @@ mosquitto:
|
|||
image: toke/mosquitto
|
||||
ports:
|
||||
- "1883:1883"
|
||||
|
||||
nginx:
|
||||
image: nginx
|
||||
volumes:
|
||||
- ota:/usr/share/nginx/html
|
||||
ports:
|
||||
- "80:80"
|
||||
|
|
|
@ -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()
|
|
@ -3,10 +3,16 @@
|
|||
void SpejsNode::init() {
|
||||
deviceID = WifiStation.getMAC().substring(6, 12);
|
||||
|
||||
currentSlot = 0;
|
||||
if(!rboot_get_last_boot_rom(¤tSlot)) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
MODULES = app ../spejsiot
|
||||
DISABLE_SPIFFS = 1
|
||||
USER_CFLAGS = -I../common
|
||||
|
||||
RBOOT_ENABLED ?= 1
|
||||
RBOOT_BIG_FLASH ?= 1
|
||||
SPI_SIZE ?= 4M
|
||||
|
|
|
@ -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"
|
Loading…
Reference in New Issue