mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 22:07:04 +01:00
771 lines
28 KiB
C++
771 lines
28 KiB
C++
#include "serialap.h"
|
|
|
|
#include <Arduino.h>
|
|
#include <HardwareSerial.h>
|
|
#include <LittleFS.h>
|
|
|
|
#include "commstructs.h"
|
|
#include "flasher.h"
|
|
#include "leds.h"
|
|
#include "newproto.h"
|
|
#include "powermgt.h"
|
|
#include "settings.h"
|
|
#include "web.h"
|
|
#include "zbs_interface.h"
|
|
|
|
QueueHandle_t rxCmdQueue;
|
|
SemaphoreHandle_t txActive;
|
|
|
|
// If a command is sent, it will wait for a reply here
|
|
#define CMD_REPLY_WAIT 0x00
|
|
#define CMD_REPLY_ACK 0x01
|
|
#define CMD_REPLY_NOK 0x02
|
|
#define CMD_REPLY_NOQ 0x03
|
|
volatile uint8_t cmdReplyValue = CMD_REPLY_WAIT;
|
|
|
|
#define AP_SERIAL_PORT Serial1
|
|
|
|
uint8_t channelList[6];
|
|
struct espSetChannelPower curChannel = {0, 11, 10};
|
|
|
|
#define RX_CMD_RQB 0x01
|
|
#define RX_CMD_ADR 0x02
|
|
#define RX_CMD_XFC 0x03
|
|
#define RX_CMD_XTO 0x04
|
|
#define RX_CMD_RDY 0x05
|
|
#define RX_CMD_RSET 0x06
|
|
|
|
#define AP_ACTIVITY_MAX_INTERVAL 30 * 1000
|
|
volatile uint32_t lastAPActivity = 0;
|
|
struct APInfoS apInfo;
|
|
|
|
extern uint8_t* getDataForFile(File* file);
|
|
|
|
struct rxCmd {
|
|
uint8_t* data;
|
|
uint8_t len;
|
|
uint8_t type;
|
|
};
|
|
|
|
#define ZBS_RX_WAIT_HEADER 0
|
|
#define ZBS_RX_WAIT_PKT_LEN 1
|
|
#define ZBS_RX_WAIT_PKT_RX 2
|
|
#define ZBS_RX_WAIT_SEP1 3
|
|
#define ZBS_RX_WAIT_SEP2 4
|
|
#define ZBS_RX_WAIT_VER 6
|
|
#define ZBS_RX_BLOCK_REQUEST 7
|
|
#define ZBS_RX_WAIT_XFERCOMPLETE 8
|
|
#define ZBS_RX_WAIT_DATA_REQ 9
|
|
#define ZBS_RX_WAIT_JOINNETWORK 10
|
|
#define ZBS_RX_WAIT_XFERTIMEOUT 11
|
|
#define ZBS_RX_WAIT_MAC 12
|
|
#define ZBS_RX_WAIT_CHANNEL 13
|
|
#define ZBS_RX_WAIT_POWER 14
|
|
#define ZBS_RX_WAIT_PENDING 15
|
|
#define ZBS_RX_WAIT_NOP 16
|
|
#define ZBS_RX_WAIT_TYPE 17
|
|
|
|
bool txStart() {
|
|
while (1) {
|
|
if (xPortInIsrContext()) {
|
|
if (xSemaphoreTakeFromISR(txActive, NULL) == pdTRUE) return true;
|
|
} else {
|
|
if (xSemaphoreTake(txActive, portTICK_PERIOD_MS)) return true;
|
|
}
|
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
|
Serial.println("wait... tx busy");
|
|
}
|
|
// this never happens. Should we make a timeout?
|
|
return false;
|
|
}
|
|
void txEnd() {
|
|
if (xPortInIsrContext()) {
|
|
xSemaphoreGiveFromISR(txActive, NULL);
|
|
} else {
|
|
xSemaphoreGive(txActive);
|
|
}
|
|
}
|
|
bool waitCmdReply() {
|
|
uint32_t val = millis();
|
|
while (millis() < val + 100) {
|
|
switch (cmdReplyValue) {
|
|
case CMD_REPLY_WAIT:
|
|
break;
|
|
case CMD_REPLY_ACK:
|
|
lastAPActivity = millis();
|
|
return true;
|
|
break;
|
|
case CMD_REPLY_NOK:
|
|
lastAPActivity = millis();
|
|
return false;
|
|
break;
|
|
case CMD_REPLY_NOQ:
|
|
lastAPActivity = millis();
|
|
return false;
|
|
break;
|
|
}
|
|
vTaskDelay(1 / portTICK_RATE_MS);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#if (AP_PROCESS_PORT == FLASHER_AP_PORT)
|
|
#define AP_RESET_PIN FLASHER_AP_RESET
|
|
#define AP_POWER_PIN FLASHER_AP_POWER
|
|
#endif
|
|
#ifdef OPENEPAPERLINK_PCB
|
|
#if (AP_PROCESS_PORT == FLASHER_EXT_PORT)
|
|
#define AP_RESET_PIN FLASHER_EXT_RESET
|
|
#define AP_POWER_PIN FLASHER_EXT_POWER
|
|
#endif
|
|
#if (AP_PROCESS_PORT == FLASHER_ALTRADIO_PORT)
|
|
#define AP_RESET_PIN FLASHER_ALT_RESET
|
|
#define AP_POWER_PIN FLASHER_ALT_POWER
|
|
#endif
|
|
#endif
|
|
|
|
void APEnterEarlyReset() {
|
|
pinMode(AP_RESET_PIN, OUTPUT);
|
|
digitalWrite(AP_RESET_PIN, LOW);
|
|
}
|
|
|
|
// Reset the tag
|
|
void APTagReset() {
|
|
pinMode(AP_RESET_PIN, OUTPUT);
|
|
digitalWrite(AP_RESET_PIN, LOW);
|
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
|
rampTagPower(AP_POWER_PIN, false);
|
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
|
rampTagPower(AP_POWER_PIN, true);
|
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
|
digitalWrite(AP_RESET_PIN, HIGH);
|
|
rampTagPower(AP_POWER_PIN, true);
|
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
|
}
|
|
|
|
// Send data to the AP
|
|
uint16_t sendBlock(const void* data, const uint16_t len) {
|
|
if (!apInfo.isOnline) return false;
|
|
if (!txStart()) return 0;
|
|
for (uint8_t attempt = 0; attempt < 5; attempt++) {
|
|
cmdReplyValue = CMD_REPLY_WAIT;
|
|
AP_SERIAL_PORT.print(">D>");
|
|
if (waitCmdReply()) goto blksend;
|
|
Serial.printf("block send failed in try %d\n", attempt);
|
|
}
|
|
Serial.print("Failed sending block...\n");
|
|
txEnd();
|
|
return 0;
|
|
blksend:
|
|
uint8_t blockbuffer[sizeof(struct blockData)];
|
|
struct blockData* bd = (struct blockData*)blockbuffer;
|
|
bd->size = len;
|
|
bd->checksum = 0;
|
|
|
|
// calculate checksum
|
|
for (uint16_t c = 0; c < len; c++) {
|
|
bd->checksum += ((uint8_t*)data)[c];
|
|
}
|
|
|
|
// send blockData header
|
|
for (uint8_t c = 0; c < sizeof(struct blockData); c++) {
|
|
AP_SERIAL_PORT.write(0xAA ^ blockbuffer[c]);
|
|
}
|
|
|
|
// send an entire block of data
|
|
uint16_t c;
|
|
for (c = 0; c < len; c++) {
|
|
AP_SERIAL_PORT.write(0xAA ^ ((uint8_t*)data)[c]);
|
|
}
|
|
|
|
// fill the rest of the block-length filled with something else (will end up as 0xFF in the buffer)
|
|
for (; c < BLOCK_DATA_SIZE; c++) {
|
|
AP_SERIAL_PORT.write(0x55);
|
|
}
|
|
|
|
// dummy bytes in case some bytes were missed, makes sure the AP gets kicked out of data-loading mode
|
|
for (c = 0; c < 32; c++) {
|
|
AP_SERIAL_PORT.write(0xF5);
|
|
}
|
|
delay(10);
|
|
txEnd();
|
|
return bd->checksum;
|
|
}
|
|
bool sendDataAvail(struct pendingData* pending) {
|
|
if (!apInfo.isOnline) return false;
|
|
if (!txStart()) return false;
|
|
addCRC(pending, sizeof(struct pendingData));
|
|
for (uint8_t attempt = 0; attempt < 5; attempt++) {
|
|
cmdReplyValue = CMD_REPLY_WAIT;
|
|
AP_SERIAL_PORT.print("SDA>");
|
|
for (uint8_t c = 0; c < sizeof(struct pendingData); c++) {
|
|
AP_SERIAL_PORT.write(((uint8_t*)pending)[c]);
|
|
}
|
|
if (waitCmdReply()) goto sdasend;
|
|
Serial.printf("SDA send failed in try %d\n", attempt);
|
|
}
|
|
Serial.print("SDA failed to send...\n");
|
|
txEnd();
|
|
return false;
|
|
sdasend:
|
|
txEnd();
|
|
return true;
|
|
}
|
|
bool sendCancelPending(struct pendingData* pending) {
|
|
if (!apInfo.isOnline) return false;
|
|
if (!txStart()) return false;
|
|
addCRC(pending, sizeof(struct pendingData));
|
|
for (uint8_t attempt = 0; attempt < 5; attempt++) {
|
|
cmdReplyValue = CMD_REPLY_WAIT;
|
|
AP_SERIAL_PORT.print("CXD>");
|
|
for (uint8_t c = 0; c < sizeof(struct pendingData); c++) {
|
|
AP_SERIAL_PORT.write(((uint8_t*)pending)[c]);
|
|
}
|
|
if (waitCmdReply()) goto cxdsent;
|
|
Serial.printf("CXD send failed in try %d\n", attempt);
|
|
}
|
|
Serial.print("CXD failed to send...\n");
|
|
txEnd();
|
|
return false;
|
|
cxdsent:
|
|
txEnd();
|
|
return true;
|
|
}
|
|
bool sendChannelPower(struct espSetChannelPower* scp) {
|
|
if ((apInfo.state != AP_STATE_ONLINE) && (apInfo.state != AP_STATE_COMING_ONLINE)) return false;
|
|
if (!txStart()) return false;
|
|
addCRC(scp, sizeof(struct espSetChannelPower));
|
|
for (uint8_t attempt = 0; attempt < 5; attempt++) {
|
|
cmdReplyValue = CMD_REPLY_WAIT;
|
|
AP_SERIAL_PORT.print("SCP>");
|
|
for (uint8_t c = 0; c < sizeof(struct espSetChannelPower); c++) {
|
|
AP_SERIAL_PORT.write(((uint8_t*)scp)[c]);
|
|
}
|
|
if (waitCmdReply()) goto scpSent;
|
|
Serial.printf("SCP send failed in try %d\n", attempt);
|
|
}
|
|
Serial.print("SCP failed to send...\n");
|
|
txEnd();
|
|
return false;
|
|
scpSent:
|
|
txEnd();
|
|
return true;
|
|
}
|
|
bool sendPing() {
|
|
if (!txStart()) return false;
|
|
for (uint8_t attempt = 0; attempt < 5; attempt++) {
|
|
cmdReplyValue = CMD_REPLY_WAIT;
|
|
AP_SERIAL_PORT.print("RDY?");
|
|
if (waitCmdReply()) goto pingSent;
|
|
}
|
|
txEnd();
|
|
return false;
|
|
pingSent:
|
|
txEnd();
|
|
return true;
|
|
}
|
|
bool sendGetInfo() {
|
|
if (!txStart()) return false;
|
|
for (uint8_t attempt = 0; attempt < 5; attempt++) {
|
|
cmdReplyValue = CMD_REPLY_WAIT;
|
|
AP_SERIAL_PORT.print("NFO?");
|
|
if (waitCmdReply()) goto nfoRequested;
|
|
}
|
|
txEnd();
|
|
return false;
|
|
nfoRequested:
|
|
txEnd();
|
|
return true;
|
|
}
|
|
|
|
// add RX'd request from the AP to the processor queue
|
|
void addRXQueue(uint8_t* data, uint8_t len, uint8_t type) {
|
|
struct rxCmd* rxcmd = new struct rxCmd;
|
|
rxcmd->data = data;
|
|
rxcmd->len = len;
|
|
rxcmd->type = type;
|
|
BaseType_t queuestatus = xQueueSend(rxCmdQueue, &rxcmd, 0);
|
|
if (queuestatus == pdFALSE) {
|
|
if (data) free(data);
|
|
free(rxcmd);
|
|
}
|
|
}
|
|
|
|
// Asynchronous command processor
|
|
void rxCmdProcessor(void* parameter) {
|
|
rxCmdQueue = xQueueCreate(30, sizeof(struct rxCmd*));
|
|
txActive = xSemaphoreCreateBinary();
|
|
xSemaphoreGive(txActive);
|
|
while (1) {
|
|
struct rxCmd* rxcmd = nullptr;
|
|
BaseType_t q = xQueueReceive(rxCmdQueue, &rxcmd, 10);
|
|
if (q == pdTRUE) {
|
|
switch (rxcmd->type) {
|
|
case RX_CMD_RQB:
|
|
processBlockRequest((struct espBlockRequest*)rxcmd->data);
|
|
#ifdef HAS_RGB_LED
|
|
shortBlink(CRGB::Blue);
|
|
#else
|
|
quickBlink(3);
|
|
#endif
|
|
break;
|
|
case RX_CMD_ADR:
|
|
processDataReq((struct espAvailDataReq*)rxcmd->data, true);
|
|
#ifdef HAS_RGB_LED
|
|
shortBlink(CRGB::Aqua);
|
|
#else
|
|
quickBlink(1);
|
|
#endif
|
|
break;
|
|
case RX_CMD_XFC:
|
|
processXferComplete((struct espXferComplete*)rxcmd->data, true);
|
|
#ifdef HAS_RGB_LED
|
|
shortBlink(CRGB::Purple);
|
|
#endif
|
|
break;
|
|
case RX_CMD_XTO:
|
|
processXferTimeout((struct espXferComplete*)rxcmd->data, true);
|
|
break;
|
|
}
|
|
if (rxcmd->data) free(rxcmd->data);
|
|
if (rxcmd) free(rxcmd);
|
|
}
|
|
}
|
|
}
|
|
void rxSerialTask(void* parameter) {
|
|
static char cmdbuffer[4] = {0};
|
|
static uint8_t* packetp = nullptr;
|
|
// static uint8_t pktlen = 0;
|
|
static uint8_t pktindex = 0; // length of the command
|
|
static uint8_t RXState = ZBS_RX_WAIT_HEADER;
|
|
static char lastchar = 0;
|
|
static uint8_t charindex = 0;
|
|
|
|
while (1) {
|
|
while (AP_SERIAL_PORT.available()) {
|
|
lastchar = AP_SERIAL_PORT.read();
|
|
switch (RXState) {
|
|
case ZBS_RX_WAIT_HEADER:
|
|
// Serial.write(lastchar);
|
|
// shift characters in
|
|
for (uint8_t c = 0; c < 3; c++) {
|
|
cmdbuffer[c] = cmdbuffer[c + 1];
|
|
}
|
|
cmdbuffer[3] = lastchar;
|
|
|
|
if ((strncmp(cmdbuffer, "ACK>", 4) == 0)) cmdReplyValue = CMD_REPLY_ACK;
|
|
if ((strncmp(cmdbuffer, "NOK>", 4) == 0)) cmdReplyValue = CMD_REPLY_NOK;
|
|
if ((strncmp(cmdbuffer, "NOQ>", 4) == 0)) cmdReplyValue = CMD_REPLY_NOQ;
|
|
|
|
if ((strncmp(cmdbuffer, "VER>", 4) == 0)) {
|
|
pktindex = 0;
|
|
RXState = ZBS_RX_WAIT_VER;
|
|
charindex = 0;
|
|
memset(cmdbuffer, 0x00, 4);
|
|
}
|
|
if ((strncmp(cmdbuffer, "MAC>", 4) == 0)) {
|
|
RXState = ZBS_RX_WAIT_MAC;
|
|
charindex = 0;
|
|
memset(cmdbuffer, 0x00, 4);
|
|
}
|
|
if ((strncmp(cmdbuffer, "ZCH>", 4) == 0)) {
|
|
RXState = ZBS_RX_WAIT_CHANNEL;
|
|
charindex = 0;
|
|
memset(cmdbuffer, 0x00, 4);
|
|
}
|
|
if ((strncmp(cmdbuffer, "ZPW>", 4) == 0)) {
|
|
RXState = ZBS_RX_WAIT_POWER;
|
|
charindex = 0;
|
|
memset(cmdbuffer, 0x00, 4);
|
|
}
|
|
if ((strncmp(cmdbuffer, "PEN>", 4) == 0)) {
|
|
RXState = ZBS_RX_WAIT_PENDING;
|
|
charindex = 0;
|
|
memset(cmdbuffer, 0x00, 4);
|
|
}
|
|
if ((strncmp(cmdbuffer, "NOP>", 4) == 0)) {
|
|
RXState = ZBS_RX_WAIT_NOP;
|
|
charindex = 0;
|
|
memset(cmdbuffer, 0x00, 4);
|
|
}
|
|
if ((strncmp(cmdbuffer, "TYP>", 4) == 0)) {
|
|
RXState = ZBS_RX_WAIT_TYPE;
|
|
charindex = 0;
|
|
memset(cmdbuffer, 0x00, 4);
|
|
}
|
|
if (strncmp(cmdbuffer, "RES>", 4) == 0) {
|
|
addRXQueue(NULL, 0, RX_CMD_RSET);
|
|
}
|
|
if (strncmp(cmdbuffer, "RQB>", 4) == 0) {
|
|
RXState = ZBS_RX_BLOCK_REQUEST;
|
|
charindex = 0;
|
|
pktindex = 0;
|
|
packetp = (uint8_t*)calloc(sizeof(struct espBlockRequest) + 8, 1);
|
|
memset(cmdbuffer, 0x00, 4);
|
|
}
|
|
if (strncmp(cmdbuffer, "ADR>", 4) == 0) {
|
|
RXState = ZBS_RX_WAIT_DATA_REQ;
|
|
charindex = 0;
|
|
pktindex = 0;
|
|
packetp = (uint8_t*)calloc(sizeof(struct espAvailDataReq) + 8, 1);
|
|
memset(cmdbuffer, 0x00, 4);
|
|
}
|
|
if (strncmp(cmdbuffer, "XFC>", 4) == 0) {
|
|
RXState = ZBS_RX_WAIT_XFERCOMPLETE;
|
|
pktindex = 0;
|
|
packetp = (uint8_t*)calloc(sizeof(struct espXferComplete) + 8, 1);
|
|
memset(cmdbuffer, 0x00, 4);
|
|
}
|
|
if (strncmp(cmdbuffer, "XTO>", 4) == 0) {
|
|
RXState = ZBS_RX_WAIT_XFERTIMEOUT;
|
|
pktindex = 0;
|
|
packetp = (uint8_t*)calloc(sizeof(struct espXferComplete) + 8, 1);
|
|
memset(cmdbuffer, 0x00, 4);
|
|
}
|
|
if (strncmp(cmdbuffer, "RDY>", 4) == 0) {
|
|
addRXQueue(NULL, 0, RX_CMD_RDY);
|
|
}
|
|
break;
|
|
case ZBS_RX_BLOCK_REQUEST:
|
|
packetp[pktindex] = lastchar;
|
|
pktindex++;
|
|
if (pktindex == sizeof(struct espBlockRequest)) {
|
|
addRXQueue(packetp, pktindex, RX_CMD_RQB);
|
|
RXState = ZBS_RX_WAIT_HEADER;
|
|
}
|
|
break;
|
|
case ZBS_RX_WAIT_XFERCOMPLETE:
|
|
packetp[pktindex] = lastchar;
|
|
pktindex++;
|
|
if (pktindex == sizeof(struct espXferComplete)) {
|
|
addRXQueue(packetp, pktindex, RX_CMD_XFC);
|
|
RXState = ZBS_RX_WAIT_HEADER;
|
|
}
|
|
break;
|
|
case ZBS_RX_WAIT_XFERTIMEOUT:
|
|
packetp[pktindex] = lastchar;
|
|
pktindex++;
|
|
if (pktindex == sizeof(struct espXferComplete)) {
|
|
addRXQueue(packetp, pktindex, RX_CMD_XTO);
|
|
RXState = ZBS_RX_WAIT_HEADER;
|
|
}
|
|
break;
|
|
case ZBS_RX_WAIT_DATA_REQ:
|
|
packetp[pktindex] = lastchar;
|
|
pktindex++;
|
|
if (pktindex == sizeof(struct espAvailDataReq)) {
|
|
addRXQueue(packetp, pktindex, RX_CMD_ADR);
|
|
RXState = ZBS_RX_WAIT_HEADER;
|
|
}
|
|
break;
|
|
case ZBS_RX_WAIT_VER:
|
|
cmdbuffer[charindex] = lastchar;
|
|
charindex++;
|
|
if (charindex == 4) {
|
|
charindex = 0;
|
|
apInfo.version = (uint16_t)strtoul(cmdbuffer, NULL, 16);
|
|
RXState = ZBS_RX_WAIT_HEADER;
|
|
}
|
|
break;
|
|
case ZBS_RX_WAIT_MAC:
|
|
cmdbuffer[charindex] = lastchar;
|
|
charindex++;
|
|
if (charindex == 2) {
|
|
charindex = 0;
|
|
apInfo.mac[pktindex] = (uint8_t)strtoul(cmdbuffer, NULL, 16);
|
|
pktindex++;
|
|
}
|
|
if (pktindex == 8) {
|
|
RXState = ZBS_RX_WAIT_HEADER;
|
|
}
|
|
break;
|
|
case ZBS_RX_WAIT_CHANNEL:
|
|
cmdbuffer[charindex] = lastchar;
|
|
charindex++;
|
|
if (charindex == 2) {
|
|
RXState = ZBS_RX_WAIT_HEADER;
|
|
apInfo.channel = (uint8_t)strtoul(cmdbuffer, NULL, 16);
|
|
}
|
|
break;
|
|
case ZBS_RX_WAIT_POWER:
|
|
cmdbuffer[charindex] = lastchar;
|
|
charindex++;
|
|
if (charindex == 2) {
|
|
RXState = ZBS_RX_WAIT_HEADER;
|
|
apInfo.power = (uint8_t)strtoul(cmdbuffer, NULL, 16);
|
|
}
|
|
break;
|
|
case ZBS_RX_WAIT_PENDING:
|
|
cmdbuffer[charindex] = lastchar;
|
|
charindex++;
|
|
if (charindex == 2) {
|
|
RXState = ZBS_RX_WAIT_HEADER;
|
|
apInfo.pending = (uint8_t)strtoul(cmdbuffer, NULL, 16);
|
|
}
|
|
break;
|
|
case ZBS_RX_WAIT_NOP:
|
|
cmdbuffer[charindex] = lastchar;
|
|
charindex++;
|
|
if (charindex == 2) {
|
|
RXState = ZBS_RX_WAIT_HEADER;
|
|
apInfo.nop = (uint8_t)strtoul(cmdbuffer, NULL, 16);
|
|
}
|
|
break;
|
|
case ZBS_RX_WAIT_TYPE:
|
|
cmdbuffer[charindex] = lastchar;
|
|
charindex++;
|
|
if (charindex == 2) {
|
|
RXState = ZBS_RX_WAIT_HEADER;
|
|
apInfo.type = (uint8_t)strtoul(cmdbuffer, NULL, 16);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
vTaskDelay(1 / portTICK_PERIOD_MS);
|
|
} // end of while(1)
|
|
}
|
|
|
|
void ShowAPInfo() {
|
|
Serial.printf("+----------------------------+\n");
|
|
Serial.printf("| AP Information - type %02X |\n", apInfo.type);
|
|
Serial.printf("+----------------------------+\n");
|
|
Serial.printf("| Channel | 0x%02X |\n", apInfo.channel);
|
|
Serial.printf("| Power | %02X |\n", apInfo.power);
|
|
Serial.printf("| MAC | %02X%02X%02X%02X%02X%02X%02X%02X |\n", apInfo.mac[7], apInfo.mac[6], apInfo.mac[5], apInfo.mac[4], apInfo.mac[3], apInfo.mac[2], apInfo.mac[1], apInfo.mac[0]);
|
|
Serial.printf("| Version | 0x%04X |\n", apInfo.version);
|
|
Serial.printf("+----------------------------+\n");
|
|
}
|
|
|
|
void notifySegmentedFlash() {
|
|
sendAPSegmentedData(apInfo.mac, (String) "Fl ash", 0x0800, false, true);
|
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
|
#if (FLASHER_AP_POWER == -1)
|
|
sendAPSegmentedData(apInfo.mac, (String) "If done", 0x0800, false, true);
|
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
|
sendAPSegmentedData(apInfo.mac, (String) "RE boot", 0x0800, false, true);
|
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
|
#endif
|
|
}
|
|
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.
|
|
#if (FLASHER_AP_POWER == -1)
|
|
apInfo.isOnline = false;
|
|
apInfo.state = AP_STATE_REQUIRED_POWER_CYCLE;
|
|
// 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");
|
|
#ifdef HAS_RGB_LED
|
|
showColorPattern(CRGB::Aqua, CRGB::Aqua, CRGB::Red);
|
|
#endif
|
|
while (1) {
|
|
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
|
}
|
|
#endif
|
|
}
|
|
void segmentedShowIp() {
|
|
IPAddress IP = WiFi.localIP();
|
|
char temp[12];
|
|
sendAPSegmentedData(apInfo.mac, (String) "IP Addr", 0x0200, true, true);
|
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
|
sprintf(temp, "%03d IP %03d", IP[0], IP[1]);
|
|
sendAPSegmentedData(apInfo.mac, (String)temp, 0x0200, true, true);
|
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
|
sprintf(temp, "%03d IP %03d", IP[2], IP[3]);
|
|
sendAPSegmentedData(apInfo.mac, (String)temp, 0x0200, true, true);
|
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
|
}
|
|
|
|
bool bringAPOnline() {
|
|
apInfo.isOnline = false;
|
|
apInfo.state = AP_STATE_OFFLINE;
|
|
APTagReset();
|
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
|
uint32_t bootTimeout = millis();
|
|
bool APrdy = false;
|
|
while ((!APrdy) && (millis() - bootTimeout < 5 * 1000)) {
|
|
APrdy = sendPing();
|
|
vTaskDelay(300 / portTICK_PERIOD_MS);
|
|
}
|
|
if (!APrdy) {
|
|
return false;
|
|
} else {
|
|
apInfo.state = AP_STATE_COMING_ONLINE;
|
|
sendChannelPower(&curChannel);
|
|
vTaskDelay(200 / portTICK_PERIOD_MS);
|
|
if (!sendGetInfo()) {
|
|
apInfo.state = AP_STATE_OFFLINE;
|
|
return false;
|
|
}
|
|
vTaskDelay(200 / portTICK_PERIOD_MS);
|
|
apInfo.isOnline = true;
|
|
apInfo.state = AP_STATE_ONLINE;
|
|
refreshAllPending();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void APTask(void* parameter) {
|
|
xTaskCreate(rxCmdProcessor, "rxCmdProcessor", 3000, NULL, configMAX_PRIORITIES - 10, NULL);
|
|
xTaskCreate(rxSerialTask, "rxSerialTask", 1750, NULL, configMAX_PRIORITIES - 4, NULL);
|
|
|
|
#if (AP_PROCESS_PORT == FLASHER_AP_PORT)
|
|
AP_SERIAL_PORT.begin(115200, SERIAL_8N1, FLASHER_AP_RXD, FLASHER_AP_TXD);
|
|
#endif
|
|
#ifdef OPENEPAPERLINK_PCB
|
|
#if (AP_PROCESS_PORT == FLASHER_EXT_PORT)
|
|
AP_SERIAL_PORT.begin(115200, SERIAL_8N1, FLASHER_EXT_RXD, FLASHER_EXT_TXD);
|
|
#endif
|
|
#if (AP_PROCESS_PORT == FLASHER_ALTRADIO_PORT)
|
|
AP_SERIAL_PORT.begin(115200, SERIAL_8N1, FLASHER_AP_RXD, FLASHER_AP_TXD);
|
|
#endif
|
|
#endif
|
|
|
|
bringAPOnline();
|
|
|
|
if (checkForcedAPFlash()) {
|
|
if (apInfo.type == SOLUM_SEG_UK && apInfo.isOnline) {
|
|
Serial.printf("Showing some stuff on the segmented display about our intentions to flash...\n");
|
|
notifySegmentedFlash();
|
|
}
|
|
Serial.printf("We're going to try to perform an 'AP forced flash' in\n");
|
|
flashCountDown(10);
|
|
Serial.printf("\nPerforming force flash of the AP\n");
|
|
apInfo.isOnline = false;
|
|
apInfo.state = AP_STATE_FLASHING;
|
|
doForcedAPFlash();
|
|
checkWaitPowerCycle();
|
|
bringAPOnline();
|
|
}
|
|
|
|
if (apInfo.isOnline) {
|
|
// AP works!
|
|
ShowAPInfo();
|
|
|
|
if (apInfo.type == SOLUM_SEG_UK) {
|
|
segmentedShowIp();
|
|
showAPSegmentedInfo(apInfo.mac, true);
|
|
}
|
|
|
|
uint16_t fsversion;
|
|
fsversion = getAPUpdateVersion(apInfo.type);
|
|
if ((fsversion) && (apInfo.version != fsversion)) {
|
|
Serial.printf("Firmware version on LittleFS: %04X\n", fsversion);
|
|
|
|
Serial.printf("We're going to try to update the AP's FW in\n");
|
|
flashCountDown(30);
|
|
Serial.printf("\n");
|
|
notifySegmentedFlash();
|
|
apInfo.isOnline = false;
|
|
apInfo.state = AP_STATE_FLASHING;
|
|
if (doAPUpdate(apInfo.type)) {
|
|
checkWaitPowerCycle();
|
|
Serial.printf("Flash completed, let's try to boot the AP!\n");
|
|
if (bringAPOnline()) {
|
|
// AP works
|
|
ShowAPInfo();
|
|
setAPchannel();
|
|
} else {
|
|
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");
|
|
apInfo.state = AP_STATE_FAILED;
|
|
#ifdef HAS_RGB_LED
|
|
showColorPattern(CRGB::Red, CRGB::Yellow, CRGB::Red);
|
|
#endif
|
|
}
|
|
} else {
|
|
apInfo.state = AP_STATE_FAILED;
|
|
checkWaitPowerCycle();
|
|
Serial.println("Failed to update version on the AP :(\n");
|
|
#ifdef HAS_RGB_LED
|
|
showColorPattern(CRGB::Red, CRGB::Red, CRGB::Red);
|
|
#endif
|
|
}
|
|
}
|
|
} else {
|
|
// AP unavailable, maybe time to flash?
|
|
apInfo.isOnline = false;
|
|
apInfo.state = AP_STATE_OFFLINE;
|
|
Serial.println("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. We'll try to flash it!\n");
|
|
Serial.printf("If this tag was previously flashed succesfully but this message still shows up, there's probably something wrong with the serial connections.\n");
|
|
Serial.printf("The build of this firmware expects an AP tag with RXD/TXD on ESP32 pins %d and %d, does this match with your wiring?\n", FLASHER_AP_RXD, FLASHER_AP_TXD);
|
|
Serial.println("Performing firmware flash in about 30 seconds!\n");
|
|
flashCountDown(30);
|
|
if (doAPFlash()) {
|
|
checkWaitPowerCycle();
|
|
if (bringAPOnline()) {
|
|
// AP works
|
|
ShowAPInfo();
|
|
if (apInfo.type == SOLUM_SEG_UK) {
|
|
segmentedShowIp();
|
|
showAPSegmentedInfo(apInfo.mac, true);
|
|
}
|
|
} else {
|
|
Serial.printf("Failed to bring up the AP after successful flashing... That's not supposed to happen!\n");
|
|
Serial.printf("This generally means that the flasher connections (MISO/MOSI/CLK/RESET/CS) are okay,\n");
|
|
Serial.printf("but we can't (yet) talk to the AP over serial lines. Verify the pins mentioned above.\n\n");
|
|
|
|
if (FLASHER_AP_POWER != -1) {
|
|
Serial.printf("The firmware you're using expects soft power control over the AP tag; if it can't\n");
|
|
Serial.printf("power-cycle the AP-tag using GPIO pin %d, this can cause this very same issue.\n", FLASHER_AP_POWER);
|
|
}
|
|
#ifdef HAS_RGB_LED
|
|
showColorPattern(CRGB::Red, CRGB::Yellow, CRGB::Red);
|
|
#endif
|
|
apInfo.isOnline = false;
|
|
apInfo.state = AP_STATE_FAILED;
|
|
}
|
|
} else {
|
|
// failed to flash
|
|
#ifdef HAS_RGB_LED
|
|
showColorPattern(CRGB::Red, CRGB::Red, CRGB::Red);
|
|
#endif
|
|
apInfo.isOnline = false;
|
|
apInfo.state = AP_STATE_FAILED;
|
|
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("This ESP32-build expects the following pins connected to the ZBS243:");
|
|
Serial.println("--- ZBS243 based tag ESP32 ---");
|
|
Serial.printf(" RXD ---------------- %02d\n", FLASHER_AP_RXD);
|
|
Serial.printf(" TXD ---------------- %02d\n", FLASHER_AP_TXD);
|
|
Serial.printf(" CS/SS ---------------- %02d\n", FLASHER_AP_SS);
|
|
Serial.printf(" MOSI ---------------- %02d\n", FLASHER_AP_MOSI);
|
|
Serial.printf(" MISO ---------------- %02d\n", FLASHER_AP_MISO);
|
|
Serial.printf(" CLK ---------------- %02d\n", FLASHER_AP_CLK);
|
|
Serial.printf(" RSET ---------------- %02d\n", FLASHER_AP_RESET);
|
|
#if (FLASHER_AP_POWER == -1)
|
|
Serial.printf("Your firmware is configured without soft power control. This means you'll have to manually power-cycle the tag after flashing.\n");
|
|
#else
|
|
Serial.printf(" POWER ---------------- %02d\n", FLASHER_AP_POWER);
|
|
#endif
|
|
Serial.println("Please verify your wiring and try again!");
|
|
}
|
|
}
|
|
|
|
uint8_t attempts = 0;
|
|
while (1) {
|
|
if ((apInfo.isOnline) && (millis() - lastAPActivity > AP_ACTIVITY_MAX_INTERVAL)) {
|
|
bool reply = sendPing();
|
|
if (!reply) {
|
|
attempts++;
|
|
} else {
|
|
attempts = 0;
|
|
}
|
|
if (attempts > 5) {
|
|
apInfo.state = AP_STATE_WAIT_RESET;
|
|
apInfo.isOnline = false;
|
|
if (!bringAPOnline()) {
|
|
// tried to reset the AP, but we failed... Maybe the AP-Tag died?
|
|
apInfo.state = AP_STATE_FAILED;
|
|
#ifdef HAS_RGB_LED
|
|
showColorPattern(CRGB::Yellow, CRGB::Yellow, CRGB::Red);
|
|
#endif
|
|
} else {
|
|
apInfo.state = AP_STATE_ONLINE;
|
|
apInfo.isOnline = true;
|
|
attempts = 0;
|
|
}
|
|
}
|
|
}
|
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
|
}
|
|
} |