Skip to content

Commit 117c01a

Browse files
committedSep 5, 2021
feat: improve logging to capture also log messages without js context
1 parent c57ab88 commit 117c01a

File tree

15 files changed

+628
-139
lines changed

15 files changed

+628
-139
lines changed
 

‎components/esp32-javascript/esp32-javascript.c

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ void fileLog(duk_context *ctx, log_level_t level, char *message)
8585

8686
void jslog(log_level_t level, const char *msg, ...)
8787
{
88+
if (level < (5 - LOG_LOCAL_LEVEL))
89+
{
90+
return;
91+
}
92+
8893
char *my_string;
8994
va_list argp;
9095

@@ -115,25 +120,21 @@ void jslog(log_level_t level, const char *msg, ...)
115120
duk_eval(ctx); /* -> [ ... func ] */
116121
duk_push_string(ctx, my_string);
117122
duk_call(ctx, 1);
123+
//clear stack
124+
duk_pop(ctx);
118125
}
119126
else
120127
{
121-
if (level == DEBUG)
122-
{
123-
ESP_LOGD(tag, "No ctx present: %s", my_string);
124-
}
125-
else if (level == INFO)
126-
{
127-
ESP_LOGI(tag, "No ctx present: %s", my_string);
128-
}
129-
else if (level == WARN)
130-
{
131-
ESP_LOGW(tag, "No ctx present: %s", my_string);
132-
}
133-
else
134-
{
135-
ESP_LOGE(tag, "No ctx present: %s", my_string);
136-
}
128+
char *message = malloc(strlen(my_string) + 1);
129+
strcpy(message, my_string);
130+
131+
js_event_t event;
132+
js_eventlist_t events;
133+
events.events_len = 0;
134+
135+
el_create_event(&event, EL_LOG_EVENT_TYPE, level, message);
136+
el_add_event(&events, &event);
137+
el_fire_events(&events);
137138
}
138139
free(my_string);
139140
}
@@ -171,7 +172,7 @@ void IRAM_ATTR el_add_event(js_eventlist_t *events, js_event_t *event)
171172
{
172173
if (events->events_len >= MAX_EVENTS)
173174
{
174-
jslog(ERROR, "Event queue full. Max event number: %d => aborting.", MAX_EVENTS);
175+
ESP_LOGE(tag, "Event list is full. Max event list size: %d => aborting.", MAX_EVENTS);
175176
abort();
176177
}
177178
events->events[events->events_len] = *event;
@@ -182,17 +183,28 @@ void IRAM_ATTR el_fire_events(js_eventlist_t *events)
182183
{
183184
if (DISABLE_EVENTS)
184185
{
185-
jslog(WARN, "Events are disabled. They will never be fired.");
186+
ESP_LOGW(tag, "Events are disabled. They will never be fired.");
186187
}
187188
else
188189
{
189190
if (events->events_len > 0)
190191
{
191-
jslog(DEBUG, "Send %d events to queue...", events->events_len);
192+
ESP_LOGD(tag, "Send %d events to queue...", events->events_len);
192193
int ret = xQueueSendFromISR(el_event_queue, events, NULL);
193194
if (ret != pdTRUE)
194195
{
195-
jslog(ERROR, "Event queue is full... is something blocking the event loop?...aborting.");
196+
ESP_LOGE(tag, "Event queue is full... is something blocking the event loop?...aborting.");
197+
js_eventlist_t devents;
198+
int num = 0;
199+
while (xQueueReceive(el_event_queue, &devents, 0))
200+
{
201+
for (int i = 0; i < devents.events_len; i++)
202+
{
203+
ESP_LOGE(tag, "Events num %i, event idx %i, type %i, status %i", num, i, devents.events[i].type, devents.events[i].status);
204+
}
205+
num++;
206+
}
207+
196208
abort();
197209
}
198210
}
@@ -401,26 +413,30 @@ static duk_ret_t el_suspend(duk_context *ctx)
401413
js_eventlist_t events;
402414

403415
jslog(DEBUG, "Waiting for events...");
404-
405-
xQueueReceive(el_event_queue, &events, portMAX_DELAY);
406-
407-
jslog(DEBUG, "Receiving %d events.", events.events_len);
408-
409416
int arr_idx = duk_push_array(ctx);
410-
for (int i = 0; i < events.events_len; i++)
417+
int arrsize = 0;
418+
TickType_t timeout = portMAX_DELAY;
419+
while (xQueueReceive(el_event_queue, &events, timeout) == pdTRUE)
411420
{
412-
duk_idx_t obj_idx = duk_push_object(ctx);
421+
timeout = 0; // set timeout to 0 to not wait in while loop if there are no more events available
422+
for (int i = 0; i < events.events_len; i++)
423+
{
424+
duk_idx_t obj_idx = duk_push_object(ctx);
413425

414-
duk_push_int(ctx, events.events[i].type);
415-
duk_put_prop_string(ctx, obj_idx, "type");
416-
duk_push_int(ctx, events.events[i].status);
417-
duk_put_prop_string(ctx, obj_idx, "status");
418-
duk_push_int(ctx, (int)events.events[i].fd);
419-
duk_put_prop_string(ctx, obj_idx, "fd");
426+
duk_push_int(ctx, events.events[i].type);
427+
duk_put_prop_string(ctx, obj_idx, "type");
428+
duk_push_int(ctx, events.events[i].status);
429+
duk_put_prop_string(ctx, obj_idx, "status");
430+
duk_push_int(ctx, (int)events.events[i].fd);
431+
duk_put_prop_string(ctx, obj_idx, "fd");
420432

421-
duk_put_prop_index(ctx, arr_idx, i);
433+
duk_put_prop_index(ctx, arr_idx, arrsize);
434+
arrsize++;
435+
}
422436
}
423437

438+
jslog(DEBUG, "Received %d events.", arrsize);
439+
424440
return 1;
425441
}
426442

@@ -788,6 +804,14 @@ duk_ret_t el_partition_write(duk_context *ctx)
788804
}
789805
}
790806

