segmented tags content

This commit is contained in:
Nic Limper
2023-05-22 13:00:39 +02:00
parent 0cc7ba4fc6
commit feb8e7e69c
11 changed files with 205 additions and 101 deletions

View File

@@ -30,7 +30,8 @@
"hwtype": [
0,
1,
2
2,
240
],
"param": []
},
@@ -41,7 +42,8 @@
"hwtype": [
0,
1,
2
2,
240
],
"param": [
{
@@ -54,7 +56,12 @@
"key": "thresholdred",
"name": "Threshold",
"desc": "Value is displayed in red if higher than the threshold",
"type": "int"
"type": "int",
"hwtype": [
0,
1,
2
]
}
]
},
@@ -65,7 +72,8 @@
"hwtype": [
0,
1,
2
2,
240
],
"param": [
{
@@ -78,7 +86,12 @@
"key": "thresholdred",
"name": "Threshold",
"desc": "Value is displayed in red if higher than the threshold",
"type": "int"
"type": "int",
"hwtype": [
0,
1,
2
]
}
]
},
@@ -89,7 +102,8 @@
"hwtype": [
0,
1,
2
2,
240
],
"param": [
{
@@ -254,7 +268,8 @@
"hwtype": [
0,
1,
2
2,
240
],
"param": [
{
@@ -265,6 +280,12 @@
}
]
},
{
"id": 12,
"name": "remote AP",
"desc": "Content is generated by a remote Access Point",
"hwtype": []
},
{
"id": 13,
"name": "Set segments",
@@ -282,19 +303,13 @@
{
"key": "line2",
"name": "line 2",
"desc": "8888",
"desc": "88",
"type": "text"
},
{
"key": "line3",
"name": "line 3",
"desc": "88",
"type": "text"
},
{
"key": "symbols",
"name": "symbols",
"desc": "",
"desc": "8888",
"type": "text"
}
]

View File

@@ -83,4 +83,17 @@ struct APlist {
uint16_t version;
} __packed;
struct TagInfo {
uint8_t mac[6];
char alias[32];
uint32_t lastseen;
uint32_t nextupdate;
bool pending;
uint32_t expectedNextCheckin;
uint8_t hwType;
uint8_t wakeupReason;
uint8_t capabilities;
uint16_t pendingIdle;
} __packed;
#pragma pack(pop)

View File

@@ -17,7 +17,7 @@ struct contentTypes {
void contentRunner();
void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo);
bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin, imgParam &imageParams);
bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin, tagRecord *&taginfo, imgParam &imageParams);
void drawString(TFT_eSprite &spr, String content, uint16_t posx, uint16_t posy, String font, byte align = 0, uint16_t color = TFT_BLACK);
void initSprite(TFT_eSprite &spr, int w, int h);
void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams);

View File

@@ -7,6 +7,9 @@ struct imgParam {
bool hasRed;
uint8_t dataType;
bool dither;
char segments[12];
uint16_t symbols;
bool invert;
};
void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams);

View File

@@ -8,11 +8,11 @@ extern void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin);
extern bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t nextCheckin);
extern void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP);
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 bool sendAPSegmentedData(uint8_t* dst, String data, uint16_t icons, bool inverted);
extern bool showAPSegmentedInfo(uint8_t* dst);
extern bool sendAPSegmentedData(uint8_t* dst, String data, uint16_t icons, bool inverted, bool local);
extern bool showAPSegmentedInfo(uint8_t* dst, bool local);
void refreshAllPending();
void setAPchannel();

View File

@@ -17,7 +17,6 @@
class tagRecord {
public:
uint16_t nextCheckinpending;
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),
filename(""), data(nullptr), len(0) {}

View File

