Compare commits
15 commits
jedi/dev/c
...
stable
Author | SHA1 | Date | |
---|---|---|---|
8209a9a936 | |||
b10103377d | |||
d5c10e441d | |||
5466bceb0e | |||
7a627ee1f1 | |||
24f5e4398d | |||
|
8f052ecb37 | ||
|
c0228cdf49 | ||
5fa1ec14e2 | |||
09452ef7ab | |||
8874fecf15 | |||
495163060d | |||
c049c04e8e | |||
9e8f14846a | |||
0551aff5d1 |
41 changed files with 949 additions and 305804 deletions
|
@ -9,17 +9,19 @@ steps:
|
||||||
- name: submodules
|
- name: submodules
|
||||||
image: alpine/git
|
image: alpine/git
|
||||||
commands:
|
commands:
|
||||||
- git submodule update --init --recursive
|
- git submodule update --init --recursive --depth 1
|
||||||
|
|
||||||
- name: firmware
|
- name: firmware
|
||||||
image: docker-repo.service.intern.lab.or.it:5000/fiatlux-build-env
|
image: docker-repo.service.intern.lab.or.it:5000/fiatlux-build-env
|
||||||
depends_on: [ submodules ]
|
depends_on: [ submodules ]
|
||||||
commands:
|
commands:
|
||||||
- export PATH=$(pwd)/modules/sdk/xtensa-lx106-elf/bin:$PATH
|
- export PATH=$(pwd)/modules/sdk/xtensa-lx106-elf/bin:$PATH
|
||||||
|
- apt update
|
||||||
|
- apt install -y minify
|
||||||
- make firmware -j$(nproc)
|
- make firmware -j$(nproc)
|
||||||
|
|
||||||
- name: pcb
|
- name: pcb
|
||||||
image: setsoft/kicad_auto
|
image: setsoft/kicad_auto:ki6
|
||||||
commands:
|
commands:
|
||||||
- apt update
|
- apt update
|
||||||
- apt install -y make zip
|
- apt install -y make zip
|
||||||
|
@ -57,10 +59,11 @@ 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
|
||||||
- md5
|
- md5
|
||||||
title: buildtest
|
title: fiatlux
|
||||||
when:
|
when:
|
||||||
event: tag
|
event: tag
|
||||||
|
|
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,6 +1,6 @@
|
||||||
[submodule "modules/rtos"]
|
[submodule "modules/rtos"]
|
||||||
path = modules/rtos
|
path = modules/rtos
|
||||||
url = https://github.com/SuperHouse/esp-open-rtos.git
|
url = https://git.neulandlabor.de/j3d1/esp-open-rtos.git
|
||||||
[submodule "modules/sdk"]
|
[submodule "modules/sdk"]
|
||||||
path = modules/sdk
|
path = modules/sdk
|
||||||
url = https://github.com/pfalcon/esp-open-sdk.git
|
url = https://github.com/pfalcon/esp-open-sdk.git
|
||||||
|
|
8
Makefile
8
Makefile
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
.PHONY: firmware flash firmware_docker case pcb
|
.PHONY: firmware flash firmware_docker case pcb
|
||||||
|
|
||||||
all: firmware case pcb
|
all: firmware case pcb
|
||||||
|
@ -19,9 +18,14 @@ clean:
|
||||||
+@make -C firmware clean
|
+@make -C firmware clean
|
||||||
+@make -C pcb clean
|
+@make -C pcb clean
|
||||||
|
|
||||||
|
flash_docker:
|
||||||
|
sh -c "docker build -t fiatlux_firmware_env docker/firmware"
|
||||||
|
sh -c "docker run --volume "$$(pwd)"/firmware:/app/firmware --device=/dev/ttyUSB0 fiatlux_firmware_env make -C firmware flash"
|
||||||
|
|
||||||
|
|
||||||
firmware_docker:
|
firmware_docker:
|
||||||
sh -c "docker build -t fiatlux_firmware_env docker/firmware"
|
sh -c "docker build -t fiatlux_firmware_env docker/firmware"
|
||||||
sh -c "docker run --volume "$$(pwd)"/firmware:/app/firmware fiatlux_firmware_env make -C firmware all"
|
sh -c "docker run --volume "$$(pwd)"/firmware:/app/firmware fiatlux_firmware_env make -C firmware html all"
|
||||||
|
|
||||||
pcb_docker:
|
pcb_docker:
|
||||||
sh -c "docker build -t fiatlux_pcb_env docker/pcb"
|
sh -c "docker build -t fiatlux_pcb_env docker/pcb"
|
||||||
|
|
|
@ -13,7 +13,7 @@ git submodule update --init --recursive
|
||||||
### Build Requirements
|
### Build Requirements
|
||||||
|
|
||||||
- make
|
- make
|
||||||
- bash gawk perl
|
- bash gawk
|
||||||
- g++ gcc
|
- g++ gcc
|
||||||
- libc6-dev
|
- libc6-dev
|
||||||
- flex bison
|
- flex bison
|
||||||
|
@ -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
BIN
case/case.FCStd
BIN
case/case.FCStd
Binary file not shown.
BIN
case/case.FCStd1
BIN
case/case.FCStd1
Binary file not shown.
|
@ -6,7 +6,7 @@ RUN cd app; git clone --recursive https://github.com/SuperHouse/esp-open-rtos.gi
|
||||||
RUN cd app; sed -i 's/GNU bash, version (3\\\.\[1-9\]|4)/GNU bash, version (3.[1-9]|4|5)/g' modules/sdk/crosstool-NG/configure.ac; mkdir -p modules/sdk/crosstool-NG/.build/tarballs; wget https://github.com/libexpat/libexpat/releases/download/R_2_1_0/expat-2.1.0.tar.gz -O modules/sdk/crosstool-NG/.build/tarballs/expat-2.1.0.tar.gz
|
RUN cd app; sed -i 's/GNU bash, version (3\\\.\[1-9\]|4)/GNU bash, version (3.[1-9]|4|5)/g' modules/sdk/crosstool-NG/configure.ac; mkdir -p modules/sdk/crosstool-NG/.build/tarballs; wget https://github.com/libexpat/libexpat/releases/download/R_2_1_0/expat-2.1.0.tar.gz -O modules/sdk/crosstool-NG/.build/tarballs/expat-2.1.0.tar.gz
|
||||||
RUN cd app/modules/sdk; export CT_EXPERIMENTAL=y; export CT_ALLOW_BUILD_AS_ROOT=y; export CT_ALLOW_BUILD_AS_ROOT_SURE=y; make standalone=y -j$(nproc); wget -N https://raw.githubusercontent.com/espressif/esptool/master/esptool.py -O xtensa-lx106-elf/bin/esptool.py
|
RUN cd app/modules/sdk; export CT_EXPERIMENTAL=y; export CT_ALLOW_BUILD_AS_ROOT=y; export CT_ALLOW_BUILD_AS_ROOT_SURE=y; make standalone=y -j$(nproc); wget -N https://raw.githubusercontent.com/espressif/esptool/master/esptool.py -O xtensa-lx106-elf/bin/esptool.py
|
||||||
USER 0
|
USER 0
|
||||||
RUN apt remove --purge -y python2 && apt autoremove --purge -y && apt install -y python3 python3-serial perl
|
RUN apt remove --purge -y python2 && apt autoremove --purge -y && apt install -y python3 python3-serial
|
||||||
RUN apt install -y --reinstall python-is-python3
|
RUN apt install -y --reinstall python-is-python3
|
||||||
USER 1000
|
USER 1000
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
4
firmware/.gitignore
vendored
4
firmware/.gitignore
vendored
|
@ -142,5 +142,5 @@ dkms.conf
|
||||||
|
|
||||||
*.remove
|
*.remove
|
||||||
firmware/
|
firmware/
|
||||||
fsdata/fsdata.c
|
build/
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
|
9
firmware/.idea/firmware.iml
Normal file
9
firmware/.idea/firmware.iml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module classpath="External" external.linked.project.id="firmware" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="CompDB" type="CPP_MODULE" version="4" />
|
||||||
|
<module version="4">
|
||||||
|
<component name="FacetManager">
|
||||||
|
<facet type="Python" name="Python facet">
|
||||||
|
<configuration sdkName="Python 3.9" />
|
||||||
|
</facet>
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -1,18 +1,21 @@
|
||||||
PROGRAM=fiatlux
|
PROGRAM=fiatlux
|
||||||
|
|
||||||
EXTRA_CFLAGS=-O3 -Ifsdata
|
EXTRA_CFLAGS=-O3 -Ibuild/gen -DLWIP_NETIF_HOSTNAME=1
|
||||||
|
|
||||||
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 extras/paho_mqtt_c
|
||||||
|
|
||||||
LIBS = hal m
|
LIBS = hal m
|
||||||
|
|
||||||
FLASH_MODE = dio
|
FLASH_MODE = qio
|
||||||
|
|
||||||
include ../modules/rtos/common.mk
|
include ../modules/rtos/common.mk
|
||||||
|
|
||||||
html:
|
html: build/gen/fsdata.c
|
||||||
|
|
||||||
|
build/gen/fsdata.c: webdir/index.html webdir/404.html webdir/css/picnic.min.css webdir/css/style.css webdir/js/smoothie_min.js
|
||||||
@echo "Generating fsdata.."
|
@echo "Generating fsdata.."
|
||||||
cd fsdata && ./makefsdata
|
@mkdir -p $(dir $@)
|
||||||
|
@./mkwebfs.py --gzip --minify -o $@ $^
|
||||||
|
|
||||||
test: unittest systest
|
test: unittest systest
|
||||||
|
|
||||||
|
@ -22,4 +25,4 @@ unittest:
|
||||||
systest:
|
systest:
|
||||||
true
|
true
|
||||||
|
|
||||||
.NOTPARALLEL: html all
|
.NOTPARALLEL: html all
|
||||||
|
|
71
firmware/crc32.c
Normal file
71
firmware/crc32.c
Normal 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
14
firmware/crc32.h
Normal 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
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
|
||||||
<link rel="shortcut icon" href="img/favicon.png">
|
|
||||||
<title>HTTP Server</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<ul class="navbar">
|
|
||||||
<li><a href="/">Home</a></li>
|
|
||||||
<li><a href="websockets">WebSockets</a></li>
|
|
||||||
<li><a href="about">About</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="grid main">
|
|
||||||
<h1>404 - Page not found</h1>
|
|
||||||
<div class="alert alert-error">Sorry, the page you are requesting was not found on this server.</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
main {
|
|
||||||
padding: 4em 2em 2em 2em;
|
|
||||||
max-width: 960px;
|
|
||||||
width: 100%;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
section {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas{
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
main[data-page="dashboard"] section[id="dashboard"] {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
main[data-page="ota"] section[id="ota"] {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
main[data-page="wifi"] section[id="wifi"] {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
main[data-page="io"] section[id="io"] {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table {
|
|
||||||
width: 100%;
|
|
||||||
display: table;
|
|
||||||
}
|
|
||||||
.table>.row{
|
|
||||||
display: table-row;
|
|
||||||
}
|
|
||||||
.table>.row:nth-child(2n) {
|
|
||||||
background: rgba(17,17,17,0.05);
|
|
||||||
}
|
|
||||||
.table>.row>*{
|
|
||||||
display: table-cell;
|
|
||||||
padding: .3em 2.4em .3em .6em;
|
|
||||||
}
|
|
||||||
.table>header.row>*{
|
|
||||||
text-align: left;
|
|
||||||
font-weight: 900;
|
|
||||||
color: #fff;
|
|
||||||
background-color: #0074d9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table>.row>input{
|
|
||||||
border: none;
|
|
||||||
background: none;
|
|
||||||
font-weight: 900;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plain{
|
|
||||||
opacity: initial;
|
|
||||||
width: initial;
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
#!/usr/bin/perl
|
|
||||||
|
|
||||||
$incHttpHeader = 1;
|
|
||||||
|
|
||||||
open(OUTPUT, "> fsdata.c");
|
|
||||||
print(OUTPUT "#include \"httpd/fsdata.h\"\n\n");
|
|
||||||
|
|
||||||
chdir("fs");
|
|
||||||
open(FILES, "find . -type f |");
|
|
||||||
|
|
||||||
while($file = <FILES>) {
|
|
||||||
|
|
||||||
# Do not include files in CVS directories nor backup files.
|
|
||||||
if($file =~ /(CVS|~)/) {
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
|
|
||||||
chop($file);
|
|
||||||
|
|
||||||
if($incHttpHeader == 1) {
|
|
||||||
open(HEADER, "> /tmp/header") || die $!;
|
|
||||||
if($file =~ /404/) {
|
|
||||||
print(HEADER "HTTP/1.0 404 File not found\r\n");
|
|
||||||
} else {
|
|
||||||
print(HEADER "HTTP/1.0 200 OK\r\n");
|
|
||||||
}
|
|
||||||
print(HEADER "lwIP/1.4.1 (http://savannah.nongnu.org/projects/lwip)\r\n");
|
|
||||||
if($file =~ /\.html$/ || $file =~ /\.htm$/ || $file =~ /\.shtml$/ || $file =~ /\.shtm$/ || $file =~ /\.ssi$/) {
|
|
||||||
print(HEADER "Content-type: text/html\r\n");
|
|
||||||
} elsif($file =~ /\.js$/) {
|
|
||||||
print(HEADER "Content-type: application/x-javascript\r\n\r\n");
|
|
||||||
} elsif($file =~ /\.css$/) {
|
|
||||||
print(HEADER "Content-type: text/css\r\n\r\n");
|
|
||||||
} elsif($file =~ /\.ico$/) {
|
|
||||||
print(HEADER "Content-type: image/x-icon\r\n\r\n");
|
|
||||||
} elsif($file =~ /\.gif$/) {
|
|
||||||
print(HEADER "Content-type: image/gif\r\n");
|
|
||||||
} elsif($file =~ /\.png$/) {
|
|
||||||
print(HEADER "Content-type: image/png\r\n");
|
|
||||||
} elsif($file =~ /\.jpg$/) {
|
|
||||||
print(HEADER "Content-type: image/jpeg\r\n");
|
|
||||||
} elsif($file =~ /\.bmp$/) {
|
|
||||||
print(HEADER "Content-type: image/bmp\r\n\r\n");
|
|
||||||
} elsif($file =~ /\.class$/) {
|
|
||||||
print(HEADER "Content-type: application/octet-stream\r\n");
|
|
||||||
} elsif($file =~ /\.ram$/) {
|
|
||||||
print(HEADER "Content-type: audio/x-pn-realaudio\r\n");
|
|
||||||
} else {
|
|
||||||
print(HEADER "Content-type: text/plain\r\n");
|
|
||||||
}
|
|
||||||
print(HEADER "\r\n");
|
|
||||||
close(HEADER);
|
|
||||||
|
|
||||||
unless($file =~ /\.plain$/ || $file =~ /cgi/) {
|
|
||||||
system("cat /tmp/header $file > /tmp/file");
|
|
||||||
} else {
|
|
||||||
system("cp $file /tmp/file");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
system("cp $file /tmp/file");
|
|
||||||
}
|
|
||||||
|
|
||||||
open(FILE, "/tmp/file");
|
|
||||||
unlink("/tmp/file");
|
|
||||||
unlink("/tmp/header");
|
|
||||||
|
|
||||||
$file =~ s/\.//;
|
|
||||||
$fvar = $file;
|
|
||||||
$fvar =~ s-/-_-g;
|
|
||||||
$fvar =~ s-\.-_-g;
|
|
||||||
|
|
||||||
print(OUTPUT "static const unsigned char data".$fvar."[] = {\n");
|
|
||||||
print(OUTPUT "\t/* $file */\n\t");
|
|
||||||
for($j = 0; $j < length($file); $j++) {
|
|
||||||
printf(OUTPUT "0x%02X, ", unpack("C", substr($file, $j, 1)));
|
|
||||||
}
|
|
||||||
printf(OUTPUT "0,\n");
|
|
||||||
|
|
||||||
|
|
||||||
$i = 0;
|
|
||||||
while(read(FILE, $data, 1)) {
|
|
||||||
if($i == 0) {
|
|
||||||
print(OUTPUT "\t");
|
|
||||||
}
|
|
||||||
printf(OUTPUT "0x%02X, ", unpack("C", $data));
|
|
||||||
$i++;
|
|
||||||
if($i == 10) {
|
|
||||||
print(OUTPUT "\n");
|
|
||||||
$i = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print(OUTPUT "};\n\n");
|
|
||||||
close(FILE);
|
|
||||||
push(@fvars, $fvar);
|
|
||||||
push(@files, $file);
|
|
||||||
}
|
|
||||||
|
|
||||||
for($i = 0; $i < @fvars; $i++) {
|
|
||||||
$file = $files[$i];
|
|
||||||
$fvar = $fvars[$i];
|
|
||||||
|
|
||||||
if($i == 0) {
|
|
||||||
$prevfile = "NULL";
|
|
||||||
} else {
|
|
||||||
$prevfile = "file" . $fvars[$i - 1];
|
|
||||||
}
|
|
||||||
print(OUTPUT "const struct fsdata_file file".$fvar."[] = {{\n$prevfile,\ndata$fvar, ");
|
|
||||||
print(OUTPUT "data$fvar + ". (length($file) + 1) .",\n");
|
|
||||||
print(OUTPUT "sizeof(data$fvar) - ". (length($file) + 1) .",\n");
|
|
||||||
print(OUTPUT $incHttpHeader."\n}};\n\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
print(OUTPUT "#define FS_ROOT file$fvars[$i - 1]\n\n");
|
|
||||||
print(OUTPUT "#define FS_NUMFILES $i\n");
|
|
49
firmware/log.cpp
Normal file
49
firmware/log.cpp
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
//
|
||||||
|
// Created by jedi on 18.11.21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#include <espressif/esp_common.h>
|
||||||
|
|
||||||
|
constexpr unsigned syslog_buffer_size = 1024;
|
||||||
|
char syslog_buf[syslog_buffer_size];
|
||||||
|
volatile unsigned head = 0;
|
||||||
|
volatile unsigned streams = 0;
|
||||||
|
|
||||||
|
extern "C" void syslog(const char *msg) {
|
||||||
|
printf("syslog> %s", msg);
|
||||||
|
while (char c = *msg++) {
|
||||||
|
syslog_buf[head++ % syslog_buffer_size] = c;
|
||||||
|
}
|
||||||
|
syslog_buf[head] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned syslog_current_tail() {
|
||||||
|
if(head < syslog_buffer_size)
|
||||||
|
return 0;
|
||||||
|
return head + 1 - syslog_buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned syslog_data_after(unsigned local_tail) {
|
||||||
|
if(local_tail > head)
|
||||||
|
return 0;
|
||||||
|
return (head % syslog_buffer_size) - (local_tail % syslog_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int syslog_copy_out(char *out, int len, unsigned local_tail) {
|
||||||
|
unsigned cnt = 0;
|
||||||
|
while (cnt < syslog_data_after(local_tail) && cnt < len) {
|
||||||
|
out[cnt] = syslog_buf[local_tail % syslog_buffer_size + cnt];
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void syslog_attach() {
|
||||||
|
streams++;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void syslog_detach() {
|
||||||
|
streams--;
|
||||||
|
}
|
28
firmware/log.h
Normal file
28
firmware/log.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
//
|
||||||
|
// Created by jedi on 18.11.21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef FIRMWARE_LOG_H
|
||||||
|
#define FIRMWARE_LOG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void syslog(const char *);
|
||||||
|
|
||||||
|
unsigned syslog_current_tail();
|
||||||
|
|
||||||
|
unsigned syslog_data_after(unsigned);
|
||||||
|
|
||||||
|
int syslog_copy_out(char *, int, unsigned);
|
||||||
|
|
||||||
|
void syslog_attach();
|
||||||
|
|
||||||
|
void syslog_detach();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //FIRMWARE_LOG_H
|
130
firmware/mkwebfs.py
Executable file
130
firmware/mkwebfs.py
Executable file
|
@ -0,0 +1,130 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import gzip
|
||||||
|
import argparse
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('-o', '--output', help='Output file name', default='stdout')
|
||||||
|
parser.add_argument('-W', '--webroot', help='Output file name', default='webdir/')
|
||||||
|
parser.add_argument('--gzip', dest='gzip', action='store_true')
|
||||||
|
parser.add_argument('--no-gzip', dest='gzip', action='store_false')
|
||||||
|
parser.set_defaults(gzip=False)
|
||||||
|
parser.add_argument('--minify', dest='minify', action='store_true')
|
||||||
|
parser.add_argument('--no-minify', dest='minify', action='store_false')
|
||||||
|
parser.set_defaults(minify=False)
|
||||||
|
parser.add_argument('--header', dest='header', action='store_true')
|
||||||
|
parser.add_argument('--no-header', dest='header', action='store_false')
|
||||||
|
parser.set_defaults(header=True)
|
||||||
|
parser.add_argument('input', nargs='+', default=os.getcwd())
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def mimeFromName(name):
|
||||||
|
if name.endswith(".html") or name.endswith(".htm") or name.endswith(".shtml") or name.endswith(
|
||||||
|
".shtm") or name.endswith(".ssi"):
|
||||||
|
return "text/html"
|
||||||
|
if name.endswith(".js"):
|
||||||
|
return "application/x-javascript"
|
||||||
|
if name.endswith(".css"):
|
||||||
|
return "text/css"
|
||||||
|
if name.endswith(".ico"):
|
||||||
|
return "image/x-icon"
|
||||||
|
if name.endswith(".gif"):
|
||||||
|
return "image/gif"
|
||||||
|
if name.endswith(".png"):
|
||||||
|
return "image/png"
|
||||||
|
if name.endswith(".jpg"):
|
||||||
|
return "image/jpeg"
|
||||||
|
if name.endswith(".bmp"):
|
||||||
|
return "image/bmp"
|
||||||
|
if name.endswith(".class"):
|
||||||
|
return "application/octet-stream"
|
||||||
|
if name.endswith(".ram"):
|
||||||
|
return "audio/x-pn-realaudio"
|
||||||
|
return "text/plain"
|
||||||
|
|
||||||
|
|
||||||
|
def dumpBin2CHex(f, b):
|
||||||
|
oStr = "\t"
|
||||||
|
n = 0
|
||||||
|
for val in b:
|
||||||
|
oStr += hex(val) + ", "
|
||||||
|
n += 1
|
||||||
|
if n % 8 == 0:
|
||||||
|
oStr += "\n\t"
|
||||||
|
oStr += "\n"
|
||||||
|
f.write(oStr)
|
||||||
|
|
||||||
|
|
||||||
|
f_fsdata_c = open(args.output, 'w')
|
||||||
|
f_fsdata_c.write('#include "httpd/fsdata.h"\n\n')
|
||||||
|
|
||||||
|
httpFiles = [file for file in args.input if (args.webroot in file)]
|
||||||
|
|
||||||
|
lastFileStruct = "NULL"
|
||||||
|
|
||||||
|
for file in httpFiles:
|
||||||
|
response = b''
|
||||||
|
|
||||||
|
webPath = ("/" + file.removeprefix(args.webroot)).replace("//", "/")
|
||||||
|
print("{} > {}".format(file, webPath))
|
||||||
|
|
||||||
|
mimeType = mimeFromName(file)
|
||||||
|
|
||||||
|
if args.header:
|
||||||
|
if ("404" in file):
|
||||||
|
response = b'HTTP/1.0 404 File not found\r\n'
|
||||||
|
else:
|
||||||
|
response = b'HTTP/1.0 200 OK\r\n'
|
||||||
|
response += b"lwIP/1.4.1 (http://savannah.nongnu.org/projects/lwip)\r\n"
|
||||||
|
response += b'Content-type: ' + mimeType.encode() + b'\r\n'
|
||||||
|
|
||||||
|
binFile = open(file, 'rb')
|
||||||
|
binData = binFile.read()
|
||||||
|
compEff = False
|
||||||
|
if args.minify:
|
||||||
|
p = subprocess.Popen(["minify", "--html-keep-document-tags", "--mime", mimeType], stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE)
|
||||||
|
minData = p.communicate(binData)[0]
|
||||||
|
if len(minData) < len(binData):
|
||||||
|
print("- Minify: {} -> {}".format(len(binData), len(minData)))
|
||||||
|
compEff = True
|
||||||
|
binData = minData
|
||||||
|
|
||||||
|
if args.gzip:
|
||||||
|
compData = gzip.compress(binData, 9)
|
||||||
|
if len(compData) < len(binData):
|
||||||
|
compEff = True
|
||||||
|
print("- Compressed from {} to {}".format(len(binData), len(compData)))
|
||||||
|
binData = compData
|
||||||
|
else:
|
||||||
|
print("- Compression skipped Orig: {} Comp: {}".format(len(binData), len(compData)))
|
||||||
|
binFile.close()
|
||||||
|
|
||||||
|
if compEff:
|
||||||
|
response += b'Content-Encoding: gzip\r\n'
|
||||||
|
response += b"\r\n"
|
||||||
|
response += binData
|
||||||
|
binFile.close()
|
||||||
|
escFile = file.replace("/", "_").replace(".", "_")
|
||||||
|
escFileData = "data_" + escFile
|
||||||
|
escFileFile = "file_" + escFile
|
||||||
|
|
||||||
|
f_fsdata_c.write('static const unsigned char {}[] = {{\n'.format(escFileData))
|
||||||
|
f_fsdata_c.write('\t/* LOCAL:{} */\n'.format(file))
|
||||||
|
f_fsdata_c.write('\t/* WEB: {} */\n'.format(webPath))
|
||||||
|
fnameBin = webPath.encode("ascii") + b'\0'
|
||||||
|
dumpBin2CHex(f_fsdata_c, fnameBin)
|
||||||
|
dumpBin2CHex(f_fsdata_c, response)
|
||||||
|
f_fsdata_c.write("};\n\n")
|
||||||
|
|
||||||
|
f_fsdata_c.write("const struct fsdata_file {}[] = {{{{\n {},\n {}, {} + {}, sizeof({}) - {}, 1 }}}};\n\n"
|
||||||
|
.format(escFileFile, lastFileStruct, escFileData, escFileData, len(fnameBin), escFileData,
|
||||||
|
len(fnameBin)))
|
||||||
|
# TODO: The last value is 1 if args.header == True
|
||||||
|
lastFileStruct = escFileFile
|
||||||
|
|
||||||
|
f_fsdata_c.write("\n")
|
||||||
|
f_fsdata_c.write("#define FS_ROOT {}\n\n".format(lastFileStruct))
|
||||||
|
f_fsdata_c.write("#define FS_NUMFILES {}\n\n".format(len(httpFiles)))
|
73
firmware/otaflash.py
Executable file
73
firmware/otaflash.py
Executable file
|
@ -0,0 +1,73 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time
|
||||||
|
import websocket
|
||||||
|
import argparse
|
||||||
|
import zlib
|
||||||
|
from websocket import WebSocketTimeoutException
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Update fiatlux firmware via websocket.')
|
||||||
|
parser.add_argument("binfile")
|
||||||
|
parser.add_argument("address")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
bs = 1024
|
||||||
|
|
||||||
|
|
||||||
|
def parse_reply(bytes):
|
||||||
|
cmd = bytes[0:1].decode("utf-8")
|
||||||
|
ret = int.from_bytes(bytes[1:2], "big")
|
||||||
|
val = int.from_bytes(bytes[2:4], "big")
|
||||||
|
return {'cmd': cmd, 'ret': ret, 'val': val}
|
||||||
|
|
||||||
|
|
||||||
|
with open(args.binfile, "rb") as f:
|
||||||
|
try:
|
||||||
|
ws = websocket.WebSocket()
|
||||||
|
print("send {}".format(args.binfile))
|
||||||
|
ws.connect("ws://" + args.address)
|
||||||
|
i = 0
|
||||||
|
bytes = f.read()
|
||||||
|
rolling = zlib.crc32(bytes)
|
||||||
|
total = len(bytes)
|
||||||
|
while True:
|
||||||
|
chunk = bytes[bs * i:bs * i + bs]
|
||||||
|
msg = b'F\x00\x00\x00'
|
||||||
|
msg += i.to_bytes(2, 'big')
|
||||||
|
msg += len(chunk).to_bytes(2, 'big')
|
||||||
|
msg += (zlib.crc32(chunk) & 0xffffffff).to_bytes(4, 'big')
|
||||||
|
msg += chunk
|
||||||
|
ws.send(msg)
|
||||||
|
print("\r{:6.2f}%".format(100 * i * bs / total), end='')
|
||||||
|
reply = parse_reply(ws.recv())
|
||||||
|
if reply['cmd'] == 'F' and reply['ret'] == 0:
|
||||||
|
i += 1
|
||||||
|
elif reply['cmd'] == 'F' and reply['ret'] == 1:
|
||||||
|
print("Error: SEQUENCE_OUT_OF_ORDER")
|
||||||
|
i = reply['val']
|
||||||
|
elif reply['cmd'] == 'F' and reply['ret'] == 2:
|
||||||
|
print("Error: CHECKSUM_MISMATCH")
|
||||||
|
i = reply['val']
|
||||||
|
else:
|
||||||
|
print(reply)
|
||||||
|
|
||||||
|
time.sleep(0.05)
|
||||||
|
if len(chunk) != bs:
|
||||||
|
break
|
||||||
|
|
||||||
|
print("\rdone ")
|
||||||
|
msg = b'C\x00\x00\x00'
|
||||||
|
msg += total.to_bytes(4, 'big')
|
||||||
|
msg += (rolling).to_bytes(4, 'big')
|
||||||
|
ws.settimeout(5)
|
||||||
|
ws.send(msg)
|
||||||
|
reply = parse_reply(ws.recv())
|
||||||
|
print(reply)
|
||||||
|
ws.close()
|
||||||
|
except WebSocketTimeoutException:
|
||||||
|
pass
|
||||||
|
except ConnectionResetError:
|
||||||
|
pass
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
|
@ -3,35 +3,120 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
#include "crc32.h"
|
||||||
|
#include "log.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>
|
||||||
|
|
||||||
void system_clear_config(){
|
#define min(a, b) \
|
||||||
|
({ __typeof__ (a) _a = (a); \
|
||||||
|
__typeof__ (b) _b = (b); \
|
||||||
|
_a < _b ? _a : _b; })
|
||||||
|
|
||||||
|
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");
|
syslog("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;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum return_code system_otaflash_chunk(uint8_t *data, uint16_t len, uint16_t seq, uint32_t hash, uint16_t *ack) {
|
||||||
|
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 OK;
|
||||||
|
} else if(hash != local_hash) {
|
||||||
|
return CHECKSUM_MISMATCH;
|
||||||
|
} else {
|
||||||
|
if(ack)
|
||||||
|
*ack = otaflash_context.seq;
|
||||||
|
return SEQUENCE_OUT_OF_ORDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum return_code 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) {
|
||||||
|
syslog("OTA failed to verify firmware\r\n");
|
||||||
|
return CHECKSUM_MISMATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
vPortEnterCritical();
|
||||||
|
if(!rboot_set_current_rom(otaflash_context.slot)) {
|
||||||
|
syslog("OTA Update failed to set new rboot slot\r\n");
|
||||||
|
vPortExitCritical();
|
||||||
|
return RBOOT_SWITCH_FAILED;
|
||||||
|
}
|
||||||
|
vPortExitCritical();
|
||||||
|
return OK;
|
||||||
}
|
}
|
|
@ -5,13 +5,23 @@
|
||||||
#ifndef FIRMWARE_SYSTEM_H
|
#ifndef FIRMWARE_SYSTEM_H
|
||||||
#define FIRMWARE_SYSTEM_H
|
#define FIRMWARE_SYSTEM_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "types.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();
|
||||||
|
|
||||||
|
enum return_code system_otaflash_chunk(uint8_t *data, uint16_t len, uint16_t seq, uint32_t hash, uint16_t *ack);
|
||||||
|
|
||||||
|
enum return_code system_otaflash_verify_and_switch(uint32_t len, uint32_t hash);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
20
firmware/types.h
Normal file
20
firmware/types.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// Created by jedi on 09.09.21.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef FIRMWARE_TYPES_H
|
||||||
|
#define FIRMWARE_TYPES_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum return_code {
|
||||||
|
OK = 0, SEQUENCE_OUT_OF_ORDER, CHECKSUM_MISMATCH, RBOOT_SWITCH_FAILED, ERROR = 0xFF
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif //FIRMWARE_TYPES_H
|
200
firmware/web.cpp
200
firmware/web.cpp
|
@ -6,6 +6,7 @@
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "lux.h"
|
#include "lux.h"
|
||||||
#include "wifi.h"
|
#include "wifi.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <FreeRTOS.h>
|
#include <FreeRTOS.h>
|
||||||
|
@ -36,28 +37,46 @@ void websocket_task(void *pvParameter) {
|
||||||
|
|
||||||
size_t connstarttime = xTaskGetTickCount();
|
size_t connstarttime = xTaskGetTickCount();
|
||||||
has_changed = {true, true, true};
|
has_changed = {true, true, true};
|
||||||
|
syslog_attach();
|
||||||
|
unsigned local_log_tail = syslog_current_tail();
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if(pcb == NULL || pcb->state != ESTABLISHED) {
|
if(pcb == nullptr || pcb->state != ESTABLISHED) {
|
||||||
printf("Connection closed, deleting task\n");
|
syslog("Connection closed, deleting task\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Syslog
|
||||||
|
if(syslog_data_after(local_log_tail) != 0) {
|
||||||
|
char response[128];
|
||||||
|
response[0] = 'L';
|
||||||
|
size_t len = syslog_copy_out(&response[4], 124, local_log_tail);
|
||||||
|
response[1] = len;
|
||||||
|
((uint16_t &) response[2]) = local_log_tail & 0xFFFF;
|
||||||
|
if(len < sizeof(response)) {
|
||||||
|
LOCK_TCPIP_CORE();
|
||||||
|
websocket_write(pcb, (unsigned char *) response, len + 4, WS_BIN_MODE);
|
||||||
|
local_log_tail += len;
|
||||||
|
UNLOCK_TCPIP_CORE();
|
||||||
|
} else
|
||||||
|
syslog("buffer too small -1\n");
|
||||||
|
vTaskDelayMs(1000);
|
||||||
|
}
|
||||||
|
|
||||||
//Global Info
|
//Global Info
|
||||||
if(has_changed.global) {
|
if(has_changed.global) {
|
||||||
has_changed.global = false;
|
timeval tv{};
|
||||||
timeval tv;
|
gettimeofday(&tv, nullptr);
|
||||||
gettimeofday(&tv, NULL);
|
size_t uptime = xTaskGetTickCount() * portTICK_PERIOD_MS / 1000;
|
||||||
int 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 */
|
||||||
char response[160];
|
char response[192];
|
||||||
size_t len = snprintf(response, sizeof(response),
|
size_t len = snprintf(response, sizeof(response),
|
||||||
"{\"walltime\" : \"%d\","
|
"{\"walltime\" : \"%d\","
|
||||||
"\"uptime\" : \"%d\","
|
"\"uptime\" : \"%d\","
|
||||||
|
@ -71,38 +90,34 @@ void websocket_task(void *pvParameter) {
|
||||||
if(len < sizeof(response)) {
|
if(len < sizeof(response)) {
|
||||||
LOCK_TCPIP_CORE();
|
LOCK_TCPIP_CORE();
|
||||||
websocket_write(pcb, (unsigned char *) response, len, WS_TEXT_MODE);
|
websocket_write(pcb, (unsigned char *) response, len, WS_TEXT_MODE);
|
||||||
|
has_changed.global = false;
|
||||||
UNLOCK_TCPIP_CORE();
|
UNLOCK_TCPIP_CORE();
|
||||||
} else
|
} else
|
||||||
printf("buffer too small 1");
|
syslog("buffer too small 0\n");
|
||||||
vTaskDelayMs(2000);
|
vTaskDelayMs(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Connection Info
|
//Connection Info
|
||||||
if(has_changed.connection) {
|
if(has_changed.connection) {
|
||||||
has_changed.connection = false;
|
timeval tv{};
|
||||||
timeval tv;
|
gettimeofday(&tv, nullptr);
|
||||||
gettimeofday(&tv, NULL);
|
size_t connuptime = (xTaskGetTickCount() - connstarttime) * portTICK_PERIOD_MS / 1000;
|
||||||
int 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));
|
||||||
" <-> "
|
char response[192];
|
||||||
IPSTR
|
|
||||||
" \n", pcb->netif_idx, IP2STR(&pcb->local_ip), IP2STR(&pcb->remote_ip));
|
|
||||||
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();
|
||||||
websocket_write(pcb, (unsigned char *) response, len, WS_TEXT_MODE);
|
websocket_write(pcb, (unsigned char *) response, len, WS_TEXT_MODE);
|
||||||
|
has_changed.connection = false;
|
||||||
UNLOCK_TCPIP_CORE();
|
UNLOCK_TCPIP_CORE();
|
||||||
} else
|
} else
|
||||||
printf("buffer too small 1");
|
syslog("buffer too small 1\n");
|
||||||
vTaskDelayMs(2000);
|
vTaskDelayMs(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(has_changed.wifi) {
|
if(has_changed.wifi) {
|
||||||
|
@ -137,10 +152,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 +163,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)) {
|
||||||
|
@ -161,15 +172,15 @@ void websocket_task(void *pvParameter) {
|
||||||
websocket_write(pcb, (unsigned char *) response, len, WS_TEXT_MODE);
|
websocket_write(pcb, (unsigned char *) response, len, WS_TEXT_MODE);
|
||||||
UNLOCK_TCPIP_CORE();
|
UNLOCK_TCPIP_CORE();
|
||||||
} else
|
} else
|
||||||
printf("buffer too small 2");
|
syslog("buffer too small 2\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
vTaskDelayMs(2000);
|
vTaskDelayMs(1000);
|
||||||
|
|
||||||
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 +190,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)) {
|
||||||
|
@ -192,62 +199,121 @@ void websocket_task(void *pvParameter) {
|
||||||
websocket_write(pcb, (unsigned char *) response, len, WS_TEXT_MODE);
|
websocket_write(pcb, (unsigned char *) response, len, WS_TEXT_MODE);
|
||||||
UNLOCK_TCPIP_CORE();
|
UNLOCK_TCPIP_CORE();
|
||||||
} else
|
} else
|
||||||
printf("buffer too small 3");
|
syslog("buffer too small 3\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
syslog_detach();
|
||||||
|
|
||||||
|
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[4];
|
||||||
uint16_t val = 0;
|
auto &cmd = (char &) response[0];
|
||||||
char cmd = '0';
|
auto &ret = response[1];
|
||||||
|
auto &val = (uint16_t &) response[2];
|
||||||
|
cmd = '0';
|
||||||
|
ret = ERROR;
|
||||||
|
val = 0;
|
||||||
|
|
||||||
|
bool togl = false;
|
||||||
|
|
||||||
switch (data[0]) {
|
switch (data[0]) {
|
||||||
|
case 'R': // Restart
|
||||||
|
cmd = 'R';
|
||||||
|
ret = OK;
|
||||||
|
break;
|
||||||
|
case 'X': // Clear Config
|
||||||
|
cmd = 'X';
|
||||||
|
ret = OK;
|
||||||
|
break;
|
||||||
case 'D': // Disable LED
|
case 'D': // Disable LED
|
||||||
|
syslog("G\n");
|
||||||
signal_led(false);
|
signal_led(false);
|
||||||
val = 1;
|
|
||||||
cmd = 'G';
|
cmd = 'G';
|
||||||
|
ret = OK;
|
||||||
|
val = 1;
|
||||||
break;
|
break;
|
||||||
case 'E': // Enable LED
|
case 'E': // Enable LED
|
||||||
|
syslog("E\n");
|
||||||
signal_led(true);
|
signal_led(true);
|
||||||
val = 0;
|
|
||||||
cmd = 'G';
|
cmd = 'G';
|
||||||
|
ret = OK;
|
||||||
|
val = 0;
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
togl = !togl;
|
||||||
|
signal_led(togl);
|
||||||
|
{
|
||||||
|
auto *f = (fw_frame *) data;
|
||||||
|
if(f->seq == 0) {
|
||||||
|
system_otaflash_init();
|
||||||
|
}
|
||||||
|
uint16_t ack = 0;
|
||||||
|
ret = system_otaflash_chunk(f->data, ntohs(f->len), ntohs(f->seq), ntohl(f->hash), &ack);
|
||||||
|
val = htons(ack);
|
||||||
|
}
|
||||||
|
cmd = 'F';
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
signal_led(false);
|
||||||
|
{
|
||||||
|
auto *f = (fw_check *) data;
|
||||||
|
ret = system_otaflash_verify_and_switch(ntohl(f->len), ntohl(f->hash));
|
||||||
|
}
|
||||||
|
cmd = 'C';
|
||||||
break;
|
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;
|
ret = ERROR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
response[2] = (uint8_t) val;
|
LOCK_TCPIP_CORE();
|
||||||
response[1] = val >> 8;
|
websocket_write(pcb, response, 4, WS_BIN_MODE);
|
||||||
response[0] = cmd;
|
UNLOCK_TCPIP_CORE();
|
||||||
|
|
||||||
websocket_write(pcb, response, 3, WS_BIN_MODE);
|
if(ret == OK) {
|
||||||
|
if(cmd == 'R' || cmd == 'C') { // Restart
|
||||||
|
printf("rebooting now");
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
vPortEnterCritical();
|
||||||
|
sdk_system_restart();
|
||||||
|
} else if(cmd == 'X') { // Clear Config
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
system_clear_config();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -262,10 +328,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();
|
||||||
|
|
||||||
|
|
22
firmware/webdir/404.html
Normal file
22
firmware/webdir/404.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||||
|
<link rel="shortcut icon" href="img/favicon.png">
|
||||||
|
<title>HTTP Server</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul class="navbar">
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="websockets">WebSockets</a></li>
|
||||||
|
<li><a href="about">About</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="grid main">
|
||||||
|
<h1>404 - Page not found</h1>
|
||||||
|
<div class="alert alert-error">Sorry, the page you are requesting was not found on this server.</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
55
firmware/webdir/css/style.css
Normal file
55
firmware/webdir/css/style.css
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
main {
|
||||||
|
padding: 4em 2em 2em 2em;
|
||||||
|
max-width: 960px;
|
||||||
|
width: 100%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
main section:target ~ section, main section#io, main section#wifi, main section#ota {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
main section:target {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
width: 100%;
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table > .row {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table > .row:nth-child(2n) {
|
||||||
|
background: rgba(17, 17, 17, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table > .row > * {
|
||||||
|
display: table-cell;
|
||||||
|
padding: .3em .6em .3em .6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table > header.row > * {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 900;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #0074d9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table > .row > input {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plain {
|
||||||
|
opacity: initial;
|
||||||
|
width: initial;
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
<title>fiatlux v0.2</title>
|
<title>fiatlux v0.2</title>
|
||||||
<link rel="stylesheet" href="css/picnic.min.css">
|
<link rel="stylesheet" href="css/picnic.min.css">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav>
|
<nav>
|
||||||
|
@ -12,8 +13,68 @@
|
||||||
<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">☰</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">
|
||||||
|
<div class="full" id="progress_ct_ota">
|
||||||
|
<div id="progress_bar_ota"
|
||||||
|
style="background:#0074d9; display: block; width:9%; color: #fff"></div>
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
|
<article class="card">
|
||||||
|
<header>
|
||||||
|
<h3>Syslog</h3>
|
||||||
|
</header>
|
||||||
|
<div class="table">
|
||||||
|
<div class="row">
|
||||||
|
<pre id="syslog"></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
</section>
|
||||||
<section id="dashboard">
|
<section id="dashboard">
|
||||||
<h2>Status</h2>
|
<h2>Status</h2>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
|
@ -130,16 +191,21 @@
|
||||||
<script>
|
<script>
|
||||||
var menu = document.getElementById("bmenub");
|
var menu = document.getElementById("bmenub");
|
||||||
var voltage = document.getElementById("out_voltage");
|
var voltage = document.getElementById("out_voltage");
|
||||||
|
var syslog = document.getElementById("syslog");
|
||||||
|
|
||||||
var unused_values = {};
|
var unused_values = {};
|
||||||
|
|
||||||
DataView.prototype.setChar = function(pos, char) {
|
DataView.prototype.setChar = function (pos, char) {
|
||||||
this.setInt8(pos++, char.charCodeAt(0));
|
this.setInt8(pos++, char.charCodeAt(0));
|
||||||
return pos;
|
return pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
DataView.prototype.setString = function(pos, str) {
|
DataView.prototype.getChar = function (pos) {
|
||||||
for(var i = 0; i < str.length; i++) {
|
return String.fromCharCode(this.getInt8(pos));
|
||||||
|
};
|
||||||
|
|
||||||
|
DataView.prototype.setString = function (pos, str) {
|
||||||
|
for (var i = 0; i < str.length; i++) {
|
||||||
this.setInt8(pos++, str.charCodeAt(i));
|
this.setInt8(pos++, str.charCodeAt(i));
|
||||||
}
|
}
|
||||||
this.setInt8(pos++, 0);
|
this.setInt8(pos++, 0);
|
||||||
|
@ -154,7 +220,7 @@
|
||||||
sbox = document.getElementById('status_box');
|
sbox = document.getElementById('status_box');
|
||||||
sbox.className = "label " + cls;
|
sbox.className = "label " + cls;
|
||||||
sbox.innerHTML = text;
|
sbox.innerHTML = text;
|
||||||
console.log(text);
|
console.info(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
function startPolling() {
|
function startPolling() {
|
||||||
|
@ -169,6 +235,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,16 +259,25 @@
|
||||||
|
|
||||||
if (cmd === 'G')
|
if (cmd === 'G')
|
||||||
console.log("LED switched", val);
|
console.log("LED switched", val);
|
||||||
else if (cmd === 'V') {
|
else if ((cmd === 'F') || (cmd === "C")) {
|
||||||
|
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 if (cmd === 'L') {
|
||||||
|
var len = dv.getUint8(1);
|
||||||
|
var offset = dv.getUint16(2);
|
||||||
|
var str = "";
|
||||||
|
for (var i = 0; i < len; i++)
|
||||||
|
str += dv.getChar(4 + i);
|
||||||
|
syslog.innerHTML = syslog.innerHTML.slice(0, offset) + str;
|
||||||
} else
|
} else
|
||||||
console.log('unknown command', cmd, val);
|
console.log('unknown command', cmd, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function wsOpen() {
|
function wsOpen() {
|
||||||
var uri = "/stream"
|
var uri = "/stream";
|
||||||
if (ws === undefined || ws.readyState !== 0) {
|
if (ws === undefined || ws.readyState !== 0) {
|
||||||
if (retries)
|
if (retries)
|
||||||
setMsg("warning", "WebSocket timeout, retrying..");
|
setMsg("warning", "WebSocket timeout, retrying..");
|
||||||
|
@ -215,6 +293,12 @@
|
||||||
console.error(evt);
|
console.error(evt);
|
||||||
setMsg("error", "WebSocket error!"); /*window.location.reload(true);*/
|
setMsg("error", "WebSocket error!"); /*window.location.reload(true);*/
|
||||||
};
|
};
|
||||||
|
ws.onclose = function (evt) {
|
||||||
|
msgStyle = "warning";
|
||||||
|
if (!evt.wasClean) msgStyle = "error";
|
||||||
|
setMsg(msgStyle, "WebSocket closed!");
|
||||||
|
setTimeout(() => wsOpen(), 0);
|
||||||
|
};
|
||||||
ws.onmessage = function (evt) {
|
ws.onmessage = function (evt) {
|
||||||
onMessage(evt);
|
onMessage(evt);
|
||||||
};
|
};
|
||||||
|
@ -227,7 +311,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 +330,115 @@
|
||||||
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({cmd: dv.getChar(0), ret: dv.getUint8(1), val: dv.getUint16(2)});
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
reject({frame_error: 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({cmd: dv.getChar(0), ret: dv.getUint8(1), val: dv.getUint16(2)});
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
reject({final_error: 0});
|
||||||
|
}, 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++) {
|
||||||
|
update_progress("ota", i * chunk_size / firmware_file.byteLength * 100);
|
||||||
|
const reply = await transmit_firmware_chunk(firmware_file, i);
|
||||||
|
console.log("reply", reply)
|
||||||
|
}
|
||||||
|
await transmit_firmware_final(firmware_file, crc32(firmware_file));
|
||||||
|
update_progress("ota", 100);
|
||||||
|
})().then(() => {
|
||||||
|
console.log("transmit_firmware done");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_progress(progressBar, progress) {
|
||||||
|
var iP = Math.floor(progress);
|
||||||
|
var dBar = document.getElementById("progress_bar_" + progressBar);
|
||||||
|
dBar.innerText = iP + "%";
|
||||||
|
dBar.style.width = progress + "%";
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -3,6 +3,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "wifi.h"
|
#include "wifi.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -36,7 +37,7 @@ SemaphoreHandle_t wifi_available_semaphore = nullptr;
|
||||||
char *wifi_ap_ip_addr = nullptr;
|
char *wifi_ap_ip_addr = nullptr;
|
||||||
sysparam_get_string("wifi_ap_ip_addr", &wifi_ap_ip_addr);
|
sysparam_get_string("wifi_ap_ip_addr", &wifi_ap_ip_addr);
|
||||||
if(!wifi_ap_ip_addr) {
|
if(!wifi_ap_ip_addr) {
|
||||||
printf("dns: no ip address\n");
|
syslog("dns: no ip address\n");
|
||||||
vTaskDelete(nullptr);
|
vTaskDelete(nullptr);
|
||||||
}
|
}
|
||||||
ip4_addr_t server_addr;
|
ip4_addr_t server_addr;
|
||||||
|
@ -194,7 +195,7 @@ extern "C" void wifi_task(void *pvParameters) {
|
||||||
/* If the ssid and password are not valid then disable the AP interface. */
|
/* If the ssid and password are not valid then disable the AP interface. */
|
||||||
if(!wifi_ap_ssid || strlen(wifi_ap_ssid) < 1 || strlen(wifi_ap_ssid) >= 32 ||
|
if(!wifi_ap_ssid || strlen(wifi_ap_ssid) < 1 || strlen(wifi_ap_ssid) >= 32 ||
|
||||||
!wifi_ap_password || strlen(wifi_ap_password) < 8 || strlen(wifi_ap_password) >= 64) {
|
!wifi_ap_password || strlen(wifi_ap_password) < 8 || strlen(wifi_ap_password) >= 64) {
|
||||||
printf("len err\n");
|
syslog("len err\n");
|
||||||
wifi_ap_enable = 0;
|
wifi_ap_enable = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 503e66a500419e8863998b7ea784c5e26a7a5f7c
|
Subproject commit 7faa16b07ce0d606f9525a316990da5b58e61314
|
4
pcb/.gitignore
vendored
4
pcb/.gitignore
vendored
|
@ -31,6 +31,8 @@ fp-info-cache
|
||||||
*.wrl
|
*.wrl
|
||||||
*.step
|
*.step
|
||||||
|
|
||||||
*-bak
|
*-backups/
|
||||||
gen/
|
gen/
|
||||||
pcb.zip
|
pcb.zip
|
||||||
|
|
||||||
|
report.txt
|
3
webapp/.gitignore
vendored
Normal file
3
webapp/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules/
|
||||||
|
src/gen/
|
||||||
|
package-lock.json
|
Loading…
Reference in a new issue