mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 05:06:39 +01:00
preparations for custom LUT
This commit is contained in:
BIN
ESP32_AP-Flasher/data/gradient.jpg
Normal file
BIN
ESP32_AP-Flasher/data/gradient.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
@@ -7,6 +7,8 @@ struct imgParam {
|
||||
bool hasRed;
|
||||
uint8_t dataType;
|
||||
bool dither;
|
||||
bool grayLut = false;
|
||||
|
||||
char segments[12];
|
||||
uint16_t symbols;
|
||||
bool invert;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user