807+
duk_ret_t el_readAndFreeString(duk_context *ctx)
808+
{
809+
char *str = duk_to_int(ctx, 0);
810+
duk_push_string(ctx, str);
811+
free(str);
812+
return 1;
813+
}
814+
791815
void duktape_task(void *ignore)
792816
{
793817
spiramAvailable = spiramAvail();
@@ -901,6 +925,15 @@ void duktape_task(void *ignore)
901925
duk_push_c_function(ctx, el_find_partition, 1 /*nargs*/);
902926
duk_put_global_string(ctx, "el_find_partition");
903927

928+
duk_push_int(ctx, EL_TIMER_EVENT_TYPE);
929+
duk_put_global_string(ctx, "EL_TIMER_EVENT_TYPE");
930+
931+
duk_push_int(ctx, EL_LOG_EVENT_TYPE);
932+
duk_put_global_string(ctx, "EL_LOG_EVENT_TYPE");
933+
934+
duk_push_c_function(ctx, el_readAndFreeString, 1 /*nargs*/);
935+
duk_put_global_string(ctx, "el_readAndFreeString");
936+
904937
loadUrlPolyfill(ctx);
905938

906939
#define ESP32_JAVASCRIPT_EXTERN ESP32_JAVASCRIPT_EXTERN_REGISTER
@@ -924,7 +957,7 @@ int esp32_javascript_init()
924957
esp_log_level_set("*", ESP_LOG_ERROR);
925958
esp_log_level_set("wifi", ESP_LOG_WARN);
926959
esp_log_level_set("dhcpc", ESP_LOG_WARN);
927-
esp_log_level_set(tag, ESP_LOG_DEBUG);
960+
esp_log_level_set(tag, LOG_LOCAL_LEVEL);
928961

929962
nvs_flash_init();
930963
tcpip_adapter_init();

‎components/esp32-javascript/include/esp32-javascript.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ extern "C"
4444
void *fd;
4545
} js_event_t;
4646

