4 Commits

Author SHA1 Message Date
Nic Limper
205dfa0ce2 logging reboots 2023-06-11 13:29:49 +02:00
Jelmer
5c7b53b740 update in power management (multiple pins) 2023-06-10 14:24:50 +02:00
Nic Limper
e7fbaffbab added advanced tag options
- added advanced tag options: image rotation / always use default lut / force refresh / cancel pending
- fixed rx/tx swap in documentation
2023-06-09 12:56:58 +02:00
Nic Limper
c68b582be7 overhaul of contentmanager, 4.2" layouts, bugfixes
- added Swedish å Å to Bahnschrift20 and 30
- option to turn dither on/off in 'static image'
- major overhaul of contentmanager.cpp, content is now generated from json template.
- fallback to 1bpp for 4.2" when no psram
- added 4.2" content layouts
- fixed small apconfig bug
- pause content generation for 60 seconds after crash, to prevent uncontrollable boot loop
2023-06-08 15:41:15 +02:00
32 changed files with 959 additions and 666 deletions

View File

@@ -0,0 +1,129 @@
{
"1": {
"0": {
"weekday": [ 76, 10, "fonts/calibrib30" ],
"month": [ 76, 120, "fonts/calibrib30" ],
"day": [ 76, 42, "fonts/calibrib100" ]
},
"1": {
"weekday": [ 148, 10, "fonts/calibrib60" ],
"date": [ 148, 73, "fonts/calibrib50" ]
},
"2": {
"weekday": [ 200, 25, "fonts/calibrib60" ],
"month": [ 200, 225, "fonts/calibrib60" ],
"day": [ 200, 95, "fonts/calibrib150" ]
}
},
"2": {
"0": {
"fonts": [ "fonts/calibrib120", "fonts/calibrib80", "fonts/calibrib50", "fonts/calibrib50" ],
"xy": [ 76, 83 ]
},
"1": {
"fonts": [ "fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib120", "fonts/calibrib100" ],
"xy": [ 148, 74 ]
},
"2": {
"fonts": [ "fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib150", "fonts/calibrib120" ],
"xy": [ 200, 148 ]
}
},
"4": {
"0": {
"location": [ 10, 130, 2 ],
"wind": [ 140, 10, "fonts/bahnschrift30" ],
"temp": [ 10, 10, "fonts/bahnschrift30" ],
"icon": [ 33, 33, "fonts/weathericons78" ],
"dir": [ 100, -2, "fonts/weathericons30" ],
"umbrella": [ 115, 110 ]
},
"1": {
"location": [ 5, 5, "fonts/bahnschrift30" ],
"wind": [ 280, 5, "fonts/bahnschrift30" ],
"temp": [ 5, 65, "fonts/bahnschrift70" ],
"icon": [ 185, 32, "fonts/weathericons70" ],
"dir": [ 240, -3, "fonts/weathericons30" ],
"umbrella": [ 190, 0 ]
},
"2": {
"location": [ 20, 20, "fonts/calibrib30" ],
"wind": [ 290, 83, "fonts/calibrib60" ],
"temp": [ 20, 170, "fonts/calibrib150" ],
"icon": [ 100, 50, "fonts/weathericons78" ],
"dir": [ 220, 50, "fonts/weathericons78" ],
"umbrella": [ 330, 10 ]
}
},
"8": {
"1": {
"location": [ 5, 0, 2 ],
"column": [ 5, 59 ],
"day": [ 30, 18, "fonts/twcondensed20", 41, 108 ],
"icon": [ 12, 58, "fonts/weathericons30" ],
"wind": [ 17, 25 ],
"line": [ 20, 128 ]
},
"2": {
"location": [ 10, 10, "fonts/calibrib30" ],
"column": [ 6, 66 ],
"day": [ 33, 60, "fonts/bahnschrift20", 104, 230 ],
"rain": [ 34, 260 ],
"icon": [ 15, 145, "fonts/weathericons30" ],
"wind": [ 17, 90 ],
"line": [ 50, 300 ]
}
},
"9": {
"1": {
"title": [ 5, 3, "fonts/bahnschrift20" ],
"items": 8,
"line": [ 5, 34, 13 ],
"font": "glasstown_nbp_tf"
},
"2": {
"title": [ 10, 10, "fonts/calibrib30" ],
"items": 12,
"line": [ 10, 60, 20 ],
"font": "7x14_tf"
}
},
"10": {
"0": {
"title": [ 10, 3, 2 ],
"pos": [ 76, 20 ]
},
"1": {
"title": [ 10, 5, "fonts/bahnschrift20" ],
"pos": [ 149, 25 ]
},
"2": {
"title": [ 10, 10, "fonts/bahnschrift20" ],
"pos": [ 200, 30 ]
}
},
"11": {
"1": {
"title": [ 5, 2, "fonts/bahnschrift20" ],
"date": [ 290, 2 ],
"items": 7,
"red": [ 0, 21, 296, 14 ],
"line": [ 5, 32, 15, "t0_14b_tf", 50 ]
},
"2": {
"title": [ 10, 10, "fonts/bahnschrift30" ],
"date": [ 390, 10 ],
"items": 12,
"red": [ 0, 48, 400, 17 ],
"line": [ 10, 61, 18, "7x14_tf", 60 ]
}
},
"16": {
"1": {
"location": [ 5, 5, "fonts/bahnschrift30" ],
"title": [ 247, 11, "glasstown_nbp_tf" ],
"cols": [ 1, 125, 12 ],
"bars": [ 5, 111, 10 ]
}
}
}

View File

@@ -21,6 +21,16 @@
"name": "TimeToLive",
"desc": "Amount (minutes) that this image will stay valid. The tag might not respond meanwhile",
"type": "int"
},
{
"key": "dither",
"name": "Dithering",
"desc": "Turn halftone dithering on or off. Turn it on when displaying photos. For texts, you better leave if off",
"type": "select",
"options": {
"0": "off",
"1": "on"
}
}
]
},

View File

@@ -29,10 +29,30 @@
<button id="paintbutton"><i>A</i>&#128396;</button>
</p>
<div id="customoptions"></div>
<p>
<input type="button" value="Save" id="cfgsave">
<span id="cfgdelete"><img src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw==
"></span>
<div id="advancedoptions" style="height: 0px;">
<p>Advanced options</p>
<p>
<label for="cfgrotate">Rotate image</label>
<select id="cfgrotate">
<option value="0">0 degrees</option>
</select>
</p>
<p>
<label for="cfglut">LUT</label>
<select id="cfglut">
<option value="0">auto</option>
</select>
</p>
<p>
<button id="cfgrefresh">force refresh</button>
<button id="cfgclrpending">clear pending</button>
<button id="cfgdelete"><img src="data:image/gif;base64,R0lGODlhEAAQAPMAANXV1e3t7d/f39HR0dvb2/Hx8dTU1OLi4urq6mZmZpmZmf///wAAAAAAAAAAAAAAACH5BAEAAAwALAAAAAAQABAAAARBkMlJq71Yrp3ZXkr4WWCYnOZSgQVyEMYwJCq1nHhe20qgCAoA7QLyAYU7njE4JPV+zOSkCEUSFbmTVPPpbjvgTAQAOw==
"></button>
</p>
</div>
<p id="savebar">
<span><input type="button" value="Save" id="cfgsave"></span>
<span id="cfgmore" title="advanced options">&#x1f783;</span>
</p>
</div>
@@ -105,7 +125,7 @@ Latency will be around 40 seconds.">
<th>alias</th>
<th>tags</th>
<th>ch</th>
<th>fw ver</th>
<th>AP ver</th>
</tr>
</table>
</p>
@@ -137,6 +157,7 @@ Latency will be around 40 seconds.">
<div class="actionbox">
<div>
<div>Currently active tags:</div>
<div><span id="runstate"></div>
<div><span id="apstatecolor">&#11044;</span> <span id="apstate">loading</span></div>
<div><span id="apconfigbutton">AP config</span></div>
<div><a href="/edit" target="littlefs" class="filebutton">edit littleFS</a></div>
@@ -159,8 +180,8 @@ Latency will be around 40 seconds.">
<div class="nextcheckin"></div>
<div class="nextupdate"></div>
<div class="corner">
<div class="pendingicon">&circlearrowright;</div>
<div class="warningicon">&#9888;</div>
<div class="pendingicon" title="A new message is waiting for the tag to pick up">&circlearrowright;</div>
<div class="warningicon" title="This tag has not been seen for a long time">&#9888;</div>
</div>
</div>
</div>

