Compare commits

..

7 commits

Author SHA1 Message Date
5fa1ec14e2 add otaflash.py script to release files 2021-09-06 23:21:43 +02:00
09452ef7ab add minimal OTA Update CLI 2021-08-28 23:48:01 +02:00
8874fecf15 add basic OTA Update fucionality 2021-08-28 23:44:01 +02:00
495163060d switch to css only paging 2021-08-08 12:42:07 +02:00
c049c04e8e add pages and menu to web frontend 2021-08-03 02:02:48 +02:00
9e8f14846a add crc32 implementation 2021-08-03 01:37:04 +02:00
0551aff5d1 add some project files 2021-07-29 15:44:42 +02:00
23 changed files with 478 additions and 305578 deletions

View file

@ -57,6 +57,7 @@ steps:
base_url: https://git.neulandlabor.de/ base_url: https://git.neulandlabor.de/
files: files:
- firmware/firmware/fiatlux.bin - firmware/firmware/fiatlux.bin
- firmware/otaflash.py
- pcb/pcb.zip - pcb/pcb.zip
checksum: checksum:
- sha512 - sha512

View file

@ -27,6 +27,7 @@ git submodule update --init --recursive
- ncurses-dev libexpat-dev - ncurses-dev libexpat-dev
- python3 python3-serial python-dev - python3 python3-serial python-dev
- pip install websocket-client (for otaflash.py, optional)
### Build Steps ### Build Steps

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="Python" name="Python facet">
<configuration sdkName="Python 3.9" />
</facet>
</component>
</module>

View file

@ -2,7 +2,7 @@ PROGRAM=fiatlux
EXTRA_CFLAGS=-O3 -Ifsdata EXTRA_CFLAGS=-O3 -Ifsdata
EXTRA_COMPONENTS=extras/i2s_dma extras/ws2812_i2s extras/dhcpserver extras/mbedtls extras/httpd extras/sntp extras/cpp_support EXTRA_COMPONENTS=extras/i2s_dma extras/ws2812_i2s extras/dhcpserver extras/rboot-ota extras/mbedtls extras/httpd extras/sntp extras/cpp_support
LIBS = hal m LIBS = hal m
@ -10,7 +10,9 @@ FLASH_MODE = dio
include ../modules/rtos/common.mk include ../modules/rtos/common.mk
html: html: fsdata/fsdata.c
fsdata/fsdata.c: fsdata/fs/index.html fsdata/fs/404.html fsdata/fs/css/picnic.min.css fsdata/fs/css/style.css fsdata/fs/js/smoothie_min.js
@echo "Generating fsdata.." @echo "Generating fsdata.."
cd fsdata && ./makefsdata cd fsdata && ./makefsdata

71
firmware/crc32.c Normal file
View file

@ -0,0 +1,71 @@
//
// Created by jedi on 02.08.21.
//
#include "crc32.h"
#define UPDC32(octet, crc) (crc_32_tab[((crc)\
^ ((uint8_t)octet)) & 0xff] ^ ((crc) >> 8))
static uint32_t crc_32_tab[] = { /* CRC polynomial 0xedb88320 */
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
uint32_t updateCRC32(uint8_t ch, uint32_t crc) {
return UPDC32(ch, crc);
}
uint32_t crc32_partial(uint32_t init, uint8_t *buf, uint32_t len) {
register uint32_t oldcrc32;
oldcrc32 = ~init;
for (; len; --len, ++buf) {
oldcrc32 = UPDC32(*buf, oldcrc32);
}
return ~oldcrc32;
}
uint32_t crc32(uint8_t *data, uint32_t len) {
return crc32_partial(0, data, len);
}

14
firmware/crc32.h Normal file
View file

@ -0,0 +1,14 @@
//
// Created by jedi on 02.08.21.
//
#ifndef FIRMWARE_CRC32_H
#define FIRMWARE_CRC32_H
#include <stdint.h>
uint32_t crc32_partial(uint32_t init, uint8_t *data, uint32_t len);
uint32_t crc32(uint8_t *data, uint32_t len);
#endif //FIRMWARE_CRC32_H

View file

@ -22,9 +22,9 @@ void user_init(void)
wifi_available_semaphore = xSemaphoreCreateBinary(); wifi_available_semaphore = xSemaphoreCreateBinary();
xTaskCreate(wifi_task, "wifi_task", 512, NULL, 2, NULL); xTaskCreate(wifi_task, "wifi_task", 1024, NULL, 1, NULL);
xTaskCreate(&httpd_task, "httpd_task", 512, NULL, 2, NULL); xTaskCreate(&httpd_task, "httpd_task", 1024, NULL, 2, NULL);
xTaskCreate(&lux_task, "lux_task", 512, NULL, 1, NULL); xTaskCreate(&lux_task, "lux_task", 512, NULL, 1, NULL);
} }

