preparations for custom LUT

This commit is contained in:
Nic Limper
2023-05-25 14:28:54 +02:00
parent f73ce60468
commit 49d86620bd
11 changed files with 142 additions and 34 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -344,5 +344,22 @@
"type": "text"
}
]
},
{
"id": 15,
"name": "Send custom LUT",
"desc": "EXPERIMENTAL. Don't use. YOU RISK DAMAGING YOUR SCREEN.",
"hwtype": [
1
],
"capabilities": 4,
"param": [
{
"key": "bytes",
"name": "bytes",
"desc": "70 bytes, formatted as 0x00,0x00,...",
"type": "text"
}
]
}
]

View File

@@ -13,7 +13,7 @@ models[240] = "Segmented tag"
models[17] = "2.9\" 296x128px (UC8151)"
const displaySizeLookup = { 0: [152, 152], 1: [128, 296], 2: [400, 300] };
displaySizeLookup[17] = [128, 296];
const colorTable = { 0: [255, 255, 255], 1: [0, 0, 0], 2: [255, 0, 0], 3: [255, 0, 0] };
const colorTable = { 0: [255, 255, 255], 1: [0, 0, 0], 2: [255, 0, 0], 3: [150, 150, 150] };
const imageQueue = [];
let isProcessing = false;
@@ -64,7 +64,7 @@ function connect() {
});
socket.addEventListener("message", (event) => {
//console.log(event.data)
console.log(event.data)
const msg = JSON.parse(event.data);
if (msg.logMsg) {
showMessage(msg.logMsg, false);
@@ -213,7 +213,7 @@ function updatecards() {
if (item.dataset.lastseen && item.dataset.lastseen > 1672531200) {
let idletime = (Date.now() / 1000) - servertimediff - item.dataset.lastseen;
$('#tag' + tagmac + ' .lastseen').innerHTML = "<span>last seen</span>" + displayTime(Math.floor(idletime)) + " ago";
if ((Date.now() / 1000) - servertimediff - 300 > item.dataset.nextcheckin) {
if ((Date.now() / 1000) - servertimediff - 600 > item.dataset.nextcheckin) {
$('#tag' + tagmac + ' .warningicon').style.display = 'inline-block';
$('#tag' + tagmac).classList.remove("tagpending")
$('#tag' + tagmac).style.background = '#e0e0a0';

View File

@@ -48,6 +48,7 @@ struct espAvailDataReq {
#define EPD_LUT_NO_REPEATS 1
#define EPD_LUT_FAST_NO_REDS 2
#define EPD_LUT_FAST 3
#define EPD_LUT_OTA 0x10
struct AvailDataInfo {
uint8_t checksum;

View File

@@ -33,5 +33,6 @@ char *formatHttpDate(time_t t);
String urlEncode(const char *msg);
int windSpeedToBeaufort(float windSpeed);
String windDirectionIcon(int degrees);
String mac62hex(uint8_t *mac);
void getLocation(JsonObject &cfgobj);
void prepareNFCReq(uint8_t* dst, const char* url);
void prepareLUTreq(uint8_t *dst, String input);

View File

@@ -7,6 +7,8 @@ struct imgParam {
bool hasRed;
uint8_t dataType;
bool dither;
bool grayLut = false;
char segments[12];
uint16_t symbols;
bool invert;

View File

@@ -5,7 +5,7 @@ extern bool checkCRC(void* p, uint8_t len);
extern void processBlockRequest(struct espBlockRequest* br);
extern void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin);
extern void prepareNFCReq(uint8_t* dst, const char* url);
extern void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, uint8_t* dst);
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);

View File

@@ -13,7 +13,7 @@
class tagRecord {
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),
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),
filename(""), data(nullptr), len(0) {}
uint8_t mac[8];
@@ -36,7 +36,7 @@ class tagRecord {
uint32_t lastfullupdate;
bool isExternal;
uint16_t pendingIdle;
bool hasCustomLUT;
String filename;
uint8_t* data;
uint32_t len;

View File

@@ -1,19 +1,30 @@
#include "contentmanager.h"
// possibility to turn off, to save space if needed
#define CONTENT_QR
#define CONTENT_RSS
#define CONTENT_CAL
#include <Arduino.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>
#include <MD5Builder.h>
#include <locale.h>
#ifdef CONTENT_RSS
#include <rssClass.h>
#endif
#include <time.h>
#include <LittleFS.h>
#if defined CONTENT_RSS || defined CONTENT_CAL
#include "U8g2_for_TFT_eSPI.h"
#endif
#include "commstructs.h"
#include "makeimage.h"
#include "newproto.h"
#ifdef CONTENT_QR
#include "qrcode.h"
#endif
#include "tag_db.h"
#include "settings.h"
#include "web.h"
@@ -95,6 +106,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
imageParams.hasRed = false;
imageParams.dataType = DATATYPE_IMG_RAW_1BPP;
imageParams.dither = true;
if (taginfo->hasCustomLUT) imageParams.grayLut = true;
imageParams.invert = false;
imageParams.symbols = 0;
@@ -108,7 +120,7 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
if (imageParams.hasRed) imageParams.dataType = DATATYPE_IMG_RAW_2BPP;
if (prepareDataAvail(&filename, imageParams.dataType, mac, cfgobj["timetolive"].as<int>())) {
cfgobj["#fetched"] = true;
if (cfgobj["delete"].as<String>()) LittleFS.remove("/"+cfgobj["filename"].as<String>());
if (cfgobj["delete"].as<String>()=="1") LittleFS.remove("/"+cfgobj["filename"].as<String>());
} else {
wsErr("Error accessing " + filename);
}
@@ -237,10 +249,15 @@ void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo) {
case 14: // NFC URL
taginfo->nextupdate = 3216153600;
memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
prepareNFCReq(mac, cfgobj["url"].as<const char *>());
break;
case 15: // send gray LUT
taginfo->nextupdate = 3216153600;
prepareLUTreq(mac, cfgobj["bytes"]);
taginfo->hasCustomLUT = true;
break;
}
taginfo->modeConfigJson = doc.as<String>();
@@ -783,9 +800,12 @@ bool getImgURL(String &filename, String URL, time_t fetched, imgParam &imagePara
return (httpCode == 200 || httpCode == 304);
}
#ifdef CONTENT_RSS
rssClass reader;
#endif
bool getRssFeed(String &filename, String URL, String title, tagRecord *&taginfo, imgParam &imageParams) {
#ifdef CONTENT_RSS
// https://github.com/garretlab/shoddyxml2
// http://feeds.feedburner.com/tweakers/nieuws
@@ -854,6 +874,7 @@ bool getRssFeed(String &filename, String URL, String title, tagRecord *&taginfo,
spr2buffer(spr, filename, imageParams);
spr.deleteSprite();
#endif
return true;
}
@@ -878,6 +899,7 @@ char *epoch_to_display(time_t utc) {
}
bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo, imgParam &imageParams) {
#ifdef CONTENT_CAL
// google apps scripts method to retrieve calendar
// see /data/calendar.txt for description
@@ -988,11 +1010,12 @@ bool getCalFeed(String &filename, String URL, String title, tagRecord *&taginfo,
spr2buffer(spr, filename, imageParams);
spr.deleteSprite();
#endif
return true;
}
void drawQR(String &filename, String qrcontent, String title, tagRecord *&taginfo, imgParam &imageParams) {
#ifdef CONTENT_QR
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite spr = TFT_eSprite(&tft);
LittleFS.begin();
@@ -1037,6 +1060,7 @@ void drawQR(String &filename, String qrcontent, String title, tagRecord *&taginf
spr2buffer(spr, filename, imageParams);
spr.deleteSprite();
#endif
}
char *formatHttpDate(time_t t) {
@@ -1111,4 +1135,41 @@ void getLocation(JsonObject &cfgobj) {
cfgobj["#tz"] = tz;
}
}
}
}
void prepareNFCReq(uint8_t *dst, const char *url) {
uint8_t *data;
size_t len = strlen(url);
data = new uint8_t[len + 8];
// TLV
data[0] = 0x03; // NDEF message (TLV type)
data[1] = 4 + len + 1;
// ndef record
data[2] = 0xD1;
data[3] = 0x01; // well known record type
data[4] = len + 1; // payload length
data[5] = 0x55; // payload type (URI record)
data[6] = 0x00; // URI identifier code (no prepending)
memcpy(data + 7, reinterpret_cast<const uint8_t *>(url), len);
len = 7 + len;
data[len] = 0xFE;
len = 1 + len;
prepareDataAvail(data, len, DATATYPE_NFC_RAW_CONTENT, dst);
}
void prepareLUTreq(uint8_t *dst, String input) {
const char *delimiters = ", \t";
const int maxValues = 70;
uint8_t waveform[maxValues];
char *ptr = strtok(const_cast<char *>(input.c_str()), delimiters);
int i = 0;
while (ptr != nullptr && i < maxValues) {
waveform[i++] = static_cast<uint8_t>(strtol(ptr, nullptr, 16));
ptr = strtok(nullptr, delimiters);
}
Serial.println(String(i) + " bytes found");
size_t waveformLen = sizeof(waveform);
prepareDataAvail(waveform, waveformLen, DATATYPE_CUSTOM_LUT_OTA, dst);
}

View File

@@ -116,6 +116,11 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
{0, 0, 0}, // Black
{255, 0, 0} // Red
};
if (imageParams.grayLut) {
Color newColor = {150, 150, 150};
palette.push_back(newColor);
Serial.println("rendering with gray");
}
int num_colors = palette.size();
Color color;
Error *error_bufferold = new Error[bufw];
@@ -143,13 +148,40 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
uint16_t bitIndex = 7 - (x % 8);
uint16_t byteIndex = (y * bufw + x) / 8;
// this looks a bit ugly, but it's performing better than shorter notations
switch (best_color_index) {
case 1:
blackBuffer[byteIndex] |= (1 << bitIndex);
break;
case 2:
imageParams.hasRed = true;
redBuffer[byteIndex] |= (1 << bitIndex);
break;
case 3:
blackBuffer[byteIndex] |= (1 << bitIndex);
redBuffer[byteIndex] |= (1 << bitIndex);
imageParams.hasRed = true;
break;
}
/*
alt 1:
if (best_color_index & 1) {
blackBuffer[byteIndex] |= (1 << bitIndex);
} else if (best_color_index & 2) {
}
if (best_color_index & 2) {
imageParams.hasRed = true;
redBuffer[byteIndex] |= (1 << bitIndex);
}
alt 2:
blackBuffer[byteIndex] |= ((best_color_index & 1) << bitIndex);
redBuffer[byteIndex] |= ((best_color_index & 2) << bitIndex);
imageParams.hasRed |= (best_color_index & 2);
*/
if (imageParams.dither) {
Error error = {
((float)color.r + error_bufferold[x].r - palette[best_color_index].r) / 16.0f,

View File

@@ -79,7 +79,7 @@ void prepareIdleReq(uint8_t* dst, uint16_t nextCheckin) {
}
}
void prepareNFCReq(uint8_t* dst, const char* url) {
void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, uint8_t* dst) {
tagRecord* taginfo = nullptr;
taginfo = tagRecord::findByMAC(dst);
if (taginfo == nullptr) {
@@ -88,32 +88,22 @@ void prepareNFCReq(uint8_t* dst, const char* url) {
}
clearPending(taginfo);
size_t len = strlen(url);
taginfo->data = new uint8_t[len + 8];
// TLV
taginfo->data[0] = 0x03; // NDEF message (TLV type)
taginfo->data[1] = 4 + len + 1;
// ndef record
taginfo->data[2] = 0xD1;
taginfo->data[3] = 0x01; // well known record type
taginfo->data[4] = len + 1; // payload length
taginfo->data[5] = 0x55; // payload type (URI record)
taginfo->data[6] = 0x00; // URI identifier code (no prepending)
memcpy(taginfo->data + 7, reinterpret_cast<const uint8_t*>(url), len);
len = 7 + len;
taginfo->data[len] = 0xFE;
len = 1 + len;
taginfo->data = (uint8_t*)malloc(len);
if (taginfo->data == nullptr) {
wsErr("no memory allocation for data");
return;
}
memcpy(taginfo->data, data, len);
taginfo->pending = true;
taginfo->len = len;
taginfo->expectedNextCheckin = 0;
taginfo->filename = String();
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
struct pendingData pending = {0};
memcpy(pending.targetMac, dst, 8);
pending.availdatainfo.dataSize = len;
pending.availdatainfo.dataType = DATATYPE_NFC_RAW_CONTENT;
pending.availdatainfo.dataType = dataType;
pending.availdatainfo.nextCheckIn = 0;
pending.availdatainfo.dataVer = millis();
pending.attemptsLeft = 10;
@@ -182,6 +172,10 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
lut = EPD_LUT_DEFAULT; // full update once a day
taginfo->lastfullupdate = now;
}
if (taginfo->hasCustomLUT && taginfo->capabilities & CAPABILITY_SUPPORTS_CUSTOM_LUTS) {
Serial.println("using custom LUT");
lut = EPD_LUT_OTA;
}
if (dataType != DATATYPE_FW_UPDATE) {
char dst_path[64];