View File

@@ -142,6 +142,33 @@ select {
width: 80px;
}
#advancedoptions {
overflow: hidden;
transition: height 0.3s ease;
}
#advancedoptions p:first-child {
font-weight: 700;
font-size: 1.2em;
}
#savebar {
display: flex;
align-items: flex-end;
justify-content: space-between;
}
#savebar:first-child {
flex-grow: 2;
}
#cfgmore {
padding: 2px 5px;
font-weight: 700;
font-size: 1.2em;
cursor: pointer;
}
#apconfigbox {
background-color: #e6f0d3;
}
@@ -179,10 +206,8 @@ select {
}
#cfgdelete {
position: absolute;
bottom: 15px;
right: 15px;
cursor:pointer;
cursor: pointer;
padding: 2px 10px;
}
.closebtn {
@@ -245,7 +270,7 @@ select {
}
.tagpending {
animation: pending 1s ease infinite;
animation: pending 1.5s ease infinite;
}
.currimg {
@@ -514,9 +539,9 @@ ul.messages li.new {
}
@keyframes pending {
0% { background-color: #d0d0e0;}
50% { background-color: #b0b0e0;}
100% { background-color: #d0d0e0;}
0% { }
50% { background-color: #d4d4f5;}
100% { }
}
@media screen and (max-width: 480px) {
@@ -562,5 +587,10 @@ ul.messages li.new {
.actionbox>div {
gap: 5px;
flex-flow: wrap;
}
.actionbox>div:first-child>div:first-child {
flex-basis: 100%;
}
}

View File

@@ -11,8 +11,9 @@ const WAKEUP_REASON_WDT_RESET = 0xFE;
const models = ["1.54\" 152x152px", "2.9\" 296x128px", "4.2\" 400x300px"];
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 displaySizeLookup = { 0: [152, 152, 4], 1: [128, 296, 2], 2: [400, 300, 2] }; // w, h, rotate
displaySizeLookup[17] = [128, 296, 2];
displaySizeLookup[240] = [0, 0, 0];
const colorTable = { 0: [255, 255, 255], 1: [0, 0, 0], 2: [255, 0, 0], 3: [150, 150, 150] };
const apstate = [
@@ -24,6 +25,12 @@ const apstate = [
{ state: "failed", color: "red" },
{ state: "coming online", color: "yellow" }
];
const runstate = [
{ state: "⏹︎ stopped" },
{ state: "⏸pause" },
{ state: "" }, // hide running
{ state: "⏳︎ init" }
];
const imageQueue = [];
let isProcessing = false;
@@ -91,6 +98,7 @@ function connect() {
if (msg.sys.apstate) {
$("#apstatecolor").style.color = apstate[msg.sys.apstate].color;
$("#apstate").innerHTML = apstate[msg.sys.apstate].state;
$("#runstate").innerHTML = runstate[msg.sys.runstate].state;
}
servertimediff = (Date.now() / 1000) - msg.sys.currtime;
}
@@ -208,6 +216,7 @@ function processTags(tagArray) {
break;
case WAKEUP_REASON_GPIO:
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "GPIO wakeup"
$('#tag' + tagmac).style.background = "#c8f1bb";
break;
case WAKEUP_REASON_NFC:
$('#tag' + tagmac + ' .nextcheckin').innerHTML = "NFC wakeup"
@@ -272,6 +281,7 @@ $('#clearlog').onclick = function () {
document.querySelectorAll('.closebtn').forEach(button => {
button.addEventListener('click', (event) => {
event.target.parentNode.style.display = 'none';
$('#advancedoptions').style.height = '0px';
});
});
@@ -295,19 +305,29 @@ $('#taglist').addEventListener("click", (event) => {
.then(data => {
var tagdata = data.tags[0];
$('#cfgalias').value = tagdata.alias;
$('#cfgmore').style.display = "none";
if (populateSelectTag(tagdata.hwType, tagdata.capabilities)) {
$('#cfgcontent').parentNode.style.display = "flex";
$('#cfgcontent').value = tagdata.contentMode;
$('#cfgcontent').dataset.json = tagdata.modecfgjson;
contentselected();
if (tagdata.contentMode != 12) $('#cfgmore').style.display = 'block';
} else {
$('#customoptions').innerHTML = "";
$('#cfgcontent').parentNode.style.display = "none";
}
$('#cfgrotate').value = tagdata.rotate;
$('#cfglut').value = tagdata.lut;
$('#cfgmore').innerHTML = '&#x1f783;';
$('#configbox').style.display = 'block';
})
})
$('#cfgmore').onclick = function () {
$('#cfgmore').innerHTML = $('#advancedoptions').style.height == '0px' ? '&#x1f781;' : '&#x1f783;';
$('#advancedoptions').style.height = $('#advancedoptions').style.height == '0px' ? $('#advancedoptions').scrollHeight + 'px' : '0px';
};
$('#cfgsave').onclick = function () {
let contentMode = $('#cfgcontent').value;
let contentDef = getContentDefById(contentMode);
@@ -332,6 +352,9 @@ $('#cfgsave').onclick = function () {
formData.append("modecfgjson", String());
}
formData.append("rotate", $('#cfgrotate').value);
formData.append("lut", $('#cfglut').value);
fetch("/save_cfg", {
method: "POST",
body: formData
@@ -340,26 +363,41 @@ $('#cfgsave').onclick = function () {
.then(data => showMessage(data))
.catch(error => showMessage('Error: ' + error));
$('#advancedoptions').style.height = '0px';
$('#configbox').style.display = 'none';
}
$('#cfgdelete').onclick = function () {
function sendCmd(mac, cmd) {
let formData = new FormData();
formData.append("mac", $('#cfgmac').dataset.mac);
fetch("/delete_cfg", {
formData.append("mac", mac);
formData.append("cmd", cmd);
fetch("/tag_cmd", {
method: "POST",
body: formData
})
.then(response => response.text())
.then(data => {
var div = $('#tag' + $('#cfgmac').dataset.mac);
div.remove();
if (cmd == "del") div.remove();
showMessage(data);
})
.catch(error => showMessage('Error: ' + error));
$('#advancedoptions').style.height = '0px';
$('#configbox').style.display = 'none';
}
$('#cfgdelete').onclick = function () {
sendCmd($('#cfgmac').dataset.mac, "del");
}
$('#cfgclrpending').onclick = function () {
sendCmd($('#cfgmac').dataset.mac, "clear");
}
$('#cfgrefresh').onclick = function () {
sendCmd($('#cfgmac').dataset.mac, "refresh");
}
$('#rebootbutton').onclick = function () {
showMessage("rebooting AP....", true);
fetch("/reboot", {
@@ -459,7 +497,6 @@ function contentselected() {
obj = JSON.parse($('#cfgcontent').dataset.json);
}
$('#paintbutton').style.display = 'none';
if (contentMode) {
let contentDef = getContentDefById(contentMode);
if (contentDef) {
@@ -487,6 +524,15 @@ function contentselected() {
input.type = "text";
input.disabled = true;
break;
case 'select':
input = document.createElement("select");
for (const key in element.options) {
const optionElement = document.createElement("option");
optionElement.value = key;
optionElement.text = element.options[key];
input.appendChild(optionElement);
}
break;
}
input.id = 'opt' + element.key;
input.title = element.desc;
@@ -505,17 +551,46 @@ function populateSelectTag(hwtype, capabilities) {
var selectTag = $("#cfgcontent");
selectTag.innerHTML = "";
var optionsAdded = false;
var option;
cardconfig.forEach(item => {
var capcheck = item.capabilities ?? 0;
var hwtypeArray = item.hwtype;
if (hwtypeArray.includes(hwtype) && (capabilities & capcheck || capcheck == 0)) {
var option = document.createElement("option");
option = document.createElement("option");
option.value = item.id;
option.text = item.name;
selectTag.appendChild(option);
optionsAdded = true;
}
});
var rotateTag = $("#cfgrotate");
rotateTag.innerHTML = "";
for (let i = 0; i < 4; i++) {
if (i == 0 || displaySizeLookup[hwtype][2] == 4 || (i == 2 && displaySizeLookup[hwtype][2] == 2)) {
option = document.createElement("option");
option.value = i;
option.text = (i * 90) + " degrees";
rotateTag.appendChild(option);
}
}
var lutTag = $("#cfglut");
lutTag.innerHTML = "";
option = document.createElement("option");
option.value = "0";
option.text = "auto";
lutTag.appendChild(option);
if (hwtype != 240) {
option = document.createElement("option");
option.value = "1";
option.text = "Always full refresh";
lutTag.appendChild(option);
}
return optionsAdded;
}

View File

@@ -2,6 +2,7 @@
#include <LittleFS.h>
#include <TFT_eSPI.h>
#include "U8g2_for_TFT_eSPI.h"
#include "makeimage.h"
#include "tag_db.h"
@@ -18,7 +19,7 @@ void contentRunner();
void drawNew(uint8_t mac[8], bool buttonPressed, tagRecord *&taginfo);
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 initSprite(TFT_eSprite &spr, int w, int h, imgParam &imageParams);
void drawDate(String &filename, tagRecord *&taginfo, imgParam &imageParams);
void drawNumber(String &filename, int32_t count, int32_t thresholdred, tagRecord *&taginfo, imgParam &imageParams);
void drawWeather(String &filename, JsonObject &cfgobj, tagRecord *&taginfo, imgParam &imageParams);
@@ -35,4 +36,6 @@ int windSpeedToBeaufort(float windSpeed);
String windDirectionIcon(int degrees);
void getLocation(JsonObject &cfgobj);
void prepareNFCReq(uint8_t* dst, const char* url);
void prepareLUTreq(uint8_t *dst, String input);
void prepareLUTreq(uint8_t *dst, String input);
void getTemplate(JsonDocument &json, const char *filePath, uint8_t id, uint8_t hwtype);
void setU8G2Font(const String &title, U8g2_for_TFT_eSPI &u8f);

View File

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

View File

@@ -1,5 +1,5 @@
#include <Arduino.h>
void doLeds();
void rampTagPower(uint8_t pin, bool up);
//void doLeds();
void powerControl(bool powerState, uint8_t* pin, uint8_t pincount);

View File

@@ -0,0 +1,14 @@
#include <Arduino.h>
#define WAKEUP_REASON_TIMED 0
#define WAKEUP_REASON_BOOT 1
#define WAKEUP_REASON_GPIO 2
#define WAKEUP_REASON_NFC 3
#define WAKEUP_REASON_FIRSTBOOT 0xFC
#define WAKEUP_REASON_NETWORK_SCAN 0xFD
#define WAKEUP_REASON_WDT_RESET 0xFE
void init_time();
void logLine(char* buffer);
void logLine(String text);
void logStartUp();

View File

@@ -14,11 +14,12 @@
#define RUNSTATUS_STOP 0
#define RUNSTATUS_PAUSE 1
#define RUNSTATUS_RUN 2
#define RUNSTATUS_INIT 3
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), hasCustomLUT(false),
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), pendingIdle(0), hasCustomLUT(false), rotate(0), lut(0),
dataType(0), filename(""), data(nullptr), len(0) {}
uint8_t mac[8];
String alias;
@@ -41,6 +42,10 @@ class tagRecord {
bool isExternal;
uint16_t pendingIdle;
bool hasCustomLUT;
uint8_t rotate;
uint8_t lut;
uint8_t dataType;
String filename;
uint8_t* data;
uint32_t len;
@@ -58,7 +63,7 @@ struct Config {
uint8_t runStatus;
};
extern SemaphoreHandle_t tagDBOwner;
// extern SemaphoreHandle_t tagDBOwner;
extern Config config;
extern std::vector<tagRecord*> tagDB;
extern DynamicJsonDocument APconfig;

View File

@@ -9,7 +9,7 @@
class ZBS_interface
{
public:
uint8_t begin(uint8_t SS, uint8_t CLK, uint8_t MOSI, uint8_t MISO, uint8_t RESET, uint8_t POWER = -1, uint32_t spi_speed = 8000000);
uint8_t begin(uint8_t SS, uint8_t CLK, uint8_t MOSI, uint8_t MISO, uint8_t RESET, uint8_t* POWER = nullptr, uint8_t powerPins = 1, uint32_t spi_speed = 8000000);
void setSpeed(uint32_t speed);
void set_power(uint8_t state);
void enable_debug();
@@ -38,7 +38,8 @@ private:
uint8_t _MOSI_PIN = -1;
uint8_t _MISO_PIN = -1;
uint8_t _RESET_PIN = -1;
uint8_t _POWER_PIN = -1;
uint8_t* _POWER_PIN = nullptr;
uint8_t _POWER_PINS = 1;
int ZBS_spi_delay = 1;
uint8_t spi_ready = 0;
uint32_t after_byte_delay = 10;

View File

@@ -21,6 +21,7 @@ lib_deps =
https://github.com/Bodmer/U8g2_for_TFT_eSPI
https://github.com/ricmoo/qrcode
fastled/FastLED
platform_packages =
board_build.filesystem = littlefs
monitor_filters = esp32_exception_decoder
@@ -42,18 +43,18 @@ build_flags =
-D ARDUINO_USB_MODE=0
-D CONFIG_SPIRAM_USE_MALLOC=1
-D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
;-D DEBUG_VERSION
-D HAS_RGB_LED
-D BOARD_HAS_PSRAM
;-D HAS_USB
-D POWER_NO_SOFT_POWER
-D FLASHER_AP_SS=11
-D FLASHER_AP_CLK=9
-D FLASHER_AP_MOSI=10
-D FLASHER_AP_MISO=8
-D FLASHER_AP_RESET=13
-D FLASHER_AP_POWER=-1 ;this board has no soft power control
-D FLASHER_AP_POWER={-1} ;this board has no soft power control
-D FLASHER_AP_TXD=7
-D FLASHER_AP_RXD=6
-D FLASHER_AP_TEST=12
@@ -89,21 +90,15 @@ build_flags =
-D ARDUINO_USB_MODE=0
-D CONFIG_SPIRAM_USE_MALLOC=1
-D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
;-D DEBUG_VERSION
;-D HAS_RGB_LED
-D BOARD_HAS_PSRAM
;-D HAS_USB
-D FLASHER_AP_SS=38
-D FLASHER_AP_CLK=40
-D FLASHER_AP_MOSI=39
-D FLASHER_AP_MISO=33
-D FLASHER_AP_RESET=37
-D FLASHER_AP_POWER=18
-D FLASHER_AP_POWER2=21
-D FLASHER_AP_POWER3=16
-D FLASHER_AP_POWER4=17
-D FLASHER_AP_POWER={16,17,18,21}
-D FLASHER_AP_TXD=35
-D FLASHER_AP_RXD=36
-D FLASHER_AP_TEST=34
@@ -150,12 +145,15 @@ build_flags =
-D BOARD_HAS_PSRAM
-D CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
-D POWER_RAMPING
-D POWER_HIGH_SIDE_DRIVER
-D FLASHER_AP_SS=4
-D FLASHER_AP_CLK=5
-D FLASHER_AP_MOSI=7
-D FLASHER_AP_MISO=6
-D FLASHER_AP_RESET=15
-D FLASHER_AP_POWER=0
-D FLASHER_AP_POWER={0}
-D FLASHER_AP_TXD=16
-D FLASHER_AP_RXD=18
-D FLASHER_AP_TEST=17
@@ -165,7 +163,7 @@ build_flags =
-D FLASHER_EXT_MOSI=2
-D FLASHER_EXT_MISO=42
-D FLASHER_EXT_RESET=1
-D FLASHER_EXT_POWER=8
-D FLASHER_EXT_POWER={8}
-D FLASHER_EXT_TXD=38
-D FLASHER_EXT_RXD=39
-D FLASHER_EXT_TEST=47
@@ -175,6 +173,7 @@ build_flags =
-D FLASHER_ALT_MOSI=10
-D FLASHER_ALT_MISO=9
-D FLASHER_ALT_RESET=11
-D FLASHER_ALT_POWER={-1}
-D FLASHER_ALT_TXD=12
-D FLASHER_ALT_RXD=14
-D FLASHER_ALT_TEST=13
@@ -211,8 +210,7 @@ build_flags =
-D FLASHER_AP_MOSI=23
-D FLASHER_AP_MISO=19
-D FLASHER_AP_RESET=2
-D FLASHER_AP_POWER=13 ;// switching HIGH side; connect to 3V3 of tag
-D FLASHER_AP_POWER2=15
-D FLASHER_AP_POWER={13,15}
-D FLASHER_AP_TEST=-1
-D FLASHER_AP_TXD=17
-D FLASHER_AP_RXD=16
@@ -244,8 +242,7 @@ build_flags =
-D FLASHER_AP_MOSI=23
-D FLASHER_AP_MISO=33
-D FLASHER_AP_RESET=27
-D FLASHER_AP_POWER=4
-D FLASHER_AP_POWER2=4
-D FLASHER_AP_POWER={4}
-D FLASHER_AP_TEST=-1
-D FLASHER_AP_TXD=26
-D FLASHER_AP_RXD=25

File diff suppressed because it is too large Load Diff

View File

@@ -50,6 +50,16 @@ void dump(uint8_t *a, uint16_t l) {
Serial.printf("\n");
}
int8_t powerPinsAP[] = FLASHER_AP_POWER;
int8_t pinsAP[] = {FLASHER_AP_CLK, FLASHER_AP_MISO, FLASHER_AP_MOSI, FLASHER_AP_RESET, FLASHER_AP_RXD, FLASHER_AP_SS, FLASHER_AP_TEST, FLASHER_AP_TXD};
#ifdef OPENEPAPERLINK_PCB
int8_t powerPinsExt[] = FLASHER_EXT_POWER;
int8_t powerPinsAlt[] = FLASHER_ALT_POWER;
uint8_t pinsExt[] = {FLASHER_EXT_CLK, FLASHER_EXT_MISO, FLASHER_EXT_MOSI, FLASHER_EXT_RESET, FLASHER_EXT_RXD, FLASHER_EXT_SS, FLASHER_EXT_TEST, FLASHER_EXT_TXD};
#endif
class flasher {
public:
class ZBS_interface *zbs = nullptr;
@@ -106,16 +116,17 @@ flasher::~flasher() {
bool flasher::connectTag(uint8_t port) {
bool result;
switch (port) {
case 0:
result = zbs->begin(FLASHER_AP_SS, FLASHER_AP_CLK, FLASHER_AP_MOSI, FLASHER_AP_MISO, FLASHER_AP_RESET, FLASHER_AP_POWER, 8000000);
result = zbs->begin(FLASHER_AP_SS, FLASHER_AP_CLK, FLASHER_AP_MOSI, FLASHER_AP_MISO, FLASHER_AP_RESET, (uint8_t *)powerPinsAP, sizeof(powerPinsAP), 8000000);
break;
#ifdef OPENEPAPERLINK_PCB
case 1:
result = zbs->begin(FLASHER_EXT_SS, FLASHER_EXT_CLK, FLASHER_EXT_MOSI, FLASHER_EXT_MISO, FLASHER_EXT_RESET, FLASHER_EXT_POWER, 8000000);
result = zbs->begin(FLASHER_EXT_SS, FLASHER_EXT_CLK, FLASHER_EXT_MOSI, FLASHER_EXT_MISO, FLASHER_EXT_RESET, (uint8_t *)powerPinsExt, sizeof(powerPinsExt), 8000000);
break;
case 2:
result = zbs->begin(FLASHER_ALT_SS, FLASHER_ALT_CLK, FLASHER_ALT_MOSI, FLASHER_ALT_MISO, FLASHER_ALT_RESET, 255, 8000000);
result = zbs->begin(FLASHER_ALT_SS, FLASHER_ALT_CLK, FLASHER_ALT_MOSI, FLASHER_ALT_MISO, FLASHER_ALT_RESET, (uint8_t *)powerPinsAlt, sizeof(powerPinsAlt), 8000000);
break;
#endif
default:
@@ -625,6 +636,42 @@ void flashCountDown(uint8_t c) {
}
}
void pinTest() {
uint8_t *pintest;
pintest = (uint8_t *)pinsAP;
for (uint8_t c = 0; c < 8; c++) {
if (pintest[c] != -1) {
pinMode(pintest[c], INPUT_PULLDOWN);
vTaskDelay(10 / portTICK_PERIOD_MS);
if (digitalRead(pintest[c])) {
Serial.printf("Pin %d failed to become low\n", c);
} else {
pinMode(pintest[c], INPUT_PULLUP);
bool pinChange = false;
uint16_t pinTime = 0;
for (uint16_t t = 0; t < 65535; t++) {
if (digitalRead(pintest[c])) {
pinChange = true;
pinTime = t;
break;
}
ets_delay_us(1);
}
if (pinChange) {
Serial.printf("Pin %d went high in %d µS\n", pintest[c], pinTime);
} else {
Serial.printf("Pin %d timeout becoming high\n", pintest[c]);
}
}
}
}
for (uint8_t c = 0; c < 8; c++) {
if (pintest[c] != -1) {
pinMode(pintest[c], INPUT_PULLDOWN);
}
}
}
#ifdef OPENEPAPERLINK_PCB
// perform device flash, save mac, everything
bool doTagFlash() {

View File

@@ -8,6 +8,7 @@
#include "makeimage.h"
#include "serialap.h"
#include "settings.h"
#include "system.h"
#include "tag_db.h"
#ifdef HAS_USB
@@ -19,24 +20,27 @@
#include "udp.h"
#include "web.h"
void timeTask(void* parameter) {
void pinTest();
void delayedStart(void* parameter) {
vTaskDelay(30000 / portTICK_PERIOD_MS);
Serial.println("Resuming content generation");
wsLog("resuming content generation");
config.runStatus = RUNSTATUS_RUN;
esp_reset_reason_t resetReason = esp_reset_reason();
// if (resetReason == ESP_RST_PANIC) config.runStatus = RUNSTATUS_PAUSE;
vTaskDelay(10 / portTICK_PERIOD_MS);
vTaskDelete(NULL);
}
void timeTask(void* parameter) {
wsSendSysteminfo();
while (1) {
time_t now;
time(&now);
tm tm;
if (!getLocalTime(&tm)) {
Serial.println("Waiting for valid time from NTP-server");
} else {
if (now % 5 == 0 || apInfo.state != AP_STATE_ONLINE) {
wsSendSysteminfo();
}
if (now % 300 == 6 && config.runStatus != RUNSTATUS_STOP) saveDB("/current/tagDB.json");
if (apInfo.state == AP_STATE_ONLINE) contentRunner();
}
if (now % 5 == 0 || apInfo.state != AP_STATE_ONLINE || config.runStatus != RUNSTATUS_RUN) wsSendSysteminfo();
if (now % 300 == 6 && config.runStatus != RUNSTATUS_STOP) saveDB("/current/tagDB.json");
if (apInfo.state == AP_STATE_ONLINE) contentRunner();
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
@@ -66,6 +70,7 @@ void setup() {
Serial.begin(115200);
Serial.print(">\n");
pinTest();
#ifdef BOARD_HAS_PSRAM
if (!psramInit()) {
Serial.printf("This build of the AP expects PSRAM, but we couldn't find/init any. Something is terribly wrong here! System halted.");
@@ -124,16 +129,37 @@ void setup() {
rgbIdle();
#endif
loadDB("/current/tagDB.json");
tagDBOwner = xSemaphoreCreateMutex();
// tagDBOwner = xSemaphoreCreateMutex();
xTaskCreate(APTask, "AP Process", 6000, NULL, 2, NULL);
xTaskCreate(webSocketSendProcess, "ws", 2000, NULL, configMAX_PRIORITIES - 10, NULL);
vTaskDelay(10 / portTICK_PERIOD_MS);
config.runStatus = RUNSTATUS_INIT;
xTaskCreate(timeTask, "timed tasks", 12000, NULL, 2, NULL);
init_time();
logStartUp();
esp_reset_reason_t resetReason = esp_reset_reason();
if (resetReason == ESP_RST_PANIC) {
Serial.println("Panic! Pausing content generation for 30 seconds");
config.runStatus = RUNSTATUS_PAUSE;
xTaskCreate(delayedStart, "delaystart", 2000, NULL, 2, NULL);
} else {
config.runStatus = RUNSTATUS_RUN;
}
}
void loop() {
vTaskDelay(10000 / portTICK_PERIOD_MS);
// performDeviceFlash();
while (1) {
// pinTest();
while (1) {
vTaskDelay(10000 / portTICK_PERIOD_MS);
// pinTest();
}
#ifdef OPENEPAPERLINK_PCB
if (extTagConnected()) {
flashCountDown(3);

View File

@@ -37,9 +37,10 @@ void jpg2buffer(String filein, String fileout, imgParam &imageParams) {
#endif
spr.createSprite(w, h);
if (spr.getPointer() == nullptr) {
//no heap space for 8bpp, fallback to 1bpp
wsErr("fallback to 1bpp");
wsErr("low on memory. Fallback to 1bpp");
spr.setColorDepth(1);
spr.setBitmapColor(TFT_WHITE, TFT_BLACK);
imageParams.bpp = 1;
spr.createSprite(w, h);
}
if (spr.getPointer() == nullptr) {
@@ -79,11 +80,12 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
fs::File f_out = LittleFS.open(fileout, "w");
bool dither = true, rotated = false;
bool dither = true;
uint8_t rotate = imageParams.rotate;
long bufw = spr.width(), bufh = spr.height();
if (bufw > bufh && bufw!=400 && bufh!=300) {
rotated = true;
rotate = (rotate + 3) % 4;
bufw = spr.height();
bufh = spr.width();
}
@@ -105,6 +107,7 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
Serial.println("rendering with gray");
}
int num_colors = palette.size();
if (imageParams.bpp == 1) num_colors = 2;
Color color;
Error *error_bufferold = new Error[bufw + 4];
Error *error_buffernew = new Error[bufw + 4];
@@ -113,10 +116,19 @@ void spr2buffer(TFT_eSprite &spr, String &fileout, imgParam &imageParams) {
for (uint16_t y = 0; y < bufh; y++) {
memset(error_buffernew, 0, bufw * sizeof(Error));
for (uint16_t x = 0; x < bufw; x++) {
if (rotated) {
color = Color(spr.readPixel(bufh - 1 - y, x));
} else {
color = Color(spr.readPixel(x, y));
switch (rotate) {
case 0:
color = Color(spr.readPixel(x, y));
break;
case 1:
color = Color(spr.readPixel(y, bufw - 1 - x));
break;
case 2:
color = Color(spr.readPixel(bufw - 1 - x, bufh - 1 - y));
break;
case 3:
color = Color(spr.readPixel(bufh - 1 - y, x));
break;
}
int best_color_index = 0;

View File

@@ -12,6 +12,7 @@
#include "commstructs.h"
#include "serialap.h"
#include "settings.h"
#include "system.h"
#include "tag_db.h"
#include "udp.h"
#include "web.h"
@@ -98,6 +99,7 @@ void prepareDataAvail(uint8_t* data, uint16_t len, uint8_t dataType, uint8_t* ds
taginfo->len = len;
taginfo->expectedNextCheckin = 0;
taginfo->filename = String();
taginfo->dataType = dataType;
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
struct pendingData pending = {0};
@@ -168,11 +170,11 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
time_t now;
time(&now);
time_t last_midnight = now - now % (24 * 60 * 60) + 3 * 3600; // somewhere in the middle of the night
if (taginfo->lastfullupdate < last_midnight || taginfo->hwType == SOLUM_29_UC8151) {
if (taginfo->lastfullupdate < last_midnight || taginfo->hwType == SOLUM_29_UC8151 || taginfo->lut == 1) {
lut = EPD_LUT_DEFAULT; // full update once a day
taginfo->lastfullupdate = now;
}
if (taginfo->hasCustomLUT && taginfo->capabilities & CAPABILITY_SUPPORTS_CUSTOM_LUTS) {
if (taginfo->hasCustomLUT && taginfo->capabilities & CAPABILITY_SUPPORTS_CUSTOM_LUTS && taginfo->lut != 1) {
Serial.println("using custom LUT");
lut = EPD_LUT_OTA;
}
@@ -190,16 +192,18 @@ bool prepareDataAvail(String* filename, uint8_t dataType, uint8_t* dst, uint16_t
time_t now;
time(&now);
taginfo->expectedNextCheckin = now + nextCheckin * 60 + 60;
clearPending(taginfo);
taginfo->filename = *filename;
taginfo->len = filesize;
clearPending(taginfo);
taginfo->dataType = dataType;
taginfo->pending = true;
memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes));
} else {
wsLog("firmware upload pending");
clearPending(taginfo);
taginfo->filename = *filename;
taginfo->len = filesize;
clearPending(taginfo);
taginfo->dataType = dataType;
taginfo->pending = true;
}
@@ -272,9 +276,10 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
}
file.close();
clearPending(taginfo);
taginfo->filename = filename;
taginfo->len = filesize;
clearPending(taginfo);
taginfo->dataType = pending->availdatainfo.dataType;
taginfo->pending = true;
memcpy(taginfo->md5pending, md5bytes, sizeof(md5bytes));
break;
@@ -296,6 +301,7 @@ void prepareExternalDataAvail(struct pendingData* pending, IPAddress remoteIP) {
taginfo->data = new uint8_t[len];
WiFiClient* stream = http.getStreamPtr();
stream->readBytes(taginfo->data, len);
taginfo->dataType = pending->availdatainfo.dataType;
taginfo->pending = true;
taginfo->len = len;
}
@@ -334,7 +340,7 @@ void processBlockRequest(struct espBlockRequest* br) {
// not cached. open file, cache the data
fs::File file = LittleFS.open(taginfo->filename);
if (!file) {
Serial.print("Dunno how this happened... File pending but deleted in the meantime?\n");
Serial.print("No current file. Canceling request\n");
prepareCancelPending(br->src);
return;
}
@@ -463,6 +469,23 @@ void processDataReq(struct espAvailDataReq* eadr, bool local) {
taginfo->lastseen = now;
if (eadr->adr.lastPacketRSSI != 0) {
if (eadr->adr.wakeupReason >= 0xF0) {
if (!taginfo->pending) taginfo->nextupdate = 0;
memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
const char* reason = "";
if (eadr->adr.wakeupReason == WAKEUP_REASON_FIRSTBOOT) reason = "Booting";
else if (eadr->adr.wakeupReason == WAKEUP_REASON_NETWORK_SCAN) reason = "Network scan";
else if (eadr->adr.wakeupReason == WAKEUP_REASON_WDT_RESET) reason = "Watchdog reset";
sprintf(buffer, "%02X%02X%02X%02X%02X%02X%02X%02X %s", eadr->src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0], reason);
logLine(buffer);
}
if (taginfo->batteryMv != eadr->adr.batteryMv) {
sprintf(buffer, "%02X%02X%02X%02X%02X%02X%02X%02X battery went from %.2fV to %.2fV", eadr->src[7], eadr->src[6], eadr->src[5], eadr->src[4], eadr->src[3], eadr->src[2], eadr->src[1], eadr->src[0], static_cast<float>(taginfo->batteryMv) / 1000.0, static_cast<float>(eadr->adr.batteryMv) / 1000.0);
logLine(buffer);
}
taginfo->LQI = eadr->adr.lastPacketLQI;
taginfo->hwType = eadr->adr.hwType;
taginfo->RSSI = eadr->adr.lastPacketRSSI;
@@ -471,11 +494,6 @@ void processDataReq(struct espAvailDataReq* eadr, bool local) {
taginfo->hwType = eadr->adr.hwType;
taginfo->wakeupReason = eadr->adr.wakeupReason;
taginfo->capabilities = eadr->adr.capabilities;
if (eadr->adr.wakeupReason >= 0xF0) {
if (!taginfo->pending) taginfo->nextupdate = 0;
memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
}
}
if (local) {
sprintf(buffer, "<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]);

View File

@@ -9,50 +9,35 @@
#include "soc/soc.h"
#endif
#ifdef SIMPLE_AP
void simpleAPPower(bool state) {
if (FLASHER_AP_POWER >= 0 && FLASHER_AP_POWER2 >= 0) {
pinMode(FLASHER_AP_POWER, INPUT);
pinMode(FLASHER_AP_POWER2, INPUT);
digitalWrite(FLASHER_AP_POWER, state);
digitalWrite(FLASHER_AP_POWER2, state);
pinMode(FLASHER_AP_POWER, OUTPUT);
pinMode(FLASHER_AP_POWER2, OUTPUT);
void simpleAPPower(uint8_t* pin, uint8_t pincount, bool state) {
for (uint8_t c = 0; c < pincount; c++) {
pinMode(pin[c], INPUT);
}
for (uint8_t c = 0; c < pincount; c++) {
#ifdef POWER_HIGH_SIDE_DRIVER
digitalWrite(pin[c], !state);
#else
digitalWrite(pin[c], state);
#endif
}
for (uint8_t c = 0; c < pincount; c++) {
pinMode(pin[c], OUTPUT);
}
}
#endif
#ifdef OPENEPAPERLINK_NANO_AP_PCB
void simpleAPPower(bool state) {
if (FLASHER_AP_POWER >= 0 && FLASHER_AP_POWER2 >= 0 && FLASHER_AP_POWER3 >= 0 && FLASHER_AP_POWER4 >= 0) {
pinMode(FLASHER_AP_POWER, INPUT);
pinMode(FLASHER_AP_POWER2, INPUT);
pinMode(FLASHER_AP_POWER3, INPUT);
pinMode(FLASHER_AP_POWER4, INPUT);
digitalWrite(FLASHER_AP_POWER, state);
digitalWrite(FLASHER_AP_POWER2, state);
digitalWrite(FLASHER_AP_POWER3, state);
digitalWrite(FLASHER_AP_POWER4, state);
pinMode(FLASHER_AP_POWER, OUTPUT);
pinMode(FLASHER_AP_POWER2, OUTPUT);
pinMode(FLASHER_AP_POWER3, OUTPUT);
pinMode(FLASHER_AP_POWER4, OUTPUT);
}
}
#endif
// On the OpenEPaperLink board, there is no in-rush current limiting. The tags that can be connected to the board can have significant capacity, which,
// when drained if the board applies power, will cause the 3v3 rail to sag enough to reset the ESP32. This is obviously not great. To prevent this from happening,
// we ramp up/down the voltage with PWM. Ramping down really is unnecessary, as the board has a resistor to dump the charge into.
void rampTagPower(uint8_t pin, bool up) {
void rampTagPower(uint8_t* pin, bool up) {
#ifdef OPENEPAPERLINK_PCB
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
#endif
if (up) {
ledcSetup(0, 152000, 8); // 141251 okay // 101251 okay
ledcWrite(0, 254);
vTaskDelay(1 / portTICK_PERIOD_MS);
ledcAttachPin(pin, 0);
pinMode(pin, OUTPUT);
ledcAttachPin(pin[0], 0);
pinMode(pin[0], OUTPUT);
vTaskDelay(10 / portTICK_PERIOD_MS);
for (uint8_t c = 254; c != 0xFF; c--) {
ledcWrite(0, c);
@@ -62,23 +47,54 @@ void rampTagPower(uint8_t pin, bool up) {
delayMicroseconds(100);
}
}
digitalWrite(pin, LOW);
ledcDetachPin(pin);
digitalWrite(pin, LOW);
digitalWrite(pin[0], LOW);
ledcDetachPin(pin[0]);
digitalWrite(pin[0], LOW);
} else {
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
ledcSetup(0, 152000, 8); // 141251 okay // 101251 okay
ledcWrite(0, 0);
vTaskDelay(1 / portTICK_PERIOD_MS);
ledcAttachPin(pin[0], 0);
pinMode(pin[0], OUTPUT);
vTaskDelay(10 / portTICK_PERIOD_MS);
for (uint8_t c = 0; c < 0xFF; c++) {
ledcWrite(0, c);
if (c > 250) {
vTaskDelay(2 / portTICK_PERIOD_MS);
} else {
delayMicroseconds(100);
}
}
digitalWrite(pin[0], HIGH);
ledcDetachPin(pin[0]);
digitalWrite(pin[0], HIGH);
}
#ifdef OPENEPAPERLINK_PCB
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1);
#endif
#ifdef SIMPLE_AP
simpleAPPower(false);
delay(500);
simpleAPPower(up);
}
void powerControl(bool powerState, uint8_t* pin, uint8_t pincount) {
if (pin[0] == -1) return;
#ifdef POWER_RAMPING
if (powerState == true) {
#ifdef POWER_HIGH_SIDE_DRIVER
rampTagPower(pin, true);
#else
rampTagPower(pin, false);
#endif
#ifdef OPENEPAPERLINK_NANO_AP_PCB
simpleAPPower(false);
} else {
pinMode(pin[0], OUTPUT);
#ifdef POWER_HIGH_SIDE_DRIVER
digitalWrite(pin[0], HIGH);
#else
digitalWrite(pin[0], LOW);
#endif
}
#else
simpleAPPower(pin, pincount, false);
delay(500);
simpleAPPower(up);
simpleAPPower(pin, pincount, true);
#endif
}

View File

@@ -110,15 +110,18 @@ bool waitCmdReply() {
}
#if (AP_PROCESS_PORT == FLASHER_AP_PORT)
int8_t APpowerPins[] = FLASHER_AP_POWER;
#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)
int8_t APpowerPins[] = FLASHER_EXT_POWER;
#define AP_RESET_PIN FLASHER_EXT_RESET
#define AP_POWER_PIN FLASHER_EXT_POWER
#endif
#if (AP_PROCESS_PORT == FLASHER_ALTRADIO_PORT)
int8_t APpowerPins[] = FLASHER_ALT_POWER;
#define AP_RESET_PIN FLASHER_ALT_RESET
#define AP_POWER_PIN FLASHER_ALT_POWER
#endif
@@ -134,9 +137,9 @@ void APTagReset() {
pinMode(AP_RESET_PIN, OUTPUT);
digitalWrite(AP_RESET_PIN, LOW);
vTaskDelay(50 / portTICK_PERIOD_MS);
rampTagPower(AP_POWER_PIN, false);
powerControl(false, (uint8_t*)APpowerPins, sizeof(APpowerPins));
vTaskDelay(300 / portTICK_PERIOD_MS);
rampTagPower(AP_POWER_PIN, true);
powerControl(true, (uint8_t*)APpowerPins, sizeof(APpowerPins));
vTaskDelay(100 / portTICK_PERIOD_MS);
digitalWrite(AP_RESET_PIN, HIGH);
vTaskDelay(100 / portTICK_PERIOD_MS);
@@ -538,7 +541,7 @@ void ShowAPInfo() {
void notifySegmentedFlash() {
sendAPSegmentedData(apInfo.mac, (String) "Fl ash", 0x0800, false, true);
vTaskDelay(2000 / portTICK_PERIOD_MS);
#if (FLASHER_AP_POWER == -1)
#ifdef POWER_NO_SOFT_POWER
sendAPSegmentedData(apInfo.mac, (String) "If done", 0x0800, false, true);
vTaskDelay(2000 / portTICK_PERIOD_MS);
sendAPSegmentedData(apInfo.mac, (String) "RE boot", 0x0800, false, true);
@@ -547,7 +550,7 @@ void notifySegmentedFlash() {
}
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)
#ifdef POWER_NO_SOFT_POWER
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
@@ -690,7 +693,7 @@ void APTask(void* parameter) {
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.printf("The build of this firmware expects an AP tag with TXD/RXD 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()) {
@@ -708,10 +711,11 @@ void APTask(void* parameter) {
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);
}
#ifndef POWER_NO_SOFT_POWER
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", APpowerPins[0]);
#endif
#ifdef HAS_RGB_LED
showColorPattern(CRGB::Red, CRGB::Yellow, CRGB::Red);
#endif
@@ -729,17 +733,17 @@ void APTask(void* parameter) {
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(" TXD ---------------- %02d\n", FLASHER_AP_RXD);
Serial.printf(" RXD ---------------- %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)
#ifdef POWER_NO_SOFT_POWER
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);
Serial.printf(" POWER ---------------- %02d\n", APpowerPins[0]);
#endif
Serial.println("Please verify your wiring and try again!");
}

View File

@@ -0,0 +1,80 @@
#include "system.h"
#include <Arduino.h>
#include <FS.h>
#include "LittleFS.h"
void init_time() {
struct tm timeinfo;
while (true) {
if (!getLocalTime(&timeinfo)) {
Serial.println("Waiting for valid time from NTP-server");
vTaskDelay(1000 / portTICK_PERIOD_MS);
} else {
break;
}
}
}
void logLine(char* buffer) {
logLine(String(buffer));
}
void logLine(String text) {
time_t now;
time(&now);
char timeStr[24];
strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S ", localtime(&now));
File logFile = LittleFS.open("/log.txt", "a");
if (logFile) {
logFile.print(timeStr);
logFile.println(text);
logFile.close();
}
}
void logStartUp() {
esp_reset_reason_t resetReason = esp_reset_reason();
String logEntry = "Reboot. Reason: ";
switch (resetReason) {
case ESP_RST_POWERON:
logEntry += "Power-on";
break;
case ESP_RST_EXT:
logEntry += "External";
break;
case ESP_RST_SW:
logEntry += "Software";
break;
case ESP_RST_PANIC:
logEntry += "Panic";
break;
case ESP_RST_INT_WDT:
logEntry += "Watchdog";
break;
case ESP_RST_TASK_WDT:
logEntry += "Task Watchdog";
break;
case ESP_RST_WDT:
logEntry += "Other Watchdog";
break;
case ESP_RST_DEEPSLEEP:
logEntry += "Deep Sleep";
break;
case ESP_RST_BROWNOUT:
logEntry += "Brownout";
break;
case ESP_RST_SDIO:
logEntry += "SDIO";
break;
default:
logEntry += "Unknown";
break;
}
logLine(logEntry);
}

View File

@@ -11,7 +11,7 @@
std::vector<tagRecord*> tagDB;
Config config;
SemaphoreHandle_t tagDBOwner;
// SemaphoreHandle_t tagDBOwner;
tagRecord* tagRecord::findByMAC(uint8_t mac[8]) {
for (int16_t c = 0; c < tagDB.size(); c++) {
@@ -117,6 +117,8 @@ void fillNode(JsonObject &tag, tagRecord* &taginfo) {
tag["capabilities"] = taginfo->capabilities;
tag["modecfgjson"] = taginfo->modeConfigJson;
tag["isexternal"] = taginfo->isExternal;
tag["rotate"] = taginfo->rotate;
tag["lut"] = taginfo->lut;
}
void saveDB(String filename) {
@@ -210,6 +212,8 @@ void loadDB(String filename) {
taginfo->capabilities = tag["capabilities"];
taginfo->modeConfigJson = tag["modecfgjson"].as<String>();
taginfo->isExternal = tag["isexternal"].as<bool>();
taginfo->rotate = tag["rotate"] | 0;
taginfo->lut = tag["lut"] | 0;
}
} else {
Serial.print(F("deserializeJson() failed: "));
@@ -251,6 +255,7 @@ uint8_t getTagCount() {
}
void clearPending(tagRecord* taginfo) {
taginfo->filename = String();
if (taginfo->data != nullptr) {
free(taginfo->data);
taginfo->data = nullptr;
@@ -260,13 +265,14 @@ void clearPending(tagRecord* taginfo) {
void initAPconfig() {
LittleFS.begin(true);
DynamicJsonDocument APconfig(150);
DynamicJsonDocument APconfig(500);
File configFile = LittleFS.open("/current/apconfig.json", "r");
if (configFile) {
DeserializationError error = deserializeJson(APconfig, configFile);
if (error) {
configFile.close();
Serial.println("failed to read apconfig.json. Using default config");
Serial.println(error.c_str());
}
configFile.close();
}
@@ -280,7 +286,7 @@ void initAPconfig() {
void saveAPconfig() {
fs::File configFile = LittleFS.open("/current/apconfig.json", "w");
DynamicJsonDocument APconfig(150);
DynamicJsonDocument APconfig(500);
APconfig["channel"] = config.channel;
APconfig["alias"] = config.alias;
APconfig["led"] = config.led;

View File

@@ -36,6 +36,12 @@ void enterConsoleMode() {
xTaskCreate(consoleTask, "consoleTask", 10000, NULL, 2, &consoleTaskHandle);
}
int8_t powerPins[] = FLASHER_AP_POWER;
#ifdef OPENEPAPERLINK_PCB
int8_t powerPins2[] = FLASHER_EXT_POWER;
int8_t powerPins3[] = FLASHER_ALT_POWER;
#endif
void sendFlasherAnswer(uint8_t answer_cmd, uint8_t* ans_buff, uint8_t len) {
uint8_t* answer_buffer = (uint8_t*)calloc(2 + 2 + len + 2, 1);
if (answer_buffer == nullptr) return;
@@ -259,16 +265,17 @@ void processFlasherCommand(struct flasherCommand* cmd) {
spi_speed = 8000000;
}
curspeed = spi_speed;
if (cmd->data[0] & 2) {
temp_buff[0] = zbs->begin(FLASHER_AP_SS, FLASHER_AP_CLK, FLASHER_AP_MOSI, FLASHER_AP_MISO, FLASHER_AP_RESET, FLASHER_AP_POWER, spi_speed);
temp_buff[0] = zbs->begin(FLASHER_AP_SS, FLASHER_AP_CLK, FLASHER_AP_MOSI, FLASHER_AP_MISO, FLASHER_AP_RESET, (uint8_t*)powerPins, spi_speed);
} else if (cmd->data[0] & 4) {
#ifdef OPENEPAPERLINK_PCB
temp_buff[0] = zbs->begin(FLASHER_ALT_SS, FLASHER_ALT_CLK, FLASHER_ALT_MOSI, FLASHER_ALT_MISO, FLASHER_ALT_RESET, 255, spi_speed);
#endif
#ifdef OPENEPAPERLINK_PCB
temp_buff[0] = zbs->begin(FLASHER_ALT_SS, FLASHER_ALT_CLK, FLASHER_ALT_MOSI, FLASHER_ALT_MISO, FLASHER_ALT_RESET, (uint8_t*)powerPins3, spi_speed);
#endif
} else {
#ifdef OPENEPAPERLINK_PCB
temp_buff[0] = zbs->begin(FLASHER_EXT_SS, FLASHER_EXT_CLK, FLASHER_EXT_MOSI, FLASHER_EXT_MISO, FLASHER_EXT_RESET, FLASHER_EXT_POWER, spi_speed);
#endif
#ifdef OPENEPAPERLINK_PCB
temp_buff[0] = zbs->begin(FLASHER_EXT_SS, FLASHER_EXT_CLK, FLASHER_EXT_MOSI, FLASHER_EXT_MISO, FLASHER_EXT_RESET, (uint8_t*)powerPins2, spi_speed);
#endif
}
sendFlasherAnswer(cmd->command, temp_buff, 1);
break;

View File

@@ -319,6 +319,12 @@ void init_web() {
taginfo->modeConfigJson = request->getParam("modecfgjson", true)->value();
taginfo->contentMode = atoi(request->getParam("contentmode", true)->value().c_str());
taginfo->nextupdate = 0;
if (request->hasParam("rotate", true)) {
taginfo->rotate = atoi(request->getParam("rotate", true)->value().c_str());
}
if (request->hasParam("lut", true)) {
taginfo->lut = atoi(request->getParam("lut", true)->value().c_str());
}
// memset(taginfo->md5, 0, 16 * sizeof(uint8_t));
// memset(taginfo->md5pending, 0, 16 * sizeof(uint8_t));
wsSendTaginfo(mac, SYNC_USERCFG);
@@ -332,20 +338,33 @@ void init_web() {
request->send(200, "text/plain", "Ok, saved");
});
server.on("/delete_cfg", HTTP_POST, [](AsyncWebServerRequest *request) {
if (request->hasParam("mac", true)) {
String dst = request->getParam("mac", true)->value();
server.on("/tag_cmd", HTTP_POST, [](AsyncWebServerRequest *request) {
if (request->hasParam("mac", true) && request->hasParam("cmd", true)) {
uint8_t mac[8];
if (hex2mac(dst, mac)) {
wsSendTaginfo(mac, SYNC_DELETE);
if (deleteRecord(mac)) {
request->send(200, "text/plain", "Ok, deleted");
if (hex2mac(request->getParam("mac", true)->value(), mac)) {
tagRecord *taginfo = nullptr;
taginfo = tagRecord::findByMAC(mac);
if (taginfo != nullptr) {
const char *cmdValue = request->getParam("cmd", true)->value().c_str();
if (strcmp(cmdValue, "del") == 0) {
wsSendTaginfo(mac, SYNC_DELETE);
deleteRecord(mac);
}
if (strcmp(cmdValue, "clear") == 0) {
clearPending(taginfo);
memcpy(taginfo->md5pending, taginfo->md5, sizeof(taginfo->md5pending));
wsSendTaginfo(mac, SYNC_TAGSTATUS);
}
if (strcmp(cmdValue, "refresh") == 0) {
updateContent(mac);
}
request->send(200, "text/plain", "Ok, done");
} else {
request->send(200, "text/plain", "Error while saving: mac not found");
request->send(200, "text/plain", "Error: mac not found");
}
}
} else {
request->send(500, "text/plain", "no mac");
request->send(500, "text/plain", "param error");
}
});

View File

@@ -9,7 +9,7 @@
#include "powermgt.h"
uint8_t ZBS_interface::begin(uint8_t SS, uint8_t CLK, uint8_t MOSI, uint8_t MISO, uint8_t RESET, uint8_t POWER, uint32_t spi_speed) {
uint8_t ZBS_interface::begin(uint8_t SS, uint8_t CLK, uint8_t MOSI, uint8_t MISO, uint8_t RESET, uint8_t* POWER, uint8_t powerPins, uint32_t spi_speed) {
_SS_PIN = SS;
_CLK_PIN = CLK;
_MOSI_PIN = MOSI;
@@ -49,9 +49,7 @@ ZBS_interface::~ZBS_interface() {
if(spi)delete spi;
}
void ZBS_interface::set_power(uint8_t state) {
if (_POWER_PIN != 255) {
rampTagPower(_POWER_PIN, state);
}
powerControl(state, _POWER_PIN, _POWER_PINS);
}
void ZBS_interface::enable_debug() {