View file

@ -5,28 +5,17 @@ main {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
section {
display: none;
}
canvas{ canvas{
width: 100%; width: 100%;
} }
main[data-page="dashboard"] section[id="dashboard"] { main section:target ~ section, main section#io, main section#wifi, main section#ota {
display: block; display: none;
} }
main[data-page="ota"] section[id="ota"] { main section:target{
display: block; display: block !important;
}
main[data-page="wifi"] section[id="wifi"] {
display: block;
}
main[data-page="io"] section[id="io"] {
display: block;
} }
.table { .table {

View file

@ -12,8 +12,52 @@
<span>fiatlux v0.2</span> <span>fiatlux v0.2</span>
<span class="label warning" id="status_box">Loading...</span> <span class="label warning" id="status_box">Loading...</span>
</a> </a>
<input id="bmenub" type="checkbox" class="show">
<label for="bmenub" class="burger pseudo button">&#9776;</label>
<div class="menu">
<a href="/#" class="button icon-picture">Dashboard</a>
<a href="/#ota" class="button icon-picture">System</a>
</div>
</nav> </nav>
<main data-page="dashboard" id="page"> <main id="page">
<section id="ota">
<h2>System</h2>
<article class="card">
<header>
<h3>Firmware Update</h3>
</header>
<div class="table">
<div class="row">
<span><input id="firmware_file" type="file" onchange="load_firmware(event)"/></span>
</div>
<div class="row">
<span><input id="transmit_firmware" disabled type="submit" value="Upload"
onclick="transmit_firmware(event)"></span>
</div>
</div>
</article>
<article class="card">
<header>
<h3>Restart</h3>
</header>
<div class="table">
<div class="row">
<span><input type="submit" value="Restart" onclick="wsWrite('R')"></span>
</div>
</div>
</article>
<article class="card">
<header>
<h3>Reset Config</h3>
</header>
<div class="table">
<div class="row">
<span><input type="submit" class="warning" value="Reset" onclick="wsWrite('X')"></span>
</div>
</div>
</article>
</section>
<section id="dashboard"> <section id="dashboard">
<h2>Status</h2> <h2>Status</h2>
<div class="flex"> <div class="flex">
@ -169,6 +213,9 @@
chart.streamTo(canvas, 500); chart.streamTo(canvas, 500);
} }
var receive_chunk_confirmation = () => {
};
function onMessage(evt) { function onMessage(evt) {
retries = 0; retries = 0;
if (typeof evt.data == 'string') { if (typeof evt.data == 'string') {
@ -190,7 +237,9 @@
if (cmd === 'G') if (cmd === 'G')
console.log("LED switched", val); console.log("LED switched", val);
else if (cmd === 'V') { else if (cmd === 'F') {
receive_chunk_confirmation(dv);
} else if (cmd === 'V') {
voltage.innerHTML = (val * 13 / 1024).toFixed(2); voltage.innerHTML = (val * 13 / 1024).toFixed(2);
series.append(new Date().getTime(), val); series.append(new Date().getTime(), val);
} else } else
@ -227,7 +276,7 @@
} }
function wsWrite(data) { function wsWrite(data) {
console.info(buf2hex(data)); //console.info(buf2hex(data));
if (ws.readyState === 3 || retries++ > 5) if (ws.readyState === 3 || retries++ > 5)
wsOpen(); wsOpen();
else if (ws.readyState === 1) else if (ws.readyState === 1)
@ -246,6 +295,105 @@
startPolling(); startPolling();
} }
var makeCRCTable = function () {
var c;
var crcTable = [];
for (var n = 0; n < 256; n++) {
c = n;
for (var k = 0; k < 8; k++) {
c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
}
crcTable[n] = c;
}
return crcTable;
}
var crc32 = function (buf) {
const bufview = new DataView(buf);
var crcTable = window.crcTable || (window.crcTable = makeCRCTable());
var crc = 0 ^ (-1);
for (var i = 0; i < bufview.byteLength; i++) {
crc = (crc >>> 8) ^ crcTable[(crc ^ bufview.getInt8(i)) & 0xFF];
}
return (crc ^ (-1)) >>> 0;
};
var firmware_file;
function load_firmware(evt) {
var file = evt.target.files[0];
if (!file) {
return;
}
var reader = new FileReader();
reader.onload = function (e) {
firmware_file = e.target.result;
document.getElementById("transmit_firmware").disabled = false;
};
reader.readAsArrayBuffer(file)
}
const chunk_size = 512;
function transmit_firmware_chunk(buf, i) {
return new Promise((resolve, reject) => {
const begin = i * chunk_size;
const end = Math.min((i + 1) * chunk_size, buf.byteLength);
const slice = buf.slice(begin, end)
var header = new ArrayBuffer(12);
var headerview = new DataView(header);
headerview.setChar(0, 'F');
headerview.setInt16(4, i);
headerview.setInt16(6, (end - begin));
headerview.setInt32(8, crc32(slice));
var frame = new Uint8Array(12 + slice.byteLength);
frame.set(new Uint8Array(header), 0);
frame.set(new Uint8Array(slice), 12);
receive_chunk_confirmation = (dv) => {
setTimeout(() => {
resolve(i);
}, 50);
}
setTimeout(() => {
reject(i);
}, 2000);
wsWrite(frame.buffer);
});
}
function transmit_firmware_final(buf, hash) {
return new Promise((resolve, reject) => {
var frame = new ArrayBuffer(12);
var headerview = new DataView(frame);
headerview.setChar(0, 'C');
headerview.setInt32(4, buf.byteLength);
headerview.setInt32(8, hash);
receive_chunk_confirmation = (dv) => {
resolve(i);
}
setTimeout(() => {
reject(i);
}, 500);
wsWrite(frame);
});
}
function transmit_firmware(evt) {
console.log("transmit_firmware begin");
if (firmware_file) {
(async () => {
const ash = crc32(firmware_file);
for (var i = 0; i * chunk_size < firmware_file.byteLength; i++) {
await transmit_firmware_chunk(firmware_file, i);
}
await transmit_firmware_final(firmware_file, crc32(firmware_file));
})().then(() => {
console.log("transmit_firmware done");
})
}
}
</script> </script>
</body> </body>
</html> </html>

44
firmware/otaflash.py Executable file
View file

@ -0,0 +1,44 @@
#!/usr/bin/env python3
import time
import websocket
import argparse
import zlib
parser = argparse.ArgumentParser(description='Update fiatlux firmware via websocket.')
parser.add_argument("binfile")
args = parser.parse_args()
with open(args.binfile, "rb") as f:
try:
ws = websocket.WebSocket()
ws.connect("ws://172.16.0.1")
i = 0
rolling = 0
total = 0
while True:
bytes = f.read(512)
rolling = zlib.crc32(bytes, rolling)
total += len(bytes)
msg = b'F\x00\x00\x00'
msg += i.to_bytes(2, 'big')
msg += len(bytes).to_bytes(2, 'big')
msg += (zlib.crc32(bytes) & 0xffffffff).to_bytes(4, 'big')
msg += bytes
ws.send(msg)
reply = ws.recv()
time.sleep(0.05)
i += 1
if len(bytes) != 512:
break
msg = b'C\x00\x00\x00'
msg += total.to_bytes(4, 'big')
msg += rolling.to_bytes(4, 'big')
ws.send(msg)
print(ws.recv())
ws.close()
except ConnectionResetError:
pass
except KeyboardInterrupt:
pass

View file

@ -3,35 +3,114 @@
// //
#include "system.h" #include "system.h"
#include "crc32.h"
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <task.h>
#include <sysparam.h> #include <sysparam.h>
#include <spiflash.h> #include <spiflash.h>
#include <espressif/esp_common.h>
#include <espressif/user_interface.h> #include <espressif/user_interface.h>
#include <espressif/esp_system.h>
#include <rboot/rboot.h>
#include <rboot-ota/rboot-api.h>
#include <string.h>
#define min(a, b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
void system_clear_config() { void system_clear_config() {
vPortEnterCritical(); vPortEnterCritical();
uint32_t num_sectors = 5 + DEFAULT_SYSPARAM_SECTORS; uint32_t num_sectors = 0x2000 / sdk_flashchip.sector_size;
uint32_t start = sdk_flashchip.chip_size - num_sectors * sdk_flashchip.sector_size; uint32_t start = 0x00100000;
for (uint32_t i = 0; i < num_sectors; i++) { for (uint32_t i = 0; i < num_sectors; i++) {
spiflash_erase_sector(start + i * sdk_flashchip.sector_size); spiflash_erase_sector(start + i * sdk_flashchip.sector_size);
} }
if(sysparam_create_area(start, num_sectors, true) == SYSPARAM_OK) {
sysparam_init(start, 0);
}
sysparam_init(start, start + 0x2000);
sdk_system_restart(); sdk_system_restart();
} }
void system_init_config() { void system_init_config() {
uint32_t base_addr; uint32_t base_addr = 0x00100000;
uint32_t num_sectors; uint32_t num_sectors;
sysparam_init(base_addr, 0);
if(sysparam_get_info(&base_addr, &num_sectors) != SYSPARAM_OK) { if(sysparam_get_info(&base_addr, &num_sectors) != SYSPARAM_OK) {
printf("Warning: WiFi config, sysparam not initialized\n"); printf("Warning: WiFi config, sysparam not initialized\n");
num_sectors = DEFAULT_SYSPARAM_SECTORS; num_sectors = 0x2000 / sdk_flashchip.sector_size;
base_addr = sdk_flashchip.chip_size - (5 + num_sectors) * sdk_flashchip.sector_size;
if(sysparam_create_area(base_addr, num_sectors, true) == SYSPARAM_OK) { if(sysparam_create_area(base_addr, num_sectors, true) == SYSPARAM_OK) {
sysparam_init(base_addr, 0); sysparam_init(base_addr, 0);
} }
sdk_system_restart(); sdk_system_restart();
} }
} }
#define MAX_IMAGE_SIZE 0x100000
struct {
rboot_write_status status;
uint32_t head;
uint32_t base;
uint16_t seq;
uint8_t slot;
} otaflash_context;
void system_otaflash_init() {
rboot_config conf;
conf = rboot_get_config();
otaflash_context.slot = (conf.current_rom + 1) % conf.count;
otaflash_context.base = rboot_get_slot_offset(otaflash_context.slot);
otaflash_context.status = rboot_write_init(otaflash_context.base);
otaflash_context.head = otaflash_context.base;
otaflash_context.seq = 0;
}
int system_otaflash_chunk(uint8_t *data, uint16_t len, uint16_t seq, uint32_t hash) {
uint32_t local_hash = crc32(data, len);
if(hash == local_hash && otaflash_context.seq == seq) {
if(otaflash_context.head % SECTOR_SIZE == 0) {
sdk_spi_flash_erase_sector(otaflash_context.head / SECTOR_SIZE);
}
if(((uint32_t) data) % 4) {
uint32 buf[len / 4];
memcpy(buf, data, len);
sdk_spi_flash_write(otaflash_context.head, buf, len);
} else {
sdk_spi_flash_write(otaflash_context.head, (uint32_t *) data, len);
}
otaflash_context.head += len;
otaflash_context.seq++;
return 0x88;
} else {
return 0xff;
}
}
void system_otaflash_verify_chunk(void *ctx, void *data, size_t len) {
uint32_t digest = *(uint32_t *) ctx;
digest = crc32_partial(digest, data, len);
*(uint32_t *) ctx = digest;
}
int system_otaflash_verify_and_switch(uint32_t len, uint32_t hash) {
uint32_t digest = 0;
rboot_digest_image(otaflash_context.base, min(len, MAX_IMAGE_SIZE), system_otaflash_verify_chunk, &digest);
if(hash != digest) {
printf("OTA failed to verify firmware\r\n");
return 0x99;
}
vPortEnterCritical();
if(!rboot_set_current_rom(otaflash_context.slot)) {
printf("OTA failed to set new rboot slot\r\n");
}
sdk_system_restart();
vPortExitCritical(); // | should not be reached
return 0x77; // |
}

View file

@ -5,13 +5,22 @@
#ifndef FIRMWARE_SYSTEM_H #ifndef FIRMWARE_SYSTEM_H
#define FIRMWARE_SYSTEM_H #define FIRMWARE_SYSTEM_H
#include <stdint.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
void system_clear_config(); void system_clear_config();
void system_init_config(); void system_init_config();
void system_otaflash_init();
int system_otaflash_chunk(uint8_t *data, uint16_t len, uint16_t seq, uint32_t hash);
int system_otaflash_verify_and_switch(uint32_t len, uint32_t hash);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -38,7 +38,7 @@ void websocket_task(void *pvParameter) {
has_changed = {true, true, true}; has_changed = {true, true, true};
for (;;) { for (;;) {
if(pcb == NULL || pcb->state != ESTABLISHED) { if(pcb == nullptr || pcb->state != ESTABLISHED) {
printf("Connection closed, deleting task\n"); printf("Connection closed, deleting task\n");
break; break;
} }
@ -46,14 +46,14 @@ void websocket_task(void *pvParameter) {
//Global Info //Global Info
if(has_changed.global) { if(has_changed.global) {
has_changed.global = false; has_changed.global = false;
timeval tv; timeval tv{};
gettimeofday(&tv, NULL); gettimeofday(&tv, nullptr);
int uptime = xTaskGetTickCount() * portTICK_PERIOD_MS / 1000; size_t uptime = xTaskGetTickCount() * portTICK_PERIOD_MS / 1000;
int heap = (int) xPortGetFreeHeapSize(); int heap = (int) xPortGetFreeHeapSize();
uint32_t chip_id = sdk_system_get_chip_id(); uint32_t chip_id = sdk_system_get_chip_id();
uint32_t flash_id = sdk_spi_flash_get_id(); uint32_t flash_id = sdk_spi_flash_get_id();
uint32_t flash_size = sdk_flashchip.chip_size >> 10; uint32_t flash_size = sdk_flashchip.chip_size >> 10;
char *hostname = NULL; char *hostname = nullptr;
sysparam_get_string("hostname", &hostname); sysparam_get_string("hostname", &hostname);
/* Generate response in JSON format */ /* Generate response in JSON format */
@ -80,21 +80,16 @@ void websocket_task(void *pvParameter) {
//Connection Info //Connection Info
if(has_changed.connection) { if(has_changed.connection) {
has_changed.connection = false; has_changed.connection = false;
timeval tv; timeval tv{};
gettimeofday(&tv, NULL); gettimeofday(&tv, nullptr);
int connuptime = (xTaskGetTickCount() - connstarttime) * portTICK_PERIOD_MS / 1000; size_t connuptime = (xTaskGetTickCount() - connstarttime) * portTICK_PERIOD_MS / 1000;
printf("conn %d: " printf("conn %d: " IPSTR " <-> " IPSTR " \n", pcb->netif_idx, IP2STR(&pcb->local_ip),
IPSTR IP2STR(&pcb->remote_ip));
" <-> "
IPSTR
" \n", pcb->netif_idx, IP2STR(&pcb->local_ip), IP2STR(&pcb->remote_ip));
char response[160]; char response[160];
size_t len = snprintf(response, sizeof(response), size_t len = snprintf(response, sizeof(response),
"{\"connage\" : \"%d\"," "{\"connage\" : \"%d\","
"\"clientip\" : \"" "\"clientip\" : \"" IPSTR "\""
IPSTR
"\""
"}", connuptime, IP2STR(&pcb->remote_ip)); "}", connuptime, IP2STR(&pcb->remote_ip));
if(len < sizeof(response)) { if(len < sizeof(response)) {
LOCK_TCPIP_CORE(); LOCK_TCPIP_CORE();
@ -137,10 +132,10 @@ void websocket_task(void *pvParameter) {
if(opmode == SOFTAP_MODE || opmode == STATIONAP_MODE) { if(opmode == SOFTAP_MODE || opmode == STATIONAP_MODE) {
uint8_t hwaddr[6]; uint8_t hwaddr[6];
sdk_wifi_get_macaddr(SOFTAP_IF, hwaddr); sdk_wifi_get_macaddr(SOFTAP_IF, hwaddr);
ip_info info; ip_info info{};
sdk_wifi_get_ip_info(SOFTAP_IF, &info); sdk_wifi_get_ip_info(SOFTAP_IF, &info);
char *apssid = NULL; char *apssid = nullptr;
sysparam_get_string("wifi_ap_ssid", &apssid); sysparam_get_string("wifi_ap_ssid", &apssid);
/* Generate response in JSON format */ /* Generate response in JSON format */
@ -148,12 +143,8 @@ void websocket_task(void *pvParameter) {
size_t len = snprintf(response, sizeof(response), size_t len = snprintf(response, sizeof(response),
"{\"opmode\" : \"%s\"," "{\"opmode\" : \"%s\","
" \"apssid\" : \"%s\"," " \"apssid\" : \"%s\","
" \"apip\" : \"" " \"apip\" : \"" IPSTR "\","
IPSTR " \"apmac\" : \"" MACSTR "\""
"\","
" \"apmac\" : \""
MACSTR
"\""
"}", opmode_str, apssid, IP2STR(&info.ip), MAC2STR(hwaddr)); "}", opmode_str, apssid, IP2STR(&info.ip), MAC2STR(hwaddr));
free(apssid); free(apssid);
if(len < sizeof(response)) { if(len < sizeof(response)) {
@ -169,7 +160,7 @@ void websocket_task(void *pvParameter) {
if(opmode == STATION_MODE || opmode == STATIONAP_MODE) { if(opmode == STATION_MODE || opmode == STATIONAP_MODE) {
uint8_t hwaddr[6]; uint8_t hwaddr[6];
sdk_wifi_get_macaddr(STATION_IF, hwaddr); sdk_wifi_get_macaddr(STATION_IF, hwaddr);
ip_info info; ip_info info{};
sdk_wifi_get_ip_info(STATION_IF, &info); sdk_wifi_get_ip_info(STATION_IF, &info);
char *stassid = nullptr; char *stassid = nullptr;
sysparam_get_string("wifi_sta_ssid", &stassid); sysparam_get_string("wifi_sta_ssid", &stassid);
@ -179,12 +170,8 @@ void websocket_task(void *pvParameter) {
size_t len = snprintf(response, sizeof(response), size_t len = snprintf(response, sizeof(response),
"{\"opmode\" : \"%s\"," "{\"opmode\" : \"%s\","
" \"stassid\" : \"%s\"," " \"stassid\" : \"%s\","
" \"staip\" : \"" " \"staip\" : \"" IPSTR "\","
IPSTR " \"stamac\" : \"" MACSTR "\""
"\","
" \"stamac\" : \""
MACSTR
"\""
"}", opmode_str, stassid, IP2STR(&info.ip), MAC2STR(hwaddr)); "}", opmode_str, stassid, IP2STR(&info.ip), MAC2STR(hwaddr));
free(stassid); free(stassid);
if(len < sizeof(response)) { if(len < sizeof(response)) {
@ -198,34 +185,49 @@ void websocket_task(void *pvParameter) {
} }
vTaskDelayMs(500); vTaskDelayMs(500);
{
uint8_t response[3];
uint16_t val = 0;
val = sdk_system_adc_read();
response[2] = (uint8_t) val;
response[1] = val >> 8;
response[0] = 'V';
websocket_write(pcb, response, 3, WS_BIN_MODE);
}
vTaskDelayMs(500);
} }
vTaskDelete(NULL); vTaskDelete(nullptr);
} }
struct fw_frame {
char t;
uint8_t reserved[3];
uint16_t seq;
uint16_t len;
uint32_t hash;
uint8_t data[];
} __attribute__((packed));
struct fw_check {
char t;
uint8_t reserved[3];
uint32_t len;
uint32_t hash;
} __attribute__((packed));
/** /**
* This function is called when websocket frame is received. * This function is called when websocket frame is received.
* *
* Note: this function is executed on TCP thread and should return as soon * Note: this function is executed on TCP thread and should return as soon
* as possible. * as possible.
*/ */
void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, uint8_t mode) { void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len,
uint8_t /*mode*/) { //mode should be WS_BIN_MODE or WS_TEXT_MODE
uint8_t response[3]; uint8_t response[3];
uint16_t val = 0; uint16_t val = 0;
char cmd = '0'; char cmd = '0';
bool togl = 0;
switch (data[0]) { switch (data[0]) {
case 'R': // Restart
cmd = 'R';
break;
case 'X': // Clear Config
cmd = 'X';
break;
case 'D': // Disable LED case 'D': // Disable LED
signal_led(false); signal_led(false);
val = 1; val = 1;
@ -236,9 +238,29 @@ void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, uint8_t mode)
val = 0; val = 0;
cmd = 'G'; cmd = 'G';
break; break;
case 'F':
togl = ~togl;
signal_led(togl);
{
auto *f = (fw_frame *) data;
if(f->seq == 0) {
system_otaflash_init();
}
val = system_otaflash_chunk(f->data, ntohs(f->len), ntohs(f->seq), ntohl(f->hash));
}
cmd = 'F';
break;
case 'C':
signal_led(false);
{
auto *f = (fw_check *) data;
val = system_otaflash_verify_and_switch(ntohl(f->len), ntohl(f->hash));
}
cmd = 'C';
break;
default: default:
printf("[websocket_callback]:\n%.*s\n", (int) data_len, (char *) data); printf("[websocket_callback]:\n%.*s\n", (int) data_len, (char *) data);
printf("Unknown command\n"); printf("Unknown command %c\n", data[0]);
val = 0; val = 0;
break; break;
} }
@ -247,7 +269,18 @@ void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, uint8_t mode)
response[1] = val >> 8; response[1] = val >> 8;
response[0] = cmd; response[0] = cmd;
LOCK_TCPIP_CORE();
websocket_write(pcb, response, 3, WS_BIN_MODE); websocket_write(pcb, response, 3, WS_BIN_MODE);
UNLOCK_TCPIP_CORE();
if(data[0] == 'R') { // Restart
vTaskDelay(500 / portTICK_PERIOD_MS);
vPortEnterCritical();
sdk_system_restart();
} else if(data[0] == 'X') { // Clear Config
vTaskDelay(500 / portTICK_PERIOD_MS);
system_clear_config();
}
} }
/** /**
@ -262,10 +295,10 @@ void websocket_open_cb(struct tcp_pcb *pcb, const char *uri) {
} }
extern "C" void httpd_task(void *pvParameters) { extern "C" void httpd_task(void *pvParameters) {
(void) pvParameters;
while (!uxSemaphoreGetCount(wifi_available_semaphore)) while (!uxSemaphoreGetCount(wifi_available_semaphore))
vTaskDelay(500 / portTICK_PERIOD_MS); vTaskDelay(500 / portTICK_PERIOD_MS);
websocket_register_callbacks((tWsOpenHandler) websocket_open_cb, (tWsHandler) websocket_cb); websocket_register_callbacks((tWsOpenHandler) websocket_open_cb, (tWsHandler) websocket_cb);
httpd_init(); httpd_init();