mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-29 16:07:48 +02:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a0ca319e7 | ||
|
|
c095f4c881 | ||
|
|
c586c9f541 | ||
|
|
6c4f8ef35b | ||
|
|
c403c06b09 | ||
|
|
ddd043f44f | ||
|
|
81cc5ccc9a | ||
|
|
be325b0e62 | ||
|
|
3621c84cc4 | ||
|
|
ed82795e5f | ||
|
|
5b9f8b324e | ||
|
|
db80d23b52 | ||
|
|
125922f8e7 | ||
|
|
aa484575b8 | ||
|
|
fa97daef3c | ||
|
|
0c591660bc | ||
|
|
c8fb0ca4de | ||
|
|
87ce823776 | ||
|
|
7fe4a1e6ad | ||
|
|
29b8c9bc21 | ||
|
|
2e44889b19 | ||
|
|
4d08454fff | ||
|
|
f131b5ce84 | ||
|
|
33ba6a7aa7 | ||
|
|
75c6a6c0f9 | ||
|
|
f7e2025487 | ||
|
|
a91dd5c2a2 | ||
|
|
0ba287f734 | ||
|
|
ad52c64b94 | ||
|
|
8c06bb04f3 | ||
|
|
07807afe08 | ||
|
|
eb173e355f | ||
|
|
b792b71608 |
40
.github/workflows/release.yml
vendored
40
.github/workflows/release.yml
vendored
@@ -39,18 +39,18 @@ jobs:
|
|||||||
- name: Install intelhex
|
- name: Install intelhex
|
||||||
run: pip install --upgrade intelhex
|
run: pip install --upgrade intelhex
|
||||||
|
|
||||||
- name: Build NRF firmware
|
# - name: Build NRF firmware
|
||||||
run: |
|
# run: |
|
||||||
cd ARM_Tag_FW/Newton_M3_nRF52811
|
# cd ARM_Tag_FW/Newton_M3_nRF52811
|
||||||
pio run --environment Newton_M3_22_BWR
|
# pio run --environment Newton_M3_22_BWR
|
||||||
pio run --environment Newton_M3_29_BWR
|
# pio run --environment Newton_M3_29_BWR
|
||||||
pio run --environment Newton_M3_75_BWR
|
# pio run --environment Newton_M3_75_BWR
|
||||||
cp Newton_M3_22_BWR-ota.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/binaries/Newton_M3_22_BWR-ota.bin
|
# cp Newton_M3_22_BWR-ota.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/binaries/Newton_M3_22_BWR-ota.bin
|
||||||
cp Newton_M3_22_BWR-full-flash.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/binaries/Newton_M3_22_BWR-full-flash.bin
|
# cp Newton_M3_22_BWR-full-flash.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/binaries/Newton_M3_22_BWR-full-flash.bin
|
||||||
cp Newton_M3_29_BWR-ota.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/binaries/Newton_M3_29_BWR-ota.bin
|
# cp Newton_M3_29_BWR-ota.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/binaries/Newton_M3_29_BWR-ota.bin
|
||||||
cp Newton_M3_29_BWR-full-flash.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/binaries/Newton_M3_29_BWR-full-flash.bin
|
# cp Newton_M3_29_BWR-full-flash.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/binaries/Newton_M3_29_BWR-full-flash.bin
|
||||||
cp Newton_M3_75_BWR-ota.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/binaries/Newton_M3_75_BWR-ota.bin
|
# cp Newton_M3_75_BWR-ota.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/binaries/Newton_M3_75_BWR-ota.bin
|
||||||
cp Newton_M3_75_BWR-full-flash.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/binaries/Newton_M3_75_BWR-full-flash.bin
|
# cp Newton_M3_75_BWR-full-flash.bin /home/runner/work/OpenEPaperLink/OpenEPaperLink/binaries/Newton_M3_75_BWR-full-flash.bin
|
||||||
|
|
||||||
- name: Install esptool
|
- name: Install esptool
|
||||||
run: pip install esptool
|
run: pip install esptool
|
||||||
@@ -199,14 +199,14 @@ jobs:
|
|||||||
file_glob: true
|
file_glob: true
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
- name: Add tag bins to release
|
# - name: Add tag bins to release
|
||||||
uses: svenstaro/upload-release-action@v2
|
# uses: svenstaro/upload-release-action@v2
|
||||||
with:
|
# with:
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
# repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
file: binaries/*
|
# file: binaries/*
|
||||||
tag: ${{ github.ref }}
|
# tag: ${{ github.ref }}
|
||||||
file_glob: true
|
# file_glob: true
|
||||||
overwrite: true
|
# overwrite: true
|
||||||
|
|
||||||
# this is down here intentionally to be able to modify the binary folder before adding it to the Tag_Flasher later (ota binaries can be removed)
|
# this is down here intentionally to be able to modify the binary folder before adding it to the Tag_Flasher later (ota binaries can be removed)
|
||||||
|
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ void watchdog_enable(int timeout) {
|
|||||||
|
|
||||||
uint32_t sleepMsEntry = 0;
|
uint32_t sleepMsEntry = 0;
|
||||||
uint32_t loops = 0;
|
uint32_t loops = 0;
|
||||||
bool interruped = false;
|
bool interrupted = false;
|
||||||
|
|
||||||
// uint8_t ledcfg[12] = {0b00100010,0x78,0b00100100,5,0x03,0b01000011,1,0xC2,0b1100001,10,10,0};
|
// uint8_t ledcfg[12] = {0b00100010,0x78,0b00100100,5,0x03,0b01000011,1,0xC2,0b1100001,10,10,0};
|
||||||
// uint8_t ledcfg[12] = {0b00010010,0x7D,0,0,0x03,0xE8,0,0,0,0,0,0};
|
// uint8_t ledcfg[12] = {0b00010010,0x7D,0,0,0x03,0xE8,0,0,0,0,0,0};
|
||||||
@@ -185,14 +185,14 @@ void resettimer() {
|
|||||||
// tell the sleep function to net sleep again
|
// tell the sleep function to net sleep again
|
||||||
sleepMsEntry = sleepMsEntry - 999999999;
|
sleepMsEntry = sleepMsEntry - 999999999;
|
||||||
loops = 0;
|
loops = 0;
|
||||||
interruped = true;
|
interrupted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void flashled(uint8_t color, uint8_t brightnes) {
|
void flashled(uint8_t color, uint8_t brightness) {
|
||||||
uint8_t colorred = (color >> 5) & 0b00000111;
|
uint8_t colorred = (color >> 5) & 0b00000111;
|
||||||
uint8_t colorgreen = (color >> 2) & 0b00000111;
|
uint8_t colorgreen = (color >> 2) & 0b00000111;
|
||||||
uint8_t colorblue = color & 0b00000011;
|
uint8_t colorblue = color & 0b00000011;
|
||||||
for (uint16_t i = 0; i < brightnes; i++) {
|
for (uint16_t i = 0; i < brightness; i++) {
|
||||||
digitalWrite(LED_RED, !(colorred >= 7));
|
digitalWrite(LED_RED, !(colorred >= 7));
|
||||||
digitalWrite(LED_GREEN, !(colorgreen >= 7));
|
digitalWrite(LED_GREEN, !(colorgreen >= 7));
|
||||||
digitalWrite(LED_BLUE, !(colorblue >= 3));
|
digitalWrite(LED_BLUE, !(colorblue >= 3));
|
||||||
@@ -248,11 +248,10 @@ void sleepwithinterrupts(uint32_t sleepinterval) {
|
|||||||
|
|
||||||
void ledflashlogic(uint32_t ms) {
|
void ledflashlogic(uint32_t ms) {
|
||||||
watchdog_enable(ms + 1000);
|
watchdog_enable(ms + 1000);
|
||||||
uint8_t brightnes = ledcfg[0] >> 4 & 0b00001111;
|
uint8_t brightness = ledcfg[0] >> 4 & 0b00001111;
|
||||||
uint8_t mode = ledcfg[0] & 0b00001111;
|
uint8_t mode = ledcfg[0] & 0b00001111;
|
||||||
// lets not blink for short delays
|
// lets not blink for short delays
|
||||||
if (ms < 2000) mode = 15;
|
if (ms < 2000) mode = 15;
|
||||||
// if(mode == 0)sleepwithinterrupts(ms);
|
|
||||||
if (mode == 1) {
|
if (mode == 1) {
|
||||||
uint8_t color = ledcfg[1];
|
uint8_t color = ledcfg[1];
|
||||||
uint32_t ledinerv = (ledcfg[2] << 24) + (ledcfg[3] << 16) + (ledcfg[4] << 8) + ledcfg[5];
|
uint32_t ledinerv = (ledcfg[2] << 24) + (ledcfg[3] << 16) + (ledcfg[4] << 8) + ledcfg[5];
|
||||||
@@ -264,11 +263,12 @@ void ledflashlogic(uint32_t ms) {
|
|||||||
}
|
}
|
||||||
if (sleepinterval > ms) sleepinterval = ms;
|
if (sleepinterval > ms) sleepinterval = ms;
|
||||||
for (uint32_t i = 0; i < loops; i++) {
|
for (uint32_t i = 0; i < loops; i++) {
|
||||||
flashled(color, brightnes);
|
flashled(color, brightness);
|
||||||
sleepwithinterrupts(sleepinterval);
|
sleepwithinterrupts(sleepinterval);
|
||||||
}
|
}
|
||||||
} else if (mode == 0) {
|
}
|
||||||
interruped = false;
|
else if (mode == 0) {
|
||||||
|
interrupted = false;
|
||||||
uint8_t interloopdelayfactor = 100;
|
uint8_t interloopdelayfactor = 100;
|
||||||
u_int8_t loopdelayfactor = 100;
|
u_int8_t loopdelayfactor = 100;
|
||||||
uint8_t c1 = ledcfg[1];
|
uint8_t c1 = ledcfg[1];
|
||||||
@@ -290,36 +290,40 @@ void ledflashlogic(uint32_t ms) {
|
|||||||
uint32_t looptimesum = fulllooptime1 + fulllooptime2 + fulllooptime3;
|
uint32_t looptimesum = fulllooptime1 + fulllooptime2 + fulllooptime3;
|
||||||
int fittingrepeats = (int)ms / looptimesum;
|
int fittingrepeats = (int)ms / looptimesum;
|
||||||
|
|
||||||
grouprepeats = fittingrepeats;
|
//catch edge case
|
||||||
if (grouprepeats == 0) grouprepeats = 1;
|
if (grouprepeats == 0) sleepwithinterrupts(ms);
|
||||||
|
|
||||||
for (int j = 0; j < grouprepeats; j++) {
|
for (int j = 0; j < fittingrepeats; j++) {
|
||||||
if (!interruped) {
|
if(j > grouprepeats){
|
||||||
|
brightness = 0;
|
||||||
|
ledcfg[0] = 0xff;
|
||||||
|
}
|
||||||
|
if (!interrupted) {
|
||||||
for (int i = 0; i < loopcnt1; i++) {
|
for (int i = 0; i < loopcnt1; i++) {
|
||||||
flashled(c1, brightnes);
|
flashled(c1, brightness);
|
||||||
sleepwithinterrupts(loop1delay * loopdelayfactor);
|
sleepwithinterrupts(loop1delay * loopdelayfactor);
|
||||||
if (interruped) break;
|
if (interrupted) break;
|
||||||
}
|
}
|
||||||
sleepwithinterrupts(ildelay1 * interloopdelayfactor);
|
sleepwithinterrupts(ildelay1 * interloopdelayfactor);
|
||||||
}
|
}
|
||||||
if (!interruped) {
|
if (!interrupted) {
|
||||||
for (int i = 0; i < loopcnt2; i++) {
|
for (int i = 0; i < loopcnt2; i++) {
|
||||||
flashled(c2, brightnes);
|
flashled(c2, brightness);
|
||||||
sleepwithinterrupts(loop2delay * loopdelayfactor);
|
sleepwithinterrupts(loop2delay * loopdelayfactor);
|
||||||
if (interruped) break;
|
if (interrupted) break;
|
||||||
}
|
}
|
||||||
sleepwithinterrupts(ildelay2 * interloopdelayfactor);
|
sleepwithinterrupts(ildelay2 * interloopdelayfactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!interruped) {
|
if (!interrupted) {
|
||||||
for (int i = 0; i < loopcnt3; i++) {
|
for (int i = 0; i < loopcnt3; i++) {
|
||||||
flashled(c3, brightnes);
|
flashled(c3, brightness);
|
||||||
sleepwithinterrupts(loop3delay * loopdelayfactor);
|
sleepwithinterrupts(loop3delay * loopdelayfactor);
|
||||||
if (interruped) break;
|
if (interrupted) break;
|
||||||
}
|
}
|
||||||
sleepwithinterrupts(ildelay3 * interloopdelayfactor);
|
sleepwithinterrupts(ildelay3 * interloopdelayfactor);
|
||||||
}
|
}
|
||||||
if (interruped) break;
|
if (interrupted) break;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
sleepwithinterrupts(ms);
|
sleepwithinterrupts(ms);
|
||||||
|
|||||||
@@ -2,3 +2,7 @@ build
|
|||||||
*.axf
|
*.axf
|
||||||
# Allow
|
# Allow
|
||||||
!*.bin
|
!*.bin
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
sdkconfig
|
||||||
|
sdkconfig.old
|
||||||
|
|||||||
28
ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/main/Kconfig.projbuild
Normal file
28
ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/main/Kconfig.projbuild
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
menu "OEPL Hardware config"
|
||||||
|
|
||||||
|
choice OEPL_HARDWARE_PROFILE
|
||||||
|
prompt "Hardware profile"
|
||||||
|
default OEPL_HARDWARE_PROFILE_DEFAULT
|
||||||
|
|
||||||
|
config OEPL_HARDWARE_PROFILE_DEFAULT
|
||||||
|
bool "Default"
|
||||||
|
|
||||||
|
config OEPL_HARDWARE_PROFILE_POE_AP
|
||||||
|
bool "PoE-AP"
|
||||||
|
|
||||||
|
config OEPL_HARDWARE_PROFILE_CUSTOM
|
||||||
|
bool "Custom"
|
||||||
|
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config OEPL_HARDWARE_UART_TX
|
||||||
|
depends on OEPL_HARDWARE_PROFILE_CUSTOM
|
||||||
|
int "GPIO - UART TX"
|
||||||
|
default 3
|
||||||
|
|
||||||
|
config OEPL_HARDWARE_UART_RX
|
||||||
|
depends on OEPL_HARDWARE_PROFILE_CUSTOM
|
||||||
|
int "GPIO - UART RX"
|
||||||
|
default 2
|
||||||
|
|
||||||
|
endmenu
|
||||||
@@ -326,15 +326,15 @@ void processSerial(uint8_t lastchar) {
|
|||||||
}
|
}
|
||||||
goto SCPfailed;
|
goto SCPfailed;
|
||||||
SCPchannelFound:
|
SCPchannelFound:
|
||||||
pr("ACK>");
|
pr("ACK>");
|
||||||
if (curChannel != scp->channel) {
|
if (curChannel != scp->channel) {
|
||||||
radioSetChannel(scp->channel);
|
radioSetChannel(scp->channel);
|
||||||
curChannel = scp->channel;
|
curChannel = scp->channel;
|
||||||
}
|
}
|
||||||
curPower = scp->power;
|
curPower = scp->power;
|
||||||
radioSetTxPower(scp->power);
|
radioSetTxPower(scp->power);
|
||||||
ESP_LOGI(TAG, "Set channel: %d power: %d", curChannel, curPower);
|
ESP_LOGI(TAG, "Set channel: %d power: %d", curChannel, curPower);
|
||||||
} else {
|
} else {
|
||||||
SCPfailed:
|
SCPfailed:
|
||||||
pr("NOK>");
|
pr("NOK>");
|
||||||
}
|
}
|
||||||
@@ -412,27 +412,27 @@ void espNotifyAPInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void espNotifyTagReturnData(uint8_t *src, uint8_t len) {
|
void espNotifyTagReturnData(uint8_t *src, uint8_t len) {
|
||||||
struct tagReturnData *trd = (struct tagReturnData *)(radiorxbuffer + sizeof(struct MacFrameBcast) + 1); // oh how I'd love to pass this as an argument, but sdcc won't let me
|
struct tagReturnData *trd = (struct tagReturnData *)(radiorxbuffer + sizeof(struct MacFrameBcast) + 1); // oh how I'd love to pass this as an argument, but sdcc won't let me
|
||||||
struct espTagReturnData *etrd = (struct espTagReturnData *)radiotxbuffer;
|
struct espTagReturnData *etrd = (struct espTagReturnData *)radiotxbuffer;
|
||||||
|
|
||||||
if (memcmp((void *) & trd->dataVer, lastTagReturn, 8) == 0) {
|
if (memcmp((void *) & trd->dataVer, lastTagReturn, 8) == 0) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
memcpy(lastTagReturn, &trd->dataVer, 8);
|
memcpy(lastTagReturn, &trd->dataVer, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(etrd->src, src, 8);
|
memcpy(etrd->src, src, 8);
|
||||||
etrd->len = len;
|
etrd->len = len;
|
||||||
memcpy(&etrd->returnData, trd, len);
|
memcpy(&etrd->returnData, trd, len);
|
||||||
addCRC(etrd, len + 10);
|
addCRC(etrd, len + 10);
|
||||||
|
|
||||||
uartTx('T');
|
uartTx('T');
|
||||||
uartTx('R');
|
uartTx('R');
|
||||||
uartTx('D');
|
uartTx('D');
|
||||||
uartTx('>');
|
uartTx('>');
|
||||||
for (uint8_t c = 0; c < len + 10; c++) {
|
for (uint8_t c = 0; c < len + 10; c++) {
|
||||||
uartTx(((uint8_t *)etrd)[c]);
|
uartTx(((uint8_t *)etrd)[c]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// process data from tag
|
// process data from tag
|
||||||
@@ -492,7 +492,7 @@ void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload) {
|
|||||||
if (blockStartTimer == 0) {
|
if (blockStartTimer == 0) {
|
||||||
if (requestDataDownload) {
|
if (requestDataDownload) {
|
||||||
if (highspeedSerial == true) {
|
if (highspeedSerial == true) {
|
||||||
blockRequestAck->pleaseWaitMs = 220;
|
blockRequestAck->pleaseWaitMs = 140;
|
||||||
} else {
|
} else {
|
||||||
blockRequestAck->pleaseWaitMs = 550;
|
blockRequestAck->pleaseWaitMs = 550;
|
||||||
}
|
}
|
||||||
@@ -583,23 +583,23 @@ void processXferComplete(uint8_t *buffer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void processTagReturnData(uint8_t *buffer, uint8_t len) {
|
void processTagReturnData(uint8_t *buffer, uint8_t len) {
|
||||||
struct MacFrameBcast *rxframe = (struct MacFrameBcast *)buffer;
|
struct MacFrameBcast *rxframe = (struct MacFrameBcast *)buffer;
|
||||||
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
|
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
|
||||||
|
|
||||||
if (!checkCRC((buffer + sizeof(struct MacFrameBcast) + 1), len - (sizeof(struct MacFrameBcast) + 1))) {
|
if (!checkCRC((buffer + sizeof(struct MacFrameBcast) + 1), len - (sizeof(struct MacFrameBcast) + 1))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_TAG_RETURN_DATA_ACK;
|
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_TAG_RETURN_DATA_ACK;
|
||||||
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + RAW_PKT_PADDING;
|
radiotxbuffer[0] = sizeof(struct MacFrameNormal) + 1 + RAW_PKT_PADDING;
|
||||||
memcpy(frameHeader->src, mSelfMac, 8);
|
memcpy(frameHeader->src, mSelfMac, 8);
|
||||||
memcpy(frameHeader->dst, rxframe->src, 8);
|
memcpy(frameHeader->dst, rxframe->src, 8);
|
||||||
radiotxbuffer[1] = 0x41; // fast way to set the appropriate bits
|
radiotxbuffer[1] = 0x41; // fast way to set the appropriate bits
|
||||||
radiotxbuffer[2] = 0xCC; // normal frame
|
radiotxbuffer[2] = 0xCC; // normal frame
|
||||||
frameHeader->seq = seq++;
|
frameHeader->seq = seq++;
|
||||||
frameHeader->pan = rxframe->srcPan;
|
frameHeader->pan = rxframe->srcPan;
|
||||||
radioTx(radiotxbuffer);
|
radioTx(radiotxbuffer);
|
||||||
|
|
||||||
espNotifyTagReturnData(rxframe->src, len - (sizeof(struct MacFrameBcast) + 1));
|
espNotifyTagReturnData(rxframe->src, len - (sizeof(struct MacFrameBcast) + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// send block data to the tag
|
// send block data to the tag
|
||||||
@@ -628,6 +628,18 @@ void sendBlockData() {
|
|||||||
pr("Invalid block request received, 0 parts..\n");
|
pr("Invalid block request received, 0 parts..\n");
|
||||||
requestedData.requestedParts[0] |= 0x01;
|
requestedData.requestedParts[0] |= 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pr("Sending parts:");
|
||||||
|
for (uint8_t c = 0; (c < BLOCK_MAX_PARTS); c++) {
|
||||||
|
if (c % 10 == 0) pr(" ");
|
||||||
|
if (requestedData.requestedParts[c / 8] & (1 << (c % 8))) {
|
||||||
|
pr("X");
|
||||||
|
} else {
|
||||||
|
pr(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pr("\n");
|
||||||
|
|
||||||
uint8_t partNo = 0;
|
uint8_t partNo = 0;
|
||||||
while (partNo < BLOCK_MAX_PARTS) {
|
while (partNo < BLOCK_MAX_PARTS) {
|
||||||
for (uint8_t c = 0; (c < BLOCK_MAX_PARTS) && (partNo < BLOCK_MAX_PARTS); c++) {
|
for (uint8_t c = 0; (c < BLOCK_MAX_PARTS) && (partNo < BLOCK_MAX_PARTS); c++) {
|
||||||
@@ -684,24 +696,24 @@ void sendPong(void *buf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void app_main(void) {
|
void app_main(void) {
|
||||||
esp_event_loop_create_default();
|
esp_event_loop_create_default();
|
||||||
|
|
||||||
init_nvs();
|
init_nvs();
|
||||||
init_led();
|
init_led();
|
||||||
init_second_uart();
|
init_second_uart();
|
||||||
|
|
||||||
requestedData.blockId = 0xFF;
|
requestedData.blockId = 0xFF;
|
||||||
// clear the array with pending information
|
// clear the array with pending information
|
||||||
memset(pendingDataArr, 0, sizeof(pendingDataArr));
|
memset(pendingDataArr, 0, sizeof(pendingDataArr));
|
||||||
|
|
||||||
radio_init(curChannel);
|
radio_init(curChannel);
|
||||||
radioSetTxPower(10);
|
radioSetTxPower(10);
|
||||||
|
|
||||||
pr("RES>");
|
pr("RES>");
|
||||||
pr("RDY>");
|
pr("RDY>");
|
||||||
ESP_LOGI(TAG, "C6 ready!");
|
ESP_LOGI(TAG, "C6 ready!");
|
||||||
|
|
||||||
housekeepingTimer = getMillis();
|
housekeepingTimer = getMillis();
|
||||||
while (1) {
|
while (1) {
|
||||||
while ((getMillis() - housekeepingTimer) < ((1000 * HOUSEKEEPING_INTERVAL) - 100)) {
|
while ((getMillis() - housekeepingTimer) < ((1000 * HOUSEKEEPING_INTERVAL) - 100)) {
|
||||||
int8_t ret = commsRxUnencrypted(radiorxbuffer);
|
int8_t ret = commsRxUnencrypted(radiorxbuffer);
|
||||||
@@ -741,11 +753,11 @@ void app_main(void) {
|
|||||||
processAvailDataReq(radiorxbuffer);
|
processAvailDataReq(radiorxbuffer);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PKT_TAG_RETURN_DATA:
|
case PKT_TAG_RETURN_DATA:
|
||||||
processTagReturnData(radiorxbuffer, ret);
|
processTagReturnData(radiorxbuffer, ret);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ESP_LOGI(TAG, "t=%02X" , getPacketType(radiorxbuffer));
|
ESP_LOGI(TAG, "t=%02X" , getPacketType(radiorxbuffer));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (blockStartTimer == 0) {
|
} else if (blockStartTimer == 0) {
|
||||||
@@ -763,8 +775,8 @@ void app_main(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&lastTagReturn, 0, 8);
|
memset(&lastTagReturn, 0, 8);
|
||||||
for (uint8_t cCount = 0; cCount < MAX_PENDING_MACS; cCount++) {
|
for (uint8_t cCount = 0; cCount < MAX_PENDING_MACS; cCount++) {
|
||||||
if (pendingDataArr[cCount].attemptsLeft == 1) {
|
if (pendingDataArr[cCount].attemptsLeft == 1) {
|
||||||
if (pendingDataArr[cCount].availdatainfo.dataType != DATATYPE_NOUPDATE) {
|
if (pendingDataArr[cCount].availdatainfo.dataType != DATATYPE_NOUPDATE) {
|
||||||
espNotifyTimeOut(pendingDataArr[cCount].targetMac);
|
espNotifyTimeOut(pendingDataArr[cCount].targetMac);
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
// if you get an error about soc/lp_uart_reg.h not being found,
|
||||||
|
// you didn't choose the right build target. :-)
|
||||||
#include "soc/lp_uart_reg.h"
|
#include "soc/lp_uart_reg.h"
|
||||||
#include "soc/uart_struct.h"
|
#include "soc/uart_struct.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
@@ -48,62 +50,62 @@ void esp_ieee802154_transmit_done(const uint8_t *frame, const uint8_t *ack, esp_
|
|||||||
}
|
}
|
||||||
|
|
||||||
void radio_init(uint8_t ch) {
|
void radio_init(uint8_t ch) {
|
||||||
if (packet_buffer == NULL) packet_buffer = xQueueCreate(32, 130);
|
if (packet_buffer == NULL) packet_buffer = xQueueCreate(32, 130);
|
||||||
|
|
||||||
// this will trigger a "IEEE802154 MAC sleep init failed" when called a second time, but it works
|
// this will trigger a "IEEE802154 MAC sleep init failed" when called a second time, but it works
|
||||||
esp_ieee802154_enable();
|
esp_ieee802154_enable();
|
||||||
esp_ieee802154_set_channel(ch);
|
esp_ieee802154_set_channel(ch);
|
||||||
// esp_ieee802154_set_txpower(int8_t power);
|
// esp_ieee802154_set_txpower(int8_t power);
|
||||||
esp_ieee802154_set_panid(PROTO_PAN_ID);
|
esp_ieee802154_set_panid(PROTO_PAN_ID);
|
||||||
esp_ieee802154_set_promiscuous(false);
|
esp_ieee802154_set_promiscuous(false);
|
||||||
esp_ieee802154_set_coordinator(false);
|
esp_ieee802154_set_coordinator(false);
|
||||||
esp_ieee802154_set_pending_mode(ESP_IEEE802154_AUTO_PENDING_ZIGBEE);
|
esp_ieee802154_set_pending_mode(ESP_IEEE802154_AUTO_PENDING_ZIGBEE);
|
||||||
|
|
||||||
// esp_ieee802154_set_extended_address needs the MAC in reversed byte order
|
// esp_ieee802154_set_extended_address needs the MAC in reversed byte order
|
||||||
esp_read_mac(mSelfMac, ESP_MAC_IEEE802154);
|
esp_read_mac(mSelfMac, ESP_MAC_IEEE802154);
|
||||||
uint8_t eui64_rev[8] = {0};
|
uint8_t eui64_rev[8] = {0};
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
eui64_rev[7 - i] = mSelfMac[i];
|
eui64_rev[7 - i] = mSelfMac[i];
|
||||||
}
|
}
|
||||||
esp_ieee802154_set_extended_address(eui64_rev);
|
esp_ieee802154_set_extended_address(eui64_rev);
|
||||||
esp_ieee802154_get_extended_address(mSelfMac);
|
esp_ieee802154_get_extended_address(mSelfMac);
|
||||||
|
|
||||||
esp_ieee802154_set_short_address(0xFFFE);
|
esp_ieee802154_set_short_address(0xFFFE);
|
||||||
esp_ieee802154_set_rx_when_idle(true);
|
esp_ieee802154_set_rx_when_idle(true);
|
||||||
esp_ieee802154_receive();
|
esp_ieee802154_receive();
|
||||||
|
|
||||||
led_flash(1);
|
led_flash(1);
|
||||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
led_flash(0);
|
led_flash(0);
|
||||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
led_flash(1);
|
led_flash(1);
|
||||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
led_flash(0);
|
led_flash(0);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Receiver ready, panId=0x%04x, channel=%d, long=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, short=%04x",
|
ESP_LOGI(TAG, "Receiver ready, panId=0x%04x, channel=%d, long=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, short=%04x",
|
||||||
esp_ieee802154_get_panid(), esp_ieee802154_get_channel(),
|
esp_ieee802154_get_panid(), esp_ieee802154_get_channel(),
|
||||||
mSelfMac[0], mSelfMac[1], mSelfMac[2], mSelfMac[3],
|
mSelfMac[0], mSelfMac[1], mSelfMac[2], mSelfMac[3],
|
||||||
mSelfMac[4], mSelfMac[5], mSelfMac[6], mSelfMac[7],
|
mSelfMac[4], mSelfMac[5], mSelfMac[6], mSelfMac[7],
|
||||||
esp_ieee802154_get_short_address());
|
esp_ieee802154_get_short_address());
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t lastZbTx = 0;
|
// uint32_t lastZbTx = 0;
|
||||||
bool radioTx(uint8_t *packet) {
|
bool radioTx(uint8_t *packet) {
|
||||||
static uint8_t txPKT[130];
|
static uint8_t txPKT[130];
|
||||||
while (isInTransmit) {
|
|
||||||
}
|
|
||||||
while (getMillis() - lastZbTx < 6) {
|
|
||||||
}
|
|
||||||
led_flash(1);
|
led_flash(1);
|
||||||
memcpy(txPKT, packet, packet[0]);
|
while (isInTransmit) {
|
||||||
isInTransmit = 1;
|
}
|
||||||
lastZbTx = getMillis();
|
// while (getMillis() - lastZbTx < 6) {
|
||||||
esp_ieee802154_transmit(txPKT, false);
|
// }
|
||||||
return true;
|
// lastZbTx = getMillis();
|
||||||
|
memcpy(txPKT, packet, packet[0]);
|
||||||
|
isInTransmit = 1;
|
||||||
|
esp_ieee802154_transmit(txPKT, false);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void radioSetChannel(uint8_t ch) {
|
void radioSetChannel(uint8_t ch) {
|
||||||
radio_init(ch);
|
radio_init(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void radioSetTxPower(uint8_t power) {}
|
void radioSetTxPower(uint8_t power) {}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "soc/uart_struct.h"
|
#include "soc/uart_struct.h"
|
||||||
#include "soc/lp_uart_reg.h"
|
#include "soc/lp_uart_reg.h"
|
||||||
|
#include "second_uart.h"
|
||||||
|
|
||||||
static const char *TAG = "SECOND_UART";
|
static const char *TAG = "SECOND_UART";
|
||||||
|
|
||||||
@@ -32,9 +33,6 @@ volatile int curr_buff_pos = 0;
|
|||||||
volatile int worked_buff_pos = 0;
|
volatile int worked_buff_pos = 0;
|
||||||
volatile uint8_t buff_pos[MAX_BUFF_POS + 5];
|
volatile uint8_t buff_pos[MAX_BUFF_POS + 5];
|
||||||
|
|
||||||
#define S3_TX_PIN 3
|
|
||||||
#define S3_RX_PIN 2
|
|
||||||
|
|
||||||
static void uart_event_task(void *pvParameters);
|
static void uart_event_task(void *pvParameters);
|
||||||
void init_second_uart() {
|
void init_second_uart() {
|
||||||
uart_config_t uart_config = {
|
uart_config_t uart_config = {
|
||||||
@@ -47,7 +45,7 @@ void init_second_uart() {
|
|||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(uart_driver_install(1, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0));
|
ESP_ERROR_CHECK(uart_driver_install(1, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0));
|
||||||
ESP_ERROR_CHECK(uart_param_config(1, &uart_config));
|
ESP_ERROR_CHECK(uart_param_config(1, &uart_config));
|
||||||
ESP_ERROR_CHECK(uart_set_pin(1, S3_TX_PIN, S3_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
|
ESP_ERROR_CHECK(uart_set_pin(1, CONFIG_OEPL_HARDWARE_UART_TX, CONFIG_OEPL_HARDWARE_UART_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
|
||||||
|
|
||||||
xTaskCreate(uart_event_task, "uart_event_task", 16384, NULL, 12, NULL);
|
xTaskCreate(uart_event_task, "uart_event_task", 16384, NULL, 12, NULL);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
void init_second_uart();
|
void init_second_uart();
|
||||||
void uart_switch_speed(int baudrate);
|
void uart_switch_speed(int baudrate);
|
||||||
|
|
||||||
@@ -9,3 +11,15 @@ bool getRxCharSecond(uint8_t *newChar);
|
|||||||
void uart_printf(const char *format, ...);
|
void uart_printf(const char *format, ...);
|
||||||
|
|
||||||
#define pr uart_printf
|
#define pr uart_printf
|
||||||
|
|
||||||
|
#if defined(CONFIG_OEPL_HARDWARE_PROFILE_DEFAULT)
|
||||||
|
#define CONFIG_OEPL_HARDWARE_UART_TX 3
|
||||||
|
#define CONFIG_OEPL_HARDWARE_UART_RX 2
|
||||||
|
#elif defined(CONFIG_OEPL_HARDWARE_PROFILE_POE_AP)
|
||||||
|
#define CONFIG_OEPL_HARDWARE_UART_TX 5
|
||||||
|
#define CONFIG_OEPL_HARDWARE_UART_RX 18
|
||||||
|
#elif defined(CONFIG_OEPL_HARDWARE_PROFILE_CUSTOM)
|
||||||
|
#if !defined(CONFIG_OEPL_HARDWARE_UART_TX) || !defined(CONFIG_OEPL_HARDWARE_UART_RX)
|
||||||
|
#error "No UART TX / RX pins defined. Please check menuconfig"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
8
ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/sdkconfig.defaults
Normal file
8
ARM_Tag_FW/OpenEPaperLink_esp32_C6_AP/sdkconfig.defaults
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||||
|
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
|
||||||
|
#
|
||||||
|
CONFIG_IDF_TARGET="esp32c6"
|
||||||
|
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||||
|
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||||
|
CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE=y
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||||
4
ESP32_AP-Flasher/.vscode/settings.json
vendored
4
ESP32_AP-Flasher/.vscode/settings.json
vendored
@@ -50,6 +50,8 @@
|
|||||||
"stdexcept": "cpp",
|
"stdexcept": "cpp",
|
||||||
"streambuf": "cpp",
|
"streambuf": "cpp",
|
||||||
"cinttypes": "cpp",
|
"cinttypes": "cpp",
|
||||||
"typeinfo": "cpp"
|
"typeinfo": "cpp",
|
||||||
|
"chrono": "cpp",
|
||||||
|
"ratio": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
8
ESP32_AP-Flasher/16MB_partition table.csv
Normal file
8
ESP32_AP-Flasher/16MB_partition table.csv
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
nvs, data, nvs, 0x9000, 0x4000
|
||||||
|
otadata, data, ota, 0xD000, 0x2000
|
||||||
|
phy_init, data, phy, 0xF000, 0x1000
|
||||||
|
app0, app, ota_0, 0x10000, 0x200000
|
||||||
|
app1, app, ota_1, 0x210000, 0x200000
|
||||||
|
spiffs, data, spiffs, 0x410000, 0xBE0000
|
||||||
|
coredump, data, coredump, 0xFF0000, 0x10000
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -19,6 +19,7 @@ class SPIFFSEditor: public AsyncWebHandler {
|
|||||||
virtual void handleRequest(AsyncWebServerRequest *request) override final;
|
virtual void handleRequest(AsyncWebServerRequest *request) override final;
|
||||||
virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final;
|
virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final;
|
||||||
virtual bool isRequestHandlerTrivial() override final {return false;}
|
virtual bool isRequestHandlerTrivial() override final {return false;}
|
||||||
|
virtual String listFilesRecursively(String path, bool recursive = false);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ void setBrightness(int brightness);
|
|||||||
void updateBrightnessFromConfig();
|
void updateBrightnessFromConfig();
|
||||||
|
|
||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
|
extern CRGB rgbIdleColor;
|
||||||
|
extern uint16_t rgbIdlePeriod;
|
||||||
void shortBlink(CRGB cname);
|
void shortBlink(CRGB cname);
|
||||||
void showColorPattern(CRGB colorone, CRGB colortwo, CRGB colorthree);
|
void showColorPattern(CRGB colorone, CRGB colortwo, CRGB colorthree);
|
||||||
void rgbIdle();
|
void rgbIdle();
|
||||||
|
|||||||
@@ -24,10 +24,14 @@ struct imgParam {
|
|||||||
|
|
||||||
char segments[12];
|
char segments[12];
|
||||||
uint16_t symbols;
|
uint16_t symbols;
|
||||||
bool invert;
|
uint8_t invert;
|
||||||
|
|
||||||
uint8_t lut;
|
uint8_t lut;
|
||||||
uint8_t shortlut;
|
uint8_t shortlut;
|
||||||
|
|
||||||
|
bool preload;
|
||||||
|
uint8_t preloadtype;
|
||||||
|
uint8_t preloadlut;
|
||||||
};
|
};
|
||||||
|
|
||||||
void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams);
|
void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ extern bool prepareDataAvail(String& filename, uint8_t dataType, uint8_t dataTyp
|
|||||||
extern void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP);
|
extern void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP);
|
||||||
extern void processXferComplete(struct espXferComplete* xfc, bool local);
|
extern void processXferComplete(struct espXferComplete* xfc, bool local);
|
||||||
extern void processXferTimeout(struct espXferComplete* xfc, bool local);
|
extern void processXferTimeout(struct espXferComplete* xfc, bool local);
|
||||||
extern void processDataReq(struct espAvailDataReq* adr, bool local);
|
extern void processDataReq(struct espAvailDataReq* adr, bool local, IPAddress remoteIP = IPAddress(0, 0, 0, 0));
|
||||||
extern void processTagReturnData(struct espTagReturnData* trd, uint8_t len, bool local);
|
extern void processTagReturnData(struct espTagReturnData* trd, uint8_t len, bool local);
|
||||||
|
|
||||||
extern bool sendTagCommand(const uint8_t* dst, uint8_t cmd, bool local, const uint8_t* payload = nullptr);
|
extern bool sendTagCommand(const uint8_t* dst, uint8_t cmd, bool local, const uint8_t* payload = nullptr);
|
||||||
|
|||||||
@@ -33,4 +33,5 @@ void APEnterEarlyReset();
|
|||||||
bool sendChannelPower(struct espSetChannelPower* scp);
|
bool sendChannelPower(struct espSetChannelPower* scp);
|
||||||
void rxSerialTask2(void* parameter);
|
void rxSerialTask2(void* parameter);
|
||||||
void APTagReset();
|
void APTagReset();
|
||||||
bool bringAPOnline();
|
bool bringAPOnline();
|
||||||
|
void setAPstate(bool isOnline, uint8_t state);
|
||||||
@@ -27,12 +27,13 @@ class DynStorage {
|
|||||||
void begin();
|
void begin();
|
||||||
void end();
|
void end();
|
||||||
void listFiles();
|
void listFiles();
|
||||||
size_t freeSpace();
|
uint64_t freeSpace();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isInited;
|
bool isInited;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern SemaphoreHandle_t fsMutex;
|
||||||
extern DynStorage Storage;
|
extern DynStorage Storage;
|
||||||
extern fs::FS *contentFS;
|
extern fs::FS *contentFS;
|
||||||
extern void copyFile(File in, File out);
|
extern void copyFile(File in, File out);
|
||||||
|
|||||||
@@ -12,5 +12,5 @@
|
|||||||
|
|
||||||
void initTime(void* parameter);
|
void initTime(void* parameter);
|
||||||
void logLine(const char* buffer);
|
void logLine(const char* buffer);
|
||||||
void logLine(String text);
|
void logLine(const String& text);
|
||||||
void logStartUp();
|
void logStartUp();
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
class tagRecord {
|
class tagRecord {
|
||||||
public:
|
public:
|
||||||
tagRecord() : mac{0}, alias(""), lastseen(0), nextupdate(0), contentMode(0), pending(false), md5{0}, md5pending{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0) {}
|
tagRecord() : mac{0}, alias(""), lastseen(0), nextupdate(0), contentMode(0), pending(false), md5{0}, md5pending{0}, expectedNextCheckin(0), modeConfigJson(""), LQI(0), RSSI(0), temperature(0), batteryMv(0), hwType(0), wakeupReason(0), capabilities(0), lastfullupdate(0), isExternal(false), apIp(IPAddress(0, 0, 0, 0)), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0), tagSoftwareVersion(0), currentChannel(0), dataType(0), filename(""), data(nullptr), len(0), invert(0) {}
|
||||||
|
|
||||||
uint8_t mac[8];
|
uint8_t mac[8];
|
||||||
String alias;
|
String alias;
|
||||||
@@ -42,12 +42,14 @@ class tagRecord {
|
|||||||
uint8_t capabilities;
|
uint8_t capabilities;
|
||||||
uint32_t lastfullupdate;
|
uint32_t lastfullupdate;
|
||||||
bool isExternal;
|
bool isExternal;
|
||||||
|
IPAddress apIp;
|
||||||
uint16_t pendingIdle;
|
uint16_t pendingIdle;
|
||||||
bool hasCustomLUT;
|
bool hasCustomLUT;
|
||||||
uint8_t rotate;
|
uint8_t rotate;
|
||||||
uint8_t lut;
|
uint8_t lut;
|
||||||
uint16_t tagSoftwareVersion;
|
uint16_t tagSoftwareVersion;
|
||||||
uint8_t currentChannel;
|
uint8_t currentChannel;
|
||||||
|
uint8_t invert;
|
||||||
|
|
||||||
uint8_t dataType;
|
uint8_t dataType;
|
||||||
String filename;
|
String filename;
|
||||||
@@ -61,6 +63,7 @@ struct Config {
|
|||||||
uint8_t channel;
|
uint8_t channel;
|
||||||
char alias[32];
|
char alias[32];
|
||||||
int16_t led;
|
int16_t led;
|
||||||
|
uint8_t tft;
|
||||||
uint8_t language;
|
uint8_t language;
|
||||||
uint8_t maxsleep;
|
uint8_t maxsleep;
|
||||||
uint8_t stopsleep;
|
uint8_t stopsleep;
|
||||||
@@ -70,6 +73,8 @@ struct Config {
|
|||||||
char timeZone[52];
|
char timeZone[52];
|
||||||
uint8_t sleepTime1;
|
uint8_t sleepTime1;
|
||||||
uint8_t sleepTime2;
|
uint8_t sleepTime2;
|
||||||
|
String repo;
|
||||||
|
String env;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HwType {
|
struct HwType {
|
||||||
@@ -85,12 +90,10 @@ struct varStruct {
|
|||||||
bool changed;
|
bool changed;
|
||||||
};
|
};
|
||||||
|
|
||||||
// extern SemaphoreHandle_t tagDBOwner;
|
|
||||||
extern Config config;
|
extern Config config;
|
||||||
extern std::vector<tagRecord*> tagDB;
|
extern std::vector<tagRecord*> tagDB;
|
||||||
extern std::unordered_map<int, HwType> hwtype;
|
extern std::unordered_map<int, HwType> hwtype;
|
||||||
extern std::unordered_map<std::string, varStruct> varDB;
|
extern std::unordered_map<std::string, varStruct> varDB;
|
||||||
extern DynamicJsonDocument APconfig;
|
|
||||||
extern String tagDBtoJson(const uint8_t mac[8] = nullptr, uint8_t startPos = 0);
|
extern String tagDBtoJson(const uint8_t mac[8] = nullptr, uint8_t startPos = 0);
|
||||||
extern bool deleteRecord(const uint8_t mac[8]);
|
extern bool deleteRecord(const uint8_t mac[8]);
|
||||||
extern void fillNode(JsonObject& tag, const tagRecord* taginfo);
|
extern void fillNode(JsonObject& tag, const tagRecord* taginfo);
|
||||||
@@ -105,7 +108,14 @@ extern void clearPending(tagRecord* taginfo);
|
|||||||
extern void initAPconfig();
|
extern void initAPconfig();
|
||||||
extern void saveAPconfig();
|
extern void saveAPconfig();
|
||||||
extern HwType getHwType(const uint8_t id);
|
extern HwType getHwType(const uint8_t id);
|
||||||
extern bool setVarDB(const std::string& key, const String& value);
|
/// @brief Update a variable with the given key and value
|
||||||
|
///
|
||||||
|
/// @param key Variable key
|
||||||
|
/// @param value Variable value
|
||||||
|
/// @param notify Should the change be notified (true, default) or not (false)
|
||||||
|
/// @return true If variable was created/updated
|
||||||
|
/// @return false If not
|
||||||
|
extern bool setVarDB(const std::string& key, const String& value, const bool notify = true);
|
||||||
extern void cleanupCurrent();
|
extern void cleanupCurrent();
|
||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|||||||
140
ESP32_AP-Flasher/include/tagdata.h
Normal file
140
ESP32_AP-Flasher/include/tagdata.h
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
/// @file tagdata.h
|
||||||
|
/// @author Moritz Wirger (contact@wirmo.de)
|
||||||
|
/// @brief Custom tag data parser and helpers
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "storage.h"
|
||||||
|
#include "system.h"
|
||||||
|
#include "web.h"
|
||||||
|
|
||||||
|
/// @brief Functions for custom tag data parser
|
||||||
|
namespace TagData {
|
||||||
|
|
||||||
|
/// @brief All available data types
|
||||||
|
enum class Type {
|
||||||
|
/// @brief Signed integer type
|
||||||
|
INT,
|
||||||
|
/// @brief Unsigned integer type
|
||||||
|
UINT,
|
||||||
|
/// @brief Float type
|
||||||
|
FLOAT,
|
||||||
|
/// @brief String type
|
||||||
|
STRING,
|
||||||
|
|
||||||
|
/// @brief Not a type, just a helper to determine max type
|
||||||
|
MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Field that can be parsed
|
||||||
|
struct Field {
|
||||||
|
/// @brief Field name
|
||||||
|
String name;
|
||||||
|
/// @brief Field type
|
||||||
|
Type type;
|
||||||
|
/// @brief Field byte length
|
||||||
|
uint8_t length;
|
||||||
|
/// @brief Number of decimals numeric types
|
||||||
|
uint8_t decimals;
|
||||||
|
/// @brief Optional multiplication
|
||||||
|
std::optional<double> mult;
|
||||||
|
|
||||||
|
Field(const String &name, const Type type, const uint8_t length, uint8_t decimals = 0, std::optional<double> mult = std::nullopt)
|
||||||
|
: name(name), type(type), length(length), decimals(decimals), mult(mult) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Parser for parsing custom tag data
|
||||||
|
struct Parser {
|
||||||
|
/// @brief Parser name
|
||||||
|
String name;
|
||||||
|
/// @brief Parsed fields
|
||||||
|
std::vector<Field> fields = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Maps parser id to parser
|
||||||
|
extern std::unordered_map<size_t, Parser> parsers;
|
||||||
|
|
||||||
|
/// @brief Load all parsers from the given json file
|
||||||
|
/// @param filename File name
|
||||||
|
extern void loadParsers(const String &filename);
|
||||||
|
|
||||||
|
/// @brief Parse the incoming custom message
|
||||||
|
/// @param src Source mac address
|
||||||
|
/// @param id Message identifier
|
||||||
|
/// @param data Payload
|
||||||
|
/// @param len Payload length
|
||||||
|
extern void parse(const uint8_t src[8], const size_t id, const uint8_t *data, const uint8_t len);
|
||||||
|
|
||||||
|
/// @brief Convert the given byte array @ref data with given @ref length to an unsigned integer
|
||||||
|
///
|
||||||
|
/// Will also convert non standard integer sizes (e.g. 3, 5, 6, and 7 bytes)
|
||||||
|
/// @tparam T Unsigned integer type
|
||||||
|
/// @param data Byte array
|
||||||
|
/// @param length Length of byte array
|
||||||
|
/// @return Unsigned integer
|
||||||
|
template <typename T, std::enable_if_t<std::is_unsigned_v<T> && std::is_integral_v<T>, bool> = true>
|
||||||
|
inline T bytesTo(const uint8_t *data, const uint8_t length) {
|
||||||
|
T value = 0;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
value |= (data[i] & 0xFF) << (8 * i);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Convert the given byte array @ref data with given @ref length to a signed integer
|
||||||
|
///
|
||||||
|
/// Will also convert non standard integer sizes (e.g. 3, 5, 6, and 7 bytes)
|
||||||
|
/// @tparam T Signed integer type
|
||||||
|
/// @param data Byte array
|
||||||
|
/// @param length Length of byte array
|
||||||
|
/// @return Signed integer
|
||||||
|
template <typename T, std::enable_if_t<std::is_signed_v<T> && std::is_integral_v<T>, bool> = true>
|
||||||
|
inline T bytesTo(const uint8_t *data, const uint8_t length) {
|
||||||
|
T value = 0;
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
value |= (data[i] & 0xFF) << (8 * i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If data is smaller than T and last byte is negative set all upper bytes negative
|
||||||
|
if (length < sizeof(T) && (data[length - 1] & 0x80) != 0) {
|
||||||
|
value |= ~((1 << (length * 8)) - 1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Convert the given byte array to a float/double
|
||||||
|
/// @param data Byte array, should be at least 4/8 bytes long
|
||||||
|
/// @param length Length of byte array
|
||||||
|
/// @return float/double
|
||||||
|
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
|
||||||
|
inline T bytesTo(const uint8_t *data, const uint8_t length) {
|
||||||
|
const size_t len = sizeof(T) < length ? sizeof(T) : length;
|
||||||
|
T value;
|
||||||
|
memcpy(&value, data, len);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Convert the given byte array to a string
|
||||||
|
/// @param data Byte array representing a string
|
||||||
|
/// @param length Length of byte array
|
||||||
|
/// @return String
|
||||||
|
template <typename T, std::enable_if_t<std::is_same_v<T, String>, bool> = true>
|
||||||
|
inline T bytesTo(const uint8_t *data, int length) {
|
||||||
|
return T(data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Convert the given byte array to a string
|
||||||
|
/// @param data Byte array representing a string
|
||||||
|
/// @param length Length of byte array
|
||||||
|
/// @return std::string
|
||||||
|
template <typename T, std::enable_if_t<std::is_same_v<T, std::string>, bool> = true>
|
||||||
|
inline T bytesTo(const uint8_t *data, int length) {
|
||||||
|
return T(data, data + length);
|
||||||
|
}
|
||||||
|
} // namespace TagData
|
||||||
@@ -4,8 +4,10 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
|
|
||||||
|
#include "system.h"
|
||||||
#include "web.h"
|
#include "web.h"
|
||||||
|
|
||||||
|
/// @brief Different utility functions
|
||||||
namespace util {
|
namespace util {
|
||||||
|
|
||||||
/// @brief Can be used to wrap a stream and see what's going on
|
/// @brief Can be used to wrap a stream and see what's going on
|
||||||
@@ -72,6 +74,7 @@ static void printLargestFreeBlock() {
|
|||||||
static bool httpGetJson(String &url, JsonDocument &json, const uint16_t timeout, JsonDocument *filter = nullptr) //, const followRedirects_t redirects = followRedirects_t::HTTPC_DISABLE_FOLLOW_REDIRECTS)
|
static bool httpGetJson(String &url, JsonDocument &json, const uint16_t timeout, JsonDocument *filter = nullptr) //, const followRedirects_t redirects = followRedirects_t::HTTPC_DISABLE_FOLLOW_REDIRECTS)
|
||||||
{
|
{
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
|
logLine("http httpGetJson " + url);
|
||||||
http.begin(url);
|
http.begin(url);
|
||||||
http.setTimeout(timeout);
|
http.setTimeout(timeout);
|
||||||
// http.setFollowRedirects(redirects);
|
// http.setFollowRedirects(redirects);
|
||||||
@@ -100,7 +103,7 @@ static bool httpGetJson(String &url, JsonDocument &json, const uint16_t timeout,
|
|||||||
///
|
///
|
||||||
/// @param str String to check
|
/// @param str String to check
|
||||||
/// @return True if empty or null, false if not
|
/// @return True if empty or null, false if not
|
||||||
static inline bool isEmptyOrNull(const String &str) {
|
inline bool isEmptyOrNull(const String &str) {
|
||||||
return str.isEmpty() || str == "null";
|
return str.isEmpty() || str == "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,5 +126,68 @@ static bool isSleeping(int sleeptime1, int sleeptime2) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Get the time_t for midnight
|
||||||
|
/// @return time_t for midnight
|
||||||
|
inline time_t getMidnightTime() {
|
||||||
|
struct tm time_info;
|
||||||
|
getLocalTime(&time_info);
|
||||||
|
time_info.tm_hour = time_info.tm_min = time_info.tm_sec = 0;
|
||||||
|
time_info.tm_mday++;
|
||||||
|
return mktime(&time_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Timer for kind of scheduling things
|
||||||
|
class Timer {
|
||||||
|
public:
|
||||||
|
/// @brief Construct a timer
|
||||||
|
/// @param interval Interval in ms at which @ref doRun() returns true
|
||||||
|
/// @param delay Delay in ms until first execution to defer start
|
||||||
|
Timer(const unsigned long interval, const unsigned long delay = 0) : m_interval(interval), m_nextMillis(millis() + delay) {}
|
||||||
|
|
||||||
|
/// @brief Change the interval
|
||||||
|
/// @param interval New interval in ms
|
||||||
|
void setInterval(const unsigned long interval) {
|
||||||
|
m_interval = interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Check if interval is met
|
||||||
|
/// @param currentMillis Optionally provide the current time in millis
|
||||||
|
/// @return True if interval is met, false if not
|
||||||
|
bool doRun(const unsigned long currentMillis = millis()) {
|
||||||
|
if (currentMillis >= m_nextMillis) {
|
||||||
|
m_nextMillis = currentMillis + m_interval;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// @brief Timer interval in ms
|
||||||
|
unsigned long m_interval;
|
||||||
|
/// @brief Next timeer interval in ms
|
||||||
|
unsigned long m_nextMillis;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Create a String from format
|
||||||
|
/// @param buffer Buffer to use for sprintf
|
||||||
|
/// @param format String format
|
||||||
|
/// @return String
|
||||||
|
template <size_t bufSize>
|
||||||
|
inline String formatString(char buffer[bufSize], const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
const size_t size = vsnprintf(buffer, bufSize, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return String(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|
||||||
|
/// @brief Converts seconds to milliseconds
|
||||||
|
#define seconds(s) s * 1000
|
||||||
|
/// @brief Converts minutes to milliseconds
|
||||||
|
#define minutes(m) seconds(m * 60)
|
||||||
|
/// @brief Converts hours to milliseconds
|
||||||
|
#define hours(m) minutes(m * 60)
|
||||||
|
|||||||
@@ -6,13 +6,12 @@
|
|||||||
void init_web();
|
void init_web();
|
||||||
void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
||||||
void doJsonUpload(AsyncWebServerRequest *request);
|
void doJsonUpload(AsyncWebServerRequest *request);
|
||||||
extern void networkProcess(void *parameter);
|
void wsLog(const String &text);
|
||||||
void wsLog(String text);
|
void wsErr(const String &text);
|
||||||
void wsErr(String text);
|
|
||||||
void wsSendTaginfo(const uint8_t *mac, uint8_t syncMode);
|
void wsSendTaginfo(const uint8_t *mac, uint8_t syncMode);
|
||||||
void wsSendSysteminfo();
|
void wsSendSysteminfo();
|
||||||
void wsSendAPitem(struct APlist *apitem);
|
void wsSendAPitem(struct APlist *apitem);
|
||||||
void wsSerial(String text);
|
void wsSerial(const String &text);
|
||||||
uint8_t wsClientCount();
|
uint8_t wsClientCount();
|
||||||
|
|
||||||
extern AsyncWebSocket ws;
|
extern AsyncWebSocket ws;
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ board_build.filesystem = littlefs
|
|||||||
monitor_filters = esp32_exception_decoder
|
monitor_filters = esp32_exception_decoder
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
board_build.f_cpu = 240000000L
|
board_build.f_cpu = 240000000L
|
||||||
|
build_unflags =
|
||||||
|
-std=gnu++11
|
||||||
build_flags =
|
build_flags =
|
||||||
|
-std=gnu++17
|
||||||
-D BUILD_ENV_NAME=$PIOENV
|
-D BUILD_ENV_NAME=$PIOENV
|
||||||
-D BUILD_TIME=$UNIX_TIME
|
-D BUILD_TIME=$UNIX_TIME
|
||||||
-D USER_SETUP_LOADED
|
-D USER_SETUP_LOADED
|
||||||
@@ -42,8 +45,10 @@ platform = https://github.com/platformio/platform-espressif32.git
|
|||||||
board=lolin_s2_mini
|
board=lolin_s2_mini
|
||||||
board_build.partitions = default.csv
|
board_build.partitions = default.csv
|
||||||
build_unflags =
|
build_unflags =
|
||||||
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
-std=gnu++11
|
||||||
|
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
||||||
build_flags =
|
build_flags =
|
||||||
|
-std=gnu++17
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
-D OPENEPAPERLINK_MINI_AP_PCB
|
-D OPENEPAPERLINK_MINI_AP_PCB
|
||||||
-D ARDUINO_USB_MODE=0
|
-D ARDUINO_USB_MODE=0
|
||||||
@@ -64,7 +69,7 @@ build_flags =
|
|||||||
-D FLASHER_LED=15
|
-D FLASHER_LED=15
|
||||||
-D FLASHER_RGB_LED=33
|
-D FLASHER_RGB_LED=33
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
||||||
board_build.psram_type=qspi_opi
|
board_build.psram_type=qspi_opi
|
||||||
board_upload.maximum_size = 4194304
|
board_upload.maximum_size = 4194304
|
||||||
board_upload.maximum_ram_size = 327680
|
board_upload.maximum_ram_size = 327680
|
||||||
@@ -79,8 +84,10 @@ platform = https://github.com/platformio/platform-espressif32.git
|
|||||||
board=lolin_s2_mini
|
board=lolin_s2_mini
|
||||||
board_build.partitions = default.csv
|
board_build.partitions = default.csv
|
||||||
build_unflags =
|
build_unflags =
|
||||||
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
-std=gnu++11
|
||||||
|
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
||||||
build_flags =
|
build_flags =
|
||||||
|
-std=gnu++17
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
-D OPENEPAPERLINK_NANO_AP_PCB
|
-D OPENEPAPERLINK_NANO_AP_PCB
|
||||||
-D ARDUINO_USB_MODE=0
|
-D ARDUINO_USB_MODE=0
|
||||||
@@ -99,7 +106,7 @@ build_flags =
|
|||||||
-D FLASHER_LED=15
|
-D FLASHER_LED=15
|
||||||
-D FLASHER_RGB_LED=-1
|
-D FLASHER_RGB_LED=-1
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
||||||
board_build.psram_type=qspi_opi
|
board_build.psram_type=qspi_opi
|
||||||
board_upload.maximum_size = 4194304
|
board_upload.maximum_size = 4194304
|
||||||
board_upload.maximum_ram_size = 327680
|
board_upload.maximum_ram_size = 327680
|
||||||
@@ -114,9 +121,11 @@ platform = https://github.com/platformio/platform-espressif32.git
|
|||||||
board = esp32-s3-devkitc-1
|
board = esp32-s3-devkitc-1
|
||||||
board_build.partitions = default_16MB.csv
|
board_build.partitions = default_16MB.csv
|
||||||
build_unflags =
|
build_unflags =
|
||||||
-D ARDUINO_USB_MODE=1
|
-std=gnu++11
|
||||||
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
-D ARDUINO_USB_MODE=1
|
||||||
|
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
||||||
build_flags =
|
build_flags =
|
||||||
|
-std=gnu++17
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
-D OPENEPAPERLINK_PCB
|
-D OPENEPAPERLINK_PCB
|
||||||
-D ARDUINO_USB_MODE=0
|
-D ARDUINO_USB_MODE=0
|
||||||
@@ -158,7 +167,7 @@ build_flags =
|
|||||||
-D FLASHER_LED=21
|
-D FLASHER_LED=21
|
||||||
-D FLASHER_RGB_LED=48
|
-D FLASHER_RGB_LED=48
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
+<*>-<espflasher.cpp>
|
+<*>-<espflasher.cpp>
|
||||||
board_build.flash_mode=qio
|
board_build.flash_mode=qio
|
||||||
board_build.arduino.memory_type = qio_opi
|
board_build.arduino.memory_type = qio_opi
|
||||||
board_build.psram_type=qspi_opi
|
board_build.psram_type=qspi_opi
|
||||||
@@ -173,7 +182,10 @@ board_upload.flash_size = 16MB
|
|||||||
[env:Simple_AP]
|
[env:Simple_AP]
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
board_build.partitions = default.csv
|
board_build.partitions = default.csv
|
||||||
|
build_unflags =
|
||||||
|
-std=gnu++11
|
||||||
build_flags =
|
build_flags =
|
||||||
|
-std=gnu++17
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
-D CORE_DEBUG_LEVEL=0
|
-D CORE_DEBUG_LEVEL=0
|
||||||
-D SIMPLE_AP
|
-D SIMPLE_AP
|
||||||
@@ -188,7 +200,7 @@ build_flags =
|
|||||||
-D FLASHER_AP_RXD=16
|
-D FLASHER_AP_RXD=16
|
||||||
-D FLASHER_LED=22
|
-D FLASHER_LED=22
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
||||||
|
|
||||||
; ----------------------------------------------------------------------------------------
|
; ----------------------------------------------------------------------------------------
|
||||||
; !!! this configuration expects an wemos_d1_mini32
|
; !!! this configuration expects an wemos_d1_mini32
|
||||||
@@ -197,7 +209,10 @@ build_src_filter =
|
|||||||
[env:Wemos_d1_mini32_AP]
|
[env:Wemos_d1_mini32_AP]
|
||||||
board = wemos_d1_mini32
|
board = wemos_d1_mini32
|
||||||
board_build.partitions = default.csv
|
board_build.partitions = default.csv
|
||||||
|
build_unflags =
|
||||||
|
-std=gnu++11
|
||||||
build_flags =
|
build_flags =
|
||||||
|
-std=gnu++17
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
-D CORE_DEBUG_LEVEL=0
|
-D CORE_DEBUG_LEVEL=0
|
||||||
|
|
||||||
@@ -214,7 +229,7 @@ build_flags =
|
|||||||
-D FLASHER_AP_RXD=17
|
-D FLASHER_AP_RXD=17
|
||||||
-D FLASHER_LED=22
|
-D FLASHER_LED=22
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
||||||
|
|
||||||
; ----------------------------------------------------------------------------------------
|
; ----------------------------------------------------------------------------------------
|
||||||
; !!! this configuration expects an m5stack esp32
|
; !!! this configuration expects an m5stack esp32
|
||||||
@@ -224,7 +239,10 @@ build_src_filter =
|
|||||||
platform = espressif32
|
platform = espressif32
|
||||||
board = m5stack-core-esp32
|
board = m5stack-core-esp32
|
||||||
board_build.partitions = esp32_sdcard.csv
|
board_build.partitions = esp32_sdcard.csv
|
||||||
|
build_unflags =
|
||||||
|
-std=gnu++11
|
||||||
build_flags =
|
build_flags =
|
||||||
|
-std=gnu++17
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
-D CORE_DEBUG_LEVEL=0
|
-D CORE_DEBUG_LEVEL=0
|
||||||
|
|
||||||
@@ -251,7 +269,7 @@ build_flags =
|
|||||||
-D ILI9341_DRIVER
|
-D ILI9341_DRIVER
|
||||||
-D SMOOTH_FONT
|
-D SMOOTH_FONT
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
||||||
; ----------------------------------------------------------------------------------------
|
; ----------------------------------------------------------------------------------------
|
||||||
; !!! this configuration expects an ESP32-S3 16MB Flash 8MB RAM
|
; !!! this configuration expects an ESP32-S3 16MB Flash 8MB RAM
|
||||||
;
|
;
|
||||||
@@ -260,12 +278,14 @@ build_src_filter =
|
|||||||
board = esp32-s3-devkitc-1
|
board = esp32-s3-devkitc-1
|
||||||
board_build.partitions = large_spiffs_16MB.csv
|
board_build.partitions = large_spiffs_16MB.csv
|
||||||
build_unflags =
|
build_unflags =
|
||||||
|
-std=gnu++11
|
||||||
-D ARDUINO_USB_MODE=1
|
-D ARDUINO_USB_MODE=1
|
||||||
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
||||||
-D ILI9341_DRIVER
|
-D ILI9341_DRIVER
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
build_flags =
|
build_flags =
|
||||||
|
-std=gnu++17
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
-D YELLOW_IPS_AP
|
-D YELLOW_IPS_AP
|
||||||
-D CORE_DEBUG_LEVEL=0
|
-D CORE_DEBUG_LEVEL=0
|
||||||
@@ -307,7 +327,7 @@ build_flags =
|
|||||||
-D SERIAL_FLASHER_BOOT_HOLD_TIME_MS=50
|
-D SERIAL_FLASHER_BOOT_HOLD_TIME_MS=50
|
||||||
-D SERIAL_FLASHER_RESET_HOLD_TIME_MS=100
|
-D SERIAL_FLASHER_RESET_HOLD_TIME_MS=100
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
+<*>-<usbflasher.cpp>-<swd.cpp>
|
+<*>-<usbflasher.cpp>-<swd.cpp>
|
||||||
board_build.flash_mode=qio
|
board_build.flash_mode=qio
|
||||||
board_build.arduino.memory_type = qio_opi
|
board_build.arduino.memory_type = qio_opi
|
||||||
board_build.psram_type=qspi_opi
|
board_build.psram_type=qspi_opi
|
||||||
@@ -321,7 +341,10 @@ board_upload.flash_size = 16MB
|
|||||||
[env:Sonoff_zb_bridge_P_AP]
|
[env:Sonoff_zb_bridge_P_AP]
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
board_build.partitions = default.csv
|
board_build.partitions = default.csv
|
||||||
|
build_unflags =
|
||||||
|
-std=gnu++11
|
||||||
build_flags =
|
build_flags =
|
||||||
|
-std=gnu++17
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
-D CORE_DEBUG_LEVEL=0
|
-D CORE_DEBUG_LEVEL=0
|
||||||
|
|
||||||
@@ -340,7 +363,7 @@ build_flags =
|
|||||||
-D FLASHER_AP_RXD=23
|
-D FLASHER_AP_RXD=23
|
||||||
-D FLASHER_LED=2
|
-D FLASHER_LED=2
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
||||||
board_build.psram_type=qspi_opi
|
board_build.psram_type=qspi_opi
|
||||||
board_upload.maximum_size = 4194304
|
board_upload.maximum_size = 4194304
|
||||||
board_upload.maximum_ram_size = 327680
|
board_upload.maximum_ram_size = 327680
|
||||||
@@ -354,8 +377,10 @@ platform = https://github.com/platformio/platform-espressif32.git
|
|||||||
board=lolin_s2_mini
|
board=lolin_s2_mini
|
||||||
board_build.partitions = default.csv
|
board_build.partitions = default.csv
|
||||||
build_unflags =
|
build_unflags =
|
||||||
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
-std=gnu++11
|
||||||
|
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
||||||
build_flags =
|
build_flags =
|
||||||
|
-std=gnu++17
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
-D OPENEPAPERLINK_MINI_AP_PCB
|
-D OPENEPAPERLINK_MINI_AP_PCB
|
||||||
-D ARDUINO_USB_MODE=0
|
-D ARDUINO_USB_MODE=0
|
||||||
@@ -375,7 +400,7 @@ build_flags =
|
|||||||
-D FLASHER_LED=2
|
-D FLASHER_LED=2
|
||||||
-D FLASHER_RGB_LED=-1
|
-D FLASHER_RGB_LED=-1
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
||||||
board_build.psram_type=qspi_opi
|
board_build.psram_type=qspi_opi
|
||||||
board_upload.maximum_size = 4194304
|
board_upload.maximum_size = 4194304
|
||||||
board_upload.maximum_ram_size = 327680
|
board_upload.maximum_ram_size = 327680
|
||||||
@@ -389,9 +414,11 @@ board_upload.flash_size = 4MB
|
|||||||
board = esp32-s3-devkitc-1
|
board = esp32-s3-devkitc-1
|
||||||
board_build.partitions = 32MB_partition table.csv
|
board_build.partitions = 32MB_partition table.csv
|
||||||
build_unflags =
|
build_unflags =
|
||||||
-D ARDUINO_USB_MODE=1
|
-std=gnu++11
|
||||||
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
-D ARDUINO_USB_MODE=1
|
||||||
|
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
||||||
build_flags =
|
build_flags =
|
||||||
|
-std=gnu++17
|
||||||
${env.build_flags}
|
${env.build_flags}
|
||||||
-D OutdoorAP
|
-D OutdoorAP
|
||||||
-D HAS_RGB_LED
|
-D HAS_RGB_LED
|
||||||
@@ -414,7 +441,7 @@ build_flags =
|
|||||||
-D FLASHER_LED=21
|
-D FLASHER_LED=21
|
||||||
-D FLASHER_RGB_LED=38
|
-D FLASHER_RGB_LED=38
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
||||||
board_build.flash_mode=opi
|
board_build.flash_mode=opi
|
||||||
board_build.arduino.memory_type = opi_opi
|
board_build.arduino.memory_type = opi_opi
|
||||||
board_build.psram_type=qspi_opi
|
board_build.psram_type=qspi_opi
|
||||||
@@ -422,3 +449,53 @@ board_upload.maximum_size = 16777216
|
|||||||
board_upload.maximum_ram_size = 327680
|
board_upload.maximum_ram_size = 327680
|
||||||
board_upload.flash_size = 32MB
|
board_upload.flash_size = 32MB
|
||||||
#upload_flags = --no-stub
|
#upload_flags = --no-stub
|
||||||
|
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------------------------------
|
||||||
|
; !!! this configuration expects the PoE-AP and is work in progress right now !!!
|
||||||
|
; ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[env:OpenEPaperLink_PoE_AP]
|
||||||
|
platform = https://github.com/platformio/platform-espressif32.git
|
||||||
|
board=esp32dev
|
||||||
|
board_build.partitions = 16MB_partition table.csv
|
||||||
|
build_unflags =
|
||||||
|
-D CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y
|
||||||
|
-std=gnu++11
|
||||||
|
lib_deps =
|
||||||
|
${env.lib_deps}
|
||||||
|
build_flags =
|
||||||
|
-std=gnu++17
|
||||||
|
${env.build_flags}
|
||||||
|
; -D CORE_DEBUG_LEVEL=5
|
||||||
|
-D OPENEPAPERLINK_POE_AP_PCB
|
||||||
|
-D CONFIG_SPIRAM_USE_MALLOC=1
|
||||||
|
-D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
|
||||||
|
-D HAS_RGB_LED
|
||||||
|
-D BOARD_HAS_PSRAM
|
||||||
|
-mfix-esp32-psram-cache-issue
|
||||||
|
-D HAS_SDCARD
|
||||||
|
-D POWER_NO_SOFT_POWER
|
||||||
|
-D FLASHER_AP_SS=-1
|
||||||
|
-D FLASHER_AP_CLK=-1
|
||||||
|
-D FLASHER_AP_MOSI=-1
|
||||||
|
-D FLASHER_AP_MISO=-1
|
||||||
|
-D FLASHER_AP_RESET=-1
|
||||||
|
-D FLASHER_AP_POWER={-1} ;this board has no soft power control
|
||||||
|
-D FLASHER_AP_TXD=15
|
||||||
|
-D FLASHER_AP_RXD=4
|
||||||
|
-D FLASHER_AP_TEST=-1
|
||||||
|
-D FLASHER_LED=-1
|
||||||
|
-D FLASHER_RGB_LED=5
|
||||||
|
-D SD_CARD_CLK=13
|
||||||
|
-D SD_CARD_MISO=36
|
||||||
|
-D SD_CARD_MOSI=14
|
||||||
|
-D SD_CARD_SS=12
|
||||||
|
build_src_filter =
|
||||||
|
+<*>-<usbflasher.cpp>-<swd.cpp>-<espflasher.cpp>
|
||||||
|
|
||||||
|
board_build.flash_mode=qio
|
||||||
|
|
||||||
|
board_upload.maximum_size = 16777216
|
||||||
|
board_upload.maximum_ram_size = 327680
|
||||||
|
board_upload.flash_size = 16MB
|
||||||
|
|||||||
@@ -43,6 +43,37 @@ bool SPIFFSEditor::canHandle(AsyncWebServerRequest *request) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String SPIFFSEditor::listFilesRecursively(String path, bool recursive) {
|
||||||
|
if (recursive && (path == "//www" || path == "//tagtypes" || path == "//current")) return "";
|
||||||
|
|
||||||
|
File dir = _fs.open(path);
|
||||||
|
String output = "";
|
||||||
|
|
||||||
|
File file = dir.openNextFile();
|
||||||
|
bool isFirstFile = true;
|
||||||
|
|
||||||
|
while (file) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
if (recursive) {
|
||||||
|
String subDirPath = String(path + "/" + file.name());
|
||||||
|
String subDirOutput = listFilesRecursively(subDirPath, true);
|
||||||
|
output += subDirOutput;
|
||||||
|
} else {
|
||||||
|
output += ",{\"type\":\"dir\",\"name\":\"" + String(file.name()) + "\"}";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (recursive) {
|
||||||
|
output += ",{\"type\":\"file\",\"name\":\"" + path.substring(2) + "/" + String(file.name()) + "\",\"size\":" + file.size() + "}";
|
||||||
|
} else {
|
||||||
|
output += ",{\"type\":\"file\",\"name\":\"" + String(file.name()) + "\",\"size\":" + file.size() + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file = dir.openNextFile();
|
||||||
|
}
|
||||||
|
dir.close();
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request) {
|
void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request) {
|
||||||
if (_username.length() && _password.length() && !request->authenticate(_username.c_str(), _password.c_str())) {
|
if (_username.length() && _password.length() && !request->authenticate(_username.c_str(), _password.c_str())) {
|
||||||
return request->requestAuthentication();
|
return request->requestAuthentication();
|
||||||
@@ -51,23 +82,7 @@ void SPIFFSEditor::handleRequest(AsyncWebServerRequest *request) {
|
|||||||
if (request->method() == HTTP_GET) {
|
if (request->method() == HTTP_GET) {
|
||||||
if (request->hasParam("list")) {
|
if (request->hasParam("list")) {
|
||||||
const String path = request->getParam("list")->value();
|
const String path = request->getParam("list")->value();
|
||||||
|
String output = "[" + listFilesRecursively(path, request->hasParam("recursive")).substring(1) + "]";
|
||||||
File dir = _fs.open(path);
|
|
||||||
String output = "[";
|
|
||||||
File file = dir.openNextFile();
|
|
||||||
while (file) {
|
|
||||||
if (output != "[") {
|
|
||||||
output += ',';
|
|
||||||
}
|
|
||||||
if (file.isDirectory()) {
|
|
||||||
output += "{\"type\":\"dir\",\"name\":\"" + String(file.name()) + "\",\"size\":" + file.size() + "}";
|
|
||||||
} else {
|
|
||||||
output += "{\"type\":\"file\",\"name\":\"" + String(file.name()) + "\",\"size\":" + file.size() + "}";
|
|
||||||
}
|
|
||||||
file = dir.openNextFile();
|
|
||||||
}
|
|
||||||
dir.close();
|
|
||||||
output += "]";
|
|
||||||
request->send(200, "application/json", output);
|
request->send(200, "application/json", output);
|
||||||
} else if (request->hasParam("edit") || request->hasParam("download")) {
|
} else if (request->hasParam("edit") || request->hasParam("download")) {
|
||||||
request->send(request->_tempFile, request->_tempFile.name(), String(), request->hasParam("download"));
|
request->send(request->_tempFile, request->_tempFile.name(), String(), request->hasParam("download"));
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "language.h"
|
#include "language.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "system.h"
|
||||||
#include "tag_db.h"
|
#include "tag_db.h"
|
||||||
#include "truetype.h"
|
#include "truetype.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@@ -148,9 +149,24 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
|
|||||||
memset(&wifimac[6], 0, 2);
|
memset(&wifimac[6], 0, 2);
|
||||||
|
|
||||||
const bool isAp = memcmp(mac, wifimac, 8) == 0;
|
const bool isAp = memcmp(mac, wifimac, 8) == 0;
|
||||||
if ((taginfo->wakeupReason == WAKEUP_REASON_FIRSTBOOT || taginfo->wakeupReason == WAKEUP_REASON_WDT_RESET) && taginfo->contentMode == 0 && isAp) {
|
if ((taginfo->wakeupReason == WAKEUP_REASON_FIRSTBOOT || taginfo->wakeupReason == WAKEUP_REASON_WDT_RESET) && taginfo->contentMode == 0) {
|
||||||
taginfo->contentMode = 21;
|
if (isAp) {
|
||||||
taginfo->nextupdate = 0;
|
taginfo->contentMode = 21;
|
||||||
|
taginfo->nextupdate = 0;
|
||||||
|
} else if (contentFS->exists("/tag_defaults.json")) {
|
||||||
|
DynamicJsonDocument doc(1000);
|
||||||
|
fs::File tagDefaults = contentFS->open("/tag_defaults.json", "r");
|
||||||
|
DeserializationError err = deserializeJson(doc, tagDefaults);
|
||||||
|
if (!err) {
|
||||||
|
if (doc.containsKey("contentMode")) {
|
||||||
|
taginfo->contentMode = doc["contentMode"];
|
||||||
|
}
|
||||||
|
if (doc.containsKey("modecfgjson")) {
|
||||||
|
taginfo->modeConfigJson = doc["modecfgjson"].as<String>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tagDefaults.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char hexmac[17];
|
char hexmac[17];
|
||||||
@@ -162,12 +178,6 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct tm time_info;
|
|
||||||
getLocalTime(&time_info);
|
|
||||||
time_info.tm_hour = time_info.tm_min = time_info.tm_sec = 0;
|
|
||||||
time_info.tm_mday++;
|
|
||||||
const time_t midnight = mktime(&time_info);
|
|
||||||
|
|
||||||
DynamicJsonDocument doc(500);
|
DynamicJsonDocument doc(500);
|
||||||
deserializeJson(doc, taginfo->modeConfigJson);
|
deserializeJson(doc, taginfo->modeConfigJson);
|
||||||
JsonObject cfgobj = doc.as<JsonObject>();
|
JsonObject cfgobj = doc.as<JsonObject>();
|
||||||
@@ -177,7 +187,6 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
|
|||||||
taginfo->nextupdate = now + 60;
|
taginfo->nextupdate = now + 60;
|
||||||
|
|
||||||
imgParam imageParams;
|
imgParam imageParams;
|
||||||
|
|
||||||
imageParams.width = hwdata.width;
|
imageParams.width = hwdata.width;
|
||||||
imageParams.height = hwdata.height;
|
imageParams.height = hwdata.height;
|
||||||
imageParams.bpp = hwdata.bpp;
|
imageParams.bpp = hwdata.bpp;
|
||||||
@@ -188,7 +197,7 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
|
|||||||
imageParams.dither = false;
|
imageParams.dither = false;
|
||||||
if (taginfo->hasCustomLUT && taginfo->lut != 1) imageParams.grayLut = true;
|
if (taginfo->hasCustomLUT && taginfo->lut != 1) imageParams.grayLut = true;
|
||||||
|
|
||||||
imageParams.invert = false;
|
imageParams.invert = taginfo->invert;
|
||||||
imageParams.symbols = 0;
|
imageParams.symbols = 0;
|
||||||
imageParams.rotate = taginfo->rotate;
|
imageParams.rotate = taginfo->rotate;
|
||||||
|
|
||||||
@@ -215,12 +224,20 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
|
|||||||
}
|
}
|
||||||
if (contentFS->exists(configFilename)) {
|
if (contentFS->exists(configFilename)) {
|
||||||
imageParams.dither = cfgobj["dither"] && cfgobj["dither"] == "1";
|
imageParams.dither = cfgobj["dither"] && cfgobj["dither"] == "1";
|
||||||
|
|
||||||
|
imageParams.preload = cfgobj["preload"] && cfgobj["preload"] == "1";
|
||||||
|
imageParams.preloadlut = cfgobj["preload_lut"];
|
||||||
|
imageParams.preloadtype = cfgobj["preload_type"];
|
||||||
|
|
||||||
jpg2buffer(configFilename, filename, imageParams);
|
jpg2buffer(configFilename, filename, imageParams);
|
||||||
} else {
|
} else {
|
||||||
filename = "/current/" + String(hexmac) + ".raw";
|
filename = "/current/" + String(hexmac) + ".pending";
|
||||||
|
if (!contentFS->exists(filename)) {
|
||||||
|
filename = "/current/" + String(hexmac) + ".raw";
|
||||||
|
}
|
||||||
if (contentFS->exists(filename)) {
|
if (contentFS->exists(filename)) {
|
||||||
prepareDataAvail(filename, imageParams.dataType, imageParams.lut, mac, cfgobj["timetolive"].as<int>(), true);
|
prepareDataAvail(filename, imageParams.dataType, imageParams.lut, mac, cfgobj["timetolive"].as<int>(), true);
|
||||||
wsLog("File " + configFilename + " not found, resending image " + filename);
|
wsLog("Resending image " + filename);
|
||||||
} else {
|
} else {
|
||||||
wsErr("File " + configFilename + " not found");
|
wsErr("File " + configFilename + " not found");
|
||||||
}
|
}
|
||||||
@@ -231,7 +248,18 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
|
|||||||
imageParams.dataType = DATATYPE_IMG_RAW_2BPP;
|
imageParams.dataType = DATATYPE_IMG_RAW_2BPP;
|
||||||
if (imageParams.lut = EPD_LUT_NO_REPEATS && imageParams.shortlut == SHORTLUT_ONLY_BLACK) imageParams.lut = EPD_LUT_DEFAULT;
|
if (imageParams.lut = EPD_LUT_NO_REPEATS && imageParams.shortlut == SHORTLUT_ONLY_BLACK) imageParams.lut = EPD_LUT_DEFAULT;
|
||||||
}
|
}
|
||||||
if (prepareDataAvail(filename, imageParams.dataType, imageParams.lut, mac, cfgobj["timetolive"].as<int>())) {
|
|
||||||
|
struct imageDataTypeArgStruct arg = {0};
|
||||||
|
// load parameters in case we do need to preload an image
|
||||||
|
if (imageParams.preload) {
|
||||||
|
arg.preloadImage = 1;
|
||||||
|
arg.specialType = imageParams.preloadtype;
|
||||||
|
arg.lut = imageParams.preloadlut;
|
||||||
|
} else {
|
||||||
|
arg.lut = imageParams.lut & 0x03;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prepareDataAvail(filename, imageParams.dataType, *((uint8_t *)&arg), mac, cfgobj["timetolive"].as<int>())) {
|
||||||
if (cfgobj["delete"].as<String>() == "1") {
|
if (cfgobj["delete"].as<String>() == "1") {
|
||||||
contentFS->remove("/" + configFilename);
|
contentFS->remove("/" + configFilename);
|
||||||
}
|
}
|
||||||
@@ -245,12 +273,12 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
|
|||||||
case 1: // Today
|
case 1: // Today
|
||||||
|
|
||||||
drawDate(filename, taginfo, imageParams);
|
drawDate(filename, taginfo, imageParams);
|
||||||
taginfo->nextupdate = midnight;
|
taginfo->nextupdate = util::getMidnightTime();
|
||||||
updateTagImage(filename, mac, (midnight - now) / 60 - 10, taginfo, imageParams);
|
updateTagImage(filename, mac, (taginfo->nextupdate - now) / 60 - 10, taginfo, imageParams);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: // CountDays
|
case 2: // CountDays
|
||||||
drawCounter(mac, buttonPressed, taginfo, cfgobj, filename, imageParams, midnight, 15);
|
drawCounter(mac, buttonPressed, taginfo, cfgobj, filename, imageParams, util::getMidnightTime(), 15);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3: // CountHours
|
case 3: // CountHours
|
||||||
@@ -393,7 +421,7 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
|
|||||||
if (!util::isEmptyOrNull(configFilename)) {
|
if (!util::isEmptyOrNull(configFilename)) {
|
||||||
String configUrl = cfgobj["url"].as<String>();
|
String configUrl = cfgobj["url"].as<String>();
|
||||||
if (!util::isEmptyOrNull(configUrl)) {
|
if (!util::isEmptyOrNull(configUrl)) {
|
||||||
StaticJsonDocument<1000> json;
|
DynamicJsonDocument json(1000);
|
||||||
Serial.println("Get json url + file");
|
Serial.println("Get json url + file");
|
||||||
if (util::httpGetJson(configUrl, json, 1000)) {
|
if (util::httpGetJson(configUrl, json, 1000)) {
|
||||||
if (getJsonTemplateFileExtractVariables(filename, configFilename, json, taginfo, imageParams)) {
|
if (getJsonTemplateFileExtractVariables(filename, configFilename, json, taginfo, imageParams)) {
|
||||||
@@ -447,7 +475,7 @@ void drawNew(const uint8_t mac[8], const bool buttonPressed, tagRecord *&taginfo
|
|||||||
|
|
||||||
bool updateTagImage(String &filename, const uint8_t *dst, uint16_t nextCheckin, tagRecord *&taginfo, imgParam &imageParams) {
|
bool updateTagImage(String &filename, const uint8_t *dst, uint16_t nextCheckin, tagRecord *&taginfo, imgParam &imageParams) {
|
||||||
if (taginfo->hwType == SOLUM_SEG_UK) {
|
if (taginfo->hwType == SOLUM_SEG_UK) {
|
||||||
sendAPSegmentedData(dst, (String)imageParams.segments, imageParams.symbols, imageParams.invert, (taginfo->isExternal == false));
|
sendAPSegmentedData(dst, (String)imageParams.segments, imageParams.symbols, (imageParams.invert == 1), (taginfo->isExternal == false));
|
||||||
} else {
|
} else {
|
||||||
if (imageParams.hasRed) {
|
if (imageParams.hasRed) {
|
||||||
imageParams.dataType = DATATYPE_IMG_RAW_2BPP;
|
imageParams.dataType = DATATYPE_IMG_RAW_2BPP;
|
||||||
@@ -474,11 +502,19 @@ void replaceVariables(String &format) {
|
|||||||
size_t startIndex = 0;
|
size_t startIndex = 0;
|
||||||
size_t openBraceIndex, closeBraceIndex;
|
size_t openBraceIndex, closeBraceIndex;
|
||||||
|
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
struct tm timedef;
|
||||||
|
localtime_r(&now, &timedef);
|
||||||
|
char timeBuffer[80];
|
||||||
|
strftime(timeBuffer, sizeof(timeBuffer), "%H:%M:%S", &timedef);
|
||||||
|
setVarDB("ap_time", timeBuffer, false);
|
||||||
|
|
||||||
while ((openBraceIndex = format.indexOf('{', startIndex)) != -1 &&
|
while ((openBraceIndex = format.indexOf('{', startIndex)) != -1 &&
|
||||||
(closeBraceIndex = format.indexOf('}', openBraceIndex + 1)) != -1) {
|
(closeBraceIndex = format.indexOf('}', openBraceIndex + 1)) != -1) {
|
||||||
const std::string variableName = format.substring(openBraceIndex + 1, closeBraceIndex).c_str();
|
const std::string variableName = format.substring(openBraceIndex + 1, closeBraceIndex).c_str();
|
||||||
const std::string varKey = "{" + variableName + "}";
|
const std::string varKey = "{" + variableName + "}";
|
||||||
auto var = varDB.find(variableName);
|
const auto var = varDB.find(variableName);
|
||||||
if (var != varDB.end()) {
|
if (var != varDB.end()) {
|
||||||
format.replace(varKey.c_str(), var->second.value);
|
format.replace(varKey.c_str(), var->second.value);
|
||||||
}
|
}
|
||||||
@@ -656,7 +692,7 @@ const String getWeatherIcon(const uint8_t id, const bool isNight = false) {
|
|||||||
"\uf01b", "", "\uf01b", "", "\uf01b", "", "\uf076", "", "", "\uf01a",
|
"\uf01b", "", "\uf01b", "", "\uf01b", "", "\uf076", "", "", "\uf01a",
|
||||||
"\uf01a", "\uf01a", "", "", "\uf064", "\uf064", "", "", "", "",
|
"\uf01a", "\uf01a", "", "", "\uf064", "\uf064", "", "", "", "",
|
||||||
"", "", "", "", "\uf01e", "\uf01d", "", "", "\uf01e"};
|
"", "", "", "", "\uf01e", "\uf01d", "", "", "\uf01e"};
|
||||||
if (isNight && id <= 3) {
|
if (isNight && id <= 2) {
|
||||||
const String nightIcons[] = {"\uf02e", "\uf083", "\uf086"};
|
const String nightIcons[] = {"\uf02e", "\uf083", "\uf086"};
|
||||||
return nightIcons[id];
|
return nightIcons[id];
|
||||||
}
|
}
|
||||||
@@ -676,7 +712,7 @@ void drawWeather(String &filename, JsonObject &cfgobj, const tagRecord *taginfo,
|
|||||||
units += "&temperature_unit=fahrenheit&windspeed_unit=mph";
|
units += "&temperature_unit=fahrenheit&windspeed_unit=mph";
|
||||||
}
|
}
|
||||||
|
|
||||||
StaticJsonDocument<1000> doc;
|
DynamicJsonDocument doc(1000);
|
||||||
const bool success = util::httpGetJson("https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "¤t_weather=true&windspeed_unit=ms&timezone=" + tz + units, doc, 5000);
|
const bool success = util::httpGetJson("https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "¤t_weather=true&windspeed_unit=ms&timezone=" + tz + units, doc, 5000);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return;
|
return;
|
||||||
@@ -836,17 +872,22 @@ int getImgURL(String &filename, String URL, time_t fetched, imgParam &imageParam
|
|||||||
Storage.begin();
|
Storage.begin();
|
||||||
|
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
|
logLine("http getImgURL " + URL);
|
||||||
http.begin(URL);
|
http.begin(URL);
|
||||||
http.addHeader("If-Modified-Since", formatHttpDate(fetched));
|
http.addHeader("If-Modified-Since", formatHttpDate(fetched));
|
||||||
http.addHeader("X-ESL-MAC", MAC);
|
http.addHeader("X-ESL-MAC", MAC);
|
||||||
http.setTimeout(5000); // timeout in ms
|
http.setTimeout(5000); // timeout in ms
|
||||||
const int httpCode = http.GET();
|
const int httpCode = http.GET();
|
||||||
if (httpCode == 200) {
|
if (httpCode == 200) {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File f = contentFS->open("/temp/temp.jpg", "w");
|
File f = contentFS->open("/temp/temp.jpg", "w");
|
||||||
if (f) {
|
if (f) {
|
||||||
http.writeToStream(&f);
|
http.writeToStream(&f);
|
||||||
f.close();
|
f.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
jpg2buffer("/temp/temp.jpg", filename, imageParams);
|
jpg2buffer("/temp/temp.jpg", filename, imageParams);
|
||||||
|
} else {
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (httpCode != 304) {
|
if (httpCode != 304) {
|
||||||
@@ -937,6 +978,7 @@ bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo,
|
|||||||
strftime(dateString, sizeof(dateString), "%d.%m.%Y", &timeinfo);
|
strftime(dateString, sizeof(dateString), "%d.%m.%Y", &timeinfo);
|
||||||
|
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
|
logLine("http getCalFeed " + URL);
|
||||||
http.begin(URL);
|
http.begin(URL);
|
||||||
http.setTimeout(10000);
|
http.setTimeout(10000);
|
||||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||||
@@ -1039,6 +1081,7 @@ uint8_t drawBuienradar(String &filename, JsonObject &cfgobj, tagRecord *&taginfo
|
|||||||
|
|
||||||
String lat = cfgobj["#lat"];
|
String lat = cfgobj["#lat"];
|
||||||
String lon = cfgobj["#lon"];
|
String lon = cfgobj["#lon"];
|
||||||
|
logLine("http drawBuienradar");
|
||||||
http.begin("https://gps.buienradar.nl/getrr.php?lat=" + lat + "&lon=" + lon);
|
http.begin("https://gps.buienradar.nl/getrr.php?lat=" + lat + "&lon=" + lon);
|
||||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||||
http.setTimeout(5000);
|
http.setTimeout(5000);
|
||||||
@@ -1120,7 +1163,7 @@ void drawAPinfo(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgPa
|
|||||||
}
|
}
|
||||||
|
|
||||||
TFT_eSprite spr = TFT_eSprite(&tft);
|
TFT_eSprite spr = TFT_eSprite(&tft);
|
||||||
StaticJsonDocument<2048> loc;
|
DynamicJsonDocument loc(2048);
|
||||||
getTemplate(loc, 21, taginfo->hwType);
|
getTemplate(loc, 21, taginfo->hwType);
|
||||||
|
|
||||||
initSprite(spr, imageParams.width, imageParams.height, imageParams);
|
initSprite(spr, imageParams.width, imageParams.height, imageParams);
|
||||||
@@ -1300,6 +1343,7 @@ bool getJsonTemplateFileExtractVariables(String &filename, String jsonfile, Json
|
|||||||
int getJsonTemplateUrl(String &filename, String URL, time_t fetched, String MAC, tagRecord *&taginfo, imgParam &imageParams) {
|
int getJsonTemplateUrl(String &filename, String URL, time_t fetched, String MAC, tagRecord *&taginfo, imgParam &imageParams) {
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
http.useHTTP10(true);
|
http.useHTTP10(true);
|
||||||
|
logLine("http getJsonTemplateUrl " + URL);
|
||||||
http.begin(URL);
|
http.begin(URL);
|
||||||
http.addHeader("If-Modified-Since", formatHttpDate(fetched));
|
http.addHeader("If-Modified-Since", formatHttpDate(fetched));
|
||||||
http.addHeader("X-ESL-MAC", MAC);
|
http.addHeader("X-ESL-MAC", MAC);
|
||||||
@@ -1320,7 +1364,7 @@ int getJsonTemplateUrl(String &filename, String URL, time_t fetched, String MAC,
|
|||||||
void drawJsonStream(Stream &stream, String &filename, tagRecord *&taginfo, imgParam &imageParams) {
|
void drawJsonStream(Stream &stream, String &filename, tagRecord *&taginfo, imgParam &imageParams) {
|
||||||
TFT_eSprite spr = TFT_eSprite(&tft);
|
TFT_eSprite spr = TFT_eSprite(&tft);
|
||||||
initSprite(spr, imageParams.width, imageParams.height, imageParams);
|
initSprite(spr, imageParams.width, imageParams.height, imageParams);
|
||||||
DynamicJsonDocument doc(300);
|
DynamicJsonDocument doc(500);
|
||||||
if (stream.find("[")) {
|
if (stream.find("[")) {
|
||||||
do {
|
do {
|
||||||
DeserializationError error = deserializeJson(doc, stream);
|
DeserializationError error = deserializeJson(doc, stream);
|
||||||
@@ -1429,7 +1473,7 @@ void getLocation(JsonObject &cfgobj) {
|
|||||||
filter["results"][0]["latitude"] = true;
|
filter["results"][0]["latitude"] = true;
|
||||||
filter["results"][0]["longitude"] = true;
|
filter["results"][0]["longitude"] = true;
|
||||||
filter["results"][0]["timezone"] = true;
|
filter["results"][0]["timezone"] = true;
|
||||||
StaticJsonDocument<1000> doc;
|
DynamicJsonDocument doc(1000);
|
||||||
if (util::httpGetJson("https://geocoding-api.open-meteo.com/v1/search?name=" + urlEncode(cfgobj["location"]) + "&count=1", doc, 5000, &filter)) {
|
if (util::httpGetJson("https://geocoding-api.open-meteo.com/v1/search?name=" + urlEncode(cfgobj["location"]) + "&count=1", doc, 5000, &filter)) {
|
||||||
cfgobj["#lat"] = doc["results"][0]["latitude"].as<String>();
|
cfgobj["#lat"] = doc["results"][0]["latitude"].as<String>();
|
||||||
cfgobj["#lon"] = doc["results"][0]["longitude"].as<String>();
|
cfgobj["#lon"] = doc["results"][0]["longitude"].as<String>();
|
||||||
@@ -1493,7 +1537,7 @@ void prepareConfigFile(const uint8_t *dst, const JsonObject &config) {
|
|||||||
|
|
||||||
void getTemplate(JsonDocument &json, const uint8_t id, const uint8_t hwtype) {
|
void getTemplate(JsonDocument &json, const uint8_t id, const uint8_t hwtype) {
|
||||||
StaticJsonDocument<80> filter;
|
StaticJsonDocument<80> filter;
|
||||||
StaticJsonDocument<2048> doc;
|
DynamicJsonDocument doc(2048);
|
||||||
|
|
||||||
const String idstr = String(id);
|
const String idstr = String(id);
|
||||||
constexpr const char *templateKey = "template";
|
constexpr const char *templateKey = "template";
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "esp32_port.h"
|
#include "esp32_port.h"
|
||||||
#include "esp_littlefs.h"
|
#include "esp_littlefs.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
|
#include "tag_db.h"
|
||||||
#include "web.h"
|
#include "web.h"
|
||||||
|
|
||||||
esp_loader_error_t connect_to_target(uint32_t higher_transmission_rate) {
|
esp_loader_error_t connect_to_target(uint32_t higher_transmission_rate) {
|
||||||
@@ -125,24 +126,30 @@ bool downloadAndWriteBinary(String &filename, const char *url) {
|
|||||||
int binaryResponseCode = binaryHttp.GET();
|
int binaryResponseCode = binaryHttp.GET();
|
||||||
Serial.println(binaryResponseCode);
|
Serial.println(binaryResponseCode);
|
||||||
if (binaryResponseCode == HTTP_CODE_OK) {
|
if (binaryResponseCode == HTTP_CODE_OK) {
|
||||||
|
int contentLength = binaryHttp.getSize();
|
||||||
|
Serial.println(contentLength);
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File file = contentFS->open(filename, "wb");
|
File file = contentFS->open(filename, "wb");
|
||||||
if (file) {
|
if (file) {
|
||||||
wsSerial("downloading " + String(filename));
|
wsSerial("downloading " + String(filename));
|
||||||
WiFiClient *stream = binaryHttp.getStreamPtr();
|
WiFiClient *stream = binaryHttp.getStreamPtr();
|
||||||
uint8_t buffer[256];
|
uint8_t buffer[1024];
|
||||||
size_t totalBytesRead = 0;
|
size_t totalBytesRead = 0;
|
||||||
while (stream->available()) {
|
time_t timeOut = millis() + 5000;
|
||||||
|
// while (stream->available()) {
|
||||||
|
while (millis() < timeOut) {
|
||||||
size_t bytesRead = stream->readBytes(buffer, sizeof(buffer));
|
size_t bytesRead = stream->readBytes(buffer, sizeof(buffer));
|
||||||
file.write(buffer, bytesRead);
|
file.write(buffer, bytesRead);
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
if (totalBytesRead == contentLength) break;
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
binaryHttp.end();
|
binaryHttp.end();
|
||||||
|
|
||||||
file = contentFS->open(filename, "r");
|
file = contentFS->open(filename, "r");
|
||||||
if (file) {
|
if (file) {
|
||||||
if (totalBytesRead == file.size() && file.size() > 0) {
|
if (totalBytesRead == contentLength || (contentLength == 0 && file.size() > 0)) {
|
||||||
file.close();
|
file.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -150,6 +157,7 @@ bool downloadAndWriteBinary(String &filename, const char *url) {
|
|||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
wsSerial("file open error " + String(filename));
|
wsSerial("file open error " + String(filename));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -160,8 +168,7 @@ bool downloadAndWriteBinary(String &filename, const char *url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool doC6flash(uint8_t doDownload) {
|
bool doC6flash(uint8_t doDownload) {
|
||||||
const char *githubUrl = "https://raw.githubusercontent.com/jjwbruijn/OpenEPaperLink/master/binaries/ESP32-C6/firmware.json";
|
const String githubUrl = "https://raw.githubusercontent.com/" + config.repo + "/master/binaries/ESP32-C6/firmware.json";
|
||||||
|
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
Serial.println(githubUrl);
|
Serial.println(githubUrl);
|
||||||
http.begin(githubUrl);
|
http.begin(githubUrl);
|
||||||
@@ -178,7 +185,8 @@ bool doC6flash(uint8_t doDownload) {
|
|||||||
JsonArray jsonArray = jsonDoc.as<JsonArray>();
|
JsonArray jsonArray = jsonDoc.as<JsonArray>();
|
||||||
for (JsonObject obj : jsonArray) {
|
for (JsonObject obj : jsonArray) {
|
||||||
String filename = "/" + obj["filename"].as<String>();
|
String filename = "/" + obj["filename"].as<String>();
|
||||||
String binaryUrl = "https://raw.githubusercontent.com/jjwbruijn/OpenEPaperLink/master/binaries/ESP32-C6" + String(filename);
|
// String binaryUrl = "https://raw.githubusercontent.com/" + config.repo + "/master/binaries/ESP32-C6" + String(filename);
|
||||||
|
String binaryUrl = "http://www.openepaperlink.eu/binaries/ESP32-C6" + String(filename);
|
||||||
for (int retry = 0; retry < 10; retry++) {
|
for (int retry = 0; retry < 10; retry++) {
|
||||||
if (downloadAndWriteBinary(filename, binaryUrl.c_str())) {
|
if (downloadAndWriteBinary(filename, binaryUrl.c_str())) {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -175,8 +175,7 @@ bool flasher::getInfoBlockType() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool flasher::findTagByMD5() {
|
bool flasher::findTagByMD5() {
|
||||||
StaticJsonDocument<3000> doc;
|
DynamicJsonDocument doc(3000);
|
||||||
DynamicJsonDocument APconfig(600);
|
|
||||||
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
|
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
|
||||||
DeserializationError err = deserializeJson(doc, readfile);
|
DeserializationError err = deserializeJson(doc, readfile);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
@@ -205,8 +204,7 @@ bool flasher::findTagByMD5() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool flasher::findTagByType(uint8_t type) {
|
bool flasher::findTagByType(uint8_t type) {
|
||||||
StaticJsonDocument<3000> doc;
|
DynamicJsonDocument doc(3000);
|
||||||
DynamicJsonDocument APconfig(600);
|
|
||||||
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
|
fs::File readfile = contentFS->open("/tag_md5_db.json", "r");
|
||||||
DeserializationError err = deserializeJson(doc, readfile);
|
DeserializationError err = deserializeJson(doc, readfile);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
@@ -271,11 +269,13 @@ bool flasher::backupFlash() {
|
|||||||
getFirmwareMD5();
|
getFirmwareMD5();
|
||||||
if (!zbs->select_flash(0)) return false;
|
if (!zbs->select_flash(0)) return false;
|
||||||
md5char[16] = 0x00;
|
md5char[16] = 0x00;
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
fs::File backup = contentFS->open("/" + (String)md5char + "_backup.bin", "w", true);
|
fs::File backup = contentFS->open("/" + (String)md5char + "_backup.bin", "w", true);
|
||||||
for (uint32_t c = 0; c < 65535; c++) {
|
for (uint32_t c = 0; c < 65535; c++) {
|
||||||
backup.write(zbs->read_flash(c));
|
backup.write(zbs->read_flash(c));
|
||||||
}
|
}
|
||||||
backup.close();
|
backup.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,8 +443,7 @@ bool flasher::writeFlashFromPackOffset(fs::File *file, uint16_t length) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool flasher::writeFlashFromPack(String filename, uint8_t type) {
|
bool flasher::writeFlashFromPack(String filename, uint8_t type) {
|
||||||
StaticJsonDocument<512> doc;
|
DynamicJsonDocument doc(1024);
|
||||||
DynamicJsonDocument APconfig(512);
|
|
||||||
fs::File readfile = contentFS->open(filename, "r");
|
fs::File readfile = contentFS->open(filename, "r");
|
||||||
DeserializationError err = deserializeJson(doc, readfile);
|
DeserializationError err = deserializeJson(doc, readfile);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
@@ -467,6 +466,7 @@ bool flasher::writeFlashFromPack(String filename, uint8_t type) {
|
|||||||
}
|
}
|
||||||
Serial.print("Failed to find this tag's type in the FW pack database.\n");
|
Serial.print("Failed to find this tag's type in the FW pack database.\n");
|
||||||
} else {
|
} else {
|
||||||
|
Serial.println(err.c_str());
|
||||||
Serial.print("Failed to read json header from FW pack\n");
|
Serial.print("Failed to read json header from FW pack\n");
|
||||||
}
|
}
|
||||||
readfile.close();
|
readfile.close();
|
||||||
@@ -503,7 +503,6 @@ bool flasher::writeBlock(uint16_t offset, uint8_t *data, uint16_t len, bool info
|
|||||||
|
|
||||||
uint16_t getAPUpdateVersion(uint8_t type) {
|
uint16_t getAPUpdateVersion(uint8_t type) {
|
||||||
StaticJsonDocument<512> doc;
|
StaticJsonDocument<512> doc;
|
||||||
DynamicJsonDocument APconfig(512);
|
|
||||||
fs::File readfile = contentFS->open("/AP_FW_Pack.bin", "r");
|
fs::File readfile = contentFS->open("/AP_FW_Pack.bin", "r");
|
||||||
DeserializationError err = deserializeJson(doc, readfile);
|
DeserializationError err = deserializeJson(doc, readfile);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ void yellow_ap_display_init(void) {
|
|||||||
|
|
||||||
ledcSetup(6, 5000, 8);
|
ledcSetup(6, 5000, 8);
|
||||||
ledcAttachPin(TFT_BACKLIGHT, 6);
|
ledcAttachPin(TFT_BACKLIGHT, 6);
|
||||||
ledcWrite(6, 255); // config.led
|
ledcWrite(6, config.tft);
|
||||||
|
|
||||||
tft2.init();
|
tft2.init();
|
||||||
tft2.setRotation(YellowSense == 1 ? 1 : 3);
|
tft2.setRotation(YellowSense == 1 ? 1 : 3);
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ int maxledbrightness = 255;
|
|||||||
|
|
||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
QueueHandle_t rgbLedQueue;
|
QueueHandle_t rgbLedQueue;
|
||||||
|
CRGB rgbIdleColor = CRGB::Green;
|
||||||
|
uint16_t rgbIdlePeriod = 511;
|
||||||
|
|
||||||
struct ledInstructionRGB {
|
struct ledInstructionRGB {
|
||||||
CRGB ledColor;
|
CRGB ledColor;
|
||||||
@@ -56,6 +58,7 @@ void addFadeColor(CRGB cname) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void shortBlink(CRGB cname) {
|
void shortBlink(CRGB cname) {
|
||||||
|
#ifndef YELLOW_IPS_AP
|
||||||
struct ledInstructionRGB* rgb = new struct ledInstructionRGB;
|
struct ledInstructionRGB* rgb = new struct ledInstructionRGB;
|
||||||
rgb->ledColor = CRGB::Black;
|
rgb->ledColor = CRGB::Black;
|
||||||
rgb->fadeTime = 0;
|
rgb->fadeTime = 0;
|
||||||
@@ -72,6 +75,7 @@ void shortBlink(CRGB cname) {
|
|||||||
rgb->fadeTime = 0;
|
rgb->fadeTime = 0;
|
||||||
rgb->length = 3;
|
rgb->length = 3;
|
||||||
addToRGBQueue(rgb, false);
|
addToRGBQueue(rgb, false);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void flushRGBQueue() {
|
void flushRGBQueue() {
|
||||||
@@ -119,9 +123,6 @@ void showRGB() {
|
|||||||
FastLED.show();
|
FastLED.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
volatile CRGB rgbIdleColor = CRGB::Green;
|
|
||||||
volatile uint16_t rgbIdlePeriod = 800;
|
|
||||||
|
|
||||||
void rgbIdleStep() {
|
void rgbIdleStep() {
|
||||||
static bool dirUp = true;
|
static bool dirUp = true;
|
||||||
static uint16_t step = 0;
|
static uint16_t step = 0;
|
||||||
@@ -129,7 +130,7 @@ void rgbIdleStep() {
|
|||||||
if (dirUp) {
|
if (dirUp) {
|
||||||
// up
|
// up
|
||||||
step++;
|
step++;
|
||||||
if (step == rgbIdlePeriod) {
|
if (step >= rgbIdlePeriod) {
|
||||||
dirUp = false;
|
dirUp = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -139,7 +140,7 @@ void rgbIdleStep() {
|
|||||||
dirUp = true;
|
dirUp = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CRGB newvalue = blend(CRGB::Black, (const CRGB&)rgbIdleColor, gamma8[map(step, 0, rgbIdlePeriod, 0, 255)]);
|
CRGB newvalue = blend(CRGB::Black, (const CRGB&)rgbIdleColor, map(step, 0, rgbIdlePeriod, 0, 255));
|
||||||
if (newvalue != leds[0]) {
|
if (newvalue != leds[0]) {
|
||||||
leds[0] = newvalue;
|
leds[0] = newvalue;
|
||||||
showRGB();
|
showRGB();
|
||||||
@@ -150,7 +151,7 @@ void rgbIdleStep() {
|
|||||||
void setBrightness(int brightness) {
|
void setBrightness(int brightness) {
|
||||||
maxledbrightness = brightness;
|
maxledbrightness = brightness;
|
||||||
#ifdef YELLOW_IPS_AP
|
#ifdef YELLOW_IPS_AP
|
||||||
// ledcWrite(6, config.led);
|
ledcWrite(6, config.tft);
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
FastLED.setBrightness(maxledbrightness);
|
FastLED.setBrightness(maxledbrightness);
|
||||||
@@ -165,6 +166,7 @@ void updateBrightnessFromConfig() {
|
|||||||
setBrightness(newbrightness);
|
setBrightness(newbrightness);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ledcWrite(6, config.tft);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addToMonoQueue(struct ledInstruction* mono) {
|
void addToMonoQueue(struct ledInstruction* mono) {
|
||||||
@@ -193,7 +195,11 @@ void showMono(uint8_t brightness) {
|
|||||||
void quickBlink(uint8_t repeat) {
|
void quickBlink(uint8_t repeat) {
|
||||||
for (int i = 0; i < repeat; i++) {
|
for (int i = 0; i < repeat; i++) {
|
||||||
struct ledInstruction* mono = new struct ledInstruction;
|
struct ledInstruction* mono = new struct ledInstruction;
|
||||||
|
#ifdef YELLOW_IPS_AP
|
||||||
|
mono->value = 255;
|
||||||
|
#else
|
||||||
mono->value = maxledbrightness;
|
mono->value = maxledbrightness;
|
||||||
|
#endif
|
||||||
mono->fadeTime = 120 / repeat;
|
mono->fadeTime = 120 / repeat;
|
||||||
mono->length = 0;
|
mono->length = 0;
|
||||||
addToMonoQueue(mono);
|
addToMonoQueue(mono);
|
||||||
@@ -207,31 +213,6 @@ void quickBlink(uint8_t repeat) {
|
|||||||
|
|
||||||
volatile uint16_t monoIdlePeriod = 900;
|
volatile uint16_t monoIdlePeriod = 900;
|
||||||
|
|
||||||
uint8_t monoValue = 0;
|
|
||||||
|
|
||||||
void monoIdleStep() {
|
|
||||||
static bool dirUp = true;
|
|
||||||
static uint16_t step = 0;
|
|
||||||
if (dirUp) {
|
|
||||||
// up
|
|
||||||
step++;
|
|
||||||
if (step == monoIdlePeriod) {
|
|
||||||
dirUp = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// down
|
|
||||||
step--;
|
|
||||||
if (step == 0) {
|
|
||||||
dirUp = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uint8_t newvalue = map(step, 0, monoIdlePeriod, 0, maxledbrightness);
|
|
||||||
if (newvalue != monoValue) {
|
|
||||||
monoValue = newvalue;
|
|
||||||
showMono(newvalue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ledTask(void* parameter) {
|
void ledTask(void* parameter) {
|
||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
FastLED.addLeds<WS2812B, FLASHER_RGB_LED, GRB>(leds, 1); // GRB ordering is typical
|
FastLED.addLeds<WS2812B, FLASHER_RGB_LED, GRB>(leds, 1); // GRB ordering is typical
|
||||||
@@ -244,9 +225,6 @@ void ledTask(void* parameter) {
|
|||||||
addFadeColor(CRGB::Red);
|
addFadeColor(CRGB::Red);
|
||||||
addFadeColor(CRGB::Green);
|
addFadeColor(CRGB::Green);
|
||||||
addFadeColor(CRGB::Blue);
|
addFadeColor(CRGB::Blue);
|
||||||
addFadeColor(CRGB::Red);
|
|
||||||
addFadeColor(CRGB::Green);
|
|
||||||
addFadeColor(CRGB::Blue);
|
|
||||||
CRGB oldColor = CRGB::Black;
|
CRGB oldColor = CRGB::Black;
|
||||||
uint16_t rgbInstructionFadeTime = 0;
|
uint16_t rgbInstructionFadeTime = 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -263,7 +241,11 @@ void ledTask(void* parameter) {
|
|||||||
struct ledInstruction* monoled = nullptr;
|
struct ledInstruction* monoled = nullptr;
|
||||||
|
|
||||||
addFadeMono(0);
|
addFadeMono(0);
|
||||||
|
#ifdef YELLOW_IPS_AP
|
||||||
|
addFadeMono(255);
|
||||||
|
#else
|
||||||
addFadeMono(maxledbrightness);
|
addFadeMono(maxledbrightness);
|
||||||
|
#endif
|
||||||
addFadeMono(0);
|
addFadeMono(0);
|
||||||
|
|
||||||
uint8_t oldBrightness = 0;
|
uint8_t oldBrightness = 0;
|
||||||
@@ -324,9 +306,7 @@ void ledTask(void* parameter) {
|
|||||||
if (monoled->fadeTime <= 1) {
|
if (monoled->fadeTime <= 1) {
|
||||||
showMono(monoled->value);
|
showMono(monoled->value);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// monoIdleStep();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (monoled->fadeTime) {
|
if (monoled->fadeTime) {
|
||||||
monoled->fadeTime--;
|
monoled->fadeTime--;
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "tag_db.h"
|
#include "tag_db.h"
|
||||||
|
#include "tagdata.h"
|
||||||
|
#include "wifimanager.h"
|
||||||
|
|
||||||
#ifdef HAS_USB
|
#ifdef HAS_USB
|
||||||
#include "usbflasher.h"
|
#include "usbflasher.h"
|
||||||
@@ -21,6 +23,18 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "web.h"
|
#include "web.h"
|
||||||
|
|
||||||
|
util::Timer intervalContentRunner(seconds(1));
|
||||||
|
util::Timer intervalSysinfo(seconds(3));
|
||||||
|
util::Timer intervalVars(seconds(10));
|
||||||
|
util::Timer intervalSaveDB(minutes(5));
|
||||||
|
util::Timer intervalCheckDate(minutes(5));
|
||||||
|
|
||||||
|
#ifdef OPENEPAPERLINK_PCB
|
||||||
|
util::Timer tagConnectTimer(seconds(1));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SET_LOOP_TASK_STACK_SIZE(16 * 1024);
|
||||||
|
|
||||||
void pinTest();
|
void pinTest();
|
||||||
|
|
||||||
void delayedStart(void* parameter) {
|
void delayedStart(void* parameter) {
|
||||||
@@ -33,32 +47,6 @@ void delayedStart(void* parameter) {
|
|||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void timeTask(void* parameter) {
|
|
||||||
wsSendSysteminfo();
|
|
||||||
util::printHeap();
|
|
||||||
while (1) {
|
|
||||||
unsigned long startMillis = millis();
|
|
||||||
time_t now;
|
|
||||||
time(&now);
|
|
||||||
if (now % 5 == 0 || apInfo.state != AP_STATE_ONLINE || config.runStatus != RUNSTATUS_RUN) {
|
|
||||||
wsSendSysteminfo();
|
|
||||||
}
|
|
||||||
if (now % 10 == 9 && config.runStatus != RUNSTATUS_STOP) {
|
|
||||||
checkVars();
|
|
||||||
}
|
|
||||||
if (now % 300 == 7 && config.runStatus != RUNSTATUS_STOP) {
|
|
||||||
saveDB("/current/tagDB.json");
|
|
||||||
}
|
|
||||||
if (apInfo.state == AP_STATE_ONLINE) {
|
|
||||||
contentRunner();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (millis() - startMillis < 1000) {
|
|
||||||
vTaskDelay((1000 - millis() + startMillis) / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
Serial.print(">\n");
|
Serial.print(">\n");
|
||||||
@@ -70,11 +58,6 @@ void setup() {
|
|||||||
xTaskCreate(ledTask, "ledhandler", 2000, NULL, 2, NULL);
|
xTaskCreate(ledTask, "ledhandler", 2000, NULL, 2, NULL);
|
||||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
#ifdef HAS_RGB_LED
|
|
||||||
// show a nice pattern to indicate the AP is booting / waiting for WiFi setup
|
|
||||||
showColorPattern(CRGB::Aqua, CRGB::Green, CRGB::Blue);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(OPENEPAPERLINK_MINI_AP_PCB) || defined(OPENEPAPERLINK_NANO_AP_PCB)
|
#if defined(OPENEPAPERLINK_MINI_AP_PCB) || defined(OPENEPAPERLINK_NANO_AP_PCB)
|
||||||
APEnterEarlyReset();
|
APEnterEarlyReset();
|
||||||
// this allows us to view the booting process. After the device showing up, you have 3 seconds to open a terminal on the COM port
|
// this allows us to view the booting process. After the device showing up, you have 3 seconds to open a terminal on the COM port
|
||||||
@@ -136,6 +119,7 @@ void setup() {
|
|||||||
|
|
||||||
initAPconfig();
|
initAPconfig();
|
||||||
|
|
||||||
|
xTaskCreate(initTime, "init time", 5000, NULL, 2, NULL);
|
||||||
updateLanguageFromConfig();
|
updateLanguageFromConfig();
|
||||||
updateBrightnessFromConfig();
|
updateBrightnessFromConfig();
|
||||||
|
|
||||||
@@ -144,11 +128,10 @@ void setup() {
|
|||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
rgbIdle();
|
rgbIdle();
|
||||||
#endif
|
#endif
|
||||||
|
TagData::loadParsers("/parsers.json");
|
||||||
loadDB("/current/tagDB.json");
|
loadDB("/current/tagDB.json");
|
||||||
cleanupCurrent();
|
cleanupCurrent();
|
||||||
// tagDBOwner = xSemaphoreCreateMutex();
|
|
||||||
xTaskCreate(APTask, "AP Process", 6000, NULL, 2, NULL);
|
xTaskCreate(APTask, "AP Process", 6000, NULL, 2, NULL);
|
||||||
xTaskCreate(networkProcess, "Wifi", 6000, NULL, configMAX_PRIORITIES - 10, NULL);
|
|
||||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
config.runStatus = RUNSTATUS_INIT;
|
config.runStatus = RUNSTATUS_INIT;
|
||||||
@@ -158,39 +141,63 @@ void setup() {
|
|||||||
config.runStatus = RUNSTATUS_PAUSE;
|
config.runStatus = RUNSTATUS_PAUSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
xTaskCreate(timeTask, "timed tasks", 12000, NULL, 2, NULL);
|
|
||||||
xTaskCreate(initTime, "init time", 5000, NULL, 2, NULL);
|
|
||||||
xTaskCreate(delayedStart, "delaystart", 2000, NULL, 2, NULL);
|
xTaskCreate(delayedStart, "delaystart", 2000, NULL, 2, NULL);
|
||||||
|
|
||||||
|
wsSendSysteminfo();
|
||||||
|
util::printHeap();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
ws.cleanupClients();
|
||||||
// performDeviceFlash();
|
wm.poll();
|
||||||
while (1) {
|
|
||||||
// pinTest();
|
|
||||||
while (1) {
|
|
||||||
#ifdef YELLOW_IPS_AP
|
|
||||||
extern void yellow_ap_display_loop(void);
|
|
||||||
yellow_ap_display_loop();
|
|
||||||
#else
|
|
||||||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
|
||||||
// pinTest();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef OPENEPAPERLINK_PCB
|
|
||||||
if (extTagConnected()) {
|
|
||||||
flashCountDown(3);
|
|
||||||
|
|
||||||
pinMode(FLASHER_EXT_TEST, OUTPUT);
|
if (intervalSysinfo.doRun()) {
|
||||||
digitalWrite(FLASHER_EXT_TEST, LOW);
|
wsSendSysteminfo();
|
||||||
|
|
||||||
doTagFlash();
|
|
||||||
|
|
||||||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
|
||||||
pinMode(FLASHER_EXT_TEST, INPUT);
|
|
||||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
|
||||||
}
|
}
|
||||||
}
|
if (intervalVars.doRun() && config.runStatus != RUNSTATUS_STOP) {
|
||||||
|
checkVars();
|
||||||
|
}
|
||||||
|
if (intervalSaveDB.doRun() && config.runStatus != RUNSTATUS_STOP) {
|
||||||
|
saveDB("/current/tagDB.json");
|
||||||
|
}
|
||||||
|
if (intervalContentRunner.doRun() && apInfo.state == AP_STATE_ONLINE) {
|
||||||
|
contentRunner();
|
||||||
|
}
|
||||||
|
if (intervalCheckDate.doRun() && config.runStatus == RUNSTATUS_RUN) {
|
||||||
|
static uint8_t day = 0;
|
||||||
|
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
struct tm timedef;
|
||||||
|
localtime_r(&now, &timedef);
|
||||||
|
|
||||||
|
if (day != timedef.tm_mday) {
|
||||||
|
day = timedef.tm_mday;
|
||||||
|
char timeBuffer[80];
|
||||||
|
strftime(timeBuffer, sizeof(timeBuffer), "%d-%m-%Y", &timedef);
|
||||||
|
setVarDB("ap_date", timeBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef YELLOW_IPS_AP
|
||||||
|
extern void yellow_ap_display_loop(void);
|
||||||
|
yellow_ap_display_loop();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef OPENEPAPERLINK_PCB
|
||||||
|
if (tagConnectTimer.doRun() && extTagConnected()) {
|
||||||
|
flashCountDown(3);
|
||||||
|
|
||||||
|
pinMode(FLASHER_EXT_TEST, OUTPUT);
|
||||||
|
digitalWrite(FLASHER_EXT_TEST, LOW);
|
||||||
|
|
||||||
|
doTagFlash();
|
||||||
|
|
||||||
|
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
||||||
|
pinMode(FLASHER_EXT_TEST, INPUT);
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
|||||||
@@ -97,6 +97,9 @@ void spr2color(TFT_eSprite &spr, imgParam &imageParams, uint8_t *buffer, size_t
|
|||||||
{0, 0, 0}, // Black
|
{0, 0, 0}, // Black
|
||||||
{255, 0, 0} // Red
|
{255, 0, 0} // Red
|
||||||
};
|
};
|
||||||
|
if (imageParams.invert == 1) {
|
||||||
|
std::swap(palette[0], palette[1]);
|
||||||
|
}
|
||||||
if (imageParams.grayLut) {
|
if (imageParams.grayLut) {
|
||||||
Color newColor = {160, 160, 160};
|
Color newColor = {160, 160, 160};
|
||||||
palette.push_back(newColor);
|
palette.push_back(newColor);
|
||||||
@@ -217,6 +220,7 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
fs::File f_out = contentFS->open(fileout, "w");
|
fs::File f_out = contentFS->open(fileout, "w");
|
||||||
|
|
||||||
switch (imageParams.bpp) {
|
switch (imageParams.bpp) {
|
||||||
@@ -232,6 +236,8 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
|
|||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
Serial.println("Failed to allocate buffer");
|
Serial.println("Failed to allocate buffer");
|
||||||
util::printLargestFreeBlock();
|
util::printLargestFreeBlock();
|
||||||
|
f_out.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
spr2color(spr, imageParams, buffer, buffer_size, false);
|
spr2color(spr, imageParams, buffer, buffer_size, false);
|
||||||
@@ -251,5 +257,6 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f_out.close();
|
f_out.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
Serial.println("finished writing buffer " + String(millis() - t) + "ms");
|
Serial.println("finished writing buffer " + String(millis() - t) + "ms");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "tag_db.h"
|
#include "tag_db.h"
|
||||||
|
#include "tagdata.h"
|
||||||
#include "udp.h"
|
#include "udp.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "web.h"
|
#include "web.h"
|
||||||
@@ -110,6 +111,7 @@ void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, const uint8
|
|||||||
} else {
|
} else {
|
||||||
sendDataAvail(&pending);
|
sendDataAvail(&pending);
|
||||||
}
|
}
|
||||||
|
|
||||||
wsSendTaginfo(dst, SYNC_TAGSTATUS);
|
wsSendTaginfo(dst, SYNC_TAGSTATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,21 +239,27 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
|
|||||||
String imageUrl = "http://" + remoteIP.toString() + filename;
|
String imageUrl = "http://" + remoteIP.toString() + filename;
|
||||||
wsLog("GET " + imageUrl);
|
wsLog("GET " + imageUrl);
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
|
logLine("http prepareExternalDataAvail " + imageUrl);
|
||||||
http.begin(imageUrl);
|
http.begin(imageUrl);
|
||||||
int httpCode = http.GET();
|
int httpCode = http.GET();
|
||||||
if (httpCode == 200) {
|
if (httpCode == 200) {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File file = contentFS->open(filename, "w");
|
File file = contentFS->open(filename, "w");
|
||||||
http.writeToStream(&file);
|
http.writeToStream(&file);
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
} else if (httpCode == 404) {
|
} else if (httpCode == 404) {
|
||||||
imageUrl = "http://" + remoteIP.toString() + "/current/" + String(hexmac) + ".raw";
|
imageUrl = "http://" + remoteIP.toString() + "/current/" + String(hexmac) + ".raw";
|
||||||
http.end();
|
http.end();
|
||||||
|
logLine("http prepareExternalDataAvail " + imageUrl);
|
||||||
http.begin(imageUrl);
|
http.begin(imageUrl);
|
||||||
httpCode = http.GET();
|
httpCode = http.GET();
|
||||||
if (httpCode == 200) {
|
if (httpCode == 200) {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File file = contentFS->open(filename, "w");
|
File file = contentFS->open(filename, "w");
|
||||||
http.writeToStream(&file);
|
http.writeToStream(&file);
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
http.end();
|
http.end();
|
||||||
@@ -290,6 +298,7 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
|
|||||||
String dataUrl = "http://" + remoteIP.toString() + "/getdata?mac=" + String(hexmac);
|
String dataUrl = "http://" + remoteIP.toString() + "/getdata?mac=" + String(hexmac);
|
||||||
wsLog("GET " + dataUrl);
|
wsLog("GET " + dataUrl);
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
|
logLine("http DATATYPE_CUSTOM_LUT_OTA " + dataUrl);
|
||||||
http.begin(dataUrl);
|
http.begin(dataUrl);
|
||||||
int httpCode = http.GET();
|
int httpCode = http.GET();
|
||||||
if (httpCode == 200) {
|
if (httpCode == 200) {
|
||||||
@@ -321,7 +330,9 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void processBlockRequest(struct espBlockRequest* br) {
|
void processBlockRequest(struct espBlockRequest* br) {
|
||||||
if (config.runStatus == RUNSTATUS_STOP) return;
|
if (config.runStatus == RUNSTATUS_STOP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!checkCRC(br, sizeof(struct espBlockRequest))) {
|
if (!checkCRC(br, sizeof(struct espBlockRequest))) {
|
||||||
Serial.print("Failed CRC on a blockrequest received by the AP");
|
Serial.print("Failed CRC on a blockrequest received by the AP");
|
||||||
return;
|
return;
|
||||||
@@ -363,7 +374,9 @@ void processBlockRequest(struct espBlockRequest* br) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void processXferComplete(struct espXferComplete* xfc, bool local) {
|
void processXferComplete(struct espXferComplete* xfc, bool local) {
|
||||||
if (config.runStatus == RUNSTATUS_STOP) return;
|
if (config.runStatus == RUNSTATUS_STOP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
sprintf(buffer, "< %02X%02X%02X%02X%02X%02X%02X%02X reports xfer complete\n\0", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]);
|
sprintf(buffer, "< %02X%02X%02X%02X%02X%02X%02X%02X reports xfer complete\n\0", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]);
|
||||||
wsLog((String)buffer);
|
wsLog((String)buffer);
|
||||||
@@ -407,7 +420,9 @@ void processXferComplete(struct espXferComplete* xfc, bool local) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void processXferTimeout(struct espXferComplete* xfc, bool local) {
|
void processXferTimeout(struct espXferComplete* xfc, bool local) {
|
||||||
if (config.runStatus == RUNSTATUS_STOP) return;
|
if (config.runStatus == RUNSTATUS_STOP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
sprintf(buffer, "< %02X%02X%02X%02X%02X%02X%02X%02X xfer timeout\n\0", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]);
|
sprintf(buffer, "< %02X%02X%02X%02X%02X%02X%02X%02X xfer timeout\n\0", xfc->src[7], xfc->src[6], xfc->src[5], xfc->src[4], xfc->src[3], xfc->src[2], xfc->src[1], xfc->src[0]);
|
||||||
wsErr((String)buffer);
|
wsErr((String)buffer);
|
||||||
@@ -430,8 +445,10 @@ void processXferTimeout(struct espXferComplete* xfc, bool local) {
|
|||||||
if (local) udpsync.netProcessXferTimeout(xfc);
|
if (local) udpsync.netProcessXferTimeout(xfc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void processDataReq(struct espAvailDataReq* eadr, bool local) {
|
void processDataReq(struct espAvailDataReq* eadr, bool local, IPAddress remoteIP) {
|
||||||
if (config.runStatus == RUNSTATUS_STOP) return;
|
if (config.runStatus == RUNSTATUS_STOP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
|
|
||||||
tagRecord* taginfo = tagRecord::findByMAC(eadr->src);
|
tagRecord* taginfo = tagRecord::findByMAC(eadr->src);
|
||||||
@@ -450,13 +467,15 @@ void processDataReq(struct espAvailDataReq* eadr, bool local) {
|
|||||||
if (!local) {
|
if (!local) {
|
||||||
if (taginfo->isExternal == false) {
|
if (taginfo->isExternal == false) {
|
||||||
wsLog("moved AP from local to external " + String(hexmac));
|
wsLog("moved AP from local to external " + String(hexmac));
|
||||||
|
taginfo->isExternal = true;
|
||||||
}
|
}
|
||||||
taginfo->isExternal = true;
|
taginfo->apIp = remoteIP;
|
||||||
} else {
|
} else {
|
||||||
if (taginfo->isExternal == true) {
|
if (taginfo->isExternal == true) {
|
||||||
wsLog("moved AP from external to local " + String(hexmac));
|
wsLog("moved AP from external to local " + String(hexmac));
|
||||||
|
taginfo->isExternal = false;
|
||||||
}
|
}
|
||||||
taginfo->isExternal = false;
|
taginfo->apIp = IPAddress(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taginfo->pendingIdle == 0) {
|
if (taginfo->pendingIdle == 0) {
|
||||||
@@ -508,9 +527,11 @@ void processDataReq(struct espAvailDataReq* eadr, bool local) {
|
|||||||
// sprintf(buffer, "<REMOTE ADR %02X%02X%02X%02X%02X%02X%02X%02X\n\0", eadr->src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0]);
|
// sprintf(buffer, "<REMOTE ADR %02X%02X%02X%02X%02X%02X%02X%02X\n\0", eadr->src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
wsSendTaginfo(eadr->src, SYNC_TAGSTATUS);
|
|
||||||
if (local) {
|
if (local) {
|
||||||
|
wsSendTaginfo(eadr->src, SYNC_TAGSTATUS);
|
||||||
udpsync.netProcessDataReq(eadr);
|
udpsync.netProcessDataReq(eadr);
|
||||||
|
} else {
|
||||||
|
wsSendTaginfo(eadr->src, SYNC_NOSYNC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,16 +539,17 @@ void processTagReturnData(struct espTagReturnData* trd, uint8_t len, bool local)
|
|||||||
if (!checkCRC(trd, len)) {
|
if (!checkCRC(trd, len)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
char buffer[64];
|
|
||||||
|
const uint8_t payloadLength = trd->len - 11;
|
||||||
|
|
||||||
// Replace this stuff with something that handles the data coming from the tag. This is here for demo purposes!
|
// Replace this stuff with something that handles the data coming from the tag. This is here for demo purposes!
|
||||||
|
char buffer[64];
|
||||||
sprintf(buffer, "<TRD %02X%02X%02X%02X%02X%02X%02X%02X\n", trd->src[7], trd->src[6], trd->src[5], trd->src[4], trd->src[3], trd->src[2], trd->src[1], trd->src[0]);
|
sprintf(buffer, "<TRD %02X%02X%02X%02X%02X%02X%02X%02X\n", trd->src[7], trd->src[6], trd->src[5], trd->src[4], trd->src[3], trd->src[2], trd->src[1], trd->src[0]);
|
||||||
wsLog((String)buffer);
|
wsLog((String)buffer);
|
||||||
sprintf(buffer, "TRD Data: len=%d, type=%d, ver=0x%08X\n", trd->len - 11, trd->returnData.dataType, trd->returnData.dataVer);
|
sprintf(buffer, "TRD Data: len=%d, type=%d, ver=0x%08X\n", payloadLength, trd->returnData.dataType, trd->returnData.dataVer);
|
||||||
wsLog((String)buffer);
|
wsLog((String)buffer);
|
||||||
|
|
||||||
uint8_t actualPayloadLength = trd->len - 11;
|
TagData::parse(trd->src, trd->returnData.dataType, trd->returnData.data, payloadLength);
|
||||||
uint8_t* actualPayload = (uint8_t*)calloc(actualPayloadLength, 1);
|
|
||||||
memcpy(actualPayload, trd->returnData.data, actualPayloadLength);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void refreshAllPending() {
|
void refreshAllPending() {
|
||||||
@@ -653,7 +675,7 @@ void updateTaginfoitem(struct TagInfo* taginfoitem, IPAddress remoteIP) {
|
|||||||
|
|
||||||
char hexmac[17];
|
char hexmac[17];
|
||||||
mac2hex(taginfo->mac, hexmac);
|
mac2hex(taginfo->mac, hexmac);
|
||||||
if (taginfo->contentMode != 12 && taginfoitem->contentMode != 12) {
|
if (taginfo->contentMode != 12 && taginfoitem->contentMode != 12 && taginfoitem->contentMode != 0) {
|
||||||
wsLog("Remote AP at " + remoteIP.toString() + " takes control over tag " + String(hexmac));
|
wsLog("Remote AP at " + remoteIP.toString() + " takes control over tag " + String(hexmac));
|
||||||
taginfo->contentMode = 12;
|
taginfo->contentMode = 12;
|
||||||
}
|
}
|
||||||
@@ -712,11 +734,15 @@ bool checkMirror(struct tagRecord* taginfo, struct pendingData* pending) {
|
|||||||
} else {
|
} else {
|
||||||
char dst_path[64];
|
char dst_path[64];
|
||||||
sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.pending\0", taginfo2->mac[7], taginfo2->mac[6], taginfo2->mac[5], taginfo2->mac[4], taginfo2->mac[3], taginfo2->mac[2], taginfo2->mac[1], taginfo2->mac[0]);
|
sprintf(dst_path, "/current/%02X%02X%02X%02X%02X%02X%02X%02X.pending\0", taginfo2->mac[7], taginfo2->mac[6], taginfo2->mac[5], taginfo2->mac[4], taginfo2->mac[3], taginfo2->mac[2], taginfo2->mac[1], taginfo2->mac[0]);
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File file = contentFS->open(dst_path, "w");
|
File file = contentFS->open(dst_path, "w");
|
||||||
if (file) {
|
if (file) {
|
||||||
file.write(taginfo2->data, taginfo2->len);
|
file.write(taginfo2->data, taginfo2->len);
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
udpsync.netSendDataAvail(&pending2);
|
udpsync.netSendDataAvail(&pending2);
|
||||||
|
} else {
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <Update.h>
|
#include <Update.h>
|
||||||
|
|
||||||
#include "espflasher.h"
|
#include "espflasher.h"
|
||||||
|
#include "leds.h"
|
||||||
#include "serialap.h"
|
#include "serialap.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "tag_db.h"
|
#include "tag_db.h"
|
||||||
@@ -94,6 +95,7 @@ void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_
|
|||||||
} else {
|
} else {
|
||||||
path = request->getParam("path", true)->value();
|
path = request->getParam("path", true)->value();
|
||||||
Serial.println("update " + path);
|
Serial.println("update " + path);
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
request->_tempFile = contentFS->open(path, "w", true);
|
request->_tempFile = contentFS->open(path, "w", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,6 +107,7 @@ void handleLittleFSUpload(AsyncWebServerRequest* request, String filename, size_
|
|||||||
}
|
}
|
||||||
if (final) {
|
if (final) {
|
||||||
request->_tempFile.close();
|
request->_tempFile.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
if (error) {
|
if (error) {
|
||||||
request->send(507, "text/plain", "Error. Disk full?");
|
request->send(507, "text/plain", "Error. Disk full?");
|
||||||
} else {
|
} else {
|
||||||
@@ -155,7 +158,6 @@ void updateFirmware(const char* url, const char* expectedMd5, const size_t size)
|
|||||||
|
|
||||||
config.runStatus = RUNSTATUS_STOP;
|
config.runStatus = RUNSTATUS_STOP;
|
||||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||||
// xSemaphoreTake(tagDBOwner, portMAX_DELAY);
|
|
||||||
saveDB("/current/tagDB.json");
|
saveDB("/current/tagDB.json");
|
||||||
// destroyDB();
|
// destroyDB();
|
||||||
|
|
||||||
@@ -213,7 +215,6 @@ void updateFirmware(const char* url, const char* expectedMd5, const size_t size)
|
|||||||
httpClient.end();
|
httpClient.end();
|
||||||
// loadDB("/current/tagDB.json");
|
// loadDB("/current/tagDB.json");
|
||||||
config.runStatus = RUNSTATUS_RUN;
|
config.runStatus = RUNSTATUS_RUN;
|
||||||
// xSemaphoreGive(tagDBOwner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleRollback(AsyncWebServerRequest* request) {
|
void handleRollback(AsyncWebServerRequest* request) {
|
||||||
@@ -240,8 +241,7 @@ void C6firmwareUpdateTask(void* parameter) {
|
|||||||
uint8_t doDownload = *((uint8_t*)parameter);
|
uint8_t doDownload = *((uint8_t*)parameter);
|
||||||
wsSerial("Stopping AP service");
|
wsSerial("Stopping AP service");
|
||||||
|
|
||||||
apInfo.isOnline = false;
|
setAPstate(false, AP_STATE_FLASHING);
|
||||||
apInfo.state = AP_STATE_FLASHING;
|
|
||||||
config.runStatus = RUNSTATUS_STOP;
|
config.runStatus = RUNSTATUS_STOP;
|
||||||
extern bool rxSerialStopTask2;
|
extern bool rxSerialStopTask2;
|
||||||
rxSerialStopTask2 = true;
|
rxSerialStopTask2 = true;
|
||||||
@@ -255,8 +255,8 @@ void C6firmwareUpdateTask(void* parameter) {
|
|||||||
wsSerial("C6 flash end");
|
wsSerial("C6 flash end");
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
apInfo.state = AP_STATE_OFFLINE;
|
setAPstate(false, AP_STATE_OFFLINE);
|
||||||
|
|
||||||
wsSerial("Finishing config...");
|
wsSerial("Finishing config...");
|
||||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
@@ -303,7 +303,7 @@ void handleUpdateActions(AsyncWebServerRequest* request) {
|
|||||||
request->send(200, "No update actions needed");
|
request->send(200, "No update actions needed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StaticJsonDocument<1000> doc;
|
DynamicJsonDocument doc(1000);
|
||||||
DeserializationError error = deserializeJson(doc, file);
|
DeserializationError error = deserializeJson(doc, file);
|
||||||
const JsonArray deleteFiles = doc["deletefile"].as<JsonArray>();
|
const JsonArray deleteFiles = doc["deletefile"].as<JsonArray>();
|
||||||
for (const auto& filePath : deleteFiles) {
|
for (const auto& filePath : deleteFiles) {
|
||||||
|
|||||||
@@ -137,6 +137,24 @@ void APEnterEarlyReset() {
|
|||||||
digitalWrite(AP_RESET_PIN, LOW);
|
digitalWrite(AP_RESET_PIN, LOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAPstate(bool isOnline, uint8_t state) {
|
||||||
|
apInfo.isOnline = isOnline;
|
||||||
|
apInfo.state = state;
|
||||||
|
#ifdef HAS_RGB_LED
|
||||||
|
CRGB colorMap[7] = {
|
||||||
|
CRGB::Orange,
|
||||||
|
CRGB::Green,
|
||||||
|
CRGB::Blue,
|
||||||
|
CRGB::Yellow,
|
||||||
|
CRGB::Aqua,
|
||||||
|
CRGB::Red,
|
||||||
|
CRGB::YellowGreen
|
||||||
|
};
|
||||||
|
rgbIdleColor = colorMap[state];
|
||||||
|
rgbIdlePeriod = (isOnline ? 767 : 255);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Reset the tag
|
// Reset the tag
|
||||||
void APTagReset() {
|
void APTagReset() {
|
||||||
Serial.println("Resetting tag");
|
Serial.println("Resetting tag");
|
||||||
@@ -270,7 +288,7 @@ bool sendPing() {
|
|||||||
Serial.print("ping");
|
Serial.print("ping");
|
||||||
int t = millis();
|
int t = millis();
|
||||||
if (!txStart()) return false;
|
if (!txStart()) return false;
|
||||||
for (uint8_t attempt = 0; attempt < 5; attempt++) {
|
for (uint8_t attempt = 0; attempt < 3; attempt++) {
|
||||||
cmdReplyValue = CMD_REPLY_WAIT;
|
cmdReplyValue = CMD_REPLY_WAIT;
|
||||||
AP_SERIAL_PORT.print("RDY?");
|
AP_SERIAL_PORT.print("RDY?");
|
||||||
if (waitCmdReply()) {
|
if (waitCmdReply()) {
|
||||||
@@ -632,8 +650,7 @@ void notifySegmentedFlash() {
|
|||||||
void checkWaitPowerCycle() {
|
void checkWaitPowerCycle() {
|
||||||
// check if we should wait for a power cycle. If we do, try to inform the user the best we can, and hang.
|
// check if we should wait for a power cycle. If we do, try to inform the user the best we can, and hang.
|
||||||
#ifdef POWER_NO_SOFT_POWER
|
#ifdef POWER_NO_SOFT_POWER
|
||||||
apInfo.isOnline = false;
|
setAPstate(false, AP_STATE_REQUIRED_POWER_CYCLE);
|
||||||
apInfo.state = AP_STATE_REQUIRED_POWER_CYCLE;
|
|
||||||
// If we have no soft power control, we'll now wait until the device is power-cycled
|
// If we have no soft power control, we'll now wait until the device is power-cycled
|
||||||
Serial.printf("Please power-cycle your AP/device\n");
|
Serial.printf("Please power-cycle your AP/device\n");
|
||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
@@ -660,16 +677,11 @@ void segmentedShowIp() {
|
|||||||
|
|
||||||
bool bringAPOnline() {
|
bool bringAPOnline() {
|
||||||
if (apInfo.state == AP_STATE_FLASHING) return false;
|
if (apInfo.state == AP_STATE_FLASHING) return false;
|
||||||
apInfo.isOnline = false;
|
setAPstate(false, AP_STATE_OFFLINE);
|
||||||
apInfo.state = AP_STATE_OFFLINE;
|
|
||||||
// try without rebooting
|
// try without rebooting
|
||||||
AP_SERIAL_PORT.updateBaudRate(115200);
|
AP_SERIAL_PORT.updateBaudRate(115200);
|
||||||
uint32_t bootTimeout = millis();
|
uint32_t bootTimeout = millis();
|
||||||
bool APrdy = false;
|
bool APrdy = sendPing();
|
||||||
while ((!APrdy) && (millis() - bootTimeout < 3 * 1000)) {
|
|
||||||
APrdy = sendPing();
|
|
||||||
vTaskDelay(300 / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
if (!APrdy) {
|
if (!APrdy) {
|
||||||
if (apInfo.state == AP_STATE_FLASHING) return false;
|
if (apInfo.state == AP_STATE_FLASHING) return false;
|
||||||
APTagReset();
|
APTagReset();
|
||||||
@@ -684,11 +696,11 @@ bool bringAPOnline() {
|
|||||||
if (!APrdy) {
|
if (!APrdy) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
apInfo.state = AP_STATE_COMING_ONLINE;
|
setAPstate(false, AP_STATE_COMING_ONLINE);
|
||||||
sendChannelPower(&curChannel);
|
sendChannelPower(&curChannel);
|
||||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||||
if (!sendGetInfo()) {
|
if (!sendGetInfo()) {
|
||||||
apInfo.state = AP_STATE_OFFLINE;
|
setAPstate(false, AP_STATE_OFFLINE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (apInfo.type == ESP32_C6) {
|
if (apInfo.type == ESP32_C6) {
|
||||||
@@ -701,8 +713,7 @@ bool bringAPOnline() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||||
apInfo.isOnline = true;
|
setAPstate(true, AP_STATE_ONLINE);
|
||||||
apInfo.state = AP_STATE_ONLINE;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -735,8 +746,7 @@ void APTask(void* parameter) {
|
|||||||
Serial.printf("We're going to try to perform an 'AP forced flash' in\n");
|
Serial.printf("We're going to try to perform an 'AP forced flash' in\n");
|
||||||
flashCountDown(10);
|
flashCountDown(10);
|
||||||
Serial.printf("\nPerforming force flash of the AP\n");
|
Serial.printf("\nPerforming force flash of the AP\n");
|
||||||
apInfo.isOnline = false;
|
setAPstate(false, AP_STATE_FLASHING);
|
||||||
apInfo.state = AP_STATE_FLASHING;
|
|
||||||
doForcedAPFlash();
|
doForcedAPFlash();
|
||||||
checkWaitPowerCycle();
|
checkWaitPowerCycle();
|
||||||
bringAPOnline();
|
bringAPOnline();
|
||||||
@@ -747,10 +757,10 @@ void APTask(void* parameter) {
|
|||||||
ShowAPInfo();
|
ShowAPInfo();
|
||||||
|
|
||||||
if (apInfo.type == SOLUM_SEG_UK) {
|
if (apInfo.type == SOLUM_SEG_UK) {
|
||||||
apInfo.state = AP_STATE_COMING_ONLINE;
|
setAPstate(true, AP_STATE_COMING_ONLINE);
|
||||||
segmentedShowIp();
|
segmentedShowIp();
|
||||||
showAPSegmentedInfo(apInfo.mac, true);
|
showAPSegmentedInfo(apInfo.mac, true);
|
||||||
apInfo.state = AP_STATE_ONLINE;
|
setAPstate(true, AP_STATE_ONLINE);
|
||||||
updateContent(apInfo.mac);
|
updateContent(apInfo.mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -765,8 +775,7 @@ void APTask(void* parameter) {
|
|||||||
flashCountDown(30);
|
flashCountDown(30);
|
||||||
Serial.printf("\n");
|
Serial.printf("\n");
|
||||||
notifySegmentedFlash();
|
notifySegmentedFlash();
|
||||||
apInfo.isOnline = false;
|
setAPstate(false, AP_STATE_FLASHING);
|
||||||
apInfo.state = AP_STATE_FLASHING;
|
|
||||||
if (doAPUpdate(apInfo.type)) {
|
if (doAPUpdate(apInfo.type)) {
|
||||||
checkWaitPowerCycle();
|
checkWaitPowerCycle();
|
||||||
Serial.printf("Flash completed, let's try to boot the AP!\n");
|
Serial.printf("Flash completed, let's try to boot the AP!\n");
|
||||||
@@ -777,18 +786,18 @@ void APTask(void* parameter) {
|
|||||||
} else {
|
} else {
|
||||||
Serial.printf("Failed to bring up the AP after flashing seemed successful... That's not supposed to happen!\n");
|
Serial.printf("Failed to bring up the AP after flashing seemed successful... That's not supposed to happen!\n");
|
||||||
Serial.printf("This can be caused by a bad AP firmware, failed or failing hardware, or the inability to fully power-cycle the AP\n");
|
Serial.printf("This can be caused by a bad AP firmware, failed or failing hardware, or the inability to fully power-cycle the AP\n");
|
||||||
apInfo.state = AP_STATE_FAILED;
|
setAPstate(false, AP_STATE_FAILED);
|
||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
showColorPattern(CRGB::Red, CRGB::Yellow, CRGB::Red);
|
showColorPattern(CRGB::Red, CRGB::Yellow, CRGB::Red);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
apInfo.state = AP_STATE_FAILED;
|
setAPstate(false, AP_STATE_FAILED);
|
||||||
checkWaitPowerCycle();
|
checkWaitPowerCycle();
|
||||||
Serial.println("Failed to update version on the AP :(\n");
|
Serial.println("Failed to update version on the AP :(\n");
|
||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
showColorPattern(CRGB::Red, CRGB::Red, CRGB::Red);
|
showColorPattern(CRGB::Red, CRGB::Red, CRGB::Red);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -804,12 +813,10 @@ void APTask(void* parameter) {
|
|||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
showColorPattern(CRGB::Red, CRGB::Yellow, CRGB::Red);
|
showColorPattern(CRGB::Red, CRGB::Yellow, CRGB::Red);
|
||||||
#endif
|
#endif
|
||||||
apInfo.isOnline = false;
|
setAPstate(false, AP_STATE_FAILED);
|
||||||
apInfo.state = AP_STATE_FAILED;
|
|
||||||
} else {
|
} else {
|
||||||
// AP unavailable, maybe time to flash?
|
// AP unavailable, maybe time to flash?
|
||||||
apInfo.isOnline = false;
|
setAPstate(false, AP_STATE_OFFLINE);
|
||||||
apInfo.state = AP_STATE_OFFLINE;
|
|
||||||
|
|
||||||
Serial.printf("I wasn't able to connect to a ZBS (AP) tag.\n");
|
Serial.printf("I wasn't able to connect to a ZBS (AP) tag.\n");
|
||||||
Serial.printf("This could be the first time this AP is booted and the AP-tag may be unflashed.\n");
|
Serial.printf("This could be the first time this AP is booted and the AP-tag may be unflashed.\n");
|
||||||
@@ -840,16 +847,14 @@ void APTask(void* parameter) {
|
|||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
showColorPattern(CRGB::Red, CRGB::Yellow, CRGB::Red);
|
showColorPattern(CRGB::Red, CRGB::Yellow, CRGB::Red);
|
||||||
#endif
|
#endif
|
||||||
apInfo.isOnline = false;
|
setAPstate(false, AP_STATE_FAILED);
|
||||||
apInfo.state = AP_STATE_FAILED;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// failed to flash
|
// failed to flash
|
||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
showColorPattern(CRGB::Red, CRGB::Red, CRGB::Red);
|
showColorPattern(CRGB::Red, CRGB::Red, CRGB::Red);
|
||||||
#endif
|
#endif
|
||||||
apInfo.isOnline = false;
|
setAPstate(false, AP_STATE_FAILED);
|
||||||
apInfo.state = AP_STATE_FAILED;
|
|
||||||
Serial.println("Failed to flash the AP :(");
|
Serial.println("Failed to flash the AP :(");
|
||||||
Serial.println("Seems like you're running into some issues with the wiring, or (very small chance) the tag itself");
|
Serial.println("Seems like you're running into some issues with the wiring, or (very small chance) the tag itself");
|
||||||
Serial.println("This ESP32-build expects the following pins connected to the ZBS243:");
|
Serial.println("This ESP32-build expects the following pins connected to the ZBS243:");
|
||||||
@@ -890,17 +895,15 @@ void APTask(void* parameter) {
|
|||||||
attempts = 0;
|
attempts = 0;
|
||||||
}
|
}
|
||||||
if (attempts > 5) {
|
if (attempts > 5) {
|
||||||
apInfo.state = AP_STATE_WAIT_RESET;
|
setAPstate(false, AP_STATE_WAIT_RESET);
|
||||||
apInfo.isOnline = false;
|
|
||||||
if (!bringAPOnline()) {
|
if (!bringAPOnline()) {
|
||||||
// tried to reset the AP, but we failed... Maybe the AP-Tag died?
|
// tried to reset the AP, but we failed... Maybe the AP-Tag died?
|
||||||
apInfo.state = AP_STATE_FAILED;
|
setAPstate(false, AP_STATE_FAILED);
|
||||||
#ifdef HAS_RGB_LED
|
#ifdef HAS_RGB_LED
|
||||||
showColorPattern(CRGB::Yellow, CRGB::Yellow, CRGB::Red);
|
showColorPattern(CRGB::Yellow, CRGB::Yellow, CRGB::Red);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
apInfo.state = AP_STATE_ONLINE;
|
setAPstate(true, AP_STATE_ONLINE);
|
||||||
apInfo.isOnline = true;
|
|
||||||
attempts = 0;
|
attempts = 0;
|
||||||
refreshAllPending();
|
refreshAllPending();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
DynStorage::DynStorage() : isInited(0) {}
|
DynStorage::DynStorage() : isInited(0) {}
|
||||||
|
|
||||||
|
SemaphoreHandle_t fsMutex;
|
||||||
|
|
||||||
static void initLittleFS() {
|
static void initLittleFS() {
|
||||||
LittleFS.begin();
|
LittleFS.begin();
|
||||||
contentFS = &LittleFS;
|
contentFS = &LittleFS;
|
||||||
@@ -44,7 +46,7 @@ static void initSDCard() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
size_t DynStorage::freeSpace(){
|
uint64_t DynStorage::freeSpace(){
|
||||||
this->begin();
|
this->begin();
|
||||||
#ifdef HAS_SDCARD
|
#ifdef HAS_SDCARD
|
||||||
return SD.totalBytes() - SD.usedBytes();
|
return SD.totalBytes() - SD.usedBytes();
|
||||||
@@ -86,12 +88,15 @@ void copyBetweenFS(FS& sourceFS, const char* source_path, FS& targetFS) {
|
|||||||
|
|
||||||
copyBetweenFS(sourceFS, file.path(), targetFS);
|
copyBetweenFS(sourceFS, file.path(), targetFS);
|
||||||
} else {
|
} else {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File target = contentFS->open(file.path(), "w");
|
File target = contentFS->open(file.path(), "w");
|
||||||
if (target) {
|
if (target) {
|
||||||
copyFile(file, target);
|
copyFile(file, target);
|
||||||
target.close();
|
target.close();
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
} else {
|
} else {
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
Serial.print("Couldn't create high target file");
|
Serial.print("Couldn't create high target file");
|
||||||
Serial.println(file.path());
|
Serial.println(file.path());
|
||||||
return;
|
return;
|
||||||
@@ -100,10 +105,14 @@ void copyBetweenFS(FS& sourceFS, const char* source_path, FS& targetFS) {
|
|||||||
file = root.openNextFile();
|
file = root.openNextFile();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File target = contentFS->open(root.path(), "w");
|
File target = contentFS->open(root.path(), "w");
|
||||||
if (target) {
|
if (target) {
|
||||||
copyFile(root, target);
|
copyFile(root, target);
|
||||||
|
target.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
} else {
|
} else {
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
Serial.print("Couldn't create target file ");
|
Serial.print("Couldn't create target file ");
|
||||||
Serial.println(root.path());
|
Serial.println(root.path());
|
||||||
return;
|
return;
|
||||||
@@ -120,6 +129,7 @@ void copyIfNeeded(const char* path) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void DynStorage::begin() {
|
void DynStorage::begin() {
|
||||||
|
fsMutex = xSemaphoreCreateMutex();
|
||||||
initLittleFS();
|
initLittleFS();
|
||||||
|
|
||||||
#ifdef HAS_SDCARD
|
#ifdef HAS_SDCARD
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ void initTime(void* parameter) {
|
|||||||
sntp_set_time_sync_notification_cb(timeSyncCallback);
|
sntp_set_time_sync_notification_cb(timeSyncCallback);
|
||||||
sntp_set_sync_interval(300 * 1000);
|
sntp_set_sync_interval(300 * 1000);
|
||||||
configTzTime(config.timeZone, "nl.pool.ntp.org", "europe.pool.ntp.org", "time.nist.gov");
|
configTzTime(config.timeZone, "nl.pool.ntp.org", "europe.pool.ntp.org", "time.nist.gov");
|
||||||
|
logStartUp();
|
||||||
struct tm timeinfo;
|
struct tm timeinfo;
|
||||||
while (millis() < 30000) {
|
while (millis() < 30000) {
|
||||||
if (!getLocalTime(&timeinfo)) {
|
if (!getLocalTime(&timeinfo)) {
|
||||||
@@ -25,8 +26,9 @@ void initTime(void* parameter) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logStartUp();
|
if (config.runStatus == RUNSTATUS_INIT) {
|
||||||
if (config.runStatus = RUNSTATUS_INIT) config.runStatus = RUNSTATUS_RUN;
|
config.runStatus = RUNSTATUS_RUN;
|
||||||
|
}
|
||||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
}
|
}
|
||||||
@@ -35,7 +37,7 @@ void logLine(const char* buffer) {
|
|||||||
logLine(String(buffer));
|
logLine(String(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
void logLine(String text) {
|
void logLine(const String& text) {
|
||||||
time_t now;
|
time_t now;
|
||||||
time(&now);
|
time(&now);
|
||||||
|
|
||||||
@@ -43,6 +45,7 @@ void logLine(String text) {
|
|||||||
const char* format = (now < (time_t)1672531200) ? " %H:%M:%S " : "%Y-%m-%d %H:%M:%S ";
|
const char* format = (now < (time_t)1672531200) ? " %H:%M:%S " : "%Y-%m-%d %H:%M:%S ";
|
||||||
strftime(timeStr, sizeof(timeStr), format, localtime(&now));
|
strftime(timeStr, sizeof(timeStr), format, localtime(&now));
|
||||||
|
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File logFile = contentFS->open("/log.txt", "a");
|
File logFile = contentFS->open("/log.txt", "a");
|
||||||
if (logFile) {
|
if (logFile) {
|
||||||
if (logFile.size() >= 10 * 1024) {
|
if (logFile.size() >= 10 * 1024) {
|
||||||
@@ -57,6 +60,7 @@ void logLine(String text) {
|
|||||||
logFile.println(text);
|
logFile.println(text);
|
||||||
logFile.close();
|
logFile.close();
|
||||||
}
|
}
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void logStartUp() {
|
void logStartUp() {
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
#define STR_IMPL(x) #x
|
||||||
|
#define STR(x) STR_IMPL(x)
|
||||||
|
|
||||||
std::vector<tagRecord*> tagDB;
|
std::vector<tagRecord*> tagDB;
|
||||||
std::unordered_map<std::string, varStruct> varDB;
|
std::unordered_map<std::string, varStruct> varDB;
|
||||||
std::unordered_map<int, HwType> hwdata = {
|
std::unordered_map<int, HwType> hwdata = {
|
||||||
@@ -19,7 +22,6 @@ std::unordered_map<int, HwType> hwdata = {
|
|||||||
{2, {400, 300, 0, 2}}};
|
{2, {400, 300, 0, 2}}};
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
// SemaphoreHandle_t tagDBOwner;
|
|
||||||
|
|
||||||
tagRecord* tagRecord::findByMAC(const uint8_t mac[8]) {
|
tagRecord* tagRecord::findByMAC(const uint8_t mac[8]) {
|
||||||
for (tagRecord* tag : tagDB) {
|
for (tagRecord* tag : tagDB) {
|
||||||
@@ -116,8 +118,10 @@ void fillNode(JsonObject& tag, const tagRecord* taginfo) {
|
|||||||
tag["capabilities"] = taginfo->capabilities;
|
tag["capabilities"] = taginfo->capabilities;
|
||||||
tag["modecfgjson"] = taginfo->modeConfigJson;
|
tag["modecfgjson"] = taginfo->modeConfigJson;
|
||||||
tag["isexternal"] = taginfo->isExternal;
|
tag["isexternal"] = taginfo->isExternal;
|
||||||
|
tag["apip"] = taginfo->apIp.toString();
|
||||||
tag["rotate"] = taginfo->rotate;
|
tag["rotate"] = taginfo->rotate;
|
||||||
tag["lut"] = taginfo->lut;
|
tag["lut"] = taginfo->lut;
|
||||||
|
tag["invert"] = taginfo->invert;
|
||||||
tag["ch"] = taginfo->currentChannel;
|
tag["ch"] = taginfo->currentChannel;
|
||||||
tag["ver"] = taginfo->tagSoftwareVersion;
|
tag["ver"] = taginfo->tagSoftwareVersion;
|
||||||
}
|
}
|
||||||
@@ -128,9 +132,11 @@ void saveDB(const String& filename) {
|
|||||||
const long t = millis();
|
const long t = millis();
|
||||||
|
|
||||||
Storage.begin();
|
Storage.begin();
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
fs::File file = contentFS->open(filename, "w");
|
fs::File file = contentFS->open(filename, "w");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
Serial.println("saveDB: Failed to open file");
|
Serial.println("saveDB: Failed to open file");
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +155,7 @@ void saveDB(const String& filename) {
|
|||||||
file.write(']');
|
file.write(']');
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
Serial.println("DB saved " + String(millis() - t) + "ms");
|
Serial.println("DB saved " + String(millis() - t) + "ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +175,7 @@ void loadDB(const String& filename) {
|
|||||||
bool parsing = true;
|
bool parsing = true;
|
||||||
|
|
||||||
if (readfile.find("[")) {
|
if (readfile.find("[")) {
|
||||||
StaticJsonDocument<1000> doc;
|
DynamicJsonDocument doc(1000);
|
||||||
while (parsing) {
|
while (parsing) {
|
||||||
DeserializationError err = deserializeJson(doc, readfile);
|
DeserializationError err = deserializeJson(doc, readfile);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
@@ -207,8 +214,10 @@ void loadDB(const String& filename) {
|
|||||||
taginfo->capabilities = tag["capabilities"];
|
taginfo->capabilities = tag["capabilities"];
|
||||||
taginfo->modeConfigJson = tag["modecfgjson"].as<String>();
|
taginfo->modeConfigJson = tag["modecfgjson"].as<String>();
|
||||||
taginfo->isExternal = tag["isexternal"].as<bool>();
|
taginfo->isExternal = tag["isexternal"].as<bool>();
|
||||||
|
taginfo->apIp.fromString(tag["apip"].as<String>());
|
||||||
taginfo->rotate = tag["rotate"] | 0;
|
taginfo->rotate = tag["rotate"] | 0;
|
||||||
taginfo->lut = tag["lut"] | 0;
|
taginfo->lut = tag["lut"] | 0;
|
||||||
|
taginfo->invert = tag["invert"] | 0;
|
||||||
taginfo->currentChannel = tag["ch"] | 0;
|
taginfo->currentChannel = tag["ch"] | 0;
|
||||||
taginfo->tagSoftwareVersion = tag["ver"] | 0;
|
taginfo->tagSoftwareVersion = tag["ver"] | 0;
|
||||||
}
|
}
|
||||||
@@ -255,7 +264,7 @@ uint32_t getTagCount(uint32_t& timeoutcount) {
|
|||||||
// not initialised, timeout if not seen last 10 minutes
|
// not initialised, timeout if not seen last 10 minutes
|
||||||
if (timeout > 600) timeoutcount++;
|
if (timeout > 600) timeoutcount++;
|
||||||
} else if (now - taginfo->expectedNextCheckin > 600) {
|
} else if (now - taginfo->expectedNextCheckin > 600) {
|
||||||
//expected checkin is behind, timeout if not seen last 10 minutes
|
// expected checkin is behind, timeout if not seen last 10 minutes
|
||||||
if (timeout > 600) timeoutcount++;
|
if (timeout > 600) timeoutcount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,6 +305,7 @@ void initAPconfig() {
|
|||||||
config.channel = APconfig["channel"] | 0;
|
config.channel = APconfig["channel"] | 0;
|
||||||
if (APconfig["alias"]) strlcpy(config.alias, APconfig["alias"], sizeof(config.alias));
|
if (APconfig["alias"]) strlcpy(config.alias, APconfig["alias"], sizeof(config.alias));
|
||||||
config.led = APconfig["led"] | 255;
|
config.led = APconfig["led"] | 255;
|
||||||
|
config.tft = APconfig["tft"] | 255;
|
||||||
config.language = APconfig["language"] | getDefaultLanguage();
|
config.language = APconfig["language"] | getDefaultLanguage();
|
||||||
config.maxsleep = APconfig["maxsleep"] | 10;
|
config.maxsleep = APconfig["maxsleep"] | 10;
|
||||||
config.stopsleep = APconfig["stopsleep"] | 1;
|
config.stopsleep = APconfig["stopsleep"] | 1;
|
||||||
@@ -305,6 +315,8 @@ void initAPconfig() {
|
|||||||
// default wifi power 8.5 dbM
|
// default wifi power 8.5 dbM
|
||||||
// see https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/src/WiFiGeneric.h#L111
|
// see https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/src/WiFiGeneric.h#L111
|
||||||
config.wifiPower = APconfig["wifipower"] | 34;
|
config.wifiPower = APconfig["wifipower"] | 34;
|
||||||
|
config.repo = APconfig["repo"] | "jjwbruijn/OpenEPaperLink";
|
||||||
|
config.env = APconfig["env"] | STR(BUILD_ENV_NAME);
|
||||||
if (APconfig["timezone"]) {
|
if (APconfig["timezone"]) {
|
||||||
strlcpy(config.timeZone, APconfig["timezone"], sizeof(config.timeZone));
|
strlcpy(config.timeZone, APconfig["timezone"], sizeof(config.timeZone));
|
||||||
} else {
|
} else {
|
||||||
@@ -313,11 +325,13 @@ void initAPconfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void saveAPconfig() {
|
void saveAPconfig() {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
fs::File configFile = contentFS->open("/current/apconfig.json", "w");
|
fs::File configFile = contentFS->open("/current/apconfig.json", "w");
|
||||||
DynamicJsonDocument APconfig(500);
|
DynamicJsonDocument APconfig(500);
|
||||||
APconfig["channel"] = config.channel;
|
APconfig["channel"] = config.channel;
|
||||||
APconfig["alias"] = config.alias;
|
APconfig["alias"] = config.alias;
|
||||||
APconfig["led"] = config.led;
|
APconfig["led"] = config.led;
|
||||||
|
APconfig["tft"] = config.tft;
|
||||||
APconfig["language"] = config.language;
|
APconfig["language"] = config.language;
|
||||||
APconfig["maxsleep"] = config.maxsleep;
|
APconfig["maxsleep"] = config.maxsleep;
|
||||||
APconfig["stopsleep"] = config.stopsleep;
|
APconfig["stopsleep"] = config.stopsleep;
|
||||||
@@ -326,8 +340,11 @@ void saveAPconfig() {
|
|||||||
APconfig["timezone"] = config.timeZone;
|
APconfig["timezone"] = config.timeZone;
|
||||||
APconfig["sleeptime1"] = config.sleepTime1;
|
APconfig["sleeptime1"] = config.sleepTime1;
|
||||||
APconfig["sleeptime2"] = config.sleepTime2;
|
APconfig["sleeptime2"] = config.sleepTime2;
|
||||||
|
APconfig["repo"] = config.repo;
|
||||||
|
APconfig["env"] = config.env;
|
||||||
serializeJsonPretty(APconfig, configFile);
|
serializeJsonPretty(APconfig, configFile);
|
||||||
configFile.close();
|
configFile.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
HwType getHwType(const uint8_t id) {
|
HwType getHwType(const uint8_t id) {
|
||||||
@@ -366,19 +383,19 @@ HwType getHwType(const uint8_t id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setVarDB(const std::string& key, const String& value) {
|
bool setVarDB(const std::string& key, const String& value, const bool notify) {
|
||||||
auto it = varDB.find(key);
|
auto it = varDB.find(key);
|
||||||
if (it == varDB.end()) {
|
if (it == varDB.end()) {
|
||||||
varStruct newVar;
|
varStruct newVar;
|
||||||
newVar.value = value;
|
newVar.value = value;
|
||||||
newVar.changed = true;
|
newVar.changed = notify;
|
||||||
varDB[key] = newVar;
|
varDB[key] = newVar;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (it->second.value != value) {
|
if (it->second.value != value) {
|
||||||
it->second.value = value;
|
it->second.value = value;
|
||||||
it->second.changed = true;
|
it->second.changed = notify;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
145
ESP32_AP-Flasher/src/tagdata.cpp
Normal file
145
ESP32_AP-Flasher/src/tagdata.cpp
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
#include "tagdata.h"
|
||||||
|
|
||||||
|
#include "tag_db.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
std::unordered_map<size_t, TagData::Parser> TagData::parsers = {};
|
||||||
|
|
||||||
|
void TagData::loadParsers(const String& filename) {
|
||||||
|
const long start = millis();
|
||||||
|
|
||||||
|
Storage.begin();
|
||||||
|
fs::File file = contentFS->open(filename, "r");
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Serial.println("Reading parsers from file");
|
||||||
|
|
||||||
|
if (file.find("[")) {
|
||||||
|
DynamicJsonDocument doc(1000);
|
||||||
|
bool parsing = true;
|
||||||
|
while (parsing) {
|
||||||
|
DeserializationError err = deserializeJson(doc, file);
|
||||||
|
if (!err) {
|
||||||
|
const JsonObject parserDoc = doc[0];
|
||||||
|
const auto& id = parserDoc["id"];
|
||||||
|
const auto& name = parserDoc["name"];
|
||||||
|
if (!id || !name) {
|
||||||
|
Serial.printf("Error: Parser must have name and id\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Parser parser;
|
||||||
|
parser.name = name.as<String>();
|
||||||
|
|
||||||
|
for (const auto& parserField : parserDoc["parser"].as<JsonArray>()) {
|
||||||
|
const uint8_t type = parserField["type"].as<uint8_t>();
|
||||||
|
if (type >= (uint8_t)Type::MAX) {
|
||||||
|
Serial.printf("Error: Type %d is not a valid tag data parser data type\n", type);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& mult = parserField["mult"];
|
||||||
|
const uint8_t decimals = parserField["decimals"].as<uint8_t>();
|
||||||
|
if (mult) {
|
||||||
|
parser.fields.emplace_back(parserField["name"].as<String>(),
|
||||||
|
static_cast<Type>(type),
|
||||||
|
parserField["length"].as<uint8_t>(),
|
||||||
|
decimals,
|
||||||
|
std::make_optional(mult.as<double>()));
|
||||||
|
} else {
|
||||||
|
parser.fields.emplace_back(parserField["name"].as<String>(),
|
||||||
|
static_cast<Type>(type),
|
||||||
|
parserField["length"].as<uint8_t>(),
|
||||||
|
decimals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parsers.emplace(id.as<uint8_t>(), parser);
|
||||||
|
} else {
|
||||||
|
Serial.print(F("deserializeJson() failed: "));
|
||||||
|
Serial.println(err.c_str());
|
||||||
|
parsing = false;
|
||||||
|
}
|
||||||
|
parsing = parsing && file.find(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
Serial.printf("Loaded %d parsers in %d ms\n", parsers.size(), millis() - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TagData::parse(const uint8_t src[8], const size_t id, const uint8_t* data, const uint8_t len) {
|
||||||
|
char buffer[64];
|
||||||
|
|
||||||
|
const auto it = parsers.find(id);
|
||||||
|
if (it == parsers.end()) {
|
||||||
|
const String log = util::formatString<64>(buffer, "Error: No parser with id %d found(%d)", id, parsers.size());
|
||||||
|
wsErr(log);
|
||||||
|
Serial.println(log);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String mac = util::formatString<64>(buffer, "%02X%02X%02X%02X%02X%02X%02X%02X.", src[7], src[6], src[5], src[4], src[3], src[2], src[1], src[0]);
|
||||||
|
|
||||||
|
uint16_t offset = 0;
|
||||||
|
for (const Field& field : it->second.fields) {
|
||||||
|
const String& name = field.name;
|
||||||
|
const uint8_t length = field.length;
|
||||||
|
|
||||||
|
if (offset + length > len) {
|
||||||
|
const String log = util::formatString<64>(buffer, "Error: Not enough data for field %s", name.c_str());
|
||||||
|
wsErr(log);
|
||||||
|
Serial.println(log);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Type type = field.type;
|
||||||
|
const uint8_t* fieldData = data + offset;
|
||||||
|
offset += length;
|
||||||
|
String value = "";
|
||||||
|
switch (type) {
|
||||||
|
case Type::INT: {
|
||||||
|
const double mult = field.mult.value_or(1.0);
|
||||||
|
value = String(bytesTo<int64_t>(fieldData, length) * mult, (unsigned int)field.decimals);
|
||||||
|
} break;
|
||||||
|
case Type::UINT: {
|
||||||
|
const double mult = field.mult.value_or(1.0f);
|
||||||
|
value = String(bytesTo<uint64_t>(fieldData, length) * mult, (unsigned int)field.decimals);
|
||||||
|
} break;
|
||||||
|
case Type::FLOAT: {
|
||||||
|
const double mult = field.mult.value_or(1.0f);
|
||||||
|
|
||||||
|
if (length == 4) {
|
||||||
|
value = String(bytesTo<float>(fieldData, length) * mult, (unsigned int)field.decimals);
|
||||||
|
} else if (length == 8) {
|
||||||
|
value = String(bytesTo<double>(fieldData, length) * mult, (unsigned int)field.decimals);
|
||||||
|
} else {
|
||||||
|
const String log = "Error: Float can only be 4 or 8 bytes long";
|
||||||
|
wsErr(log);
|
||||||
|
Serial.println(log);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Type::STRING: {
|
||||||
|
value = bytesTo<String>(fieldData, length);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
const String log = util::formatString<64>(buffer, "Error: Type %d not implemented", static_cast<uint8_t>(type));
|
||||||
|
wsErr(log);
|
||||||
|
Serial.println(log);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
const String log = util::formatString<64>(buffer, "Error: Empty value for field %s", name.c_str());
|
||||||
|
wsErr(log);
|
||||||
|
Serial.println(log);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string varName = (mac + name).c_str();
|
||||||
|
setVarDB(varName, value);
|
||||||
|
Serial.printf("Set %s to %s\n", varName.c_str(), value.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
|
#include "udp.h"
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
|
||||||
#include "AsyncUDP.h"
|
#include "AsyncUDP.h"
|
||||||
#include "commstructs.h"
|
#include "commstructs.h"
|
||||||
#include "newproto.h"
|
#include "newproto.h"
|
||||||
|
#include "serialap.h"
|
||||||
#include "tag_db.h"
|
#include "tag_db.h"
|
||||||
#include "web.h"
|
#include "web.h"
|
||||||
#include "serialap.h"
|
|
||||||
#include "udp.h"
|
|
||||||
|
|
||||||
#define UDPIP IPAddress(239, 10, 0, 1)
|
#define UDPIP IPAddress(239, 10, 0, 1)
|
||||||
#define UDPPORT 16033
|
#define UDPPORT 16033
|
||||||
@@ -41,8 +42,9 @@ void UDPcomm::init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UDPcomm::processPacket(AsyncUDPPacket packet) {
|
void UDPcomm::processPacket(AsyncUDPPacket packet) {
|
||||||
|
if (config.runStatus == RUNSTATUS_STOP) {
|
||||||
if (config.runStatus == RUNSTATUS_STOP) return;
|
return;
|
||||||
|
}
|
||||||
IPAddress senderIP = packet.remoteIP();
|
IPAddress senderIP = packet.remoteIP();
|
||||||
|
|
||||||
switch (packet.data()[0]) {
|
switch (packet.data()[0]) {
|
||||||
@@ -50,7 +52,7 @@ void UDPcomm::processPacket(AsyncUDPPacket packet) {
|
|||||||
espAvailDataReq adr;
|
espAvailDataReq adr;
|
||||||
memset(&adr, 0, sizeof(espAvailDataReq));
|
memset(&adr, 0, sizeof(espAvailDataReq));
|
||||||
memcpy(&adr, &packet.data()[1], std::min(packet.length() - 1, sizeof(espAvailDataReq)));
|
memcpy(&adr, &packet.data()[1], std::min(packet.length() - 1, sizeof(espAvailDataReq)));
|
||||||
processDataReq(&adr, false);
|
processDataReq(&adr, false, senderIP);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PKT_XFER_COMPLETE: {
|
case PKT_XFER_COMPLETE: {
|
||||||
@@ -127,7 +129,7 @@ void autoselect(void* pvParameters) {
|
|||||||
}
|
}
|
||||||
if (curChannel.channel == 0) {
|
if (curChannel.channel == 0) {
|
||||||
curChannel.channel = 11;
|
curChannel.channel = 11;
|
||||||
}
|
}
|
||||||
config.channel = curChannel.channel;
|
config.channel = curChannel.channel;
|
||||||
do {
|
do {
|
||||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
|||||||
@@ -20,14 +20,13 @@
|
|||||||
#include "serialap.h"
|
#include "serialap.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
|
#include "system.h"
|
||||||
#include "tag_db.h"
|
#include "tag_db.h"
|
||||||
#include "udp.h"
|
#include "udp.h"
|
||||||
#include "wifimanager.h"
|
#include "wifimanager.h"
|
||||||
|
|
||||||
extern uint8_t data_to_send[];
|
extern uint8_t data_to_send[];
|
||||||
|
|
||||||
// const char *http_username = "admin";
|
|
||||||
// const char *http_password = "admin";
|
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
AsyncWebSocket ws("/ws");
|
AsyncWebSocket ws("/ws");
|
||||||
WifiManager wm;
|
WifiManager wm;
|
||||||
@@ -35,16 +34,7 @@ WifiManager wm;
|
|||||||
SemaphoreHandle_t wsMutex;
|
SemaphoreHandle_t wsMutex;
|
||||||
uint32_t lastssidscan = 0;
|
uint32_t lastssidscan = 0;
|
||||||
|
|
||||||
void networkProcess(void *parameter) {
|
void wsLog(const String &text) {
|
||||||
wsMutex = xSemaphoreCreateMutex();
|
|
||||||
while (true) {
|
|
||||||
ws.cleanupClients();
|
|
||||||
wm.poll();
|
|
||||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void wsLog(String text) {
|
|
||||||
StaticJsonDocument<250> doc;
|
StaticJsonDocument<250> doc;
|
||||||
doc["logMsg"] = text;
|
doc["logMsg"] = text;
|
||||||
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
|
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
|
||||||
@@ -52,7 +42,7 @@ void wsLog(String text) {
|
|||||||
if (wsMutex) xSemaphoreGive(wsMutex);
|
if (wsMutex) xSemaphoreGive(wsMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wsErr(String text) {
|
void wsErr(const String &text) {
|
||||||
StaticJsonDocument<250> doc;
|
StaticJsonDocument<250> doc;
|
||||||
doc["errMsg"] = text;
|
doc["errMsg"] = text;
|
||||||
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
|
if (wsMutex) xSemaphoreTake(wsMutex, portMAX_DELAY);
|
||||||
@@ -77,20 +67,27 @@ void wsSendSysteminfo() {
|
|||||||
time_t now;
|
time_t now;
|
||||||
time(&now);
|
time(&now);
|
||||||
static int freeSpaceLastRun = 0;
|
static int freeSpaceLastRun = 0;
|
||||||
static size_t freeSpace = Storage.freeSpace();
|
static size_t tagDBsize = 0;
|
||||||
|
static uint64_t freeSpace = Storage.freeSpace();
|
||||||
sys["currtime"] = now;
|
sys["currtime"] = now;
|
||||||
sys["heap"] = ESP.getFreeHeap();
|
sys["heap"] = ESP.getFreeHeap();
|
||||||
sys["recordcount"] = tagDB.size();
|
sys["recordcount"] = tagDBsize;
|
||||||
sys["dbsize"] = dbSize();
|
sys["dbsize"] = dbSize();
|
||||||
if (millis() - freeSpaceLastRun > 30000) {
|
if (millis() - freeSpaceLastRun > 30000 || freeSpaceLastRun == 0) {
|
||||||
freeSpace = Storage.freeSpace();
|
freeSpace = Storage.freeSpace();
|
||||||
|
tagDBsize = tagDB.size();
|
||||||
freeSpaceLastRun = millis();
|
freeSpaceLastRun = millis();
|
||||||
}
|
}
|
||||||
sys["littlefsfree"] = freeSpace;
|
sys["littlefsfree"] = freeSpace;
|
||||||
|
|
||||||
|
#if BOARD_HAS_PSRAM
|
||||||
|
sys["psfree"] = ESP.getFreePsram();
|
||||||
|
#endif
|
||||||
|
|
||||||
sys["apstate"] = apInfo.state;
|
sys["apstate"] = apInfo.state;
|
||||||
sys["runstate"] = config.runStatus;
|
sys["runstate"] = config.runStatus;
|
||||||
#if !defined(CONFIG_IDF_TARGET_ESP32)
|
#if !defined(CONFIG_IDF_TARGET_ESP32)
|
||||||
sys["temp"] = temperatureRead();
|
// sys["temp"] = temperatureRead();
|
||||||
#endif
|
#endif
|
||||||
sys["rssi"] = WiFi.RSSI();
|
sys["rssi"] = WiFi.RSSI();
|
||||||
sys["wifistatus"] = WiFi.status();
|
sys["wifistatus"] = WiFi.status();
|
||||||
@@ -105,8 +102,14 @@ void wsSendSysteminfo() {
|
|||||||
uint32_t tagcount = getTagCount(timeoutcount);
|
uint32_t tagcount = getTagCount(timeoutcount);
|
||||||
char result[40];
|
char result[40];
|
||||||
if (timeoutcount > 0) {
|
if (timeoutcount > 0) {
|
||||||
|
#ifdef HAS_RGB_LED
|
||||||
|
if (apInfo.state == AP_STATE_ONLINE && apInfo.isOnline == true) rgbIdleColor = CRGB::DarkBlue;
|
||||||
|
#endif
|
||||||
snprintf(result, sizeof(result), "%lu / %lu, %lu timed out", tagcount, tagDB.size(), timeoutcount);
|
snprintf(result, sizeof(result), "%lu / %lu, %lu timed out", tagcount, tagDB.size(), timeoutcount);
|
||||||
} else {
|
} else {
|
||||||
|
#ifdef HAS_RGB_LED
|
||||||
|
if (apInfo.state == AP_STATE_ONLINE && apInfo.isOnline == true) rgbIdleColor = CRGB::Green;
|
||||||
|
#endif
|
||||||
snprintf(result, sizeof(result), "%lu / %lu", tagcount, tagDB.size());
|
snprintf(result, sizeof(result), "%lu / %lu", tagcount, tagDB.size());
|
||||||
}
|
}
|
||||||
setVarDB("ap_tagcount", result);
|
setVarDB("ap_tagcount", result);
|
||||||
@@ -174,7 +177,7 @@ void wsSendAPitem(struct APlist *apitem) {
|
|||||||
if (wsMutex) xSemaphoreGive(wsMutex);
|
if (wsMutex) xSemaphoreGive(wsMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wsSerial(String text) {
|
void wsSerial(const String &text) {
|
||||||
StaticJsonDocument<250> doc;
|
StaticJsonDocument<250> doc;
|
||||||
doc["console"] = text;
|
doc["console"] = text;
|
||||||
Serial.println(text);
|
Serial.println(text);
|
||||||
@@ -188,14 +191,13 @@ uint8_t wsClientCount() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init_web() {
|
void init_web() {
|
||||||
Storage.begin();
|
wsMutex = xSemaphoreCreateMutex();
|
||||||
WiFi.mode(WIFI_STA);
|
|
||||||
|
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
WiFi.setTxPower(static_cast<wifi_power_t>(config.wifiPower));
|
WiFi.setTxPower(static_cast<wifi_power_t>(config.wifiPower));
|
||||||
|
|
||||||
wm.connectToWifi();
|
wm.connectToWifi();
|
||||||
|
|
||||||
// server.addHandler(new SPIFFSEditor(*contentFS, http_username, http_password));
|
|
||||||
server.addHandler(new SPIFFSEditor(*contentFS));
|
server.addHandler(new SPIFFSEditor(*contentFS));
|
||||||
|
|
||||||
server.addHandler(&ws);
|
server.addHandler(&ws);
|
||||||
@@ -276,6 +278,9 @@ void init_web() {
|
|||||||
if (request->hasParam("lut", true)) {
|
if (request->hasParam("lut", true)) {
|
||||||
taginfo->lut = atoi(request->getParam("lut", true)->value().c_str());
|
taginfo->lut = atoi(request->getParam("lut", true)->value().c_str());
|
||||||
}
|
}
|
||||||
|
if (request->hasParam("invert", true)) {
|
||||||
|
taginfo->invert = atoi(request->getParam("invert", true)->value().c_str());
|
||||||
|
}
|
||||||
// memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
|
// memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
|
||||||
// memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
|
// memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
|
||||||
wsSendTaginfo(mac, SYNC_USERCFG);
|
wsSendTaginfo(mac, SYNC_USERCFG);
|
||||||
@@ -322,6 +327,7 @@ void init_web() {
|
|||||||
}
|
}
|
||||||
if (strcmp(cmdValue, "ledflash") == 0) {
|
if (strcmp(cmdValue, "ledflash") == 0) {
|
||||||
struct ledFlash flashData = {0};
|
struct ledFlash flashData = {0};
|
||||||
|
flashData.mode = 1;
|
||||||
flashData.flashDuration = 8;
|
flashData.flashDuration = 8;
|
||||||
flashData.color1 = 0x3C; // green
|
flashData.color1 = 0x3C; // green
|
||||||
flashData.color2 = 0xE4; // red
|
flashData.color2 = 0xE4; // red
|
||||||
@@ -338,6 +344,7 @@ void init_web() {
|
|||||||
}
|
}
|
||||||
if (strcmp(cmdValue, "ledflash_long") == 0) {
|
if (strcmp(cmdValue, "ledflash_long") == 0) {
|
||||||
struct ledFlash flashData = {0};
|
struct ledFlash flashData = {0};
|
||||||
|
flashData.mode = 1;
|
||||||
flashData.flashDuration = 15;
|
flashData.flashDuration = 15;
|
||||||
flashData.color1 = 0xE4; // red
|
flashData.color1 = 0xE4; // red
|
||||||
flashData.flashCount1 = 5;
|
flashData.flashCount1 = 5;
|
||||||
@@ -347,6 +354,12 @@ void init_web() {
|
|||||||
const uint8_t *payload = reinterpret_cast<const uint8_t *>(&flashData);
|
const uint8_t *payload = reinterpret_cast<const uint8_t *>(&flashData);
|
||||||
sendTagCommand(mac, CMD_DO_LEDFLASH, !taginfo->isExternal, payload);
|
sendTagCommand(mac, CMD_DO_LEDFLASH, !taginfo->isExternal, payload);
|
||||||
}
|
}
|
||||||
|
if (strcmp(cmdValue, "ledflash_stop") == 0) {
|
||||||
|
struct ledFlash flashData = {0};
|
||||||
|
flashData.mode = 0;
|
||||||
|
const uint8_t *payload = reinterpret_cast<const uint8_t *>(&flashData);
|
||||||
|
sendTagCommand(mac, CMD_DO_LEDFLASH, !taginfo->isExternal, payload);
|
||||||
|
}
|
||||||
request->send(200, "text/plain", "Ok, done");
|
request->send(200, "text/plain", "Ok, done");
|
||||||
} else {
|
} else {
|
||||||
request->send(400, "text/plain", "Error: mac not found");
|
request->send(400, "text/plain", "Error: mac not found");
|
||||||
@@ -360,6 +373,7 @@ void init_web() {
|
|||||||
server.on("/led_flash", HTTP_GET, [](AsyncWebServerRequest *request) {
|
server.on("/led_flash", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
// color picker: https://roger-random.github.io/RGB332_color_wheel_three.js/
|
// color picker: https://roger-random.github.io/RGB332_color_wheel_three.js/
|
||||||
// http GET to /led_flash?mac=000000000000&pattern=3/0x1C,4,5/0xE0,3,1/0x4F,5,10/5
|
// http GET to /led_flash?mac=000000000000&pattern=3/0x1C,4,5/0xE0,3,1/0x4F,5,10/5
|
||||||
|
// http://192.168.178.198/led_flash?mac=00007E1F250CB29C&pattern=1/0x1C,1,15/0xE0,1,15/0x4F,1,15/1
|
||||||
// (flashDuration/color1,flashCount1,delay1/color2,flashCount2,delay2/color3,flashCount3,delay3/repeats)
|
// (flashDuration/color1,flashCount1,delay1/color2,flashCount2,delay2/color3,flashCount3,delay3/repeats)
|
||||||
if (request->hasParam("mac")) {
|
if (request->hasParam("mac")) {
|
||||||
String dst = request->getParam("mac")->value();
|
String dst = request->getParam("mac")->value();
|
||||||
@@ -421,48 +435,59 @@ void init_web() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.on("/save_apcfg", HTTP_POST, [](AsyncWebServerRequest *request) {
|
server.on("/save_apcfg", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||||
if (request->hasParam("alias", true) && request->hasParam("channel", true)) {
|
if (request->hasParam("alias", true)) {
|
||||||
String aliasValue = request->getParam("alias", true)->value();
|
String aliasValue = request->getParam("alias", true)->value();
|
||||||
size_t aliasLength = aliasValue.length();
|
size_t aliasLength = aliasValue.length();
|
||||||
if (aliasLength > 31) aliasLength = 31;
|
if (aliasLength > 31) aliasLength = 31;
|
||||||
aliasValue.toCharArray(config.alias, aliasLength + 1);
|
aliasValue.toCharArray(config.alias, aliasLength + 1);
|
||||||
config.alias[aliasLength] = '\0';
|
config.alias[aliasLength] = '\0';
|
||||||
|
|
||||||
config.channel = static_cast<uint8_t>(request->getParam("channel", true)->value().toInt());
|
|
||||||
if (request->hasParam("led", true)) {
|
|
||||||
config.led = static_cast<int16_t>(request->getParam("led", true)->value().toInt());
|
|
||||||
updateBrightnessFromConfig();
|
|
||||||
}
|
|
||||||
if (request->hasParam("language", true)) {
|
|
||||||
config.language = static_cast<uint8_t>(request->getParam("language", true)->value().toInt());
|
|
||||||
updateLanguageFromConfig();
|
|
||||||
}
|
|
||||||
if (request->hasParam("maxsleep", true)) {
|
|
||||||
config.maxsleep = static_cast<uint8_t>(request->getParam("maxsleep", true)->value().toInt());
|
|
||||||
}
|
|
||||||
if (request->hasParam("stopsleep", true)) {
|
|
||||||
config.stopsleep = static_cast<uint8_t>(request->getParam("stopsleep", true)->value().toInt());
|
|
||||||
}
|
|
||||||
if (request->hasParam("preview", true)) {
|
|
||||||
config.preview = static_cast<uint8_t>(request->getParam("preview", true)->value().toInt());
|
|
||||||
}
|
|
||||||
if (request->hasParam("sleeptime1", true)) {
|
|
||||||
config.sleepTime1 = static_cast<uint8_t>(request->getParam("sleeptime1", true)->value().toInt());
|
|
||||||
config.sleepTime2 = static_cast<uint8_t>(request->getParam("sleeptime2", true)->value().toInt());
|
|
||||||
}
|
|
||||||
if (request->hasParam("wifipower", true)) {
|
|
||||||
config.wifiPower = static_cast<uint8_t>(request->getParam("wifipower", true)->value().toInt());
|
|
||||||
WiFi.setTxPower(static_cast<wifi_power_t>(config.wifiPower));
|
|
||||||
}
|
|
||||||
if (request->hasParam("timezone", true)) {
|
|
||||||
strncpy(config.timeZone, request->getParam("timezone", true)->value().c_str(), sizeof(config.timeZone) - 1);
|
|
||||||
config.timeZone[sizeof(config.timeZone) - 1] = '\0';
|
|
||||||
setenv("TZ", config.timeZone, 1);
|
|
||||||
tzset();
|
|
||||||
}
|
|
||||||
saveAPconfig();
|
|
||||||
setAPchannel();
|
|
||||||
}
|
}
|
||||||
|
if (request->hasParam("channel", true)) {
|
||||||
|
config.channel = static_cast<uint8_t>(request->getParam("channel", true)->value().toInt());
|
||||||
|
}
|
||||||
|
if (request->hasParam("led", true)) {
|
||||||
|
config.led = static_cast<int16_t>(request->getParam("led", true)->value().toInt());
|
||||||
|
updateBrightnessFromConfig();
|
||||||
|
}
|
||||||
|
if (request->hasParam("tft", true)) {
|
||||||
|
config.tft = static_cast<int16_t>(request->getParam("tft", true)->value().toInt());
|
||||||
|
updateBrightnessFromConfig();
|
||||||
|
}
|
||||||
|
if (request->hasParam("language", true)) {
|
||||||
|
config.language = static_cast<uint8_t>(request->getParam("language", true)->value().toInt());
|
||||||
|
updateLanguageFromConfig();
|
||||||
|
}
|
||||||
|
if (request->hasParam("maxsleep", true)) {
|
||||||
|
config.maxsleep = static_cast<uint8_t>(request->getParam("maxsleep", true)->value().toInt());
|
||||||
|
}
|
||||||
|
if (request->hasParam("stopsleep", true)) {
|
||||||
|
config.stopsleep = static_cast<uint8_t>(request->getParam("stopsleep", true)->value().toInt());
|
||||||
|
}
|
||||||
|
if (request->hasParam("preview", true)) {
|
||||||
|
config.preview = static_cast<uint8_t>(request->getParam("preview", true)->value().toInt());
|
||||||
|
}
|
||||||
|
if (request->hasParam("sleeptime1", true)) {
|
||||||
|
config.sleepTime1 = static_cast<uint8_t>(request->getParam("sleeptime1", true)->value().toInt());
|
||||||
|
config.sleepTime2 = static_cast<uint8_t>(request->getParam("sleeptime2", true)->value().toInt());
|
||||||
|
}
|
||||||
|
if (request->hasParam("wifipower", true)) {
|
||||||
|
config.wifiPower = static_cast<uint8_t>(request->getParam("wifipower", true)->value().toInt());
|
||||||
|
WiFi.setTxPower(static_cast<wifi_power_t>(config.wifiPower));
|
||||||
|
}
|
||||||
|
if (request->hasParam("timezone", true)) {
|
||||||
|
strncpy(config.timeZone, request->getParam("timezone", true)->value().c_str(), sizeof(config.timeZone) - 1);
|
||||||
|
config.timeZone[sizeof(config.timeZone) - 1] = '\0';
|
||||||
|
setenv("TZ", config.timeZone, 1);
|
||||||
|
tzset();
|
||||||
|
}
|
||||||
|
if (request->hasParam("repo", true)) {
|
||||||
|
config.repo = request->getParam("repo", true)->value();
|
||||||
|
}
|
||||||
|
if (request->hasParam("env", true)) {
|
||||||
|
config.env = request->getParam("env", true)->value();
|
||||||
|
}
|
||||||
|
saveAPconfig();
|
||||||
|
setAPchannel();
|
||||||
request->send(200, "text/plain", "Ok, saved");
|
request->send(200, "text/plain", "Ok, saved");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -560,8 +585,26 @@ void init_web() {
|
|||||||
request->send(200, "text/plain", "Ok, saved");
|
request->send(200, "text/plain", "Ok, saved");
|
||||||
|
|
||||||
ws.enable(false);
|
ws.enable(false);
|
||||||
refreshAllPending();
|
|
||||||
saveDB("/current/tagDB.json");
|
if (jsonObj["ssid"].as<String>() == "factory") {
|
||||||
|
preferences.begin("wifi", false);
|
||||||
|
preferences.putString("ssid", "");
|
||||||
|
preferences.putString("pw", "");
|
||||||
|
preferences.end();
|
||||||
|
contentFS->remove("/AP_FW_Pack.bin");
|
||||||
|
contentFS->remove("/OpenEPaperLink_esp32_C6.bin");
|
||||||
|
contentFS->remove("/bootloader.bin");
|
||||||
|
contentFS->remove("/partition-table.bin");
|
||||||
|
contentFS->remove("/update_actions.json");
|
||||||
|
contentFS->remove("/log.txt");
|
||||||
|
contentFS->remove("/current/tagDB.json");
|
||||||
|
delay(100);
|
||||||
|
ESP.restart();
|
||||||
|
} else {
|
||||||
|
refreshAllPending();
|
||||||
|
saveDB("/current/tagDB.json");
|
||||||
|
}
|
||||||
|
|
||||||
ws.closeAll();
|
ws.closeAll();
|
||||||
delay(100);
|
delay(100);
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
@@ -597,20 +640,27 @@ void init_web() {
|
|||||||
request->send(404);
|
request->send(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "content-type");
|
||||||
|
|
||||||
server.begin();
|
server.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||||
if (config.runStatus != RUNSTATUS_RUN) {
|
static bool imageUploadBusy = false;
|
||||||
request->send(409, "text/plain", "come back later");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!index) {
|
if (!index) {
|
||||||
|
if (config.runStatus != RUNSTATUS_RUN || imageUploadBusy) {
|
||||||
|
request->send(409, "text/plain", "Come back later");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (request->hasParam("mac", true)) {
|
if (request->hasParam("mac", true)) {
|
||||||
filename = request->getParam("mac", true)->value() + ".jpg";
|
filename = request->getParam("mac", true)->value() + ".jpg";
|
||||||
} else {
|
} else {
|
||||||
filename = "unknown.jpg";
|
filename = "unknown.jpg";
|
||||||
}
|
}
|
||||||
|
imageUploadBusy = true;
|
||||||
|
logLine("http imageUpload " + filename);
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
request->_tempFile = contentFS->open("/" + filename, "w");
|
request->_tempFile = contentFS->open("/" + filename, "w");
|
||||||
}
|
}
|
||||||
if (len) {
|
if (len) {
|
||||||
@@ -618,6 +668,7 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
|
|||||||
}
|
}
|
||||||
if (final) {
|
if (final) {
|
||||||
request->_tempFile.close();
|
request->_tempFile.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
if (request->hasParam("mac", true)) {
|
if (request->hasParam("mac", true)) {
|
||||||
String dst = request->getParam("mac", true)->value();
|
String dst = request->getParam("mac", true)->value();
|
||||||
uint8_t mac[8];
|
uint8_t mac[8];
|
||||||
@@ -644,6 +695,7 @@ void doImageUpload(AsyncWebServerRequest *request, String filename, size_t index
|
|||||||
} else {
|
} else {
|
||||||
request->send(400, "text/plain", "parameters incomplete");
|
request->send(400, "text/plain", "parameters incomplete");
|
||||||
}
|
}
|
||||||
|
imageUploadBusy = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,13 +708,16 @@ void doJsonUpload(AsyncWebServerRequest *request) {
|
|||||||
String dst = request->getParam("mac", true)->value();
|
String dst = request->getParam("mac", true)->value();
|
||||||
uint8_t mac[8];
|
uint8_t mac[8];
|
||||||
if (hex2mac(dst, mac)) {
|
if (hex2mac(dst, mac)) {
|
||||||
|
xSemaphoreTake(fsMutex, portMAX_DELAY);
|
||||||
File file = LittleFS.open("/current/" + dst + ".json", "w");
|
File file = LittleFS.open("/current/" + dst + ".json", "w");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
request->send(400, "text/plain", "Failed to create file");
|
request->send(400, "text/plain", "Failed to create file");
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
file.print(request->getParam("json", true)->value());
|
file.print(request->getParam("json", true)->value());
|
||||||
file.close();
|
file.close();
|
||||||
|
xSemaphoreGive(fsMutex);
|
||||||
tagRecord *taginfo = tagRecord::findByMAC(mac);
|
tagRecord *taginfo = tagRecord::findByMAC(mac);
|
||||||
if (taginfo != nullptr) {
|
if (taginfo != nullptr) {
|
||||||
uint32_t ttl = 0;
|
uint32_t ttl = 0;
|
||||||
|
|||||||
@@ -215,7 +215,6 @@ void WifiManager::pollSerial() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporary write some more debug info
|
|
||||||
void WifiManager::WiFiEvent(WiFiEvent_t event) {
|
void WifiManager::WiFiEvent(WiFiEvent_t event) {
|
||||||
Serial.printf("[WiFi-event %d] ", event);
|
Serial.printf("[WiFi-event %d] ", event);
|
||||||
String eventname="";
|
String eventname="";
|
||||||
@@ -259,8 +258,8 @@ void WifiManager::WiFiEvent(WiFiEvent_t event) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
terminalLog(eventname);
|
if (eventname) terminalLog(eventname);
|
||||||
logLine("WiFi event [" + String(event) + "]: " + eventname);
|
// logLine("WiFi event [" + String(event) + "]: " + eventname);
|
||||||
}
|
}
|
||||||
|
|
||||||
// *** Improv
|
// *** Improv
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
"id": 0,
|
"id": 0,
|
||||||
"name": "Static image",
|
"name": "Static image",
|
||||||
"desc": "Shows a static image, from file system, painter or external source",
|
"desc": "Shows a static image, from file system, painter or external source. Make sure to resize the image to the correct resolution.",
|
||||||
"hwtype": [
|
"hwtype": [
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"key": "filename",
|
"key": "filename",
|
||||||
"name": "Filename",
|
"name": "Filename",
|
||||||
"desc": "Local filename on the littlefs drive",
|
"desc": "Local filename on the littlefs drive",
|
||||||
"type": "text"
|
"type": "jpgfile"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "timetolive",
|
"key": "timetolive",
|
||||||
@@ -34,6 +34,47 @@
|
|||||||
"0": "off",
|
"0": "off",
|
||||||
"1": "on"
|
"1": "on"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "preload",
|
||||||
|
"name": "Display or Preload",
|
||||||
|
"desc": "Display now or preload for later use",
|
||||||
|
"type": "select",
|
||||||
|
"options": {
|
||||||
|
"0": "Display",
|
||||||
|
"1": "Preload"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "preload_lut",
|
||||||
|
"name": "Preload LUT",
|
||||||
|
"desc": "Triggered preload images will display with this LUT",
|
||||||
|
"type": "select",
|
||||||
|
"options": {
|
||||||
|
"0": "Default",
|
||||||
|
"1": "No Repeats",
|
||||||
|
"2": "Fast No Reds",
|
||||||
|
"3": "Fast"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "preload_type",
|
||||||
|
"name": "Preload Image type",
|
||||||
|
"desc": "Preload type to send to tag, for later recall, or special use",
|
||||||
|
"type": "select",
|
||||||
|
"options": {
|
||||||
|
"0": "Normal",
|
||||||
|
"1": "UI: Splash Screen",
|
||||||
|
"2": "UI: Lost connection",
|
||||||
|
"3": "UI: AP Found",
|
||||||
|
"4": "UI: No AP Found",
|
||||||
|
"5": "UI: Long Term Sleep",
|
||||||
|
"15": "Slideshow image",
|
||||||
|
"16": "Wake: Button 1",
|
||||||
|
"17": "Wake: Button 2",
|
||||||
|
"29": "Wake: GPIO",
|
||||||
|
"30": "Wake: NFC"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -332,7 +373,7 @@
|
|||||||
"key": "filename",
|
"key": "filename",
|
||||||
"name": "Filename",
|
"name": "Filename",
|
||||||
"desc": "Filename of the json template. See OpenEpaperLink wiki for the right json format. Specify a url OR a filename",
|
"desc": "Filename of the json template. See OpenEpaperLink wiki for the right json format. Specify a url OR a filename",
|
||||||
"type": "text"
|
"type": "jsonfile"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "interval",
|
"key": "interval",
|
||||||
@@ -406,7 +447,7 @@
|
|||||||
{
|
{
|
||||||
"id": 5,
|
"id": 5,
|
||||||
"name": "Firmware update",
|
"name": "Firmware update",
|
||||||
"desc": "To update tag firmware",
|
"desc": "To update tag firmware. Make sure you send the right .bin file! You can brick your tag if you send a wrong file.",
|
||||||
"hwtype": [
|
"hwtype": [
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
@@ -422,7 +463,7 @@
|
|||||||
"key": "filename",
|
"key": "filename",
|
||||||
"name": "Filename",
|
"name": "Filename",
|
||||||
"desc": "Local file on littlefs partition",
|
"desc": "Local file on littlefs partition",
|
||||||
"type": "text"
|
"type": "binfile"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -649,6 +690,9 @@
|
|||||||
"id": 21,
|
"id": 21,
|
||||||
"name": "Access point info",
|
"name": "Access point info",
|
||||||
"desc": "Displays information about the currently connected access point",
|
"desc": "Displays information about the currently connected access point",
|
||||||
"hwtype": [0, 1]
|
"hwtype": [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -8,15 +8,463 @@
|
|||||||
<title>Open EPaper Link Access Point</title>
|
<title>Open EPaper Link Access Point</title>
|
||||||
<link rel="stylesheet" href="main.css" type="text/css" />
|
<link rel="stylesheet" href="main.css" type="text/css" />
|
||||||
<link rel="icon" type="image/vnd.icon" href="favicon.ico">
|
<link rel="icon" type="image/vnd.icon" href="favicon.ico">
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<div class="logo">Open EPaper Link Access Point</div>
|
<div class="logo">Open EPaper Link Access Point</div>
|
||||||
|
<nav>
|
||||||
|
<div>
|
||||||
|
<!-- tabs -->
|
||||||
|
<div class="tab-container">
|
||||||
|
<div class="tablinks material-symbols-outlined" data-target="hometab" title="Dashboard">home</div>
|
||||||
|
<div class="tablinks material-symbols-outlined" data-target="tagtab" title="Tags">sell</div>
|
||||||
|
<div class="tablinks material-symbols-outlined" data-target="aptab" title="Access Points">cell_tower
|
||||||
|
</div>
|
||||||
|
<!--<div class="tablinks material-symbols-outlined" data-target="templatetab" title="Templates">browse
|
||||||
|
</div>-->
|
||||||
|
<div class="tablinks material-symbols-outlined" data-target="configtab" title="Settings">settings
|
||||||
|
</div>
|
||||||
|
<div class="tablinks material-symbols-outlined" data-target="logtab" title="Logging">text_snippet
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /tabs -->
|
||||||
|
<div><span id="runstate"></div>
|
||||||
|
<div><span id="apstatecolor">⬤</span> <span id="apstate">loading</span></div>
|
||||||
|
<div><a href="/edit" target="littlefs" class="filebutton material-symbols-outlined">folder_open</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div id="configbox">
|
<div class="container">
|
||||||
<div class="closebtn">✖</div>
|
|
||||||
|
<div class="window">
|
||||||
|
|
||||||
|
<div id="hometab" class="tabcontent">
|
||||||
|
<table>
|
||||||
|
<tr onclick="setFilterAndShow('')">
|
||||||
|
<td class="material-symbols-outlined" style="color:#239f26" id="dashboardStatusIcon">
|
||||||
|
check_circle
|
||||||
|
</td>
|
||||||
|
<td id="dashboardStatus" style="color:#239f26" colspan="2">
|
||||||
|
initialising...
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr onclick="setFilterAndShow('')">
|
||||||
|
<td class="material-symbols-outlined" style="color:#77239e">
|
||||||
|
sell
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
tags
|
||||||
|
</td>
|
||||||
|
<td id="dashboardTagCount" style="color:#77239e">
|
||||||
|
0
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr onclick="setFilterAndShow('pending')">
|
||||||
|
<td class="material-symbols-outlined" style="color:#235f9e">
|
||||||
|
hourglass_empty
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
pending data
|
||||||
|
</td>
|
||||||
|
<td id="dashboardPending" style="color:#235f9e">
|
||||||
|
0
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr onclick="setFilterAndShow('lowbatt')">
|
||||||
|
<td class="material-symbols-outlined" style="color:#9e9223">
|
||||||
|
battery_low
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
low battery
|
||||||
|
</td>
|
||||||
|
<td id="dashboardLowBatt" style="color:#9e9223">
|
||||||
|
0
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr onclick="setFilterAndShow('inactive')">
|
||||||
|
<td class="material-symbols-outlined" style="color:#9e2323">
|
||||||
|
signal_disconnected
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
timeout
|
||||||
|
</td>
|
||||||
|
<td id="dashboardTimeout" style="color:#9e2323">
|
||||||
|
0
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!--
|
||||||
|
<tr onclick="$(`[data-target='aptab']`).click()">
|
||||||
|
<td class="material-symbols-outlined" style="color:#239f26">
|
||||||
|
cell_tower
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
access points
|
||||||
|
</td>
|
||||||
|
<td id="dashboardApCount" style="color:#239f26">
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
-->
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tagtab" class="tabcontent">
|
||||||
|
<div class="tagheader">
|
||||||
|
<h3>Currently active tags</h3>
|
||||||
|
<div id="activefilter"></div><button class="material-symbols-outlined"
|
||||||
|
id="toggleFilters">filter_alt</button>
|
||||||
|
</div>
|
||||||
|
<div id="filterOptions">
|
||||||
|
<div>
|
||||||
|
<div>group by</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="group" value="" id="rnone" checked><label
|
||||||
|
for="rnone">None</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="group" value="model" id="rtagtype"><label for="rtagtype">Tag
|
||||||
|
model</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="group" value="contentmode" id="rcontent"><label
|
||||||
|
for="rcontent">Content</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="group" value="data-channel" id="rchannel"><label
|
||||||
|
for="rchannel">Channel</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>sort by</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="sort" value="alias" id="ralias" checked><label
|
||||||
|
for="ralias">Alias</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="sort" value="mac" id="rmac"><label for="rmac">Mac</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="sort" value="data-lastseen" id="rlastseen"><label
|
||||||
|
for="rlastseen">Last seen</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="radio" name="sort" value="data-nextupdate" id="rnext"><label
|
||||||
|
for="rnext">Next update</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>filter</div>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" name="filter" value="local" id="rlocal"><label
|
||||||
|
for="rlocal">local</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" name="filter" value="remote" id="rremote"><label
|
||||||
|
for="rremote">remote</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" name="filter" value="inactive" id="rinactive"><label
|
||||||
|
for="rinactive">timed out</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" name="filter" value="pending" id="rpending"><label
|
||||||
|
for="rpending">pending</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" name="filter" value="lowbatt" id="rlowbatt"><label
|
||||||
|
for="rlowbatt">low battery</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="taglist" class="taglist">
|
||||||
|
<div class="tagcard" id="tagtemplate">
|
||||||
|
<div class="currimg"><canvas class="tagimg"></div>
|
||||||
|
<div class="mac"></div>
|
||||||
|
<div class="alias"></div>
|
||||||
|
<div class="model"></div>
|
||||||
|
<div class="received"></div>
|
||||||
|
<div class="contentmode"></div>
|
||||||
|
<div class="lastseen"></div>
|
||||||
|
<div class="nextcheckin"></div>
|
||||||
|
<div class="nextupdate"></div>
|
||||||
|
<div class="corner">
|
||||||
|
<div class="pendingicon" title="A new message is waiting for the tag to pick up">
|
||||||
|
↻</div>
|
||||||
|
<div class="warningicon" title="This tag has not been seen for a long time">⚠
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="logtab" class="tabcontent">
|
||||||
|
<div class="tabheader">
|
||||||
|
<div><img id="clearlog" src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw==
|
||||||
|
"></div>
|
||||||
|
<div><input type="checkbox" id="showdebug" value="1"><label for="showdebug">Show all websocket
|
||||||
|
traffic</label></div>
|
||||||
|
</div>
|
||||||
|
<ul id="messages" class="messages">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="aptab" class="tabcontent">
|
||||||
|
<h3>Active access points</h3>
|
||||||
|
|
||||||
|
<div id="aplist">
|
||||||
|
<div id="apcard" class="apcard">
|
||||||
|
<p class="apip">194.109.6.66</p>
|
||||||
|
<p class="apalias">AP kitchen</p>
|
||||||
|
<div>
|
||||||
|
<span class="material-symbols-outlined">
|
||||||
|
sell
|
||||||
|
</span>
|
||||||
|
<span class="aptagcount">13</span>
|
||||||
|
<span class="material-symbols-outlined space">
|
||||||
|
cell_tower
|
||||||
|
</span>
|
||||||
|
<span class="apchannel">25</span>
|
||||||
|
</div>
|
||||||
|
<p class="apswversion">
|
||||||
|
fetching software version...
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="templatetab" class="tabcontent">
|
||||||
|
Work in progress...
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="configtab" class="tabcontent">
|
||||||
|
<h3>Access Point config</h3>
|
||||||
|
<p>
|
||||||
|
<label for="apcfgalias">Alias</label>
|
||||||
|
<input id="apcfgalias" type="text">
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="apcfgchid">IEEE 802.15.4 channel</label>
|
||||||
|
<select id="apcfgchid">
|
||||||
|
<option value="0" selected>auto</option>
|
||||||
|
<option value="11">11</option>
|
||||||
|
<option value="15">15</option>
|
||||||
|
<option value="20">20</option>
|
||||||
|
<option value="25">25</option>
|
||||||
|
<option value="26">26</option>
|
||||||
|
<option value="27">27</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="apcfgledbrightness">RGB LED brightness</label>
|
||||||
|
<select id="apcfgledbrightness">
|
||||||
|
<option value="-1">off</option>
|
||||||
|
<option value="15">10%</option>
|
||||||
|
<option value="31">25%</option>
|
||||||
|
<option value="127" selected>50%</option>
|
||||||
|
<option value="191">75%</option>
|
||||||
|
<option value="255">100%</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="apcfgtftbrightness">TFT brightness</label>
|
||||||
|
<select id="apcfgtftbrightness">
|
||||||
|
<option value="0">off</option>
|
||||||
|
<option value="20">10%</option>
|
||||||
|
<option value="64">25%</option>
|
||||||
|
<option value="128">50%</option>
|
||||||
|
<option value="192">75%</option>
|
||||||
|
<option value="255" selected>100%</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="apcfglanguage">Content language</label>
|
||||||
|
<select id="apcfglanguage">
|
||||||
|
<option value="0" selected>EN English</option>
|
||||||
|
<option value="1">NL Nederlands</option>
|
||||||
|
<option value="2">DE Deutsch</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p title="Depending on the content, a tag can sleep for
|
||||||
|
longer periods when no updates are expected
|
||||||
|
(like a date display). This setting specifies
|
||||||
|
the maximum sleep time.">
|
||||||
|
<label for="apclatency">Maximum sleep</label>
|
||||||
|
<select id="apclatency">
|
||||||
|
<option value="0" selected>shortest (40 sec)</option>
|
||||||
|
<option value="5">5 minutes</option>
|
||||||
|
<option value="10">10 minute</option>
|
||||||
|
<option value="30">30 minutes</option>
|
||||||
|
<option value="60">1 hour</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p title="If connected to the website, don't sleep extra.
|
||||||
|
Latency will be around 40 seconds.">
|
||||||
|
<label for="apcpreventsleep">Shorten latency during config</label>
|
||||||
|
<select id="apcpreventsleep">
|
||||||
|
<option value="0">no</option>
|
||||||
|
<option value="1" selected>yes</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
title="Stops updates at night, and put the tags to sleep. During the configured night time, this overrides the maximum sleep time.">
|
||||||
|
<label for="apcnight1">No updates between</label>
|
||||||
|
<select id="apcnight1"></select>
|
||||||
|
<span style="align-self:center;">and</span>
|
||||||
|
<select id="apcnight2"></select>
|
||||||
|
</p>
|
||||||
|
<p title="Turn off preview images on the webpage if you want to manage many tags,
|
||||||
|
to save file system space">
|
||||||
|
<label for="apcpreview">Preview images</label>
|
||||||
|
<select id="apcpreview">
|
||||||
|
<option value="1" selected>yes</option>
|
||||||
|
<option value="0">no</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p title="Wifi transmit power">
|
||||||
|
<label for="apcwifipower">Wifi power</label>
|
||||||
|
<select id="apcwifipower">
|
||||||
|
<option value="78">19.5 dBm</option>
|
||||||
|
<option value="76">19.0 dBm</option>
|
||||||
|
<option value="74">18.5 dBm</option>
|
||||||
|
<option value="68">17.0 dBm</option>
|
||||||
|
<option value="60">15.0 dBm</option>
|
||||||
|
<option value="52">13.0 dBm</option>
|
||||||
|
<option value="44">11.0 dBm</option>
|
||||||
|
<option value="34" selected>8.5 dBm</option>
|
||||||
|
<option value="28">7.0 dBm</option>
|
||||||
|
<option value="20">5.0 dBm</option>
|
||||||
|
<option value="8">2.0 dBm</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p title="Your local time zone">
|
||||||
|
<label for="apctimezone">Local time zone</label>
|
||||||
|
<select id="apctimezone">
|
||||||
|
<optgroup label="Europe">
|
||||||
|
<option value="CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00" selected>Central European
|
||||||
|
Time</option>
|
||||||
|
<option value="EET-2EEST-3,M3.5.0/03:00:00,M10.5.0/04:00:00">Athens, Greece</option>
|
||||||
|
<option value="GMT+0IST-1,M3.5.0/01:00:00,M10.5.0/02:00:00">Dublin, Ireland</option>
|
||||||
|
<option value="EET-2EEST-3,M3.5.0/03:00:00,M10.5.0/04:00:00">Helsinki, Finland</option>
|
||||||
|
<option value="WET-0WEST-1,M3.5.0/01:00:00,M10.5.0/02:00:00">Lisbon, Portugal</option>
|
||||||
|
<option value="GMT+0BST-1,M3.5.0/01:00:00,M10.5.0/02:00:00">London, Great Britain
|
||||||
|
</option>
|
||||||
|
<option value="EET-2EEST,M3.5.0/3,M10.5.0/4">Kyiv, Ukraine</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="USA / Canada">
|
||||||
|
<option value="HAW10">Hawaii Time</option>
|
||||||
|
<option value="AKST9AKDT">Alaska Time</option>
|
||||||
|
<option value="PST8PDT">Pacific Time</option>
|
||||||
|
<option value="MST7MDT">Mountain Time</option>
|
||||||
|
<option value="MST7">Arizona, no DST</option>
|
||||||
|
<option value="CST6CDT">Central Time</option>
|
||||||
|
<option value="EST5EDT">Eastern Time</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Australia / New Zealand">
|
||||||
|
<option value="EST-10EDT-11,M10.5.0/02:00:00,M3.5.0/03:00:00">Melbourne, Sydney</option>
|
||||||
|
<option value="WST-8">Perth</option>
|
||||||
|
<option value="EST-10">Brisbane</option>
|
||||||
|
<option value="CST-9:30CDT-10:30,M10.5.0/02:00:00,M3.5.0/03:00:00">Adelaide</option>
|
||||||
|
<option value="CST-9:30">Darwin</option>
|
||||||
|
<option value="EST-10EDT-11,M10.1.0/02:00:00,M3.5.0/03:00:00">Hobart</option>
|
||||||
|
<option value="NZST-12NZDT-13,M9.4.0/02:00:00,M4.1.0/03:00:00">New Zealand</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Asia">
|
||||||
|
<option value="JST-9">Tokyo</option>
|
||||||
|
<option value="WIB-7">Jakarta</option>
|
||||||
|
<option value="GMT+2">Jerusalem</option>
|
||||||
|
<option value="SGT-8">Singapore</option>
|
||||||
|
<option value="ULAT-8ULAST,M3.5.0/2,M9.5.0/2">Ulaanbaatar, Mongolia</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Central and South America">
|
||||||
|
<option value="BRST+3BRDT+2,M10.3.0,M2.3.0">Brazil, Sao Paulo</option>
|
||||||
|
<option value="UTC+3">Argentina</option>
|
||||||
|
<option value="CST+6">Central America</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<input type="button" value="Save" id="apcfgsave"><span id="apcfgmsg"></span>
|
||||||
|
</p>
|
||||||
|
<h3>Manage</h3>
|
||||||
|
<p>
|
||||||
|
<button type="button" id="rebootbutton">Reboot AP</button> Saves the tagDB and instantly reboots the Access
|
||||||
|
Point
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="/backup_db" id="downloadDBbutton">Download tagDB</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<button type="button" id="updatebutton" class="tablinks" data-target="updatetab" title="Update">Update</button> Manage firmware of the ESP32
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="/setup" target="setup" class="wifibutton">WiFi config</a> Opens a new window with WiFi
|
||||||
|
config options
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<br>
|
||||||
|
<a href="https://github.com/jjwbruijn/OpenEPaperLink" target="_new">Github
|
||||||
|
OpenEPaperLink</a><br>
|
||||||
|
<a href="https://github.com/jjwbruijn/OpenEPaperLink/wiki" target="_new">OpenEPaperLink
|
||||||
|
Wiki</a><br>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="updatetab" class="tabcontent">
|
||||||
|
<h3>Firmware Updates</h3>
|
||||||
|
<div>
|
||||||
|
<div class="updateCol1">
|
||||||
|
<div id="easyupdate"></div>
|
||||||
|
<h4>Repository</h4>
|
||||||
|
<div>
|
||||||
|
<label for="repo">Repo</label><input type="text" id="repo" value="">
|
||||||
|
<button id="selectRepo">Change</button><br>
|
||||||
|
<p id="repoWarning" style="display:none" class="warning">
|
||||||
|
To change to this repository, select and confirm the build environment.
|
||||||
|
</p>
|
||||||
|
<label for="environment">Environment</label><input type="text" id="environment" readonly value="">
|
||||||
|
<button id="confirmSelectRepo">Confirm</button><button id="cancelSelectRepo">Cancel</button>
|
||||||
|
</div>
|
||||||
|
<h4>Releases</h4>
|
||||||
|
<div id="releasetable"></div>
|
||||||
|
<h4>Other actions</h4>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<div id="rollbackOption" style="display:none"><button type="button" id="rollbackBtn">Rollback to previous
|
||||||
|
firmware</button></div>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span id="c6Option">
|
||||||
|
<div id="updateC6Option"><button type="button" id="updateC6Btn">Update ESP32-C6</button> <input type="checkbox"
|
||||||
|
value="1" checked id="c6download"> download latest version</div>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="console" id="updateconsole"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="logbox">
|
||||||
|
<p>
|
||||||
|
<span> </span>
|
||||||
|
<span id="sysinfo"></span>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- -->
|
||||||
|
|
||||||
|
<dialog id="configbox">
|
||||||
|
<div class="closebtn2">✖</div>
|
||||||
<h3 id="cfgmac">00000000</h3>
|
<h3 id="cfgmac">00000000</h3>
|
||||||
<p>
|
<p>
|
||||||
<label for="cfgalias">Alias</label>
|
<label for="cfgalias">Alias</label>
|
||||||
@@ -43,6 +491,13 @@
|
|||||||
<option value="0">auto</option>
|
<option value="0">auto</option>
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<label for="cfginvert">Invert colors</label>
|
||||||
|
<select id="cfginvert">
|
||||||
|
<option value="0">off</option>
|
||||||
|
<option value="1">on</option>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
<p class="tagbuttons">
|
<p class="tagbuttons">
|
||||||
<button id="cfgrefresh">force refresh</button>
|
<button id="cfgrefresh">force refresh</button>
|
||||||
<button id="cfgclrpending">clear pending</button>
|
<button id="cfgclrpending">clear pending</button>
|
||||||
@@ -50,269 +505,22 @@
|
|||||||
<button id="cfgscan">scan</button>
|
<button id="cfgscan">scan</button>
|
||||||
<button id="cfgdeepsleep">deep sleep</button>
|
<button id="cfgdeepsleep">deep sleep</button>
|
||||||
<button id="cfgreset">reset settings</button>
|
<button id="cfgreset">reset settings</button>
|
||||||
<button id="cfgdelete" title="remove"><img src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw== "></button>
|
<button id="cfgdelete" title="remove"><img
|
||||||
|
src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw== "></button>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p id="savebar">
|
<p id="savebar">
|
||||||
<span><input type="button" value="Save" id="cfgsave"></span>
|
<span><input type="button" value="Save" id="cfgsave"></span>
|
||||||
<span id="cfgmore" title="advanced options">▼</span>
|
<span id="cfgmore" title="advanced options">▼</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</dialog>
|
||||||
|
|
||||||
<div id="apconfigbox">
|
|
||||||
<div class="closebtn">✖</div>
|
|
||||||
<h3>Access Point config</h3>
|
|
||||||
<p>
|
|
||||||
<label for="apcfgalias">Alias</label>
|
|
||||||
<input id="apcfgalias" type="text">
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<label for="apcfgchid">Channel</label>
|
|
||||||
<select id="apcfgchid">
|
|
||||||
<option value="0" selected>auto</option>
|
|
||||||
<option value="11">11</option>
|
|
||||||
<option value="15">15</option>
|
|
||||||
<option value="20">20</option>
|
|
||||||
<option value="25">25</option>
|
|
||||||
<option value="26">26</option>
|
|
||||||
<option value="27">27</option>
|
|
||||||
</select>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<label for="apcfgledbrightness">LED brightness</label>
|
|
||||||
<select id="apcfgledbrightness">
|
|
||||||
<option value="-1">off</option>
|
|
||||||
<option value="20">10%</option>
|
|
||||||
<option value="64">25%</option>
|
|
||||||
<option value="128" selected>50%</option>
|
|
||||||
<option value="192">75%</option>
|
|
||||||
<option value="255">100%</option>
|
|
||||||
</select>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<label for="apcfglanguage">Content language</label>
|
|
||||||
<select id="apcfglanguage">
|
|
||||||
<option value="0" selected>EN English</option>
|
|
||||||
<option value="1">NL Nederlands</option>
|
|
||||||
<option value="2">DE Deutsch</option>
|
|
||||||
</select>
|
|
||||||
</p>
|
|
||||||
<p title="Depending on the content, a tag can sleep for
|
|
||||||
longer periods when no updates are expected
|
|
||||||
(like a date display). This setting specifies
|
|
||||||
the maximum sleep time.">
|
|
||||||
<label for="apclatency">Maximum sleep</label>
|
|
||||||
<select id="apclatency">
|
|
||||||
<option value="0" selected>shortest (40 sec)</option>
|
|
||||||
<option value="5">5 minutes</option>
|
|
||||||
<option value="10">10 minute</option>
|
|
||||||
<option value="30">30 minutes</option>
|
|
||||||
<option value="60">1 hour</option>
|
|
||||||
</select>
|
|
||||||
</p>
|
|
||||||
<p title="If connected to the website, don't sleep extra.
|
|
||||||
Latency will be around 40 seconds.">
|
|
||||||
<label for="apcpreventsleep">Shorten latency during config</label>
|
|
||||||
<select id="apcpreventsleep">
|
|
||||||
<option value="0">no</option>
|
|
||||||
<option value="1" selected>yes</option>
|
|
||||||
</select>
|
|
||||||
</p>
|
|
||||||
<p title="Stops updates at night, and put the tags to sleep. During the configured night time, this overrides the maximum sleep time.">
|
|
||||||
<label for="apcnight1">No updates between</label>
|
|
||||||
<select id="apcnight1"></select>
|
|
||||||
<span style="align-self:center;">and</span>
|
|
||||||
<select id="apcnight2"></select>
|
|
||||||
</p>
|
|
||||||
<p title="Turn off preview images on the webpage if you want to manage many tags,
|
|
||||||
to save file system space">
|
|
||||||
<label for="apcpreview">Preview images</label>
|
|
||||||
<select id="apcpreview">
|
|
||||||
<option value="1" selected>yes</option>
|
|
||||||
<option value="0">no</option>
|
|
||||||
</select>
|
|
||||||
</p>
|
|
||||||
<p title="Wifi transmit power">
|
|
||||||
<label for="apcwifipower">Wifi power</label>
|
|
||||||
<select id="apcwifipower">
|
|
||||||
<option value="78">19.5 dBm</option>
|
|
||||||
<option value="76">19.0 dBm</option>
|
|
||||||
<option value="74">18.5 dBm</option>
|
|
||||||
<option value="68">17.0 dBm</option>
|
|
||||||
<option value="60">15.0 dBm</option>
|
|
||||||
<option value="52">13.0 dBm</option>
|
|
||||||
<option value="44">11.0 dBm</option>
|
|
||||||
<option value="34" selected>8.5 dBm</option>
|
|
||||||
<option value="28">7.0 dBm</option>
|
|
||||||
<option value="20">5.0 dBm</option>
|
|
||||||
<option value="8">2.0 dBm</option>
|
|
||||||
</select>
|
|
||||||
</p>
|
|
||||||
<p title="Your local time zone">
|
|
||||||
<label for="apctimezone">Local time zone</label>
|
|
||||||
<select id="apctimezone">
|
|
||||||
<optgroup label="Europe">
|
|
||||||
<option value="CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00" selected>Central European Time</option>
|
|
||||||
<option value="EET-2EEST-3,M3.5.0/03:00:00,M10.5.0/04:00:00">Athens, Greece</option>
|
|
||||||
<option value="GMT+0IST-1,M3.5.0/01:00:00,M10.5.0/02:00:00">Dublin, Ireland</option>
|
|
||||||
<option value="EET-2EEST-3,M3.5.0/03:00:00,M10.5.0/04:00:00">Helsinki, Finland</option>
|
|
||||||
<option value="WET-0WEST-1,M3.5.0/01:00:00,M10.5.0/02:00:00">Lisbon, Portugal</option>
|
|
||||||
<option value="GMT+0BST-1,M3.5.0/01:00:00,M10.5.0/02:00:00">London, Great Britain</option>
|
|
||||||
<option value="EET-2EEST,M3.5.0/3,M10.5.0/4">Kyiv, Ukraine</option>
|
|
||||||
</optgroup>
|
|
||||||
<optgroup label="USA / Canada">
|
|
||||||
<option value="HAW10">Hawaii Time</option>
|
|
||||||
<option value="AKST9AKDT">Alaska Time</option>
|
|
||||||
<option value="PST8PDT">Pacific Time</option>
|
|
||||||
<option value="MST7MDT">Mountain Time</option>
|
|
||||||
<option value="MST7">Arizona, no DST</option>
|
|
||||||
<option value="CST6CDT">Central Time</option>
|
|
||||||
<option value="EST5EDT">Eastern Time</option>
|
|
||||||
</optgroup>
|
|
||||||
<optgroup label="Australia / New Zealand">
|
|
||||||
<option value="EST-10EDT-11,M10.5.0/02:00:00,M3.5.0/03:00:00">Melbourne, Sydney</option>
|
|
||||||
<option value="WST-8">Perth</option>
|
|
||||||
<option value="EST-10">Brisbane</option>
|
|
||||||
<option value="CST-9:30CDT-10:30,M10.5.0/02:00:00,M3.5.0/03:00:00">Adelaide</option>
|
|
||||||
<option value="CST-9:30">Darwin</option>
|
|
||||||
<option value="EST-10EDT-11,M10.1.0/02:00:00,M3.5.0/03:00:00">Hobart</option>
|
|
||||||
<option value="NZST-12NZDT-13,M9.4.0/02:00:00,M4.1.0/03:00:00">New Zealand</option>
|
|
||||||
</optgroup>
|
|
||||||
<optgroup label="Asia">
|
|
||||||
<option value="JST-9">Tokyo</option>
|
|
||||||
<option value="WIB-7">Jakarta</option>
|
|
||||||
<option value="GMT+2">Jerusalem</option>
|
|
||||||
<option value="SGT-8">Singapore</option>
|
|
||||||
<option value="ULAT-8ULAST,M3.5.0/2,M9.5.0/2">Ulaanbaatar, Mongolia</option>
|
|
||||||
</optgroup>
|
|
||||||
<optgroup label="Central and South America">
|
|
||||||
<option value="BRST+3BRDT+2,M10.3.0,M2.3.0">Brazil, Sao Paulo</option>
|
|
||||||
<option value="UTC+3">Argentina</option>
|
|
||||||
<option value="CST+6">Central America</option>
|
|
||||||
</optgroup>
|
|
||||||
</select>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<input type="button" value="Save" id="apcfgsave">
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Active access points:<br>
|
|
||||||
<table id="aptable">
|
|
||||||
<tr>
|
|
||||||
<th>ip</th>
|
|
||||||
<th>alias</th>
|
|
||||||
<th>tags</th>
|
|
||||||
<th>ch</th>
|
|
||||||
<th>AP ver</th>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<span id="rebootbutton">reboot AP</span>
|
|
||||||
<a href="/backup_db" id="downloadDBbutton">download tagDB</a>
|
|
||||||
<span id="updatebutton">update</span>
|
|
||||||
<a href="/setup" target="setup" class="filebutton">WiFi config</a>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<a href="https://github.com/jjwbruijn/OpenEPaperLink" target="_new">Github OpenEPaperLink</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="apupdatebox">
|
|
||||||
<div class="closebtn">✖</div>
|
|
||||||
<h3>Update dashboard</h3>
|
|
||||||
<div id="easyupdate"></div>
|
|
||||||
<div id="advanceddiv">
|
|
||||||
<div id="releasetable"></div>
|
|
||||||
<div>
|
|
||||||
<div id="rollbackOption" style="display:none"><button id="rollbackBtn">Rollback to previous firmware</button></div>
|
|
||||||
<span id="c6Option"><div id="updateC6Option"><button id="updateC6Btn">Update ESP32-C6</button> <input type="checkbox" value="1" checked id="c6download"> download latest version</div></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul id="context-menu"
|
<ul id="context-menu"
|
||||||
style="display: none; position: absolute; background: white; border: 1px solid gray; padding: 0; list-style: none;">
|
style="display: none; position: absolute; background: white; border: 1px solid gray; padding: 0; list-style: none;">
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<form>
|
|
||||||
<div class="container">
|
|
||||||
|
|
||||||
<div class="window">
|
|
||||||
|
|
||||||
<div class="actionbox">
|
|
||||||
<div>
|
|
||||||
<div>Currently active tags: <button class="filebutton" id="toggleFilters">arrange</button></div>
|
|
||||||
<div><span id="temp"></div>
|
|
||||||
<div><span id="runstate"></div>
|
|
||||||
<div><span id="apstatecolor">⬤</span> <span id="apstate">loading</span></div>
|
|
||||||
<div><span id="apconfigbutton">AP config</span></div>
|
|
||||||
<div><a href="/edit" target="littlefs" class="filebutton">File System</a></div>
|
|
||||||
</div>
|
|
||||||
<div id="filterOptions">
|
|
||||||
<div>
|
|
||||||
<div>group by</div>
|
|
||||||
<div><input type="radio" name="group" value="" id="rnone" checked><label for="rnone">None</label></div>
|
|
||||||
<div><input type="radio" name="group" value="model" id="rtagtype"><label for="rtagtype">Tag model</label></div>
|
|
||||||
<div><input type="radio" name="group" value="contentmode" id="rcontent"><label for="rcontent">Content</label></div>
|
|
||||||
<div><input type="radio" name="group" value="data-channel" id="rchannel"><label for="rchannel">Channel</label></div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div>sort by</div>
|
|
||||||
<div><input type="radio" name="sort" value="alias" id="ralias" checked><label for="ralias">Alias</label></div>
|
|
||||||
<div><input type="radio" name="sort" value="mac" id="rmac"><label for="rmac">Mac</label></div>
|
|
||||||
<div><input type="radio" name="sort" value="data-lastseen" id="rlastseen"><label for="rlastseen">Last seen</label></div>
|
|
||||||
<div><input type="radio" name="sort" value="data-nextupdate" id="rnext"><label for="rnext">Next update</label></div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div>filter</div>
|
|
||||||
<div><input type="checkbox" name="filter" value="local" id="rlocal"><label for="rlocal">only local</label></div>
|
|
||||||
<div><input type="checkbox" name="filter" value="remote" id="rremote"><label for="rremote">only remote</label></div>
|
|
||||||
<div><input type="checkbox" name="filter" value="inactive" id="rinactive"><label for="rinactive">only inactive</label></div>
|
|
||||||
<div><input type="checkbox" name="filter" value="pending" id="rpending"><label for="rpending">only pending</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="taglist" class="taglist">
|
|
||||||
<div class="tagcard" id="tagtemplate">
|
|
||||||
<div class="currimg"><canvas class="tagimg"></div>
|
|
||||||
<div class="mac"></div>
|
|
||||||
<div class="alias"></div>
|
|
||||||
<div class="model"></div>
|
|
||||||
|
|
||||||
<div class="received"></div>
|
|
||||||
|
|
||||||
<div class="contentmode"></div>
|
|
||||||
<div class="lastseen"></div>
|
|
||||||
<div class="nextcheckin"></div>
|
|
||||||
<div class="nextupdate"></div>
|
|
||||||
<div class="corner">
|
|
||||||
<div class="pendingicon" title="A new message is waiting for the tag to pick up">↻</div>
|
|
||||||
<div class="warningicon" title="This tag has not been seen for a long time">⚠</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="logbox">
|
|
||||||
<p>
|
|
||||||
<span>logging</span>
|
|
||||||
<span><img id="clearlog" src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw==
|
|
||||||
"></span>
|
|
||||||
<span id="sysinfo"></span>
|
|
||||||
</p>
|
|
||||||
<ul id="messages" class="messages">
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<script src="main.js"></script>
|
<script src="main.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
* {
|
* {
|
||||||
margin:0;
|
margin: 0;
|
||||||
padding:0;
|
padding: 0;
|
||||||
border:0;
|
border: 0;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@@ -12,7 +12,8 @@
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html,
|
||||||
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,60 +25,293 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
height: 50px;
|
|
||||||
background-color: #646260;
|
background-color: #646260;
|
||||||
|
z-index: 999;
|
||||||
|
position: sticky;
|
||||||
|
top: 0px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
nav>div:first-child>div:first-child {
|
||||||
width: 120px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
padding: 3px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
margin: 0 auto;
|
|
||||||
height: 50px;
|
|
||||||
text-indent: 50px;
|
|
||||||
overflow:hidden;
|
|
||||||
font-size: 2.5em;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.window {
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 94%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actionbox {
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actionbox>div:first-child>div:first-child {
|
|
||||||
flex-grow: 2;
|
flex-grow: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionbox>div {
|
nav>div {
|
||||||
display:flex;
|
display: flex;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionbox label {
|
nav label {
|
||||||
padding: 0px 5px;
|
padding: 0px 5px;
|
||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
width: auto;
|
width: auto;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#rebootbutton, #updatebutton, #downloadDBbutton, #apconfigbutton, .filebutton {
|
footer {
|
||||||
padding: 2px 5px;
|
padding: 5px;
|
||||||
background-color: #cccccc;
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
background-color: white;
|
||||||
|
|
||||||
|
#sysinfo {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
margin: 0 auto;
|
||||||
|
height: 50px;
|
||||||
|
text-indent: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 2.5em;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tabs */
|
||||||
|
|
||||||
|
.tab-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tablinks {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-container .active {
|
||||||
|
background-color: #ccc;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: -10px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabcontent {
|
||||||
|
display: none;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
label {
|
||||||
|
width: 200px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
padding: 4px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
#hometab {
|
||||||
|
font-size: 24px;
|
||||||
|
|
||||||
|
& table {
|
||||||
|
margin: 20px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 5px 20px;
|
||||||
|
border-spacing: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& td {
|
||||||
|
padding: 10px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& td:nth-child(3) {
|
||||||
|
text-align: right;
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& tr:hover {
|
||||||
|
background-color: #ccc;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .material-symbols-outlined {
|
||||||
|
font-size: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tagheader {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 10px;
|
||||||
|
display: flex;
|
||||||
|
gap: 2em;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
& #toggleFilters {
|
||||||
|
padding: 0px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h3 {
|
||||||
|
padding-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& #activefilter {
|
||||||
|
flex-grow: 2;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabheader {
|
||||||
|
background-color: white;
|
||||||
|
padding: 5px 10px;
|
||||||
|
display: flex;
|
||||||
|
gap: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filterOptions {
|
||||||
|
background-color: white;
|
||||||
|
max-height: 0;
|
||||||
|
padding: 0px 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
padding: 10px 10px;
|
||||||
|
max-height: 100px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& label {
|
||||||
|
width: inherit;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
padding: 0px 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#aptab,
|
||||||
|
#configtab,
|
||||||
|
#updatetab {
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
& p {
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#updatetab {
|
||||||
|
&>div {
|
||||||
|
display: flex;
|
||||||
|
gap: 2em;
|
||||||
|
flex-flow: wrap;
|
||||||
|
}
|
||||||
|
& label {
|
||||||
|
width: 100px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: baseline;
|
||||||
|
padding: 7px 0px;
|
||||||
|
}
|
||||||
|
& input[type="text"] {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
& button {
|
||||||
|
margin: 0px 5px;
|
||||||
|
}
|
||||||
|
& input:read-only {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
& .warning {
|
||||||
|
padding: 5px;
|
||||||
|
color: #f02000;
|
||||||
|
background-color: #ffffc0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#aplist {
|
||||||
|
display: flex;
|
||||||
|
gap: 1em;
|
||||||
|
flex-flow: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apcard {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 10px;
|
||||||
|
width: 300px;
|
||||||
|
|
||||||
|
& .apalias {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .space {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& div {
|
||||||
|
display: flex;
|
||||||
|
font-size: 2.0em;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#apcard {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#apcfgsave {
|
||||||
|
margin: 20px 205px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filebutton {
|
||||||
|
width: inherit;
|
||||||
|
padding: 5px 10px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
margin-top: -1px;
|
||||||
|
background-color: #ccc;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: black;
|
color: black;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #aaa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#rebootbutton,
|
||||||
|
#updatebutton,
|
||||||
|
#downloadDBbutton,
|
||||||
|
.wifibutton {
|
||||||
|
padding: 4px 5px;
|
||||||
|
background-color: #ccc;
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 120px;
|
||||||
|
margin: 2px 5px 2px 20px;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #aaa;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.columns div {
|
.columns div {
|
||||||
@@ -100,15 +334,17 @@ input {
|
|||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=button], button {
|
input[type=button],
|
||||||
|
button {
|
||||||
border: 0px;
|
border: 0px;
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
|
background-color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=button]:hover,
|
input[type=button]:hover,
|
||||||
button:hover {
|
button:hover {
|
||||||
background-color:#aaaaaa;
|
background-color: #aaaaaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@@ -117,7 +353,8 @@ select {
|
|||||||
border: solid 1px #cccccc;
|
border: solid 1px #cccccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#configbox, #apconfigbox, #apupdatebox {
|
#apconfigbox,
|
||||||
|
#apupdatebox {
|
||||||
display: none;
|
display: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 65px;
|
top: 65px;
|
||||||
@@ -131,19 +368,36 @@ select {
|
|||||||
max-height: calc(100vh - 75px);
|
max-height: calc(100vh - 75px);
|
||||||
}
|
}
|
||||||
|
|
||||||
#configbox p, #apconfigbox p, #apupdatebox p {
|
#configbox {
|
||||||
|
margin: auto;
|
||||||
|
min-width: 380px;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #f0e6d3;
|
||||||
|
z-index: 999;
|
||||||
|
box-shadow: 7px 10px 52px -19px rgba(0, 0, 0, 0.63);
|
||||||
|
overflow: auto;
|
||||||
|
max-height: calc(100vh - 75px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#configbox p,
|
||||||
|
#apconfigbox p,
|
||||||
|
#apupdatebox p {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#configbox h3, #apconfigbox h3, #apupdatebox h3 {
|
#configbox h3,
|
||||||
|
#apconfigbox h3,
|
||||||
|
#apupdatebox h3 {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
#configbox input, #apconfigbox input {
|
#configbox input,
|
||||||
|
#apconfigbox input {
|
||||||
border: solid 1px #cccccc;
|
border: solid 1px #cccccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,31 +445,6 @@ select {
|
|||||||
background-color: #e6f0d3;
|
background-color: #e6f0d3;
|
||||||
}
|
}
|
||||||
|
|
||||||
#aptable {
|
|
||||||
width: 100%;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#aptable th {
|
|
||||||
text-align: left;
|
|
||||||
background-color: #00000020;
|
|
||||||
padding: 0px 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#aptable th, #aptable td {
|
|
||||||
border-right: 1px solid #000010;
|
|
||||||
padding: 0px 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#aptable td:nth-child(1), #aptable th:nth-child(1) {
|
|
||||||
border-left: 1px solid #000010;
|
|
||||||
}
|
|
||||||
|
|
||||||
#aptable td:nth-child(3),
|
|
||||||
#aptable td:nth-child(4) {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
#apupdatebox {
|
#apupdatebox {
|
||||||
background-color: #f0d0c8;
|
background-color: #f0d0c8;
|
||||||
width: 700px;
|
width: 700px;
|
||||||
@@ -228,7 +457,8 @@ select {
|
|||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.closebtn {
|
.closebtn,
|
||||||
|
.closebtn2 {
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
float: right;
|
float: right;
|
||||||
width: 19px;
|
width: 19px;
|
||||||
@@ -239,22 +469,21 @@ select {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logbox {
|
#logtab {
|
||||||
margin: 5px;
|
& img {
|
||||||
}
|
vertical-align: bottom;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.logbox p {
|
& label {
|
||||||
background-color: #ffffff;
|
width: inherit;
|
||||||
padding: 5px 10px;
|
vertical-align: top;
|
||||||
}
|
padding: 4px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.logbox img {
|
& input {
|
||||||
vertical-align: bottom;
|
vertical-align: text-bottom;
|
||||||
cursor:pointer;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.logbox #sysinfo {
|
|
||||||
float: right;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.blink-red {
|
.blink-red {
|
||||||
@@ -265,10 +494,11 @@ select {
|
|||||||
.taglist {
|
.taglist {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
margin: 0px 10px 30px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tagtemplate {
|
#tagtemplate {
|
||||||
display:none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tagcard {
|
.tagcard {
|
||||||
@@ -280,12 +510,12 @@ select {
|
|||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: 1px solid #cccccc;
|
border: 1px solid #cccccc;
|
||||||
transition: box-shadow 0.3s ease;
|
transition: box-shadow 0.3s ease;
|
||||||
}
|
|
||||||
|
|
||||||
.tagcard:hover {
|
&:hover {
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
box-shadow: 7px 10px 52px -19px rgba(0, 0, 0, 0.63);
|
box-shadow: 7px 10px 52px -19px rgba(0, 0, 0, 0.63);
|
||||||
filter: brightness(1.02);
|
filter: brightness(1.02);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tagflash {
|
.tagflash {
|
||||||
@@ -305,30 +535,19 @@ select {
|
|||||||
margin: 0px 5px;
|
margin: 0px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#filterOptions {
|
|
||||||
max-height: 0;
|
|
||||||
padding: 0 10px;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: max-height 0.3s ease-in-out, padding 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#filterOptions.active {
|
|
||||||
max-height: 100px;
|
|
||||||
padding: 10px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.currimg {
|
.currimg {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.currimg img, .currimg canvas {
|
.currimg img,
|
||||||
|
.currimg canvas {
|
||||||
max-width: 50px;
|
max-width: 50px;
|
||||||
border: 1px solid #c0c0c0;
|
border: 1px solid #c0c0c0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mac {
|
.mac {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alias {
|
.alias {
|
||||||
@@ -344,6 +563,7 @@ select {
|
|||||||
font-size: .85em;
|
font-size: .85em;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.received div {
|
.received div {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
@@ -354,16 +574,19 @@ select {
|
|||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lastseen, .nextcheckin, .nextupdate {
|
.lastseen,
|
||||||
|
.nextcheckin,
|
||||||
|
.nextupdate {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lastseen span,
|
.lastseen span,
|
||||||
.nextcheckin span,
|
.nextcheckin span,
|
||||||
.nextupdate span {
|
.nextupdate span {
|
||||||
width:105px;
|
width: 105px;
|
||||||
display:inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.corner {
|
.corner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
@@ -383,7 +606,7 @@ select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.warningicon {
|
.warningicon {
|
||||||
display:none;
|
display: none;
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
background-color: yellow;
|
background-color: yellow;
|
||||||
color: black;
|
color: black;
|
||||||
@@ -412,12 +635,24 @@ ul.messages li.new {
|
|||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mono {
|
||||||
|
font-family: monospace;
|
||||||
|
word-break: break-all;
|
||||||
|
background-color: #666;
|
||||||
|
color: #ccc;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
#paintbutton {
|
#paintbutton {
|
||||||
padding: 1px 3px;
|
padding: 1px 3px;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
margin-left:12px;
|
margin-left: 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,6 +707,7 @@ ul.messages li.new {
|
|||||||
background-color: #dddddd;
|
background-color: #dddddd;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#buttonbar button {
|
#buttonbar button {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -513,8 +749,11 @@ ul.messages li.new {
|
|||||||
|
|
||||||
/* updatescreens */
|
/* updatescreens */
|
||||||
|
|
||||||
#easyupdate{
|
#easyupdate {
|
||||||
margin-top: 10px;
|
padding: 10px;
|
||||||
|
background-color: white;
|
||||||
|
width: 400px;
|
||||||
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#easyupdate button {
|
#easyupdate button {
|
||||||
@@ -528,13 +767,10 @@ ul.messages li.new {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
#advanceddiv {
|
h4 {
|
||||||
display: none;
|
font-size: 1.25em;
|
||||||
}
|
margin: 10px 0px;
|
||||||
|
font-weight: bold;
|
||||||
#advanceddiv div:nth-child(2) {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#releasetable {
|
#releasetable {
|
||||||
@@ -559,7 +795,7 @@ ul.messages li.new {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#releasetable td:nth-child(2) {
|
#releasetable td:nth-child(2) {
|
||||||
word-wrap: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#releasetable button {
|
#releasetable button {
|
||||||
@@ -575,18 +811,23 @@ ul.messages li.new {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.updateCol1 {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.console {
|
.console {
|
||||||
width: 100%;
|
width: 450px;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
font-family: 'lucida console','ui-monospace';
|
font-family: 'lucida console', 'ui-monospace';
|
||||||
color: white;
|
color: white;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
margin: 20px 0px;
|
|
||||||
padding-bottom: 25px;
|
padding-bottom: 25px;
|
||||||
height: 400px;
|
height: calc(100vh - 200px);
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
white-space: break-spaces;
|
white-space: break-spaces;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.console div {
|
.console div {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
@@ -594,36 +835,62 @@ ul.messages li.new {
|
|||||||
/* media */
|
/* media */
|
||||||
|
|
||||||
@media(max-width: 460px) {
|
@media(max-width: 460px) {
|
||||||
.messages li div, ul.messages li div.date, ul.messages li div.message {
|
|
||||||
display:block;
|
.messages li div,
|
||||||
position:relative;
|
ul.messages li div.date,
|
||||||
|
ul.messages li div.message {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
left: auto;
|
left: auto;
|
||||||
}
|
}
|
||||||
.messages li div.message, li.pending {
|
|
||||||
|
.messages li div.message,
|
||||||
|
li.pending {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.messages {
|
ul.messages {
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes new {
|
@keyframes new {
|
||||||
0% { background-color: rgba(255, 255, 204, 1); }
|
0% {
|
||||||
50% { background-color: rgba(255, 255, 204, .5); }
|
background-color: rgba(255, 255, 204, 1);
|
||||||
100% { background-color: rgba(255, 255, 204, 0); }
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
background-color: rgba(255, 255, 204, .5);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
background-color: rgba(255, 255, 204, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes tagflash {
|
@keyframes tagflash {
|
||||||
0% { opacity: 1; }
|
0% {
|
||||||
50% { opacity: 0; }
|
opacity: 1;
|
||||||
100% { opacity: 1; }
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pending {
|
@keyframes pending {
|
||||||
0% { }
|
0% {}
|
||||||
50% { background-color: #d4d4f5;}
|
|
||||||
100% { }
|
50% {
|
||||||
|
background-color: #d4d4f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 480px) {
|
@media screen and (max-width: 480px) {
|
||||||
@@ -638,7 +905,7 @@ ul.messages li.new {
|
|||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logbox #sysinfo {
|
footer #sysinfo {
|
||||||
float: none;
|
float: none;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@@ -682,4 +949,4 @@ ul.messages li.new {
|
|||||||
padding: 1px 1px;
|
padding: 1px 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,8 @@ const WAKEUP_REASON_NETWORK_SCAN = 0xFD;
|
|||||||
const WAKEUP_REASON_WDT_RESET = 0xFE;
|
const WAKEUP_REASON_WDT_RESET = 0xFE;
|
||||||
|
|
||||||
let tagTypes = {};
|
let tagTypes = {};
|
||||||
|
let apConfig = {};
|
||||||
|
let tagDB = {};
|
||||||
|
|
||||||
const apstate = [
|
const apstate = [
|
||||||
{ state: "offline", color: "red" },
|
{ state: "offline", color: "red" },
|
||||||
@@ -38,7 +40,22 @@ let socket;
|
|||||||
let finishedInitialLoading = false;
|
let finishedInitialLoading = false;
|
||||||
let getTagtypeBusy = false;
|
let getTagtypeBusy = false;
|
||||||
|
|
||||||
|
const loadConfig = new Event("loadConfig");
|
||||||
|
window.addEventListener("loadConfig", function () {
|
||||||
|
fetch("/get_ap_config")
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
apConfig = data;
|
||||||
|
|
||||||
|
if (data.alias) {
|
||||||
|
$(".logo").innerHTML = data.alias;
|
||||||
|
this.document.title = data.alias;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
window.addEventListener("load", function () {
|
window.addEventListener("load", function () {
|
||||||
|
initTabs();
|
||||||
fetch('/content_cards.json')
|
fetch('/content_cards.json')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
@@ -51,19 +68,39 @@ window.addEventListener("load", function () {
|
|||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
alert("I can't load /www/content_cards.json.\r\nHave you upload it to the data partition?");
|
alert("I can't load /www/content_cards.json.\r\nHave you upload it to the data partition?");
|
||||||
});
|
});
|
||||||
fetch("/get_ap_config")
|
|
||||||
.then(response => response.json())
|
window.dispatchEvent(loadConfig);
|
||||||
.then(data => {
|
|
||||||
if (data.alias) {
|
|
||||||
$(".logo").innerHTML = data.alias;
|
|
||||||
this.document.title = data.alias;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dropUpload();
|
dropUpload();
|
||||||
populateTimes($('#apcnight1'));
|
populateTimes($('#apcnight1'));
|
||||||
populateTimes($('#apcnight2'));
|
populateTimes($('#apcnight2'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* tabs */
|
||||||
|
let activeTab = '';
|
||||||
|
function initTabs() {
|
||||||
|
const tabLinks = document.querySelectorAll(".tablinks");
|
||||||
|
const tabContents = document.querySelectorAll(".tabcontent");
|
||||||
|
|
||||||
|
tabLinks.forEach(tabLink => {
|
||||||
|
tabLink.addEventListener("click", function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const targetId = this.getAttribute("data-target");
|
||||||
|
const loadTabEvent = new CustomEvent('loadTab', { detail: targetId });
|
||||||
|
document.dispatchEvent(loadTabEvent);
|
||||||
|
tabContents.forEach(tabContent => {
|
||||||
|
tabContent.style.display = "none";
|
||||||
|
});
|
||||||
|
tabLinks.forEach(link => {
|
||||||
|
link.classList.remove("active");
|
||||||
|
});
|
||||||
|
document.getElementById(targetId).style.display = "block";
|
||||||
|
this.classList.add("active");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
tabLinks[0].click();
|
||||||
|
};
|
||||||
|
|
||||||
function loadTags(pos) {
|
function loadTags(pos) {
|
||||||
fetch("/get_db?pos=" + pos)
|
fetch("/get_db?pos=" + pos)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
@@ -83,7 +120,10 @@ function connect() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.addEventListener("message", (event) => {
|
socket.addEventListener("message", (event) => {
|
||||||
// console.log(event.data)
|
if ($('#showdebug').checked) {
|
||||||
|
showMessage(event.data);
|
||||||
|
console.log(event.data);
|
||||||
|
}
|
||||||
const msg = JSON.parse(event.data);
|
const msg = JSON.parse(event.data);
|
||||||
if (msg.logMsg) {
|
if (msg.logMsg) {
|
||||||
showMessage(msg.logMsg, false);
|
showMessage(msg.logMsg, false);
|
||||||
@@ -95,26 +135,34 @@ function connect() {
|
|||||||
processTags(msg.tags);
|
processTags(msg.tags);
|
||||||
}
|
}
|
||||||
if (msg.sys) {
|
if (msg.sys) {
|
||||||
let filesystem = 'filesystem free: ' + convertSize(msg.sys.littlefsfree);
|
let str = "";
|
||||||
if (msg.sys.littlefsfree < 31000) {
|
str += `free heap: ${convertSize(msg.sys.heap)} ┇ `;
|
||||||
filesystem = 'filesystem <span class="blink-red" title="Generating content is paused">FULL! ' + convertSize(msg.sys.littlefsfree) + '</span>';
|
if (msg.sys.psfree) {
|
||||||
}
|
str += `free PSRAM: ${convertSize(msg.sys.psfree)} ┇ `;
|
||||||
$('#sysinfo').innerHTML = 'free heap: ' + convertSize(msg.sys.heap) + ' ┇ db size: ' + convertSize(msg.sys.dbsize) + ' ┇ db record count: ' + msg.sys.recordcount + ' ┇ ' + filesystem;
|
}
|
||||||
|
str += `db size: ${convertSize(msg.sys.dbsize)} ┇ `;
|
||||||
|
str += `db record count: ${msg.sys.recordcount} ┇ `;
|
||||||
|
|
||||||
|
if (msg.sys.littlefsfree < 31000) {
|
||||||
|
str += `filesystem <span class="blink-red" title="Generating content is paused">FULL! ${convertSize(
|
||||||
|
msg.sys.littlefsfree
|
||||||
|
)} </span>`;
|
||||||
|
} else {
|
||||||
|
str += `filesystem free: ${convertSize(msg.sys.littlefsfree)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#sysinfo").innerHTML = str;
|
||||||
|
|
||||||
if (msg.sys.apstate) {
|
if (msg.sys.apstate) {
|
||||||
$("#apstatecolor").style.color = apstate[msg.sys.apstate].color;
|
$("#apstatecolor").style.color = apstate[msg.sys.apstate].color;
|
||||||
$("#apstate").innerHTML = apstate[msg.sys.apstate].state;
|
$("#apstate").innerHTML = apstate[msg.sys.apstate].state;
|
||||||
$("#runstate").innerHTML = runstate[msg.sys.runstate].state;
|
$("#runstate").innerHTML = runstate[msg.sys.runstate].state;
|
||||||
if (msg.sys.temp) $("#temp").innerHTML = msg.sys.temp.toFixed(1) + '°C';
|
$('#dashboardStatus').innerHTML = apstate[msg.sys.apstate].state;
|
||||||
}
|
}
|
||||||
servertimediff = (Date.now() / 1000) - msg.sys.currtime;
|
servertimediff = (Date.now() / 1000) - msg.sys.currtime;
|
||||||
}
|
}
|
||||||
if (msg.apitem) {
|
if (msg.apitem) {
|
||||||
let row = $("#aptable").insertRow();
|
populateAPCard(msg.apitem);
|
||||||
row.insertCell(0).innerHTML = "<a href=\"http://" + msg.apitem.ip + "\" target=\"_new\">" + msg.apitem.ip + "</a>";
|
|
||||||
row.insertCell(1).innerHTML = msg.apitem.alias;
|
|
||||||
row.insertCell(2).innerHTML = msg.apitem.count;
|
|
||||||
row.insertCell(3).innerHTML = msg.apitem.channel;
|
|
||||||
row.insertCell(4).innerHTML = msg.apitem.version;
|
|
||||||
}
|
}
|
||||||
if (msg.console) {
|
if (msg.console) {
|
||||||
if (otamodule && typeof (otamodule.print) === "function") {
|
if (otamodule && typeof (otamodule.print) === "function") {
|
||||||
@@ -146,6 +194,7 @@ function convertSize(bytes) {
|
|||||||
function processTags(tagArray) {
|
function processTags(tagArray) {
|
||||||
for (const element of tagArray) {
|
for (const element of tagArray) {
|
||||||
const tagmac = element.mac;
|
const tagmac = element.mac;
|
||||||
|
tagDB[tagmac] = element;
|
||||||
|
|
||||||
let div = $('#tag' + tagmac);
|
let div = $('#tag' + tagmac);
|
||||||
if (div == null) {
|
if (div == null) {
|
||||||
@@ -173,7 +222,6 @@ function processTags(tagArray) {
|
|||||||
if (!alias) alias = tagmac.replace(/^0{1,4}/, '');
|
if (!alias) alias = tagmac.replace(/^0{1,4}/, '');
|
||||||
if ($('#tag' + tagmac + ' .alias').innerHTML != alias) {
|
if ($('#tag' + tagmac + ' .alias').innerHTML != alias) {
|
||||||
$('#tag' + tagmac + ' .alias').innerHTML = alias;
|
$('#tag' + tagmac + ' .alias').innerHTML = alias;
|
||||||
//GroupSortFilter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let contentDefObj = getContentDefById(element.contentMode);
|
let contentDefObj = getContentDefById(element.contentMode);
|
||||||
@@ -207,13 +255,18 @@ function processTags(tagArray) {
|
|||||||
$('#tag' + tagmac + ' .received').style.opacity = "0";
|
$('#tag' + tagmac + ' .received').style.opacity = "0";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.contentMode == 20) {
|
if (!apConfig.preview || element.contentMode == 20) {
|
||||||
$('#tag' + tagmac + ' .tagimg').style.display = 'none';
|
$('#tag' + tagmac + ' .tagimg').style.display = 'none'
|
||||||
} else if (div.dataset.hash != element.hash && div.dataset.hwtype > -1 && (!element.isexternal || element.contentMode != 12)) {
|
} else if (div.dataset.hash != element.hash && div.dataset.hwtype > -1) {
|
||||||
loadImage(tagmac, '/current/' + tagmac + '.raw?' + element.hash);
|
let cachetag = element.hash;
|
||||||
|
if (element.hash == '00000000000000000000000000000000') cachetag = Math.random();
|
||||||
|
if (element.isexternal && element.contentMode == 12) {
|
||||||
|
loadImage(tagmac, 'http://' + tagDB[tagmac].apip + '/current/' + tagmac + '.raw?' + cachetag);
|
||||||
|
} else {
|
||||||
|
loadImage(tagmac, '/current/' + tagmac + '.raw?' + cachetag);
|
||||||
|
}
|
||||||
div.dataset.hash = element.hash;
|
div.dataset.hash = element.hash;
|
||||||
}
|
}
|
||||||
if (element.isexternal && element.contentMode == 12) $('#tag' + tagmac + ' .tagimg').style.display = 'none';
|
|
||||||
|
|
||||||
if (element.nextupdate > 1672531200 && element.nextupdate != 3216153600) {
|
if (element.nextupdate > 1672531200 && element.nextupdate != 3216153600) {
|
||||||
const date = new Date(element.nextupdate * 1000);
|
const date = new Date(element.nextupdate * 1000);
|
||||||
@@ -262,6 +315,7 @@ function processTags(tagArray) {
|
|||||||
break;
|
break;
|
||||||
case WAKEUP_REASON_NFC:
|
case WAKEUP_REASON_NFC:
|
||||||
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "NFC wakeup"
|
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "NFC wakeup"
|
||||||
|
$('#tag' + tagmac).style.background = "#c8f1bb";
|
||||||
break;
|
break;
|
||||||
case WAKEUP_REASON_NETWORK_SCAN:
|
case WAKEUP_REASON_NETWORK_SCAN:
|
||||||
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "<font color=yellow>Network scan</font>"
|
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "<font color=yellow>Network scan</font>"
|
||||||
@@ -284,9 +338,15 @@ function processTags(tagArray) {
|
|||||||
|
|
||||||
function updatecards() {
|
function updatecards() {
|
||||||
if (servertimediff > 1000000000) servertimediff = 0;
|
if (servertimediff > 1000000000) servertimediff = 0;
|
||||||
|
let tagcount = 0;
|
||||||
|
let pendingcount = 0;
|
||||||
|
let timeoutcount = 0;
|
||||||
|
let lowbattcount = 0;
|
||||||
|
|
||||||
$('#taglist').querySelectorAll('[data-mac]').forEach(item => {
|
$('#taglist').querySelectorAll('[data-mac]').forEach(item => {
|
||||||
let tagmac = item.dataset.mac;
|
let tagmac = item.dataset.mac;
|
||||||
|
tagcount++;
|
||||||
|
if (tagDB[tagmac].batteryMv < 2400 && tagDB[tagmac].batteryMv != 0 && tagDB[tagmac].batteryMv != 1337) lowbattcount++;
|
||||||
if (item.dataset.lastseen && item.dataset.lastseen > (Date.now() / 1000) - servertimediff - 30 * 24 * 3600 * 60) {
|
if (item.dataset.lastseen && item.dataset.lastseen > (Date.now() / 1000) - servertimediff - 30 * 24 * 3600 * 60) {
|
||||||
let idletime = (Date.now() / 1000) - servertimediff - item.dataset.lastseen;
|
let idletime = (Date.now() / 1000) - servertimediff - item.dataset.lastseen;
|
||||||
$('#tag' + tagmac + ' .lastseen').innerHTML = "<span>last seen</span>" + displayTime(Math.floor(idletime)) + " ago";
|
$('#tag' + tagmac + ' .lastseen').innerHTML = "<span>last seen</span>" + displayTime(Math.floor(idletime)) + " ago";
|
||||||
@@ -294,6 +354,9 @@ function updatecards() {
|
|||||||
$('#tag' + tagmac + ' .warningicon').style.display = 'inline-block';
|
$('#tag' + tagmac + ' .warningicon').style.display = 'inline-block';
|
||||||
$('#tag' + tagmac).classList.remove("tagpending")
|
$('#tag' + tagmac).classList.remove("tagpending")
|
||||||
$('#tag' + tagmac).style.background = '#e0e0a0';
|
$('#tag' + tagmac).style.background = '#e0e0a0';
|
||||||
|
timeoutcount++;
|
||||||
|
} else {
|
||||||
|
if (tagDB[tagmac].pending) pendingcount++;
|
||||||
}
|
}
|
||||||
if (idletime > 24 * 3600) {
|
if (idletime > 24 * 3600) {
|
||||||
$('#tag' + tagmac).style.opacity = '.5';
|
$('#tag' + tagmac).style.opacity = '.5';
|
||||||
@@ -314,6 +377,11 @@ function updatecards() {
|
|||||||
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "";
|
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "";
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$('#dashboardTagCount').innerHTML = tagcount;
|
||||||
|
$('#dashboardPending').innerHTML = pendingcount;
|
||||||
|
$('#dashboardLowBatt').innerHTML = lowbattcount;
|
||||||
|
$('#dashboardTimeout').innerHTML = timeoutcount;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#clearlog').onclick = function () {
|
$('#clearlog').onclick = function () {
|
||||||
@@ -327,6 +395,13 @@ document.querySelectorAll('.closebtn').forEach(button => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('.closebtn2').forEach(button => {
|
||||||
|
button.addEventListener('click', (event) => {
|
||||||
|
event.target.parentNode.close();
|
||||||
|
$('#advancedoptions').style.height = '0px';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
//clicking on a tag: load config dialog for tag
|
//clicking on a tag: load config dialog for tag
|
||||||
$('#taglist').addEventListener("click", (event) => {
|
$('#taglist').addEventListener("click", (event) => {
|
||||||
let currentElement = event.target;
|
let currentElement = event.target;
|
||||||
@@ -364,8 +439,9 @@ function loadContentCard(mac) {
|
|||||||
}
|
}
|
||||||
$('#cfgrotate').value = tagdata.rotate;
|
$('#cfgrotate').value = tagdata.rotate;
|
||||||
$('#cfglut').value = tagdata.lut;
|
$('#cfglut').value = tagdata.lut;
|
||||||
|
$('#cfginvert').value = tagdata.invert;
|
||||||
$('#cfgmore').innerHTML = '▼';
|
$('#cfgmore').innerHTML = '▼';
|
||||||
$('#configbox').style.display = 'block';
|
$('#configbox').showModal();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,6 +493,7 @@ $('#cfgsave').onclick = function () {
|
|||||||
|
|
||||||
formData.append("rotate", $('#cfgrotate').value);
|
formData.append("rotate", $('#cfgrotate').value);
|
||||||
formData.append("lut", $('#cfglut').value);
|
formData.append("lut", $('#cfglut').value);
|
||||||
|
formData.append("invert", $('#cfginvert').value);
|
||||||
|
|
||||||
fetch("/save_cfg", {
|
fetch("/save_cfg", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -427,7 +504,7 @@ $('#cfgsave').onclick = function () {
|
|||||||
.catch(error => showMessage('Error: ' + error));
|
.catch(error => showMessage('Error: ' + error));
|
||||||
|
|
||||||
$('#advancedoptions').style.height = '0px';
|
$('#advancedoptions').style.height = '0px';
|
||||||
$('#configbox').style.display = 'none';
|
$('#configbox').close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendCmd(mac, cmd) {
|
function sendCmd(mac, cmd) {
|
||||||
@@ -446,7 +523,7 @@ function sendCmd(mac, cmd) {
|
|||||||
})
|
})
|
||||||
.catch(error => showMessage('Error: ' + error));
|
.catch(error => showMessage('Error: ' + error));
|
||||||
$('#advancedoptions').style.height = '0px';
|
$('#advancedoptions').style.height = '0px';
|
||||||
$('#configbox').style.display = 'none';
|
$('#configbox').close();
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#cfgdelete').onclick = function () {
|
$('#cfgdelete').onclick = function () {
|
||||||
@@ -477,43 +554,60 @@ $('#cfgreset').onclick = function () {
|
|||||||
sendCmd($('#cfgmac').dataset.mac, "reset");
|
sendCmd($('#cfgmac').dataset.mac, "reset");
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#rebootbutton').onclick = function () {
|
$('#rebootbutton').onclick = function (event) {
|
||||||
showMessage("rebooting AP....", true);
|
event.preventDefault();
|
||||||
|
if (!confirm('Reboot AP now?')) return;
|
||||||
|
socket.close();
|
||||||
fetch("/reboot", {
|
fetch("/reboot", {
|
||||||
method: "POST"
|
method: "POST"
|
||||||
});
|
});
|
||||||
socket.close();
|
alert('Rebooted. Webpage will reload.');
|
||||||
|
location.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#apconfigbutton').onclick = function () {
|
$('#configbox').addEventListener('click', (event) => {
|
||||||
let table = document.getElementById("aptable");
|
if (event.target.nodeName === 'DIALOG') {
|
||||||
const rowCount = table.rows.length;
|
$('#configbox').close();
|
||||||
for (let i = rowCount - 1; i > 0; i--) {
|
|
||||||
table.deleteRow(i);
|
|
||||||
}
|
}
|
||||||
fetch("/get_ap_config")
|
});
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
document.addEventListener("loadTab", function (event) {
|
||||||
$('#apcfgalias').value = data.alias;
|
activeTab = event.detail;
|
||||||
$('#apcfgchid').value = data.channel;
|
switch (event.detail) {
|
||||||
$("#apcfgledbrightness").value = data.led;
|
case 'configtab':
|
||||||
$("#apcfglanguage").value = data.language;
|
case 'aptab':
|
||||||
$("#apclatency").value = data.maxsleep;
|
fetch("/get_ap_config")
|
||||||
$("#apcpreventsleep").value = data.stopsleep;
|
.then(response => response.json())
|
||||||
$("#apcpreview").value = data.preview;
|
.then(data => {
|
||||||
$("#apcwifipower").value = data.wifipower;
|
apConfig = data;
|
||||||
$("#apctimezone").value = data.timezone;
|
$('#apcfgalias').value = data.alias;
|
||||||
$("#apcnight1").value = data.sleeptime1;
|
$('#apcfgchid').value = data.channel;
|
||||||
$("#apcnight2").value = data.sleeptime2;
|
$("#apcfgledbrightness").value = data.led;
|
||||||
})
|
$("#apcfgtftbrightness").value = data.tft;
|
||||||
$('#apconfigbox').style.display = 'block'
|
$("#apcfglanguage").value = data.language;
|
||||||
}
|
$("#apclatency").value = data.maxsleep;
|
||||||
|
$("#apcpreventsleep").value = data.stopsleep;
|
||||||
|
$("#apcpreview").value = data.preview;
|
||||||
|
$("#apcwifipower").value = data.wifipower;
|
||||||
|
$("#apctimezone").value = data.timezone;
|
||||||
|
$("#apcnight1").value = data.sleeptime1;
|
||||||
|
$("#apcnight2").value = data.sleeptime2;
|
||||||
|
})
|
||||||
|
$('#apcfgmsg').innerHTML = '';
|
||||||
|
break;
|
||||||
|
case 'updatetab':
|
||||||
|
$('#updateconsole').innerHTML = '';
|
||||||
|
loadOTA();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$('#apcfgsave').onclick = function () {
|
$('#apcfgsave').onclick = function () {
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
formData.append("alias", $('#apcfgalias').value);
|
formData.append("alias", $('#apcfgalias').value);
|
||||||
formData.append("channel", $('#apcfgchid').value);
|
formData.append("channel", $('#apcfgchid').value);
|
||||||
formData.append('led', $('#apcfgledbrightness').value);
|
formData.append('led', $('#apcfgledbrightness').value);
|
||||||
|
formData.append('tft', $('#apcfgtftbrightness').value);
|
||||||
formData.append('language', $('#apcfglanguage').value);
|
formData.append('language', $('#apcfglanguage').value);
|
||||||
formData.append('maxsleep', $('#apclatency').value);
|
formData.append('maxsleep', $('#apclatency').value);
|
||||||
formData.append('stopsleep', $('#apcpreventsleep').value);
|
formData.append('stopsleep', $('#apcpreventsleep').value);
|
||||||
@@ -522,21 +616,18 @@ $('#apcfgsave').onclick = function () {
|
|||||||
formData.append('timezone', $('#apctimezone').value);
|
formData.append('timezone', $('#apctimezone').value);
|
||||||
formData.append('sleeptime1', $('#apcnight1').value);
|
formData.append('sleeptime1', $('#apcnight1').value);
|
||||||
formData.append('sleeptime2', $('#apcnight2').value);
|
formData.append('sleeptime2', $('#apcnight2').value);
|
||||||
|
|
||||||
fetch("/save_apcfg", {
|
fetch("/save_apcfg", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(data => showMessage(data))
|
.then(data => {
|
||||||
|
showMessage(data);
|
||||||
|
window.dispatchEvent(loadConfig);
|
||||||
|
$('#apcfgmsg').innerHTML = 'OK, Saved';
|
||||||
|
})
|
||||||
.catch(error => showMessage('Error: ' + error));
|
.catch(error => showMessage('Error: ' + error));
|
||||||
$(".logo").innerHTML = $('#apcfgalias').value;
|
|
||||||
$('#apconfigbox').style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#updatebutton').onclick = function () {
|
|
||||||
$('#apconfigbox').style.display = 'none';
|
|
||||||
$('#apupdatebox').style.display = 'block';
|
|
||||||
loadOTA();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadOTA() {
|
async function loadOTA() {
|
||||||
@@ -612,6 +703,31 @@ function contentselected() {
|
|||||||
input.type = "text";
|
input.type = "text";
|
||||||
input.disabled = true;
|
input.disabled = true;
|
||||||
break;
|
break;
|
||||||
|
case 'jpgfile':
|
||||||
|
case 'binfile':
|
||||||
|
case 'jsonfile':
|
||||||
|
input = document.createElement("select");
|
||||||
|
fetch('/edit?list=%2F&recursive=1')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
let files = data.filter(item => item.type === "file" && item.name.endsWith(".jpg"));
|
||||||
|
if (element.type == 'binfile') files = data.filter(item => item.type === "file" && item.name.endsWith(".bin"));
|
||||||
|
if (element.type == 'jsonfile') files = data.filter(item => item.type === "file" && item.name.endsWith(".json"));
|
||||||
|
const optionElement = document.createElement("option");
|
||||||
|
optionElement.value = "";
|
||||||
|
optionElement.text = "";
|
||||||
|
input.appendChild(optionElement);
|
||||||
|
files.forEach(item => {
|
||||||
|
const optionElement = document.createElement("option");
|
||||||
|
optionElement.value = item.name;
|
||||||
|
optionElement.text = item.name;
|
||||||
|
input.appendChild(optionElement);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error("Error fetching JSON data:", error);
|
||||||
|
});
|
||||||
|
break;
|
||||||
case 'select':
|
case 'select':
|
||||||
input = document.createElement("select");
|
input = document.createElement("select");
|
||||||
for (const key in element.options) {
|
for (const key in element.options) {
|
||||||
@@ -698,7 +814,9 @@ function showMessage(message, iserr) {
|
|||||||
const messages = $('#messages');
|
const messages = $('#messages');
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
const time = date.toLocaleTimeString('nl-NL', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
const time = date.toLocaleTimeString('nl-NL', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||||
if (iserr) {
|
if (message.startsWith('{')) {
|
||||||
|
messages.insertAdjacentHTML("afterbegin", '<li class="new">' + htmlEncode(time) + ' <span class="mono">' + message.replace(/"([^"]+)"/g, '"<span class="quote">$1</span>"') + '</span></li>');
|
||||||
|
} else if (iserr) {
|
||||||
messages.insertAdjacentHTML("afterbegin", '<li class="new error">' + htmlEncode(time + ' ' + message) + '</li>');
|
messages.insertAdjacentHTML("afterbegin", '<li class="new error">' + htmlEncode(time + ' ' + message) + '</li>');
|
||||||
} else {
|
} else {
|
||||||
messages.insertAdjacentHTML("afterbegin", '<li class="new">' + htmlEncode(time + ' ' + message) + '</li>');
|
messages.insertAdjacentHTML("afterbegin", '<li class="new">' + htmlEncode(time + ' ' + message) + '</li>');
|
||||||
@@ -865,12 +983,18 @@ function GroupSortFilter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let show = true;
|
let show = true;
|
||||||
|
let batteryMv = tagDB[item.dataset.mac].batteryMv;
|
||||||
if ($('input[name="filter"][value="remote"]').checked && item.dataset.isexternal == "false") show = false;
|
if ($('input[name="filter"][value="remote"]').checked && item.dataset.isexternal == "false") show = false;
|
||||||
if ($('input[name="filter"][value="local"]').checked && item.dataset.isexternal == "true") show = false;
|
if ($('input[name="filter"][value="local"]').checked && item.dataset.isexternal == "true") show = false;
|
||||||
if ($('input[name="filter"][value="inactive"]').checked && item.querySelector('.warningicon').style.display != 'inline-block') show = false;
|
if ($('input[name="filter"][value="inactive"]').checked && item.querySelector('.warningicon').style.display != 'inline-block') show = false;
|
||||||
if ($('input[name="filter"][value="pending"]').checked && !item.classList.contains("tagpending")) show = false;
|
if ($('input[name="filter"][value="pending"]').checked && !item.classList.contains("tagpending")) show = false;
|
||||||
|
if ($('input[name="filter"][value="lowbatt"]').checked && (batteryMv >= 2400 || batteryMv == 0 || batteryMv == 1337)) show = false;
|
||||||
if (!show) item.style.display = 'none'; else item.style.display = 'block';
|
if (!show) item.style.display = 'none'; else item.style.display = 'block';
|
||||||
item.style.order = order++;
|
item.style.order = order++;
|
||||||
|
const checkedValues = Array.from(document.querySelectorAll('input[name="filter"]:checked'))
|
||||||
|
.map(checkbox => checkbox.value)
|
||||||
|
.join(', ');
|
||||||
|
$('#activefilter').innerHTML = (checkedValues ? 'filtered by ' + checkedValues : '');
|
||||||
});
|
});
|
||||||
|
|
||||||
headItems = Array.from($('#taglist').getElementsByClassName('taggroup'));
|
headItems = Array.from($('#taglist').getElementsByClassName('taggroup'));
|
||||||
@@ -879,7 +1003,7 @@ function GroupSortFilter() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#toggleFilters').addEventListener('click', () => {
|
$('#toggleFilters').addEventListener('click', (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const filterOptions = $('#filterOptions');
|
const filterOptions = $('#filterOptions');
|
||||||
filterOptions.classList.toggle('active');
|
filterOptions.classList.toggle('active');
|
||||||
@@ -890,11 +1014,26 @@ $('#toggleFilters').addEventListener('click', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#activefilter').addEventListener('click', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const filterOptions = $('#filterOptions');
|
||||||
|
filterOptions.classList.add('active');
|
||||||
|
filterOptions.style.maxHeight = filterOptions.scrollHeight + 20 + 'px';
|
||||||
|
});
|
||||||
|
|
||||||
async function getTagtype(hwtype) {
|
async function getTagtype(hwtype) {
|
||||||
if (tagTypes[hwtype]) {
|
if (tagTypes[hwtype] && tagTypes[hwtype].busy == false) {
|
||||||
return tagTypes[hwtype];
|
return tagTypes[hwtype];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nice, but no possibility to invalidate this cache yet.
|
||||||
|
/*
|
||||||
|
const storedData = JSON.parse(localStorage.getItem("tagTypes"));
|
||||||
|
if (storedData && storedData[hwtype]) {
|
||||||
|
return storedData[hwtype];
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if (getTagtypeBusy) {
|
if (getTagtypeBusy) {
|
||||||
await new Promise(resolve => {
|
await new Promise(resolve => {
|
||||||
const checkBusy = setInterval(() => {
|
const checkBusy = setInterval(() => {
|
||||||
@@ -913,7 +1052,7 @@ async function getTagtype(hwtype) {
|
|||||||
clearInterval(checkBusy);
|
clearInterval(checkBusy);
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
}, 10);
|
}, 50);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -922,8 +1061,8 @@ async function getTagtype(hwtype) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
tagTypes[hwtype] = { busy: true };
|
|
||||||
getTagtypeBusy = true;
|
getTagtypeBusy = true;
|
||||||
|
tagTypes[hwtype] = { busy: true };
|
||||||
const response = await fetch('/tagtypes/' + hwtype.toString(16).padStart(2, '0').toUpperCase() + '.json');
|
const response = await fetch('/tagtypes/' + hwtype.toString(16).padStart(2, '0').toUpperCase() + '.json');
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
let data = { name: 'unknown id ' + hwtype, width: 0, height: 0, bpp: 0, rotatebuffer: 0, colortable: [], busy: false };
|
let data = { name: 'unknown id ' + hwtype, width: 0, height: 0, bpp: 0, rotatebuffer: 0, colortable: [], busy: false };
|
||||||
@@ -1094,7 +1233,8 @@ $('#taglist').addEventListener('contextmenu', (e) => {
|
|||||||
if (tagTypes[hwtype].options?.includes("led")) {
|
if (tagTypes[hwtype].options?.includes("led")) {
|
||||||
contextMenuOptions.push(
|
contextMenuOptions.push(
|
||||||
{ id: 'ledflash', label: 'Flash the LED' },
|
{ id: 'ledflash', label: 'Flash the LED' },
|
||||||
{ id: 'ledflash_long', label: 'Flash the LED (long)' }
|
{ id: 'ledflash_long', label: 'Flash the LED (long)' },
|
||||||
|
{ id: 'ledflash_stop', label: 'Stop flashing' }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
contextMenuOptions.push(
|
contextMenuOptions.push(
|
||||||
@@ -1133,4 +1273,83 @@ function populateTimes(element) {
|
|||||||
option.text = i.toString().padStart(2, "0") + ":00";
|
option.text = i.toString().padStart(2, "0") + ":00";
|
||||||
element.appendChild(option);
|
element.appendChild(option);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateAPCard(msg) {
|
||||||
|
let apip = msg.ip;
|
||||||
|
let apid = apip.replace(/\./g, "-");
|
||||||
|
if (!$('#ap' + apid)) {
|
||||||
|
div = $('#apcard').cloneNode(true);
|
||||||
|
div.setAttribute('id', 'ap' + apid);
|
||||||
|
$('#aplist').appendChild(div);
|
||||||
|
}
|
||||||
|
let alias = msg.alias;
|
||||||
|
if (!alias) alias = apip;
|
||||||
|
$('#ap' + apid + ' .apip').innerHTML = "<a href=\"http://" + apip + "\" target=\"_new\">" + apip + "</a>";
|
||||||
|
$('#ap' + apid + ' .apalias').innerHTML = alias;
|
||||||
|
$('#ap' + apid + ' .aptagcount').innerHTML = msg.count;
|
||||||
|
$('#ap' + apid + ' .apchannel').innerHTML = msg.channel;
|
||||||
|
|
||||||
|
const elements = document.querySelectorAll('.apchannel');
|
||||||
|
Array.from(elements).forEach(element => {
|
||||||
|
if (element.textContent === msg.channel && element.id !== 'ap' + apid) {
|
||||||
|
$('#ap' + apid + ' .apchannel').style.color = 'red';
|
||||||
|
$('#ap' + apid + ' .apchannel').innerHTML += ' conflict';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// $('#ap' + apid + ' .apversion').innerHTML = msg.version;
|
||||||
|
if (activeTab == 'aptab') {
|
||||||
|
populateAPInfo(apip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateAPInfo(apip) {
|
||||||
|
let apid = apip.replace(/\./g, "-");
|
||||||
|
fetch('http://' + apip + '/sysinfo')
|
||||||
|
.then(response => {
|
||||||
|
if (response.status != 200) {
|
||||||
|
$('#ap' + apid + ' .apswversion').innerHTML = "Error fetching sysinfo: " + response.status;
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
if (data.env) {
|
||||||
|
let version = '';
|
||||||
|
version += `env: ${data.env}<br>`;
|
||||||
|
version += `build date: ${formatEpoch(data.buildtime)}<br>`;
|
||||||
|
version += `esp32 version: ${data.buildversion}<br>`;
|
||||||
|
version += `psram size: ${data.psramsize}<br>`;
|
||||||
|
version += `flash size: ${data.flashsize}<br>`;
|
||||||
|
$('#ap' + apid + ' .apswversion').innerHTML = version;
|
||||||
|
// if (data.env == 'ESP32_S3_16_8_YELLOW_AP') $("#c6Option").style.display = 'block';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
$('#ap' + apid + ' .apswversion').innerHTML = "Error fetching sysinfo: " + error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatEpoch(epochTime) {
|
||||||
|
const date = new Date(epochTime * 1000); // Convert seconds to milliseconds
|
||||||
|
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are zero-based
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFilterAndShow(filter) {
|
||||||
|
$('input[name="filter"][value="remote"]').checked = false;
|
||||||
|
$('input[name="filter"][value="local"]').checked = false;
|
||||||
|
$('input[name="filter"][value="inactive"]').checked = (filter == 'inactive');
|
||||||
|
$('input[name="filter"][value="pending"]').checked = (filter == 'pending');
|
||||||
|
$('input[name="filter"][value="lowbatt"]').checked = (filter == 'lowbatt');
|
||||||
|
GroupSortFilter();
|
||||||
|
$(`[data-target='tagtab']`).click();
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
const repoUrl = 'https://api.github.com/repos/jjwbruijn/OpenEPaperLink/releases';
|
var repo = apConfig.repo || 'jjwbruijn/OpenEPaperLink';
|
||||||
|
var repoUrl = 'https://api.github.com/repos/' + repo + '/releases';
|
||||||
|
|
||||||
const $ = document.querySelector.bind(document);
|
const $ = document.querySelector.bind(document);
|
||||||
|
|
||||||
@@ -8,19 +9,28 @@ let env = '', currentVer = '', currentBuildtime = 0;
|
|||||||
let buttonState = false;
|
let buttonState = false;
|
||||||
|
|
||||||
export async function initUpdate() {
|
export async function initUpdate() {
|
||||||
if (!$("#updateconsole")) {
|
|
||||||
const consoleDiv = document.createElement('div');
|
|
||||||
consoleDiv.classList.add('console');
|
|
||||||
consoleDiv.id = "updateconsole";
|
|
||||||
$('#apupdatebox').appendChild(consoleDiv);
|
|
||||||
}
|
|
||||||
$("#updateconsole").innerHTML = "";
|
|
||||||
|
|
||||||
const response = await fetch("/version.txt");
|
const response = await fetch("/version.txt");
|
||||||
let filesystemversion = await response.text();
|
let filesystemversion = await response.text();
|
||||||
if (!filesystemversion) filesystemversion = "unknown";
|
if (!filesystemversion) filesystemversion = "unknown";
|
||||||
|
$('#repo').value = repo;
|
||||||
|
|
||||||
fetch("/sysinfo")
|
const envBox = $('#environment');
|
||||||
|
if (envBox?.tagName === 'SELECT') {
|
||||||
|
const inputElement = document.createElement('input');
|
||||||
|
inputElement.type = 'text';
|
||||||
|
inputElement.id = 'environment';
|
||||||
|
envBox.parentNode.replaceChild(inputElement, envBox);
|
||||||
|
}
|
||||||
|
$('#environment').value = '';
|
||||||
|
$('#environment').setAttribute('readonly', true);
|
||||||
|
$('#repo').removeAttribute('readonly');
|
||||||
|
$('#confirmSelectRepo').style.display = 'none';
|
||||||
|
$('#cancelSelectRepo').style.display = 'none';
|
||||||
|
$('#selectRepo').style.display = 'inline-block';
|
||||||
|
$('#repoWarning').style.display = 'none';
|
||||||
|
|
||||||
|
const sysinfoPromise = fetch("/sysinfo")
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (response.status != 200) {
|
if (response.status != 200) {
|
||||||
print("Error fetching sysinfo: " + response.status, "red");
|
print("Error fetching sysinfo: " + response.status, "red");
|
||||||
@@ -34,33 +44,36 @@ export async function initUpdate() {
|
|||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(data => {
|
|
||||||
if (data.env) {
|
|
||||||
let matchtest = '';
|
|
||||||
if (data.buildversion != filesystemversion && filesystemversion != "custom" && data.buildversion != "custom") matchtest = " <- not matching!"
|
|
||||||
print(`env: ${data.env}`);
|
|
||||||
print(`build date: ${formatEpoch(data.buildtime)}`);
|
|
||||||
print(`esp32 version: ${data.buildversion}`);
|
|
||||||
print(`filesystem version: ${filesystemversion}` + matchtest);
|
|
||||||
print(`sha: ${data.sha}`);
|
|
||||||
print(`psram size: ${data.psramsize}`);
|
|
||||||
print(`flash size: ${data.flashsize}`);
|
|
||||||
print("--------------------------", "gray");
|
|
||||||
env = data.env;
|
|
||||||
currentVer = data.buildversion;
|
|
||||||
currentBuildtime = data.buildtime;
|
|
||||||
if (data.rollback) $("#rollbackOption").style.display = 'block';
|
|
||||||
if (data.env == 'ESP32_S3_16_8_YELLOW_AP') $("#c6Option").style.display = 'block';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
print('Error fetching sysinfo: ' + error, "red");
|
print('Error fetching sysinfo: ' + error, "red");
|
||||||
});
|
});
|
||||||
|
|
||||||
fetch(repoUrl)
|
const repoPromise = fetch(repoUrl)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
|
||||||
const releaseDetails = data.map(release => {
|
|
||||||
|
Promise.all([sysinfoPromise, repoPromise])
|
||||||
|
.then(([sdata, rdata]) => {
|
||||||
|
|
||||||
|
if (sdata.env) {
|
||||||
|
let matchtest = '';
|
||||||
|
if (sdata.buildversion != filesystemversion && filesystemversion != "custom" && sdata.buildversion != "custom") matchtest = " <- not matching!"
|
||||||
|
print(`env: ${sdata.env}`);
|
||||||
|
print(`build date: ${formatEpoch(sdata.buildtime)}`);
|
||||||
|
print(`esp32 version: ${sdata.buildversion}`);
|
||||||
|
print(`filesystem version: ${filesystemversion}` + matchtest);
|
||||||
|
print(`psram size: ${sdata.psramsize}`);
|
||||||
|
print(`flash size: ${sdata.flashsize}`);
|
||||||
|
print("--------------------------", "gray");
|
||||||
|
env = sdata.env;
|
||||||
|
currentVer = sdata.buildversion;
|
||||||
|
currentBuildtime = sdata.buildtime;
|
||||||
|
if (sdata.rollback) $("#rollbackOption").style.display = 'block';
|
||||||
|
if (sdata.env == 'ESP32_S3_16_8_YELLOW_AP') $("#c6Option").style.display = 'block';
|
||||||
|
$('#environment').value = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
const releaseDetails = rdata.map(release => {
|
||||||
const assets = release.assets;
|
const assets = release.assets;
|
||||||
const filesJsonAsset = assets.find(asset => asset.name === 'filesystem.json');
|
const filesJsonAsset = assets.find(asset => asset.name === 'filesystem.json');
|
||||||
const binariesJsonAsset = assets.find(asset => asset.name === 'binaries.json');
|
const binariesJsonAsset = assets.find(asset => asset.name === 'binaries.json');
|
||||||
@@ -76,7 +89,7 @@ export async function initUpdate() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const easyupdate = $('#easyupdate');
|
const easyupdate = $('#easyupdate');
|
||||||
if (releaseDetails.length === 0) {
|
if (releaseDetails.length === 0) {
|
||||||
easyupdate.innerHTML = ("No releases found.");
|
easyupdate.innerHTML = ("No releases found.");
|
||||||
@@ -92,7 +105,6 @@ export async function initUpdate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
easyupdate.innerHTML += "<br><a onclick=\"$('#advanceddiv').style.display='block'\">advanced options</a>"
|
|
||||||
|
|
||||||
const table = document.createElement('table');
|
const table = document.createElement('table');
|
||||||
const tableHeader = document.createElement('tr');
|
const tableHeader = document.createElement('tr');
|
||||||
@@ -101,9 +113,9 @@ export async function initUpdate() {
|
|||||||
|
|
||||||
let rowCounter = 0;
|
let rowCounter = 0;
|
||||||
releaseDetails.forEach(release => {
|
releaseDetails.forEach(release => {
|
||||||
if (rowCounter < 3 && release?.html_url) {
|
if (rowCounter < 4 && release?.html_url) {
|
||||||
const tableRow = document.createElement('tr');
|
const tableRow = document.createElement('tr');
|
||||||
let tablerow = `<td><a href="${release.html_url}" target="_new">${release.tag_name}</a></td><td>${release.date}</td><td>${release.name}</td><td><button onclick="otamodule.updateESP('${release.bin_url}', true)">ESP32</button></td><td><button onclick="otamodule.updateWebpage('${release.file_url}','${release.tag_name}', true)">Filesystem</button></td>`;
|
let tablerow = `<td><a href="${release.html_url}" target="_new">${release.tag_name}</a></td><td>${release.date}</td><td>${release.name}</td><td><button type="button" onclick="otamodule.updateWebpage('${release.file_url}','${release.tag_name}', true)">Filesystem</button></td><td><button type="button" onclick="otamodule.updateESP('${release.bin_url}', true)">ESP32</button></td>`;
|
||||||
if (release.tag_name == currentVer) {
|
if (release.tag_name == currentVer) {
|
||||||
tablerow += "<td>current version</td>";
|
tablerow += "<td>current version</td>";
|
||||||
} else if (release.date < formatEpoch(currentBuildtime)) {
|
} else if (release.date < formatEpoch(currentBuildtime)) {
|
||||||
@@ -362,6 +374,90 @@ $('#updateC6Btn').onclick = function () {
|
|||||||
disableButtons(false);
|
disableButtons(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#selectRepo').onclick = function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
$('#updateconsole').innerHTML = '';
|
||||||
|
|
||||||
|
let repoUrl = 'https://api.github.com/repos/' + $('#repo').value + '/releases';
|
||||||
|
fetch(repoUrl)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (Array.isArray(data) && data.length > 0) {
|
||||||
|
const release = data[0];
|
||||||
|
print("Repo found! Latest release: " + release.name + " created " + release.created_at);
|
||||||
|
const assets = release.assets;
|
||||||
|
const filesJsonAsset = assets.find(asset => asset.name === 'filesystem.json');
|
||||||
|
const binariesJsonAsset = assets.find(asset => asset.name === 'binaries.json');
|
||||||
|
if (filesJsonAsset && binariesJsonAsset) {
|
||||||
|
const updateUrl = "http://openepaperlink.eu/getupdate/?url=" + binariesJsonAsset.browser_download_url + "&env=" + $('#repo').value;
|
||||||
|
return fetch(updateUrl);
|
||||||
|
} else {
|
||||||
|
throw new Error("Json file binaries.json and/or filesystem.json not found in the release assets");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.then(updateResponse => {
|
||||||
|
if (!updateResponse.ok) {
|
||||||
|
throw new Error("Network response was not OK");
|
||||||
|
}
|
||||||
|
return updateResponse.text();
|
||||||
|
})
|
||||||
|
.then(responseBody => {
|
||||||
|
if (!responseBody.trim().startsWith("[")) {
|
||||||
|
throw new Error("Failed to fetch the release info file");
|
||||||
|
}
|
||||||
|
const updateData = JSON.parse(responseBody).filter(item => !item.name.endsWith('_full.bin'));
|
||||||
|
|
||||||
|
const inputParent = $('#environment').parentNode;
|
||||||
|
const selectElement = document.createElement('select');
|
||||||
|
selectElement.id = 'environment';
|
||||||
|
updateData.forEach(item => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = item.name.replace('.bin', '');
|
||||||
|
option.text = item.name.replace('.bin', '');
|
||||||
|
selectElement.appendChild(option);
|
||||||
|
});
|
||||||
|
inputParent.replaceChild(selectElement, $('#environment'));
|
||||||
|
$('#environment').value = env;
|
||||||
|
$('#confirmSelectRepo').style.display = 'inline-block';
|
||||||
|
$('#cancelSelectRepo').style.display = 'inline-block';
|
||||||
|
$('#selectRepo').style.display = 'none';
|
||||||
|
$('#repo').setAttribute('readonly', true);
|
||||||
|
$('#repoWarning').style.display = 'block';
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
print('Error fetching releases:' + error, "red");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#cancelSelectRepo').onclick = function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
$('#updateconsole').innerHTML = '';
|
||||||
|
initUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#confirmSelectRepo').onclick = function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
repo = $('#repo').value;
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("repo", repo);
|
||||||
|
formData.append("env", $('#environment').value);
|
||||||
|
fetch("/save_apcfg", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(data => {
|
||||||
|
window.dispatchEvent(loadConfig);
|
||||||
|
print('OK, Saved');
|
||||||
|
})
|
||||||
|
.catch(error => print('Error: ' + error));
|
||||||
|
$('#updateconsole').innerHTML = '';
|
||||||
|
repoUrl = 'https://api.github.com/repos/' + repo + '/releases';
|
||||||
|
initUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
export function print(line, color = "white") {
|
export function print(line, color = "white") {
|
||||||
const consoleDiv = document.getElementById('updateconsole');
|
const consoleDiv = document.getElementById('updateconsole');
|
||||||
if (consoleDiv) {
|
if (consoleDiv) {
|
||||||
@@ -464,7 +560,7 @@ const writeVersion = async (content, name, path) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function disableButtons(active) {
|
function disableButtons(active) {
|
||||||
$("#apupdatebox").querySelectorAll('button').forEach(button => {
|
$("#configtab").querySelectorAll('button').forEach(button => {
|
||||||
button.disabled = active;
|
button.disabled = active;
|
||||||
});
|
});
|
||||||
buttonState = active;
|
buttonState = active;
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ function startPainter(mac, width, height) {
|
|||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
xhr.open('POST', '/imgupload');
|
xhr.open('POST', '/imgupload');
|
||||||
xhr.send(formData);
|
xhr.send(formData);
|
||||||
$('#configbox').style.display = 'none';
|
$('#configbox').close();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#buttonbar").appendChild(blackButton);
|
$("#buttonbar").appendChild(blackButton);
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
BIN
Hardware/3D-Printed/4.2 mini stand v2 by Nic.stl
Normal file
BIN
Hardware/3D-Printed/4.2 mini stand v2 by Nic.stl
Normal file
Binary file not shown.
BIN
Hardware/3D-Printed/4.2_mini_stand_v2.jpeg
Normal file
BIN
Hardware/3D-Printed/4.2_mini_stand_v2.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 282 KiB |
@@ -6,4 +6,11 @@ There are holder and stands for every tag. 1.54", 2.9" and 4.2".
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
** 4.2 mini stand v2 by Nic.stl
|
||||||
|

|
||||||
|
4.2" mini stand by Nic. Note that these are designed with tight tolerances, to be printed on a resin printer. If you print it using your desktop 3D-printer, it might not fit.
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ Not all connections are required by all tags! If you want to solder fewer wires,
|
|||||||
|
|
||||||
## Flashing the flasher
|
## Flashing the flasher
|
||||||
Clone the [Tag_Flasher repo](https://github.com/jjwbruijn/OpenEPaperLink/tree/master/Tag_Flasher/ESP32_Flasher) and open into PlatformIO. Choose the correct COM-port and hit 'Upload'.
|
Clone the [Tag_Flasher repo](https://github.com/jjwbruijn/OpenEPaperLink/tree/master/Tag_Flasher/ESP32_Flasher) and open into PlatformIO. Choose the correct COM-port and hit 'Upload'.
|
||||||
|
Also, the precompiled binaries are part of any [release](https://github.com/jjwbruijn/OpenEPaperLink/releases), and to make it even easier, you can use the web flasher on https://install.openepaperlink.de to even flash it without installing any extra software.
|
||||||
|
|
||||||
## OEPL-Flasher.py
|
## OEPL-Flasher.py
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
BIN
binaries/Tag/SOLUM_154_SSD1619-tag-00-0022.bin
Normal file
BIN
binaries/Tag/SOLUM_154_SSD1619-tag-00-0022.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_29_SSD1619-tag-01-0022.bin
Normal file
BIN
binaries/Tag/SOLUM_29_SSD1619-tag-01-0022.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_29_UC8151-tag-11-0022.bin
Normal file
BIN
binaries/Tag/SOLUM_29_UC8151-tag-11-0022.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_42_SSD1619-tag-02-0022.bin
Normal file
BIN
binaries/Tag/SOLUM_42_SSD1619-tag-02-0022.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_M2_BWR_75-tag-26-0004-full.bin
Normal file
BIN
binaries/Tag/SOLUM_M2_BWR_75-tag-26-0004-full.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_M2_BWR_75-tag-26-0004-ota.bin
Normal file
BIN
binaries/Tag/SOLUM_M2_BWR_75-tag-26-0004-ota.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_M3_22_BWR-tag-31-0016-full.bin
Normal file
BIN
binaries/Tag/SOLUM_M3_22_BWR-tag-31-0016-full.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_M3_22_BWR-tag-31-0016-ota.bin
Normal file
BIN
binaries/Tag/SOLUM_M3_22_BWR-tag-31-0016-ota.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_M3_29_BWR-tag-33-0016-full.bin
Normal file
BIN
binaries/Tag/SOLUM_M3_29_BWR-tag-33-0016-full.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_M3_29_BWR-tag-33-0016-ota.bin
Normal file
BIN
binaries/Tag/SOLUM_M3_29_BWR-tag-33-0016-ota.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_M3_43_BWR-tag-34-0016-full.bin
Normal file
BIN
binaries/Tag/SOLUM_M3_43_BWR-tag-34-0016-full.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_M3_43_BWR-tag-34-0016-ota.bin
Normal file
BIN
binaries/Tag/SOLUM_M3_43_BWR-tag-34-0016-ota.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_M3_60_BWR-tag-35-0016-full.bin
Normal file
BIN
binaries/Tag/SOLUM_M3_60_BWR-tag-35-0016-full.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_M3_60_BWR-tag-35-0016-ota.bin
Normal file
BIN
binaries/Tag/SOLUM_M3_60_BWR-tag-35-0016-ota.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_M3_75_BWR-tag-36-0016-full.bin
Normal file
BIN
binaries/Tag/SOLUM_M3_75_BWR-tag-36-0016-full.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/SOLUM_M3_75_BWR-tag-36-0016-ota.bin
Normal file
BIN
binaries/Tag/SOLUM_M3_75_BWR-tag-36-0016-ota.bin
Normal file
Binary file not shown.
BIN
binaries/Tag/Tag_FW_Pack.bin
Normal file
BIN
binaries/Tag/Tag_FW_Pack.bin
Normal file
Binary file not shown.
Binary file not shown.
@@ -52,7 +52,7 @@ dt_string = now.strftime("%d/%m/%Y %H:%M:%S")
|
|||||||
tag = sys.argv[1]
|
tag = sys.argv[1]
|
||||||
|
|
||||||
binaries = generate_file_hashes2(rp + "/espbinaries",tag)
|
binaries = generate_file_hashes2(rp + "/espbinaries",tag)
|
||||||
tagota = generate_file_hashes2(rp + "/binaries",tag)
|
#tagota = generate_file_hashes2(rp + "/binaries",tag)
|
||||||
files1 = generate_file_hashes(rp + "/ESP32_AP-Flasher/data/www","/www/",tag)
|
files1 = generate_file_hashes(rp + "/ESP32_AP-Flasher/data/www","/www/",tag)
|
||||||
files1.extend(generate_file_hashes(rp + "/ESP32_AP-Flasher/data","/",tag))
|
files1.extend(generate_file_hashes(rp + "/ESP32_AP-Flasher/data","/",tag))
|
||||||
files1.extend(generate_file_hashes(rp + "/ESP32_AP-Flasher/data/fonts","/fonts/",tag))
|
files1.extend(generate_file_hashes(rp + "/ESP32_AP-Flasher/data/fonts","/fonts/",tag))
|
||||||
@@ -65,7 +65,7 @@ jsonarray = {
|
|||||||
"builddate": dt_string,
|
"builddate": dt_string,
|
||||||
"binaries": binaries,
|
"binaries": binaries,
|
||||||
"files": files1,
|
"files": files1,
|
||||||
"tagota": tagota,
|
# "tagota": tagota,
|
||||||
}
|
}
|
||||||
|
|
||||||
with open("jsonfiles/binaries.json", "w") as json_file:
|
with open("jsonfiles/binaries.json", "w") as json_file:
|
||||||
@@ -74,8 +74,8 @@ with open("jsonfiles/binaries.json", "w") as json_file:
|
|||||||
with open("jsonfiles/files.json", "w") as json_file:
|
with open("jsonfiles/files.json", "w") as json_file:
|
||||||
json.dump(jsonarray, json_file, indent=4)
|
json.dump(jsonarray, json_file, indent=4)
|
||||||
|
|
||||||
with open("jsonfiles/tagota.json", "w") as json_file:
|
#with open("jsonfiles/tagota.json", "w") as json_file:
|
||||||
json.dump(tagota, json_file, indent=4)
|
# json.dump(tagota, json_file, indent=4)
|
||||||
|
|
||||||
with open("jsonfiles/filesystem.json", "w") as json_file:
|
with open("jsonfiles/filesystem.json", "w") as json_file:
|
||||||
json.dump(files1, json_file, indent=4)
|
json.dump(files1, json_file, indent=4)
|
||||||
|
|||||||
@@ -57,18 +57,48 @@
|
|||||||
#define CMD_DO_RESET_SETTINGS 2
|
#define CMD_DO_RESET_SETTINGS 2
|
||||||
#define CMD_DO_DEEPSLEEP 3
|
#define CMD_DO_DEEPSLEEP 3
|
||||||
#define CMD_DO_LEDFLASH 4
|
#define CMD_DO_LEDFLASH 4
|
||||||
|
#define CMD_ERASE_EEPROM_IMAGES 5
|
||||||
|
#define CMD_ENTER_SLIDESHOW_FAST 0x06
|
||||||
|
#define CMD_ENTER_SLIDESHOW_MEDIUM 0x07
|
||||||
|
#define CMD_ENTER_SLIDESHOW_SLOW 0x08
|
||||||
|
#define CMD_ENTER_SLIDESHOW_GLACIAL 0x09
|
||||||
|
#define CMD_ENTER_NORMAL_MODE 0x0F
|
||||||
|
|
||||||
#define WAKEUP_REASON_TIMED 0
|
#define WAKEUP_REASON_TIMED 0
|
||||||
#define WAKEUP_REASON_GPIO 2
|
#define WAKEUP_REASON_GPIO 2
|
||||||
#define WAKEUP_REASON_NFC 3
|
#define WAKEUP_REASON_NFC 3
|
||||||
#define WAKEUP_REASON_BUTTON1 4
|
#define WAKEUP_REASON_BUTTON1 4
|
||||||
#define WAKEUP_REASON_BUTTON2 5
|
#define WAKEUP_REASON_BUTTON2 5
|
||||||
|
#define WAKEUP_REASON_RF 0x0F
|
||||||
#define WAKEUP_REASON_FIRSTBOOT 0xFC
|
#define WAKEUP_REASON_FIRSTBOOT 0xFC
|
||||||
#define WAKEUP_REASON_NETWORK_SCAN 0xFD
|
#define WAKEUP_REASON_NETWORK_SCAN 0xFD
|
||||||
#define WAKEUP_REASON_WDT_RESET 0xFE
|
#define WAKEUP_REASON_WDT_RESET 0xFE
|
||||||
|
|
||||||
|
|
||||||
#define EPD_LUT_DEFAULT 0
|
#define EPD_LUT_DEFAULT 0
|
||||||
#define EPD_LUT_NO_REPEATS 1
|
#define EPD_LUT_NO_REPEATS 1
|
||||||
#define EPD_LUT_FAST_NO_REDS 2
|
#define EPD_LUT_FAST_NO_REDS 2
|
||||||
#define EPD_LUT_FAST 3
|
#define EPD_LUT_FAST 3
|
||||||
#define EPD_LUT_OTA 0x10
|
#define EPD_LUT_OTA 0x10
|
||||||
|
|
||||||
|
// these are the 'custom image' arguments that will be sent in addition to their 'type'.
|
||||||
|
#define CUSTOM_IMAGE_NOCUSTOM 0x00 // regular image type
|
||||||
|
#define CUSTOM_IMAGE_SPLASHSCREEN 0x01 // will show at first boot/powerup
|
||||||
|
#define CUSTOM_IMAGE_LOST_CONNECTION 0x02 // this image will be shown (if it exists on the tag) if the tag looses its connection
|
||||||
|
#define CUSTOM_IMAGE_APFOUND 0x03 // this image will be shown during bootup if an AP was found
|
||||||
|
#define CUSTOM_IMAGE_NOAPFOUND 0x04 // shown if during bootup no AP was found
|
||||||
|
#define CUSTOM_IMAGE_LONGTERMSLEEP 0x05 // shown if long term sleep is entered via command
|
||||||
|
// UNUSED: 0x06-0x0F
|
||||||
|
#define CUSTOM_IMAGE_SLIDESHOW 0x0F // image is part of a slideshow
|
||||||
|
#define CUSTOM_IMAGE_BUTTON1 0x10
|
||||||
|
#define CUSTOM_IMAGE_BUTTON2 0x11
|
||||||
|
// UNUSED: 0x12 to 0x1B
|
||||||
|
#define CUSTOM_IMAGE_RF_WAKE 0x1C
|
||||||
|
#define CUSTOM_IMAGE_GPIO 0x1D
|
||||||
|
#define CUSTOM_IMAGE_NFC_WAKE 0x1E
|
||||||
|
|
||||||
|
#define TAG_CUSTOM_MODE_NONE 0x00
|
||||||
|
#define TAG_CUSTOM_SLIDESHOW_FAST 0x06
|
||||||
|
#define TAG_CUSTOM_SLIDESHOW_MEDIUM 0x07
|
||||||
|
#define TAG_CUSTOM_SLIDESHOW_SLOW 0x08
|
||||||
|
#define TAG_CUSTOM_SLIDESHOW_GLACIAL 0x09
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user