@@ -37,6 +37,7 @@ enum contentModes {
QRcode,
Calendar,
RemoteAP,
SegStatic,
};
void contentRunner() {
@@ -103,6 +104,9 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
imageParams.dataType = DATATYPE_IMG_RAW_1BPP;
imageParams.dither = true;
imageParams.invert = false;
imageParams.symbols = 0;
switch (taginfo->contentMode) {
case Image:
@@ -124,7 +128,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
drawDate(filename, taginfo, imageParams);
taginfo->nextupdate = midnight;
updateTagImage(filename, mac, (midnight - now) / 60 - 10, imageParams);
updateTagImage(filename, mac, (midnight - now) / 60 - 10, taginfo, imageParams);
break;
case CountDays:
@@ -132,7 +136,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
if (buttonPressed) cfgobj["counter"] = 0;
drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo, imageParams);
taginfo->nextupdate = midnight;
updateTagImage(filename, mac, (buttonPressed ? 0 : 15), imageParams);
updateTagImage(filename, mac, (buttonPressed ? 0 : 15), taginfo, imageParams);
cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1;
break;
@@ -141,7 +145,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
if (buttonPressed) cfgobj["counter"] = 0;
drawNumber(filename, (int32_t)cfgobj["counter"], (int32_t)cfgobj["thresholdred"], taginfo, imageParams);
taginfo->nextupdate = now + 3600;
updateTagImage(filename, mac, (buttonPressed ? 0 : 5), imageParams);
updateTagImage(filename, mac, (buttonPressed ? 0 : 5), taginfo, imageParams);
cfgobj["counter"] = (int32_t)cfgobj["counter"] + 1;
break;
@@ -154,14 +158,14 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
drawWeather(filename, cfgobj, taginfo, imageParams);
taginfo->nextupdate = now + 3600;
updateTagImage(filename, mac, 15, imageParams);
updateTagImage(filename, mac, 15, taginfo, imageParams);
break;
case Forecast:
drawForecast(filename, cfgobj, taginfo, imageParams);
taginfo->nextupdate = now + 3 * 3600;
updateTagImage(filename, mac, 15, imageParams);
updateTagImage(filename, mac, 15, taginfo, imageParams);
break;
case Firmware:
@@ -185,14 +189,14 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
drawIdentify(filename, taginfo, imageParams);
taginfo->nextupdate = now + 12 * 3600;
updateTagImage(filename, mac, 0, imageParams);
updateTagImage(filename, mac, 0, taginfo, imageParams);
break;
case ImageUrl:
if (getImgURL(filename, cfgobj["url"], (time_t)cfgobj["#fetched"], imageParams, dst)) {
taginfo->nextupdate = now + 60 * (cfgobj["interval"].as<int>() < 5 ? 5 : cfgobj["interval"].as<int>());
updateTagImage(filename, mac, cfgobj["interval"].as<int>(), imageParams);
updateTagImage(filename, mac, cfgobj["interval"].as<int>(), taginfo, imageParams);
cfgobj["#fetched"] = now;
} else {
taginfo->nextupdate = now + 300;
@@ -203,7 +207,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
if (getRssFeed(filename, cfgobj["url"], cfgobj["title"], taginfo, imageParams)) {
taginfo->nextupdate = now + 60 * (cfgobj["interval"].as<int>() < 5 ? 5 : cfgobj["interval"].as<int>());
updateTagImage(filename, mac, cfgobj["interval"].as<int>(), imageParams);
updateTagImage(filename, mac, cfgobj["interval"].as<int>(), taginfo, imageParams);
} else {
taginfo->nextupdate = now + 300;
}
@@ -213,14 +217,14 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
drawQR(filename, cfgobj["qr-content"], cfgobj["title"], taginfo, imageParams);
taginfo->nextupdate = now + 12 * 3600;
updateTagImage(filename, mac, 0, imageParams);
updateTagImage(filename, mac, 0, taginfo, imageParams);
break;
case Calendar:
if (getCalFeed(filename, cfgobj["apps_script_url"], cfgobj["title"], taginfo, imageParams)) {
taginfo->nextupdate = now + 60 * (cfgobj["interval"].as<int>() < 5 ? 5 : cfgobj["interval"].as<int>());
updateTagImage(filename, mac, cfgobj["interval"].as<int>(), imageParams);
updateTagImage(filename, mac, cfgobj["interval"].as<int>(), taginfo, imageParams);
} else {
taginfo->nextupdate = now + 300;
}
@@ -230,14 +234,25 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
taginfo->nextupdate = 3216153600;
break;
case SegStatic:
sprintf(buffer, "%-4.4s%-2.2s%-4.4s", cfgobj["line1"].as<const char *>(), cfgobj["line2"].as<const char *>(), cfgobj["line3"].as<const char *>());
sendAPSegmentedData(mac, (String)buffer, 0x0000, false, (taginfo->isExternal == false));
taginfo->nextupdate = 3216153600;
break;
}
taginfo->modeConfigJson = doc.as<String>();
}
bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin, imgParam &imageParams) {
if (imageParams.hasRed) imageParams.dataType = DATATYPE_IMG_RAW_2BPP;
prepareDataAvail(&filename, imageParams.dataType, dst, nextCheckin);
bool updateTagImage(String &filename, uint8_t *dst, uint16_t nextCheckin, tagRecord *&taginfo, imgParam &imageParams) {
if (taginfo->hwType == SOLUM_SEG_UK) {
sendAPSegmentedData(dst, (String)imageParams.segments, imageParams.symbols, imageParams.invert, (taginfo->isExternal == false));
} else {
if (imageParams.hasRed) imageParams.dataType = DATATYPE_IMG_RAW_2BPP;
prepareDataAvail(&filename, imageParams.dataType, dst, nextCheckin);
}
return true;
}
@@ -265,8 +280,6 @@ void initSprite(TFT_eSprite &spr, int w, int h) {
}
void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams) {
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
time_t now;
time(&now);
struct tm timeinfo;
@@ -274,7 +287,16 @@ void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams) {
int weekday_number = timeinfo.tm_wday;
int month_number = timeinfo.tm_mon;
int year_number = timeinfo.tm_year + 1900;
if (taginfo->hwType == SOLUM_SEG_UK) {
sprintf(imageParams.segments, "%2d%2d%-2.2s%04d", timeinfo.tm_mday, month_number + 1, languageDays[getCurrentLanguage()][timeinfo.tm_wday], year_number);
imageParams.symbols = 0x04;
return;
}
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
LittleFS.begin();
if (taginfo->hwType == SOLUM_29_033) {
@@ -299,6 +321,30 @@ void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams) {
}
void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord *&taginfo, imgParam &imageParams) {
if (taginfo->hwType == SOLUM_SEG_UK) {
imageParams.symbols = 0x00;
if (count > 19999) {
sprintf(imageParams.segments, "over flow");
} else {
if (count > 9999) {
imageParams.symbols = 0x02;
if (taginfo->contentMode == CountHours) {
sprintf(imageParams.segments, "%04d hour", count - 10000);
} else {
sprintf(imageParams.segments, "%04d days", count - 10000);
}
} else {
if (taginfo->contentMode == CountHours) {
sprintf(imageParams.segments, "%4d hour", count);
} else {
sprintf(imageParams.segments, "%4d days", count);
}
}
}
return;
}
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
LittleFS.begin();
@@ -354,11 +400,8 @@ void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord
}
void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams) {
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
wsLog("get weather");
// icons: https://erikflowers.github.io/weather-icons/
getLocation(cfgobj);
HTTPClient http;
@@ -389,23 +432,44 @@ void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgP
auto windspeed = doc["current_weather"]["windspeed"].as<int>();
auto winddirection = doc["current_weather"]["winddirection"].as<int>();
uint8_t weathercode = doc["current_weather"]["weathercode"].as<int>();
uint8_t isday = doc["current_weather"]["is_day"].as<int>();
if (weathercode > 40) weathercode -= 40;
int wind = windSpeedToBeaufort(windspeed);
doc.clear();
if (taginfo->hwType == SOLUM_SEG_UK) {
String weatherText[] = {"sun", "sun", "sun", "cldy", "cldy", "fog", "", "", "fog", "", "",
"drzl", "", "drzl", "", "drzl", "ice", "ice", "", "", "",
"rain", "", "rain", "", "rain", "ice", "ice", "", "", "",
"snow", "", "snow", "", "snow", "", "snow", "", "", "rain",
"rain", "rain", "", "", "snow", "snow", "", "", "", "",
"", "", "", "", "strm", "hail", "", "", "hail"};
if (temperature < -9.9) {
sprintf(imageParams.segments, "%3d^%2d%-4.4s", static_cast<int>(temperature), wind, weatherText[weathercode].c_str());
imageParams.symbols = 0x00;
} else {
sprintf(imageParams.segments, "%3d^%2d%-4.4s", static_cast<int>(temperature * 10), wind, weatherText[weathercode].c_str());
imageParams.symbols = 0x04;
}
http.end();
return;
}
String weatherIcons[] = {"\uf00d", "\uf00c", "\uf002", "\uf013", "\uf013", "\uf014", "-", "-", "\uf014", "-", "-",
"\uf01a", "-", "\uf01a", "-", "\uf01a", "\uf017", "\uf017", "-", "-", "-",
"\uf019", "-", "\uf019", "-", "\uf019", "\uf015", "\uf015", "-", "-", "-",
"\uf01b", "-", "\uf01b", "-", "\uf01b", "-", "\uf076", "-", "-", "\uf01a",
"\uf01a", "\uf01a", "-", "-", "\uf064", "\uf064", "-", "-", "-", "-",
"-", "-", "-", "-", "\uf01e", "\uf01d", "-", "-", "\uf01e"};
if (1 == 0) { // nacht
if (isday == 0) {
weatherIcons[0] = "\0uf02e";
weatherIcons[1] = "\0uf083";
weatherIcons[2] = "\0uf086";
}
doc.clear();
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
LittleFS.begin();
tft.setTextWrap(false, false);

View File

@@ -208,50 +208,51 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
return;
}
if (taginfo->isExternal == false) {
LittleFS.begin();
if (pending->availdatainfo.dataType != DATATYPE_UK_SEGMENTED) {
LittleFS.begin();
char buffer[64];
sprintf(buffer, "%02X%02X%02X%02X%02X%02X\0", src[2], src[3], src[4], src[5], src[6], src[7]);
String filename = "/current/" + (String)buffer + ".pending";
String imageUrl = "http://" + remoteIP.toString() + filename;
wsLog("GET " + imageUrl);
HTTPClient http;
http.begin(imageUrl);
int httpCode = http.GET();
if (httpCode == 200) {
File file = LittleFS.open(filename, "w");
http.writeToStream(&file);
file.close();
}
http.end();
fs::File file = LittleFS.open(filename);
uint32_t filesize = file.size();
if (filesize == 0) {
file.close();
wsErr("File has size 0. " + filename);
return;
}
uint8_t md5bytes[16];
{
MD5Builder md5;
md5.begin();
md5.addStream(file, filesize);
md5.calculate();
md5.getBytes(md5bytes);
}
char buffer[64];
sprintf(buffer, "%02X%02X%02X%02X%02X%02X\0", src[2], src[3], src[4], src[5], src[6], src[7]);
String filename = "/current/" + (String)buffer + ".pending";
String imageUrl = "http://" + remoteIP.toString() + filename;
wsLog("GET " + imageUrl);
HTTPClient http;
http.begin(imageUrl);
int httpCode = http.GET();
if (httpCode == 200) {
File file = LittleFS.open(filename, "w");
http.writeToStream(&file);
file.close();
taginfo->filename = filename;
taginfo->len = filesize;
clearPending(taginfo);
taginfo->pending = true;
memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes));
}
http.end();
fs::File file = LittleFS.open(filename);
uint32_t filesize = file.size();
if (filesize == 0) {
file.close();
wsErr("File has size 0. " + filename);
return;
}
uint8_t md5bytes[16];
{
MD5Builder md5;
md5.begin();
md5.addStream(file, filesize);
md5.calculate();
md5.getBytes(md5bytes);
}
file.close();
sendDataAvail(pending);
taginfo->filename = filename;
taginfo->len = filesize;
clearPending(taginfo);
taginfo->pending = true;
memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes));
taginfo->contentMode = 12;
taginfo->nextupdate = 3216153600;
sendDataAvail(pending);
wsSendTaginfo(mac);
}
@@ -456,10 +457,10 @@ void setAPchannel() {
}
}
bool sendAPSegmentedData(uint8_t* dst, String data, uint16_t icons, bool inverted) {
bool sendAPSegmentedData(uint8_t* dst, String data, uint16_t icons, bool inverted, bool local) {
struct pendingData pending = {0};
memcpy(pending.targetMac, dst, 8);
pending.availdatainfo.dataType = 0x51;
pending.availdatainfo.dataType = DATATYPE_UK_SEGMENTED;
pending.availdatainfo.dataSize = icons << 16;
memcpy((void*)&(pending.availdatainfo.dataVer), data.c_str(), 10);
pending.availdatainfo.dataTypeArgument = inverted;
@@ -470,13 +471,18 @@ bool sendAPSegmentedData(uint8_t* dst, String data, uint16_t icons, bool inverte
*((uint64_t*)srcc) = swap64(*((uint64_t*)dst));
sprintf(buffer, ">AP Segmented Data %02X%02X%02X%02X%02X%02X%02X%02X\n\0", srcc[0], srcc[1], srcc[2], srcc[3], srcc[4], srcc[5], srcc[6], srcc[7]);
Serial.print(buffer);
return sendDataAvail(&pending);
if (local) {
return sendDataAvail(&pending);
} else {
udpsync.netSendDataAvail(&pending);
return true;
}
}
bool showAPSegmentedInfo(uint8_t* dst) {
bool showAPSegmentedInfo(uint8_t* dst, bool local) {
struct pendingData pending = {0};
memcpy(pending.targetMac, dst, 8);
pending.availdatainfo.dataType = 0x51;
pending.availdatainfo.dataType = DATATYPE_UK_SEGMENTED;
pending.availdatainfo.dataSize = 0x00;
pending.availdatainfo.dataVer = 0x00;
pending.availdatainfo.dataTypeArgument = 0;
@@ -487,5 +493,10 @@ bool showAPSegmentedInfo(uint8_t* dst) {
*((uint64_t*)srcc) = swap64(*((uint64_t*)dst));
sprintf(buffer, ">SDA %02X%02X%02X%02X%02X%02X%02X%02X\n\0", srcc[0], srcc[1], srcc[2], srcc[3], srcc[4], srcc[5], srcc[6], srcc[7]);
Serial.print(buffer);
return sendDataAvail(&pending);
if (local) {
return sendDataAvail(&pending);
} else {
udpsync.netSendDataAvail(&pending);
return true;
}
}

View File

@@ -533,12 +533,12 @@ void ShowAPInfo() {
}
void notifySegmentedFlash() {
sendAPSegmentedData(apInfo.mac, (String) "Fl ash", 0x0800, false);
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);
sendAPSegmentedData(apInfo.mac, (String) "If done", 0x0800, false, true);
vTaskDelay(2000 / portTICK_PERIOD_MS);
sendAPSegmentedData(apInfo.mac, (String) "RE boot", 0x0800, false);
sendAPSegmentedData(apInfo.mac, (String) "RE boot", 0x0800, false, true);
vTaskDelay(1000 / portTICK_PERIOD_MS);
#endif
}
@@ -560,14 +560,13 @@ void checkWaitPowerCycle() {
void segmentedShowIp() {
IPAddress IP = WiFi.localIP();
char temp[12];
sprintf(temp, "%03d IP %03d", IP[0], IP[1]);
sendAPSegmentedData(apInfo.mac, (String) "IP Addr", 0x0200, true);
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);
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);
sendAPSegmentedData(apInfo.mac, (String)temp, 0x0200, true, true);
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
@@ -639,7 +638,7 @@ void APTask(void* parameter) {
if (apInfo.type == SOLUM_SEG_UK) {
segmentedShowIp();
showAPSegmentedInfo(apInfo.mac);
showAPSegmentedInfo(apInfo.mac, true);
}
uint16_t fsversion;
@@ -694,7 +693,7 @@ void APTask(void* parameter) {
ShowAPInfo();
if (apInfo.type == SOLUM_SEG_UK) {
segmentedShowIp();
showAPSegmentedInfo(apInfo.mac);
showAPSegmentedInfo(apInfo.mac, true);
}
} else {
Serial.printf("Failed to bring up the AP after successful flashing... That's not supposed to happen!\n");

View File

@@ -339,7 +339,7 @@ void init_web() {
server.onNotFound([](AsyncWebServerRequest *request) {
if (request->url() == "/" || request->url() == "index.htm") {
request->send(200, "text/html", "-");
request->send(200, "text/html", "index.html not found. Did you forget to upload the littlefs partition?");
return;
}
request->send(404);

View File

@@ -63,7 +63,7 @@ static uint8_t charDecode(uint8_t c) {
0b11000010,
0b00011100,
0b00000110,
0b00000000,
0b10101100,
0b10001100,
0b10001110,
0b01111100,
@@ -73,7 +73,7 @@ static uint8_t charDecode(uint8_t c) {
0b00011110,
0b10000110,
0b10010110,
0b11000110,
0b01011010,
0b10001000,
0b11011010,
0b01101110,
@@ -92,7 +92,7 @@ static uint8_t charDecode(uint8_t c) {
0b11000010,
0b00011100,
0b00010110,
0b00000000,
0b10101100,
0b11110100,
0b11110110,
0b01111100,
@@ -102,7 +102,7 @@ static uint8_t charDecode(uint8_t c) {
0b00011110,
0b11010110,
0b10010110,
0b11000110,
0b01011010,
0b10001000,
0b11011010,
0b01101110,
@@ -147,7 +147,7 @@ static uint8_t charDecode(uint8_t c) {
case 0x5F: // _
return 0b00000010;
case 0x5E: // ^
return 0b01110000;
return 0b01111000;
case 0x3D: // =
return 0b00001010;
case 0x23: // #