47-
#define MAX_EVENTS 4
47+
#define MAX_EVENTS 8
4848
typedef struct
4949
{
5050
js_event_t events[MAX_EVENTS];

‎components/esp32-javascript/modules/esp32-javascript/configserver.js

Lines changed: 105 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ SOFTWARE.
3636
*/
3737
var configManager = require("./config");
3838
var boot_1 = require("./boot");
39+
var native_ota_1 = require("./native-ota");
3940
var http_1 = require("./http");
41+
var filelogging_1 = require("./filelogging");
4042
var schema = {
4143
access: {
4244
type: "object",
@@ -117,14 +119,14 @@ function redirect(res, location) {
117119
res.end();
118120
}
119121
exports.redirect = redirect;
120-
function page(res, headline, text, cb) {
122+
function page(res, headline, text, cb, additionalHeadTags) {
121123
if (cb) {
122124
// register callback
123125
res.on("end", cb);
124126
}
125127
res.setStatus(200);
126128
res.headers.set("content-type", "text/html");
127-
res.write("<!doctype html><html><head><title>esp32-javascript</title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n <style>\n body {\n font-family: monospace;\n font-size: 13pt;\n }\n .input {\n font-family: monospace;\n font-size: 13pt;\n }\n .fill {\n width: calc(100% - 146px);\n }\n .full {\n width: calc(100% - 16px);\n }\n .txt {\n height: 100px;\n }\n .formlabel {\n display: inline-block;\n width: 130px;\n }\n .formpad {\n padding: 8px;\n }\n .green {\n color: green;\n }\n .red {\n color: red;\n }\n </style>\n \n </head>\n <body><div><div><div><h1>" + headline + "</h1>");
129+
res.write("<!doctype html><html><head><title>esp32-javascript</title>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n <style>\n body {\n font-family: monospace;\n font-size: 13pt;\n }\n .input {\n font-family: monospace;\n font-size: 13pt;\n }\n .fill {\n width: calc(100% - 146px);\n }\n .full {\n width: calc(100% - 16px);\n }\n .txt {\n height: 100px;\n }\n .formlabel {\n display: inline-block;\n width: 130px;\n }\n .formpad {\n padding: 8px;\n }\n .green {\n color: green;\n }\n .red {\n color: red;\n }\n .inline-form {\n display: inline;\n }\n .blink {\n animation: blinkanimation 1s linear infinite;\n }\n @keyframes blinkanimation {\n 50% {\n opacity: 0;\n }\n }\n </style>\n " + (additionalHeadTags ? additionalHeadTags : "") + "\n </head>\n <body><div><div><div><h1>" + headline + "</h1>");
128130
if (Array.isArray(text)) {
129131
res.write(text.join(""));
130132
}
@@ -133,6 +135,32 @@ function page(res, headline, text, cb) {
133135
}
134136
res.end("</div></div></div></body></html>\r\n\r\n");
135137
}
138+
function getLogFileList() {
139+
global.el_flushLogBuffer();
140+
var logFileList = [];
141+
try {
142+
var list = listDir(filelogging_1.FILE_LOGGING_DIRECTORY).sort();
143+
list.forEach(function (f) {
144+
try {
145+
logFileList.push({
146+
filename: f,
147+
size: fileSize(filelogging_1.FILE_LOGGING_DIRECTORY + "/" + f),
148+
});
149+
}
150+
catch (error) {
151+
console.error(error);
152+
}
153+
});
154+
}
155+
catch (_) {
156+
// ignore
157+
}
158+
return logFileList;
159+
}
160+
var upgradeStatus = {
161+
status: "idle",
162+
message: "",
163+
};
136164
var successMessage = "";
137165
var errorMessage = "";
138166
function startConfigServer() {
@@ -168,6 +196,8 @@ function startConfigServer() {
168196
var config_1 = http_1.parseQueryStr(req.body);
169197
storedConfig.wifi.ssid = config_1.ssid;
170198
storedConfig.wifi.password = config_1.password;
199+
storedConfig.access.username = config_1.username;
200+
storedConfig.access.password = config_1.userpass;
171201
storedConfig.ota.url = config_1.url;
172202
storedConfig.ota.offline = config_1.offline === "true";
173203
storedConfig.ota.script = config_1.script;
@@ -179,18 +209,15 @@ function startConfigServer() {
179209
}
180210
}
181211
var config = configManager.config;
182-
var logFileSize = void 0;
183-
try {
184-
logFileSize = fileSize("/data/logs.txt");
185-
}
186-
catch (_) {
187-
// ignore
188-
}
189212
page(res, "Setup", "" + (successMessage
190213
? "<div class=\"formpad green\">" + successMessage + "</div>"
191-
: "") + (errorMessage
192-
? "<div class=\"formpad red\">Saving failed. Error message: " + errorMessage + "</div>"
193-
: "") + "<form action=\"/setup\" method=\"post\">\n <div class=\"formpad\"><label for=\"ssid\" class=\"formlabel\">SSID</label><input type=\"text\" name=\"ssid\" class=\"fill input\" value=\"" + (((_a = config.wifi) === null || _a === void 0 ? void 0 : _a.ssid) || "") + "\" /></div>\n <div class=\"formpad\"><label for=\"password\" class=\"formlabel\">Password</label><input type=\"text\" name=\"password\" class=\"fill input\" value=\"" + (((_b = config.wifi) === null || _b === void 0 ? void 0 : _b.password) || "") + "\" /></div>\n <div class=\"formpad\"><label for=\"url\" class=\"formlabel\">JS file url</label><input type=\"text\" name=\"url\" class=\"fill input\" value=\"" + (((_c = config.ota) === null || _c === void 0 ? void 0 : _c.url) || "") + "\" /></div>\n <div class=\"formpad\"><label for=\"offline\"><input type=\"checkbox\" name=\"offline\" value=\"true\" " + (((_d = config.ota) === null || _d === void 0 ? void 0 : _d.offline) ? "checked" : "") + "/> Offline Mode</label></div>\n <label for=\"script\" class=\"formpad\">Offline Script</label><div class=\"formpad\"><textarea name=\"script\" class=\"full input txt\">" + (((_e = config.ota) === null || _e === void 0 ? void 0 : _e.script) || "") + "</textarea></div>\n <div class=\"formpad\"><input type=\"submit\" value=\"Save\" class=\"formpad input\"/></div></form>\n <h1>Logs</h1>\n <div class=\"formpad\">\n Log size: " + (logFileSize ? Math.floor(logFileSize / 1024) : "?") + " kB\n </div>\n <form action=\"/logs\" method=\"get\"><div class=\"formpad\"><input type=\"submit\" value=\"Show Logs\" class=\"formpad input\"/></div></form>\n <form action=\"/deletelogs\" method=\"get\"><div class=\"formpad\"><input type=\"submit\" value=\"Delete Logs\" class=\"formpad input\"/></div></form>\n <h1>Request restart</h1>\n <form action=\"/restart\" method=\"post\"><div class=\"formpad\"><input type=\"submit\" value=\"Restart\" class=\"formpad input\"/></div></form>\n <h1>Uptime</h1>\n <div class=\"formpad\">\n Boot time: " + boot_1.getBootTime() + "\n </div>\n <div class=\"formpad\">\n Uptime (hours): " + Math.floor((Date.now() - boot_1.getBootTime().getTime()) / 10 / 60 / 60) /
214+
: "") + (errorMessage ? "<div class=\"formpad red\">" + errorMessage + "</div>" : "") + "<h2>Configuration</h2><h3>Wifi</h3><form action=\"/setup\" method=\"post\">\n <div class=\"formpad\"><label for=\"ssid\" class=\"formlabel\">SSID</label><input type=\"text\" name=\"ssid\" class=\"fill input\" value=\"" + (((_a = config.wifi) === null || _a === void 0 ? void 0 : _a.ssid) || "") + "\" /></div>\n <div class=\"formpad\"><label for=\"password\" class=\"formlabel\">Password</label><input type=\"text\" name=\"password\" class=\"fill input\" value=\"" + (((_b = config.wifi) === null || _b === void 0 ? void 0 : _b.password) || "") + "\" /></div>\n <h3>Basic authentication</h3>\n <div class=\"formpad\"><label for=\"username\" class=\"formlabel\">Username</label><input type=\"text\" name=\"username\" class=\"fill input\" value=\"" + config.access.username + "\" /></div>\n <div class=\"formpad\"><label for=\"userpass\" class=\"formlabel\">Password</label><input type=\"text\" name=\"userpass\" class=\"fill input\" value=\"" + config.access.password + "\" /></div>\n <h3>JavaScript OTA</h3><div class=\"formpad\"><label for=\"url\" class=\"formlabel\">JS file url</label><input type=\"text\" name=\"url\" class=\"fill input\" value=\"" + (((_c = config.ota) === null || _c === void 0 ? void 0 : _c.url) || "") + "\" /></div>\n <div class=\"formpad\"><label for=\"offline\"><input type=\"checkbox\" name=\"offline\" value=\"true\" " + (((_d = config.ota) === null || _d === void 0 ? void 0 : _d.offline) ? "checked" : "") + "/> Offline Mode</label></div>\n <label for=\"script\" class=\"formpad\">Offline Script</label><div class=\"formpad\"><textarea name=\"script\" class=\"full input txt\">" + (((_e = config.ota) === null || _e === void 0 ? void 0 : _e.script) || "") + "</textarea></div>\n <div class=\"formpad\"><input type=\"submit\" value=\"Save\" class=\"formpad input\"/></div></form>\n <h2>Logs</h2>\n <div class=\"formpad\">\n <p>\n Showing last " + filelogging_1.LOG_FILE_NUM_LIMIT + " log files, with each having maximum of " + filelogging_1.LOG_FILE_SIZE_LIMIT / 1024 + " kB data.<br/>\n </p>\n " + getLogFileList()
215+
.map(function (e) {
216+
return e.filename + " (" + (e.size === undefined ? "?" : Math.floor(e.size / 1024)) + " kB) <form action=\"/viewlog\" method=\"post\" class=\"inline-form\"><button class=\"input\" type=\"submit\" name=\"file\" value=\"" + filelogging_1.FILE_LOGGING_DIRECTORY + "/" + e.filename + "\">View</button></form> <form action=\"/deletelog\" method=\"post\" class=\"inline-form\"><button class=\"input\" type=\"submit\" name=\"file\" value=\"" + filelogging_1.FILE_LOGGING_DIRECTORY + "/" + e.filename + "\">Delete</button></form><br />";
217+
})
218+
.join("") + "\n </form>\n </div>\n \n <h2>Native OTA Upgrade</h2>\n <form action=\"/native-ota\" method=\"post\" class=\"formpad\">\n <div class=\"formpad\"><label for=\"appbin\" class=\"formlabel\">URL to app binary</label><input type=\"text\" name=\"appbin\" class=\"fill input\" value=\"\" /></div>\n <div class=\"formpad\"><label for=\"modulesbin\" class=\"formlabel\">URL to modules binary</label><input type=\"text\" name=\"modulesbin\" class=\"fill input\" value=\"\" /></div>\n <div class=\"formpad\"><input type=\"submit\" value=\"Upgrade\" class=\"formpad input\" " + (upgradeStatus.status === "inprogress" ? "disabled" : "") + "/> " + (upgradeStatus.status !== "idle"
219+
? '<a href="/native-ota">Upgrade status</a>'
220+
: "") + "</div>\n </form>\n\n <h2>Request restart</h2>\n <form action=\"/restart\" method=\"post\"><div class=\"formpad\"><input type=\"submit\" value=\"Restart\" class=\"formpad input\"/></div></form>\n <h2>Uptime</h2>\n <div class=\"formpad\">\n Boot time: " + boot_1.getBootTime() + "\n </div>\n <div class=\"formpad\">\n Uptime (hours): " + Math.floor((Date.now() - boot_1.getBootTime().getTime()) / 10 / 60 / 60) /
194221
100 + "<br />\n </div>\n <div class=\"formpad\">\n Boot time is only available if a valid 'JS file url' is configured, otherwise it starts at unix epoch (1970).\n </div>");
195222
successMessage = "";
196223
errorMessage = "";
@@ -268,19 +295,77 @@ function startConfigServer() {
268295
}
269296
});
270297
exports.requestHandler.push(function (req, res) {
271-
if (/\/logs(|\?.*)/.exec(req.path)) {
272-
res.setStatus(200);
273-
res.headers.set("Content-type", "text/plain");
274-
global.el_flushLogBuffer();
275-
res.end(readFile("/data/logs.txt"));
298+
if (/\/viewlog/.exec(req.path)) {
299+
var parsed = http_1.parseQueryStr(req.body);
300+
if (parsed.file.indexOf(filelogging_1.FILE_LOGGING_DIRECTORY) !== 0) {
301+
res.setStatus(400, "Invalid supplied filename.");
302+
res.end();
303+
return;
304+
}
305+
try {
306+
var content = readFile(parsed.file);
307+
res.setStatus(200);
308+
res.headers.set("Content-type", "text/plain");
309+
global.el_flushLogBuffer();
310+
res.write(content);
311+
}
312+
catch (_a) {
313+
res.setStatus(404, "Not found");
314+
}
315+
finally {
316+
res.end();
317+
}
276318
}
277319
});
278320
exports.requestHandler.push(function (req, res) {
279-
if (/\/deletelogs(|\?.*)/.exec(req.path)) {
280-
removeFile("/data/logs.txt");
281-
successMessage = "Logs were deleted successfully.";
321+
if (/\/deletelog/.exec(req.path)) {
322+
var parsed = http_1.parseQueryStr(req.body);
323+
if (parsed.file.indexOf(filelogging_1.FILE_LOGGING_DIRECTORY) !== 0) {
324+
res.setStatus(400, "Invalid supplied filename.");
325+
res.end();
326+
return;
327+
}
328+
if (removeFile(parsed.file) >= 0) {
329+
successMessage = "Log file deleted successfully.";
330+
}
331+
else {
332+
errorMessage = "Log file not found.";
333+
}
282334
redirect(res, "/setup");
283335
}
284336
});
337+
exports.requestHandler.push(function (req, res) {
338+
if (/\/native-ota/.exec(req.path)) {
339+
if (req.method === "POST") {
340+
var parsed_1 = http_1.parseQueryStr(req.body);
341+
if (parsed_1.appbin && parsed_1.modulesbin) {
342+
if (upgradeStatus.status !== "inprogress") {
343+
upgradeStatus.status = "inprogress";
344+
upgradeStatus.message = "";
345+
setTimeout(function () {
346+
native_ota_1.upgrade(parsed_1.appbin, parsed_1.modulesbin, function (error) {
347+
upgradeStatus.status = "error";
348+
upgradeStatus.message = error;
349+
}, function () {
350+
upgradeStatus.status = "success";
351+
upgradeStatus.message = "";
352+
});
353+
}, 2000);
354+
}
355+
redirect(res, "/native-ota");
356+
}
357+
}
358+
else {
359+
page(res, "Upgrade", "" + ((upgradeStatus.status === "error" &&
360+
"<div class=\"formpad red\">An error occured while upgrading: " + upgradeStatus.message + "</div>") ||
361+
(upgradeStatus.status === "success" &&
362+
"<div class=\"formpad green\">Upgrade was successful. Please restart to start upgraded firmware.</div>\n <form action=\"/restart\" method=\"post\"><div class=\"formpad\"><input type=\"submit\" value=\"Restart\" class=\"formpad input\"/></div></form>") ||
363+
(upgradeStatus.status === "inprogress" &&
364+
"<div class=\"formpad\">Upgrade in progress<span class=\"blink\">...</span><br/>Page refreshes automatically.</div>") ||
365+
(upgradeStatus.status === "idle" &&
366+
"<div class=\"formpad red\">No upgrade started.</div>")), undefined, '<meta http-equiv="refresh" content="20">');
367+
}
368+
}
369+
});
285370
}
286371
exports.startConfigServer = startConfigServer;

‎components/esp32-javascript/modules/esp32-javascript/configserver.ts

Lines changed: 173 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,19 @@ SOFTWARE.
2323
*/
2424
import configManager = require("./config");
2525
import { getBootTime } from "./boot";
26+
import { upgrade } from "./native-ota";
2627

2728
import {
2829
httpServer,
2930
Esp32JsResponse,
3031
parseQueryStr,
3132
Esp32JsRequest,
3233
} from "./http";
34+
import {
35+
FILE_LOGGING_DIRECTORY,
36+
LOG_FILE_NUM_LIMIT,
37+
LOG_FILE_SIZE_LIMIT,
38+
} from "./filelogging";
3339

3440
let schema = {
3541
access: {
@@ -120,7 +126,8 @@ function page(
120126
res: Esp32JsResponse,
121127
headline: string,
122128
text: string | string[],
123-
cb?: () => void
129+
cb?: () => void,
130+
additionalHeadTags?: string
124131
) {
125132
if (cb) {
126133
// register callback
@@ -163,8 +170,19 @@ function page(
163170
.red {
164171
color: red;
165172
}
173+
.inline-form {
174+
display: inline;
175+
}
176+
.blink {
177+
animation: blinkanimation 1s linear infinite;
178+
}
179+
@keyframes blinkanimation {
180+
50% {
181+
opacity: 0;
182+
}
183+
}
166184
</style>
167-
185+
${additionalHeadTags ? additionalHeadTags : ""}
168186
</head>
169187
<body><div><div><div><h1>${headline}</h1>`);
170188
if (Array.isArray(text)) {
@@ -175,6 +193,35 @@ function page(
175193
res.end("</div></div></div></body></html>\r\n\r\n");
176194
}
177195

196+
function getLogFileList() {
197+
global.el_flushLogBuffer();
198+
const logFileList: { filename: string; size: number | undefined }[] = [];
199+
try {
200+
const list = listDir(FILE_LOGGING_DIRECTORY).sort();
201+
list.forEach((f) => {
202+
try {
203+
logFileList.push({
204+
filename: f,
205+
size: fileSize(`${FILE_LOGGING_DIRECTORY}/${f}`),
206+
});
207+
} catch (error) {
208+
console.error(error);
209+
}
210+
});
211+
} catch (_) {
212+
// ignore
213+
}
214+
return logFileList;
215+
}
216+
217+
const upgradeStatus: {
218+
status: "idle" | "error" | "success" | "inprogress";
219+
message: string;
220+
} = {
221+
status: "idle",
222+
message: "",
223+
};
224+
178225
let successMessage = "";
179226
let errorMessage = "";
180227
export function startConfigServer(): void {
@@ -218,6 +265,8 @@ export function startConfigServer(): void {
218265
const config = parseQueryStr(req.body);
219266
storedConfig.wifi.ssid = config.ssid;
220267
storedConfig.wifi.password = config.password;
268+
storedConfig.access.username = config.username;
269+
storedConfig.access.password = config.userpass;
221270
storedConfig.ota.url = config.url;
222271
storedConfig.ota.offline = config.offline === "true";
223272
storedConfig.ota.script = config.script;
@@ -230,12 +279,6 @@ export function startConfigServer(): void {
230279
}
231280
const config = configManager.config;
232281

233-
let logFileSize: number | undefined;
234-
try {
235-
logFileSize = fileSize("/data/logs.txt");
236-
} catch (_) {
237-
// ignore
238-
}
239282
page(
240283
res,
241284
"Setup",
@@ -244,17 +287,22 @@ export function startConfigServer(): void {
244287
? `<div class="formpad green">${successMessage}</div>`
245288
: ""
246289
}${
247-
errorMessage
248-
? `<div class="formpad red">Saving failed. Error message: ${errorMessage}</div>`
249-
: ""
250-
}<form action="/setup" method="post">
290+
errorMessage ? `<div class="formpad red">${errorMessage}</div>` : ""
291+
}<h2>Configuration</h2><h3>Wifi</h3><form action="/setup" method="post">
251292
<div class="formpad"><label for="ssid" class="formlabel">SSID</label><input type="text" name="ssid" class="fill input" value="${
252293
config.wifi?.ssid || ""
253294
}" /></div>
254295
<div class="formpad"><label for="password" class="formlabel">Password</label><input type="text" name="password" class="fill input" value="${
255296
config.wifi?.password || ""
256297
}" /></div>
257-
<div class="formpad"><label for="url" class="formlabel">JS file url</label><input type="text" name="url" class="fill input" value="${
298+
<h3>Basic authentication</h3>
299+
<div class="formpad"><label for="username" class="formlabel">Username</label><input type="text" name="username" class="fill input" value="${
300+
config.access.username
301+
}" /></div>
302+
<div class="formpad"><label for="userpass" class="formlabel">Password</label><input type="text" name="userpass" class="fill input" value="${
303+
config.access.password
304+
}" /></div>
305+
<h3>JavaScript OTA</h3><div class="formpad"><label for="url" class="formlabel">JS file url</label><input type="text" name="url" class="fill input" value="${
258306
config.ota?.url || ""
259307
}" /></div>
260308
<div class="formpad"><label for="offline"><input type="checkbox" name="offline" value="true" ${
@@ -264,15 +312,43 @@ export function startConfigServer(): void {
264312
config.ota?.script || ""
265313
}</textarea></div>
266314
<div class="formpad"><input type="submit" value="Save" class="formpad input"/></div></form>
267-
<h1>Logs</h1>
315+
<h2>Logs</h2>
268316
<div class="formpad">
269-
Log size: ${logFileSize ? Math.floor(logFileSize / 1024) : "?"} kB
317+
<p>
318+
Showing last ${LOG_FILE_NUM_LIMIT} log files, with each having maximum of ${
319+
LOG_FILE_SIZE_LIMIT / 1024
320+
} kB data.<br/>
321+
</p>
322+
${getLogFileList()
323+
.map((e) => {
324+
return `${e.filename} (${
325+
e.size === undefined ? "?" : Math.floor(e.size / 1024)
326+
} kB) <form action="/viewlog" method="post" class="inline-form"><button class="input" type="submit" name="file" value="${FILE_LOGGING_DIRECTORY}/${
327+
e.filename
328+
}">View</button></form> <form action="/deletelog" method="post" class="inline-form"><button class="input" type="submit" name="file" value="${FILE_LOGGING_DIRECTORY}/${
329+
e.filename
330+
}">Delete</button></form><br />`;
331+
})
332+
.join("")}
333+
</form>
270334
</div>
271-
<form action="/logs" method="get"><div class="formpad"><input type="submit" value="Show Logs" class="formpad input"/></div></form>
272-
<form action="/deletelogs" method="get"><div class="formpad"><input type="submit" value="Delete Logs" class="formpad input"/></div></form>
273-
<h1>Request restart</h1>
335+
336+
<h2>Native OTA Upgrade</h2>
337+
<form action="/native-ota" method="post" class="formpad">
338+
<div class="formpad"><label for="appbin" class="formlabel">URL to app binary</label><input type="text" name="appbin" class="fill input" value="" /></div>
339+
<div class="formpad"><label for="modulesbin" class="formlabel">URL to modules binary</label><input type="text" name="modulesbin" class="fill input" value="" /></div>
340+
<div class="formpad"><input type="submit" value="Upgrade" class="formpad input" ${
341+
upgradeStatus.status === "inprogress" ? "disabled" : ""
342+
}/> ${
343+
upgradeStatus.status !== "idle"
344+
? '<a href="/native-ota">Upgrade status</a>'
345+
: ""
346+
}</div>
347+
</form>
348+
349+
<h2>Request restart</h2>
274350
<form action="/restart" method="post"><div class="formpad"><input type="submit" value="Restart" class="formpad input"/></div></form>
275-
<h1>Uptime</h1>
351+
<h2>Uptime</h2>
276352
<div class="formpad">
277353
Boot time: ${getBootTime()}
278354
</div>
@@ -456,19 +532,89 @@ export function startConfigServer(): void {
456532
});
457533

458534
requestHandler.push((req, res) => {
459-
if (/\/logs(|\?.*)/.exec(req.path)) {
460-
res.setStatus(200);
461-
res.headers.set("Content-type", "text/plain");
462-
global.el_flushLogBuffer();
463-
res.end(readFile("/data/logs.txt"));
535+
if (/\/viewlog/.exec(req.path)) {
536+
const parsed = parseQueryStr(req.body);
537+
if (parsed.file.indexOf(FILE_LOGGING_DIRECTORY) !== 0) {
538+
res.setStatus(400, "Invalid supplied filename.");
539+
res.end();
540+
return;
541+
}
542+
try {
543+
const content = readFile(parsed.file);
544+
res.setStatus(200);
545+
res.headers.set("Content-type", "text/plain");
546+
global.el_flushLogBuffer();
547+
res.write(content);
548+
} catch {
549+
res.setStatus(404, "Not found");
550+
} finally {
551+
res.end();
552+
}
464553
}
465554
});
466555

467556
requestHandler.push((req, res) => {
468-
if (/\/deletelogs(|\?.*)/.exec(req.path)) {
469-
removeFile("/data/logs.txt");
470-
successMessage = "Logs were deleted successfully.";
557+
if (/\/deletelog/.exec(req.path)) {
558+
const parsed = parseQueryStr(req.body);
559+
if (parsed.file.indexOf(FILE_LOGGING_DIRECTORY) !== 0) {
560+
res.setStatus(400, "Invalid supplied filename.");
561+
res.end();
562+
return;
563+
}
564+
if (removeFile(parsed.file) >= 0) {
565+
successMessage = "Log file deleted successfully.";
566+
} else {
567+
errorMessage = "Log file not found.";
568+
}
471569
redirect(res, "/setup");
472570
}
473571
});
572+
573+
requestHandler.push((req, res) => {
574+
if (/\/native-ota/.exec(req.path)) {
575+
if (req.method === "POST") {
576+
const parsed = parseQueryStr(req.body);
577+
578+
if (parsed.appbin && parsed.modulesbin) {
579+
if (upgradeStatus.status !== "inprogress") {
580+
upgradeStatus.status = "inprogress";
581+
upgradeStatus.message = "";
582+
setTimeout(() => {
583+
upgrade(
584+
parsed.appbin,
585+
parsed.modulesbin,
586+
(error) => {
587+
upgradeStatus.status = "error";
588+
upgradeStatus.message = error;
589+
},
590+
() => {
591+
upgradeStatus.status = "success";
592+
upgradeStatus.message = "";
593+
}
594+
);
595+
}, 2000);
596+
}
597+
redirect(res, "/native-ota");
598+
}
599+
} else {
600+
page(
601+
res,
602+
"Upgrade",
603+
`${
604+
(upgradeStatus.status === "error" &&
605+
`<div class="formpad red">An error occured while upgrading: ${upgradeStatus.message}</div>`) ||
606+
(upgradeStatus.status === "success" &&
607+
`<div class="formpad green">Upgrade was successful. Please restart to start upgraded firmware.</div>
608+
<form action="/restart" method="post"><div class="formpad"><input type="submit" value="Restart" class="formpad input"/></div></form>`) ||
609+
(upgradeStatus.status === "inprogress" &&
610+
`<div class="formpad">Upgrade in progress<span class="blink">...</span><br/>Page refreshes automatically.</div>`) ||
611+
(upgradeStatus.status === "idle" &&
612+
`<div class="formpad red">No upgrade started.</div>`)
613+
}`,
614+
undefined,
615+
'<meta http-equiv="refresh" content="20">'
616+
);
617+
}
618+
}
619+
});
474620
}

‎components/esp32-javascript/modules/esp32-javascript/esp32-javascript.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ interface Esp32JsWifiConfig {
4141
}
4242
declare function getWifiConfig(): Esp32JsWifiConfig;
4343
declare const EL_WIFI_EVENT_TYPE: number;
44+
declare const EL_TIMER_EVENT_TYPE: number;
45+
declare const EL_LOG_EVENT_TYPE: number;
4446
declare function el_connectWifi(ssid: string, password: string): void;
4547
declare function el_createSoftAp(ssid: string, password: string): void;
4648

@@ -85,6 +87,8 @@ declare function writeFile(path: string, data: string): number;
8587
declare function appendFile(path: string, data: string): number;
8688
declare function removeFile(path: string): number;
8789
declare function fileSize(path: string): number;
90+
declare function listDir(path: string): string[];
91+
declare function mkdir(path: string): void;
8892

8993
// ota
9094
declare function el_ota_begin(): number;
@@ -99,3 +103,8 @@ declare function el_partition_write(
99103
offset: number,
100104
data: Uint8Array
101105
): void;
106+
declare function el_find_partition(name: string): {
107+
_ref: number;
108+
size: number;
109+
};
110+
declare function el_readAndFreeString(ptr: number): string;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
Object.defineProperty(exports, "__esModule", { value: true });
2+
exports.LOG_FILE_NUM_LIMIT = exports.LOG_FILE_SIZE_LIMIT = exports.FILE_LOGGING_DIRECTORY = void 0;
3+
/*
4+
MIT License
5+
6+
Copyright (c) 2021 Marcel Kottmann
7+
8+
Permission is hereby granted, free of charge, to any person obtaining a copy
9+
of this software and associated documentation files (the "Software"), to deal
10+
in the Software without restriction, including without limitation the rights
11+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
copies of the Software, and to permit persons to whom the Software is
13+
furnished to do so, subject to the following conditions:
14+
15+
The above copyright notice and this permission notice shall be included in all
16+
copies or substantial portions of the Software.
17+
18+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+
SOFTWARE.
25+
*/
26+
var stringbuffer_1 = require("./stringbuffer");
27+
exports.FILE_LOGGING_DIRECTORY = "/data/logs";
28+
exports.LOG_FILE_SIZE_LIMIT = 10240;
29+
exports.LOG_FILE_NUM_LIMIT = 10;
30+
var TDIWEF = "TDIWEF";
31+
var NUMBER_PREFIX = "00000000";
32+
var logFileNumber = -1;
33+
function getLogFileNumber() {
34+
var max = -1;
35+
if (logFileNumber < 0) {
36+
var files = listDir(exports.FILE_LOGGING_DIRECTORY);
37+
files.forEach(function (f) {
38+
var m = f.match(/\d+/);
39+
if (m) {
40+
max = Math.max(max, parseInt(m[0], 10));
41+
}
42+
});
43+
logFileNumber = max + 1;
44+
}
45+
var numStr = logFileNumber.toString();
46+
return NUMBER_PREFIX.substr(0, 8 - numStr.length) + numStr;
47+
}
48+
function cleanupOldLogs() {
49+
var files = listDir(exports.FILE_LOGGING_DIRECTORY).sort();
50+
if (files.length > exports.LOG_FILE_NUM_LIMIT) {
51+
for (var i = 0; i < files.length - exports.LOG_FILE_NUM_LIMIT; i++) {
52+
removeFile(exports.FILE_LOGGING_DIRECTORY + "/" + files[i]);
53+
}
54+
}
55+
}
56+
global.el_flushLogBuffer = function () {
57+
var swap = global.logBuffer;
58+
global.logBuffer = new stringbuffer_1.StringBuffer();
59+
var logFile = exports.FILE_LOGGING_DIRECTORY + "/logs-" + getLogFileNumber() + ".txt";
60+
appendFile(logFile, swap.toString());
61+
if (fileSize(logFile) > exports.LOG_FILE_SIZE_LIMIT) {
62+
logFileNumber++;
63+
}
64+
cleanupOldLogs();
65+
};
66+
/**
67+
* This is defines the function to append to buffered file logging.
68+
*/
69+
global.el_appendLogBuffer = function (message, level) {
70+
global.logBuffer = global.logBuffer || new stringbuffer_1.StringBuffer();
71+
var l = TDIWEF.substr(level, 1);
72+
global.logBuffer.append(l + "\t" + new Date() + "\t" + message + "\n");
73+
if (global.logBuffer.length > 1024) {
74+
global.el_flushLogBuffer();
75+
}
76+
};
77+
console.log("File logging initialized successfully.");
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
MIT License
3+
4+
Copyright (c) 2021 Marcel Kottmann
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
23+
*/
24+
import { StringBuffer } from "./stringbuffer";
25+
26+
export const FILE_LOGGING_DIRECTORY = "/data/logs";
27+
export const LOG_FILE_SIZE_LIMIT = 10240;
28+
export const LOG_FILE_NUM_LIMIT = 10;
29+
30+
const TDIWEF = "TDIWEF";
31+
const NUMBER_PREFIX = "00000000";
32+
let logFileNumber = -1;
33+
34+
function getLogFileNumber(): string {
35+
let max = -1;
36+
if (logFileNumber < 0) {
37+
const files = listDir(FILE_LOGGING_DIRECTORY);
38+
files.forEach((f) => {
39+
const m = f.match(/\d+/);
40+
if (m) {
41+
max = Math.max(max, parseInt(m[0], 10));
42+
}
43+
});
44+
logFileNumber = max + 1;
45+
}
46+
const numStr = logFileNumber.toString();
47+
return NUMBER_PREFIX.substr(0, 8 - numStr.length) + numStr;
48+
}
49+
50+
function cleanupOldLogs() {
51+
const files = listDir(FILE_LOGGING_DIRECTORY).sort();
52+
if (files.length > LOG_FILE_NUM_LIMIT) {
53+
for (let i = 0; i < files.length - LOG_FILE_NUM_LIMIT; i++) {
54+
removeFile(`${FILE_LOGGING_DIRECTORY}/${files[i]}`);
55+
}
56+
}
57+
}
58+
59+
global.el_flushLogBuffer = function (): void {
60+
const swap = global.logBuffer;
61+
global.logBuffer = new StringBuffer();
62+
const logFile = `${FILE_LOGGING_DIRECTORY}/logs-${getLogFileNumber()}.txt`;
63+
appendFile(logFile, swap.toString());
64+
if (fileSize(logFile) > LOG_FILE_SIZE_LIMIT) {
65+
logFileNumber++;
66+
}
67+
cleanupOldLogs();
68+
};
69+
70+
/**
71+
* This is defines the function to append to buffered file logging.
72+
*/
73+
global.el_appendLogBuffer = function (message: string, level: number): void {
74+
global.logBuffer = global.logBuffer || new StringBuffer();
75+
const l = TDIWEF.substr(level, 1);
76+
global.logBuffer.append(`${l}\t${new Date()}\t${message}\n`);
77+
if (global.logBuffer.length > 1024) {
78+
global.el_flushLogBuffer();
79+
}
80+
};
81+
82+
console.log("File logging initialized successfully.");

‎components/esp32-javascript/modules/esp32-javascript/index.js

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
Object.defineProperty(exports, "__esModule", { value: true });
21
/*
32
MIT License
43
@@ -22,27 +21,11 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2221
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2322
SOFTWARE.
2423
*/
25-
var stringbuffer_1 = require("./stringbuffer");
24+
Object.defineProperty(exports, "__esModule", { value: true });
2625
console.info("Load global.js (NEW)...");
2726
require("./global.js");
28-
/**
29-
* This is defines the function to append to buffered file logging.
30-
*/
31-
console.info("Loading logging buffer (NEW)...");
32-
var TDIWEF = "TDIWEF";
33-
global.el_flushLogBuffer = function () {
34-
var swap = global.logBuffer;
35-
global.logBuffer = new stringbuffer_1.StringBuffer();
36-
appendFile("/data/logs.txt", swap.toString());
37-
};
38-
global.el_appendLogBuffer = function (message, level) {
39-
global.logBuffer = global.logBuffer || new stringbuffer_1.StringBuffer();
40-
var l = TDIWEF.substr(level, 1);
41-
global.logBuffer.append(l + "\t" + new Date() + "\t" + message + "\n");
42-
if (global.logBuffer.length > 1024) {
43-
global.el_flushLogBuffer();
44-
}
45-
};
27+
console.info("Loading file logging buffer (NEW)...");
28+
require("./filelogging");
4629
console.info("Importing http (NEW)...");
4730
var http = require("./http");
4831
console.info("Importing boot (NEW)...");

‎components/esp32-javascript/modules/esp32-javascript/index.ts

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222
SOFTWARE.
2323
*/
24-
import { StringBuffer } from "./stringbuffer";
2524

2625
console.info("Load global.js (NEW)...");
2726
require("./global.js");
2827

29-
/**
30-
* This is defines the function to append to buffered file logging.
31-
*/
32-
console.info("Loading logging buffer (NEW)...");
33-
const TDIWEF = "TDIWEF";
34-
global.el_flushLogBuffer = function (): void {
35-
const swap = global.logBuffer;
36-
global.logBuffer = new StringBuffer();
37-
appendFile("/data/logs.txt", swap.toString());
38-
};
39-
global.el_appendLogBuffer = function (message: string, level: number): void {
40-
global.logBuffer = global.logBuffer || new StringBuffer();
41-
const l = TDIWEF.substr(level, 1);
42-
global.logBuffer.append(`${l}\t${new Date()}\t${message}\n`);
43-
if (global.logBuffer.length > 1024) {
44-
global.el_flushLogBuffer();
45-
}
46-
};
28+
console.info("Loading file logging buffer (NEW)...");
29+
require("./filelogging");
4730

4831
console.info("Importing http (NEW)...");
4932
import http = require("./http");

‎components/esp32-javascript/modules/esp32-js-eventloop/index.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ function el_select_next() {
6767
var _loop_1 = function (evid) {
6868
var evt = events[evid];
6969
console.debug("HANDLE EVENT: " + JSON.stringify(evt));
70-
if (evt.type === 0) {
70+
if (evt.type === EL_TIMER_EVENT_TYPE) {
7171
//TIMER EVENT
7272
var nextTimer = null;
7373
for (var timerIdx = 0; timerIdx < timers.length; timerIdx++) {
@@ -84,6 +84,28 @@ function el_select_next() {
8484
JSON.stringify(timers));
8585
}
8686
}
87+
else if (evt.type === EL_LOG_EVENT_TYPE) {
88+
//LOG EVENT
89+
var logevent_1 = evt;
90+
collected.push(function () {
91+
var logfunction = console.log;
92+
switch (logevent_1.status) {
93+
case 1:
94+
logfunction = console.debug;
95+
break;
96+
case 2:
97+
logfunction = console.info;
98+
break;
99+
case 3:
100+
logfunction = console.warn;
101+
break;
102+
case 4:
103+
logfunction = console.error;
104+
break;
105+
}
106+
logfunction(el_readAndFreeString(logevent_1.fd));
107+
});
108+
}
87109
else {
88110
var eventHandled_1 = false;
89111
if (exports.afterSuspendHandlers) {

‎components/esp32-javascript/modules/esp32-js-eventloop/index.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ type Esp32JsEventHandler = (
3838
errorhandler =
3939
typeof errorhandler === "undefined"
4040
? function (error) {
41-
console.error("Uncaught error:");
42-
console.error(error.stack || error);
43-
}
41+
console.error("Uncaught error:");
42+
console.error(error.stack || error);
43+
}
4444
: errorhandler;
4545

4646
const timers: Esp32JsTimer[] = [];
@@ -111,7 +111,7 @@ function el_select_next() {
111111
for (let evid = 0; evid < events.length; evid++) {
112112
const evt = events[evid];
113113
console.debug("HANDLE EVENT: " + JSON.stringify(evt));
114-
if (evt.type === 0) {
114+
if (evt.type === EL_TIMER_EVENT_TYPE) {
115115
//TIMER EVENT
116116
let nextTimer = null;
117117
for (let timerIdx = 0; timerIdx < timers.length; timerIdx++) {
@@ -124,11 +124,32 @@ function el_select_next() {
124124
//throw Error('UNKNOWN TIMER HANDLE!!!');
125125
console.warn(
126126
"UNKNOWN TIMER HANDLE:" +
127-
JSON.stringify(evt) +
128-
";" +
129-
JSON.stringify(timers)
127+
JSON.stringify(evt) +
128+
";" +
129+
JSON.stringify(timers)
130130
);
131131
}
132+
} else if (evt.type === EL_LOG_EVENT_TYPE) {
133+
//LOG EVENT
134+
const logevent = evt;
135+
collected.push(() => {
136+
let logfunction = console.log;
137+
switch (logevent.status) {
138+
case 1:
139+
logfunction = console.debug;
140+
break;
141+
case 2:
142+
logfunction = console.info;
143+
break;
144+
case 3:
145+
logfunction = console.warn;
146+
break;
147+
case 4:
148+
logfunction = console.error;
149+
break;
150+
}
151+
logfunction(el_readAndFreeString(logevent.fd));
152+
});
132153
} else {
133154
let eventHandled = false;
134155
if (afterSuspendHandlers) {
@@ -152,7 +173,7 @@ function el_select_next() {
152173
export function start(): void {
153174
// eslint-disable-next-line @typescript-eslint/ban-types
154175
let nextfuncs: Function[] = [main];
155-
for (; ;) {
176+
for (;;) {
156177
if (Array.isArray(nextfuncs)) {
157178
nextfuncs.forEach(function (nf) {
158179
if (typeof nf === "function") {

‎components/spiffs-events/spiffs-events.c

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ SOFTWARE.
2323
*/
2424

2525
#include <stdio.h>
26+
#include <dirent.h>
2627
#include <string.h>
2728
#include <sys/unistd.h>
2829
#include <sys/stat.h>
@@ -34,6 +35,7 @@ SOFTWARE.
3435
#include "esp32-javascript.h"
3536
#include "esp_ota_ops.h"
3637
#include "esp_partition.h"
38+
#include <errno.h>
3739

3840
void initFilesystem(const char *label, const char *basePath)
3941
{
@@ -84,7 +86,7 @@ long fileSize(const char *path)
8486
FILE *f = fopen(path, "r");
8587
if (f == NULL)
8688
{
87-
jslog(ERROR, "Failed to open file to get filesize");
89+
jslog(ERROR, "Failed to open file '%s' to get filesize, errno %i", path, errno);
8890
return -1;
8991
}
9092

@@ -130,7 +132,7 @@ int writeFile(const char *path, const char *content)
130132
FILE *f = fopen(path, "w");
131133
if (f == NULL)
132134
{
133-
jslog(ERROR, "Failed to open file for writing");
135+
jslog(ERROR, "Failed to open file '%s'for writing, errno %i", path, errno);
134136
return -1;
135137
}
136138
int result = fputs(content, f);
@@ -144,7 +146,7 @@ int appendFile(const char *path, const char *content)
144146
FILE *f = fopen(path, "a");
145147
if (f == NULL)
146148
{
147-
jslog(ERROR, "Failed to open file for appending");
149+
jslog(ERROR, "Failed to open file '%s' for appending, errno %i", path, errno);
148150
return -1;
149151
}
150152
int result = fputs(content, f);
@@ -158,6 +160,47 @@ bool fileExists(const char *path)
158160
return (stat(path, &buffer) == 0);
159161
}
160162

163+
duk_ret_t el_listDir(duk_context *ctx)
164+
{
165+
const char *path = duk_to_string(ctx, 0);
166+
167+
DIR *dp;
168+
struct dirent *ep;
169+
dp = opendir(path);
170+
171+
if (dp != NULL)
172+
{
173+
duk_idx_t arrayIdx = duk_push_array(ctx);
174+
int i = 0;
175+
while (ep = readdir(dp))
176+
{
177+
duk_push_string(ctx, ep->d_name);
178+
duk_put_prop_index(ctx, arrayIdx, i++);
179+
}
180+
closedir(dp);
181+
return 1;
182+
}
183+
184+
jslog(ERROR, "Failed to list dir '%s', errno %i", path, errno);
185+
186+
return -1;
187+
}
188+
189+
// duk_ret_t el_mkdir(duk_context *ctx)
190+
// {
191+
// const char *path = duk_to_string(ctx, 0);
192+
193+
// int ret = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
194+
195+
// if (ret == -1 && errno != EEXIST)
196+
// {
197+
// jslog(ERROR, "Failed to make directory %s, with errno %i", path, errno);
198+
// return -1;
199+
// }
200+
201+
// return 0;
202+
// }
203+
161204
duk_ret_t el_readFile(duk_context *ctx)
162205
{
163206
const char *path = duk_to_string(ctx, 0);
@@ -233,6 +276,10 @@ void registerBindings(duk_context *ctx)
233276
duk_put_global_string(ctx, "removeFile");
234277
duk_push_c_function(ctx, el_fileSize, 1);
235278
duk_put_global_string(ctx, "fileSize");
279+
duk_push_c_function(ctx, el_listDir, 1);
280+
duk_put_global_string(ctx, "listDir");
281+
// duk_push_c_function(ctx, el_mkdir, 1);
282+
// duk_put_global_string(ctx, "mkdir");
236283
}
237284

238285
void initSpiffs(duk_context *ctx)

‎components/wifi-events/wifi-events.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ static IRAM_ATTR esp_err_t event_handler(void *ctx, system_event_t *sysevent)
105105
}
106106
else
107107
{
108-
jslog(ERROR, "SSID not found %s", wifi_config.sta.ssid);
108+
jslog(DEBUG, "SSID not found %s", wifi_config.sta.ssid);
109109

110110
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
111111
el_create_event(&event, EL_WIFI_EVENT_TYPE, EL_WIFI_STATUS_DISCONNECTED, 0);

‎main/include/esp32-javascript-config.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ SOFTWARE.
2929
#define EL_TIMER_EVENT_TYPE 0
3030
#define EL_WIFI_EVENT_TYPE 1
3131
#define EL_SOCKET_EVENT_TYPE 2
32-
#define RADIO_RECEIVE_EVENT_TYPE 3
32+
#define EL_LOG_EVENT_TYPE 3
33+
#define RADIO_RECEIVE_EVENT_TYPE 4
3334
// define your custom event types here
34-
// #define CUSTOM_XXX_EVENT_TYPE 3
35+
// #define CUSTOM_XXX_EVENT_TYPE 4
3536

3637
#if ESP32_JAVASCRIPT_EXTERN == ESP32_JAVASCRIPT_EXTERN_INCLUDE
3738
extern void initSpiffs(duk_context *ctx);

‎sdkconfig.defaults

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ CONFIG_PARTITION_TABLE_MD5=y
123123
# Compiler options
124124
#
125125
# CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set
126-
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
127-
# CONFIG_COMPILER_OPTIMIZATION_PERF is not set
126+
# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set
127+
CONFIG_COMPILER_OPTIMIZATION_PERF=y
128128
# CONFIG_COMPILER_OPTIMIZATION_NONE is not set
129129
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
130130
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set

0 commit comments

Comments
 (0)
Please sign in to comment.