From ceb62993daf5217e7813e83de904d3df2a680a59 Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Sat, 5 Mar 2022 17:27:11 -0500 Subject: [PATCH 01/47] First attempt at adding support for the RMT device. (#3493) * Adding the first version of the rmt documentation. * Stub RMT module compiles. * This version seems to work in (at least) simple cases. * CLean up the docs * Minor fixes * Give the SPI module a chance of working... * Update to the released version of idf4.4 * Try to get the CI Build to work in all cases * Try to get the CI Build to work in all cases * FIx a ringbuffer return issue * Remove bogus comment * Review comments * Better example of transmission * Review comments * Add table send example * Improved documentation * Documentation comments * Install the driver correctly. * A couple of doc updates * Fix typo --- components/modules/CMakeLists.txt | 1 + components/modules/Kconfig | 7 + components/modules/rmt.c | 381 ++++++++++++++++++++++++++++++ docs/modules/rmt.md | 139 +++++++++++ 4 files changed, 528 insertions(+) create mode 100644 components/modules/rmt.c create mode 100644 docs/modules/rmt.md diff --git a/components/modules/CMakeLists.txt b/components/modules/CMakeLists.txt index e1f68d5d..aff349f9 100644 --- a/components/modules/CMakeLists.txt +++ b/components/modules/CMakeLists.txt @@ -51,6 +51,7 @@ if(IDF_TARGET STREQUAL "esp32") "eth.c" "i2s.c" "pulsecnt.c" + "rmt.c" "sdmmc.c" "touch.c" ) diff --git a/components/modules/Kconfig b/components/modules/Kconfig index b07b6d0e..3758bc59 100644 --- a/components/modules/Kconfig +++ b/components/modules/Kconfig @@ -218,6 +218,13 @@ menu "NodeMCU modules" Includes the QR Code Generator from https://www.nayuki.io/page/qr-code-generator-library + config NODEMCU_CMODULE_RMT + bool "Remote Control pulse generator/receiver" + default "n" + help + Includes the rmt module to use the ESP32's built-in + remote control hardware. + config NODEMCU_CMODULE_SDMMC depends on IDF_TARGET_ESP32 bool "SD-MMC module" diff --git a/components/modules/rmt.c b/components/modules/rmt.c new file mode 100644 index 00000000..30577700 --- /dev/null +++ b/components/modules/rmt.c @@ -0,0 +1,381 @@ +// Module for working with the rmt driver + +#include +#include "module.h" +#include "lauxlib.h" +#include "platform.h" +#include "platform_rmt.h" +#include "task/task.h" + +#include "driver/rmt.h" + +#include "common.h" + +typedef struct { + bool tx; + int channel; + int cb_ref; + struct _lrmt_cb_params *rx_params; +} *lrmt_channel_t; + +typedef struct _lrmt_cb_params { + bool dont_call; + bool rx_shutting_down; + rmt_channel_t channel; + int cb_ref; + int data_ref; + int rc; + size_t len; + rmt_item32_t *data; +} lrmt_cb_params; + +static task_handle_t cb_task_id; + +static int get_divisor(lua_State *L, int index) { + int bittime = luaL_checkinteger(L, index); + + int divisor = bittime / 12500; // 80MHz clock + + luaL_argcheck(L, divisor >= 1 && divisor <= 255, index, "Bit time out of range"); + + return divisor; +} + +static int configure_channel(lua_State *L, rmt_config_t *config, rmt_mode_t mode) { + lrmt_channel_t ud = (lrmt_channel_t)lua_newuserdata(L, sizeof(*ud)); + if (!ud) return luaL_error(L, "not enough memory"); + memset(ud, 0, sizeof(*ud)); + luaL_getmetatable(L, "rmt.channel"); + lua_setmetatable(L, -2); + + // We have allocated the channel -- must free it if the rest of this method fails + int channel = platform_rmt_allocate(1, mode); + + if (channel < 0) { + return luaL_error(L, "no spare RMT channel"); + } + + config->channel = channel; + + ud->channel = channel; + ud->tx = mode == RMT_MODE_TX; + + esp_err_t rc = rmt_config(config); + if (rc) { + platform_rmt_release(config->channel); + return luaL_error(L, "Failed to configure RMT"); + } + + rc = rmt_driver_install(config->channel, 1000, 0); + if (rc) { + platform_rmt_release(config->channel); + return luaL_error(L, "Failed to install RMT driver"); + } + + return 1; +} + +static int lrmt_txsetup(lua_State *L) { + int gpio = luaL_checkinteger(L, 1); + int divisor = get_divisor(L, 2); + + // We will set the channel later + rmt_config_t config = RMT_DEFAULT_CONFIG_TX(gpio, 0); + config.clk_div = divisor; + + if (lua_type(L, 3) == LUA_TTABLE) { + lua_getfield(L, 3, "carrier_hz"); + int hz = lua_tointeger(L, -1); + if (hz) { + config.tx_config.carrier_freq_hz = hz; + config.tx_config.carrier_en = true; + } + lua_pop(L, 1); + + lua_getfield(L, 3, "carrier_duty"); + int duty = lua_tointeger(L, -1); + if (duty) { + config.tx_config.carrier_duty_percent = duty; + } + lua_pop(L, 1); + + lua_getfield(L, 3, "idle_level"); + if (!lua_isnil(L, -1)) { + int level = lua_tointeger(L, -1); + config.tx_config.idle_level = level; + config.tx_config.idle_output_en = true; + } + lua_pop(L, 1); + + lua_getfield(L, 3, "invert"); + if (lua_toboolean(L, -1)) { + config.flags |= RMT_CHANNEL_FLAGS_INVERT_SIG; + } + lua_pop(L, 1); + } + + configure_channel(L, &config, RMT_MODE_TX); + lua_pushinteger(L, divisor * 12500); + return 2; +} + +static int lrmt_rxsetup(lua_State *L) { + int gpio = luaL_checkinteger(L, 1); + int divisor = get_divisor(L, 2); + + // We will set the channel later + rmt_config_t config = RMT_DEFAULT_CONFIG_RX(gpio, 0); + config.clk_div = divisor; + config.rx_config.idle_threshold = 65535; + + if (lua_type(L, 3) == LUA_TTABLE) { + lua_getfield(L, 3, "invert"); + if (lua_toboolean(L, -1)) { + config.flags |= RMT_CHANNEL_FLAGS_INVERT_SIG; + } + lua_pop(L, 1); + + lua_getfield(L, 3, "filter_ticks"); + if (!lua_isnil(L, -1)) { + int ticks = lua_tointeger(L, -1); + if (ticks < 0 || ticks > 255) { + return luaL_error(L, "filter_ticks must be in the range 0 - 255"); + } + config.rx_config.filter_ticks_thresh = ticks; + config.rx_config.filter_en = true; + } + lua_pop(L, 1); + + lua_getfield(L, 3, "idle_threshold"); + if (!lua_isnil(L, -1)) { + int threshold = lua_tointeger(L, -1); + if (threshold < 0 || threshold > 65535) { + return luaL_error(L, "idle_threshold must be in the range 0 - 65535"); + } + config.rx_config.idle_threshold = threshold; + } + lua_pop(L, 1); + } + + configure_channel(L, &config, RMT_MODE_RX); + lua_pushinteger(L, divisor * 12500); + return 2; +} + +static void free_transmit_wait_params(lua_State *L, lrmt_cb_params *p) { + if (!p->data) { + luaL_unref(L, LUA_REGISTRYINDEX, p->cb_ref); + luaL_unref(L, LUA_REGISTRYINDEX, p->data_ref); + } + free(p); +} + +static void handle_receive(void *param) { + lrmt_cb_params *p = (lrmt_cb_params *) param; + + RingbufHandle_t rb = NULL; + + //get RMT RX ringbuffer + rmt_get_ringbuf_handle(p->channel, &rb); + // Start receive + rmt_rx_start(p->channel, true); + while (!p->rx_shutting_down) { + size_t length = 0; + rmt_item32_t *items = NULL; + + items = (rmt_item32_t *) xRingbufferReceive(rb, &length, 50 / portTICK_PERIOD_MS); + if (items && length) { + lrmt_cb_params *rx_params = malloc(sizeof(lrmt_cb_params) + length); + if (rx_params) { + memset(rx_params, 0, sizeof(*rx_params)); + memcpy(rx_params + 1, items, length); + rx_params->cb_ref = p->cb_ref; + rx_params->data = (void *) (rx_params + 1); + rx_params->len = length; + rx_params->channel = p->channel; + task_post_high(cb_task_id, (task_param_t) rx_params); + } else { + printf("Unable allocate receive data memory\n"); + } + } + if (items) { + vRingbufferReturnItem(rb, (void *) items); + } + } + + p->dont_call = true; + task_post_high(cb_task_id, (task_param_t) p); + + /* Destroy this task */ + vTaskDelete(NULL); +} + +static int lrmt_on(lua_State *L) { + lrmt_channel_t ud = (lrmt_channel_t)luaL_checkudata(L, 1, "rmt.channel"); + if (ud->tx) { + return luaL_error(L, "Cannot receive on a TX channel"); + } + + luaL_argcheck(L, !strcmp(lua_tostring(L, 2), "data") , 2, "Must be 'data'"); + + luaL_argcheck(L, lua_type(L, 3) == LUA_TFUNCTION, 3, "Must be a function"); + + if (ud->rx_params) { + return luaL_error(L, "Can only call 'on' once"); + } + + // We have a callback + lrmt_cb_params *params = (lrmt_cb_params *) malloc(sizeof(*params)); + if (!params) { + return luaL_error(L, "Cannot allocate memory"); + } + memset(params, 0, sizeof(*params)); + + lua_pushvalue(L, 3); + params->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); + ud->rx_params = params; + params->channel = ud->channel; + + xTaskCreate(handle_receive, "rmt-rx-receiver", 3000, params, 2, NULL); + + return 0; +} + +static void wait_for_transmit(void *param) { + lrmt_cb_params *p = (lrmt_cb_params *) param; + esp_err_t rc = rmt_wait_tx_done(p->channel, 10000 / portTICK_PERIOD_MS); + + p->rc = rc; + task_post_high(cb_task_id, (task_param_t) p); + + /* Destroy this task */ + vTaskDelete(NULL); +} + +static int lrmt_send(lua_State *L) { + lrmt_channel_t ud = (lrmt_channel_t)luaL_checkudata(L, 1, "rmt.channel"); + if (!ud->tx) { + return luaL_error(L, "Cannot send on an RX channel"); + } + int string_index = 2; + + if (lua_type(L, 2) == LUA_TTABLE) { + lua_getfield(L, 2, "concat"); + lua_pushvalue(L, 2); + lua_pushstring(L, ""); + lua_call(L, 2, 1); + string_index = -1; + } + + size_t len; + const char *data = lua_tolstring(L, string_index, &len); + if (!data || !len) { + return 0; + } + + if (len & 1) { + return luaL_error(L, "Length must be a multiple of 2"); + } + + if (len & 3) { + // Just tack on a "\0\0" -- this is needed as the hardware can + // only deal with multiple of 4 bytes. + luaL_Buffer b; + luaL_buffinit(L, &b); + luaL_addlstring(&b, data, len); + luaL_addlstring(&b, "\0\0", 2); + luaL_pushresult(&b); + data = lua_tolstring(L, -1, &len); + string_index = -1; + } + + bool wait_for_done = true; + + if (lua_type(L, 3) == LUA_TFUNCTION) { + // We have a callback + lrmt_cb_params *params = (lrmt_cb_params *) malloc(sizeof(*params)); + if (!params) { + return luaL_error(L, "Cannot allocate memory"); + } + memset(params, 0, sizeof(*params)); + + params->channel = ud->channel; + + lua_pushvalue(L, 3); + params->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushvalue(L, string_index); + params->data_ref = luaL_ref(L, LUA_REGISTRYINDEX); + xTaskCreate(wait_for_transmit, "rmt-tx-waiter", 1024, params, 2, NULL); + wait_for_done = false; + } + + // We want to transmit it + rmt_write_items(ud->channel, (rmt_item32_t *) data, len / sizeof(rmt_item32_t), wait_for_done); + return 0; +} + +static int lrmt_close(lua_State *L) { + lrmt_channel_t ud = (lrmt_channel_t)luaL_checkudata(L, 1, "rmt.channel"); + + if (ud->channel >= 0) { + if (ud->rx_params) { + // We need to stop the listening task + ud->rx_params->rx_shutting_down = true; + } else { + rmt_driver_uninstall(ud->channel); + platform_rmt_release(ud->channel); + } + ud->channel = -1; + } + + return 0; +} + +static void cb_task(task_param_t param, task_prio_t prio) { + lrmt_cb_params *p = (lrmt_cb_params *) param; + lua_State *L = lua_getstate(); + + if (!p->dont_call) { + lua_rawgeti (L, LUA_REGISTRYINDEX, p->cb_ref); + if (p->data) { + lua_pushlstring(L, (char *) p->data, p->len); + } else { + lua_pushinteger(L, p->rc); + } + + int res = luaL_pcallx(L, 1, 0); + if (res) { + printf("rmt callback threw an error\n"); + } + } + + if (p->rx_shutting_down) { + rmt_driver_uninstall(p->channel); + platform_rmt_release(p->channel); + } + + free_transmit_wait_params(L, p); +} + +// Module function map +LROT_BEGIN(rmt_channel, NULL, LROT_MASK_GC_INDEX) + LROT_FUNCENTRY( __gc, lrmt_close ) + LROT_TABENTRY ( __index, rmt_channel ) + LROT_FUNCENTRY( on, lrmt_on ) + LROT_FUNCENTRY( close, lrmt_close ) + LROT_FUNCENTRY( send, lrmt_send ) +LROT_END(rmt_channel, NULL, LROT_MASK_GC_INDEX) + +LROT_BEGIN(rmt, NULL, LROT_MASK_INDEX) + LROT_TABENTRY ( __index, rmt ) + LROT_FUNCENTRY( txsetup, lrmt_txsetup ) + LROT_FUNCENTRY( rxsetup, lrmt_rxsetup ) +LROT_END(rmt, NULL, LROT_MASK_INDEX) + +int luaopen_rmt(lua_State *L) { + luaL_rometatable(L, "rmt.channel", LROT_TABLEREF(rmt_channel)); // create metatable + cb_task_id = task_get_id(cb_task); + return 0; +} + +NODEMCU_MODULE(RMT, "rmt", rmt, luaopen_rmt); diff --git a/docs/modules/rmt.md b/docs/modules/rmt.md new file mode 100644 index 00000000..d3ab062d --- /dev/null +++ b/docs/modules/rmt.md @@ -0,0 +1,139 @@ +# Remote Control Driver +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2022-01-01 | [pjsg](https://github.com/pjsg) | [pjsg](https://github.com/pjsg) | [rmt.c](../../components/modules/rmt.c)| + +The RMT module provides a simple interface onto the ESP32 RMT peripheral. This allows the generation of +arbitrary pulse trains with good timing accuracy. This can be used to generate IR remote control signals, or +servo control pulses, or pretty much any high speed signalling system. It isn't good for low speed stuff as the maximum +pulse time is under 200ms -- though you can get longer by having multiple large values in a row. See the Data Encoding +below for more details. + +## rmt.txsetup(gpio, bitrate, options) + +This sets up a transmit channel on the specified gpio pin at the specified rate. Various options described below +can be specified in the `options`. The bit time is specified in picoseconds so that integer values can be used. + +An error will be thrown if the bit time cannot be approximated. + +#### Syntax +`channel = rmt.txsetup(gpio, bittime[, options])` + +#### Parameters +- `gpio` The GPIO pin number to use. +- `bittime` The bit time to use in picoseconds. Only certain times can be handled exactly. The actual set time will be returned. The actual range is limited -- probably using 100,000 (0.1µS) or 1,000,000 (1µS). The actual constraint is that the interval is 1 - 255 cycles of an 80MHz clock. +- `options` A table with the keys as defined below. + +##### Returns +- The `rmt.channel` object that can be used for sending data +- The actual bit time in picoseconds. + +#### Example +```lua +``` + +#### Options table + +This optional table consists of a number of keys that control various aspects of the RMT transmission. + +- `invert` if true, then the output is inverted. +- `carrier_hz` specifies that the signal is to modulate the carrier at the specified frequency. This is useful for IR transmissions. +- `carrier_duty` specifies the duty cycle of the carrier. Defaults to 50% +- `idle_level` specifies what value to send when the transmission completes. + +## rmt.rxsetup(gpio, bitrate, options) + +This sets up a receive channel on the specified gpio pin at the specified rate. Various options described below +can be specified in the `options`. The bit time is specified in picoseconds so that integer values can be used. + +An error will be thrown if the bit time cannot be approximated. + +#### Syntax +`channel = rmt.rxsetup(gpio, bittime[, options])` + +#### Parameters +- `gpio` The GPIO pin number to use. +- `bittime` The bit time to use in picoseconds. Only certain times can be handled exactly. The actual set time will be returned. The actual range is limited -- probably using 100,000 (0.1µS) or 1,000,000 (1µS). The actual constraint is that the interval is 1 - 255 cycles of an 80MHz clock. +- `options` A table with the keys as defined below. + +##### Returns +- The `rmt.channel` object that can be used for receiving data +- The actual bit time in picoseconds. + +#### Example +```lua +``` + +#### Options table + +This optional table consists of a number of keys that control various aspects of the RMT transmission. + +- `invert` if true, then the input is inverted. +- `filter_ticks` If specified, then any pulse shorter than this will be ignored. This is in units of the bit time. +- `idle_threshold` If specified, then any level longer than this will set the receiver as idle. The default is 65535 bit times. + + +## channel:on(event, callback) + +This is establishes a callback to use when data is received and it also starts the data reception process. It can only be called once per receive +channel. + +#### Syntax +`channel:on(event, callback)` + +#### Parameters +- `event` This must be the string 'data' and it sets the callback that gets invoked when data is received. +- `callback` This is invoked with a single argument that is a string that contains the data received in the format described for `send` below. `struct.unpack` is your friend. + +#### Returns +`nil` + +## channel:send(data, cb) + +This is a (default) blocking call that transmits the data using the parameters specified on the `txsetup` call. + +#### Syntax +`channel:send(data[, cb])` + +#### Parameters +- `data` This is either a string or a table of strings. +- `cb` This is an optional callback when the transmission is actually complete. If specified, then the `send` call is non-blocking, and the callback invoked when the transmission is complete. Otherwise the `send` call is synchronous and does not return until transmission is complete. + +#### Data Encoding + +If the `data` supplied is a table (really an array), then the elements of the table are concatenated together and sent. The elements of the table must be strings. + +If the item being sent is a string, then it contains 16 bit packed integers. The top bit of the integer controls the output level. +`struct.pack("H", value)` generates a suitable value to output a zero bit. `struct.pack("H", 32768 + value)` generates a one bit of the specified width. + +The widths are in units of the interval specified when the channel was setup. + + +#### Returns +`nil` + +#### Example + +This example sends a single R character at 19200 bps. You wouldn't actually want to do it this way.... In some applications this would be inverted. + +``` +channel = rmt.txsetup(25, 1000000000 / 19200, {idle_level=1}) +one = struct.pack("H", 32768 + 1000) +zero = struct.pack("H", 1000) +-- Send start bit, then R = 0x52 (reversed) then stop bit +channel:send(zero .. zero .. one .. zero .. zero .. one .. zero .. one .. zero .. one) +-- or using the table interface +channel:send({zero, zero, one, zero, zero, one, zero, one, zero, one}) +``` + +## channel:close() + +This shuts down the RMT channel and makes it available for other uses (e.g. ws2812). The channel cannot be used after this call returns. The channel +is also released when the garbage collector frees it up. However you should always `close` the channel explicitly as otherwise you can run out of RMT channels +before the garbage collector frees some up. + +#### Syntax +`channel:close()` + +#### Returns +`nil` From c03e7cffb7cd1cf981f10fbc0b557fe793e76f42 Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Fri, 20 May 2022 01:36:36 -0400 Subject: [PATCH 02/47] Add the invert option for the ledc module (#3506) --- components/modules/ledc.c | 2 ++ docs/modules/ledc.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/components/modules/ledc.c b/components/modules/ledc.c index add40b6d..9f0fc9e5 100644 --- a/components/modules/ledc.c +++ b/components/modules/ledc.c @@ -43,6 +43,8 @@ static int lledc_new_channel( lua_State *L ) channel_config.gpio_num = opt_checkint_range(L, "gpio", -1, 0, GPIO_NUM_MAX-1); + channel_config.flags.output_invert = opt_checkbool(L, "invert", 0); + lua_settop(L, top); esp_err_t timerErr = ledc_timer_config(&ledc_timer); diff --git a/docs/modules/ledc.md b/docs/modules/ledc.md index 68653409..1237b9cc 100644 --- a/docs/modules/ledc.md +++ b/docs/modules/ledc.md @@ -22,6 +22,7 @@ myChannel = ledc.newChannel({ timer=ledc.TIMER_0 || ledc.TIMER_1 || ledc.TIMER_2 || ledc.TIMER_3, channel=ledc.CHANNEL_0 || ledc.CHANNEL_1 || ledc.CHANNEL_2 || ledc.CHANNEL_3 || ledc.CHANNEL_4 || ledc.CHANNEL_5 || ledc.CHANNEL_6 || ledc.CHANNEL_7, frequency=x, + invert=false, duty=x }); ``` @@ -46,6 +47,7 @@ List of configuration tables: - ... - `ledc.CHANNEL_7` - `frequency` Timer frequency(Hz) +- `invert` Inverts the output. False, with duty 0, is always low. - `duty` Channel duty, the duty range is [0, (2**bit_num) - 1]. Example: if ledc.TIMER_13_BIT is used maximum value is 4096 x 2 -1 = 8091 #### Returns From e8caaebc5a95765330ee72458899bc25975b3ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Fri, 20 May 2022 07:51:42 +0200 Subject: [PATCH 03/47] Pin jinja2, see https://twitter.com/readthedocs/status/1507388916013314048 --- docs/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/requirements.txt b/docs/requirements.txt index c2e8755b..ba6e4ea8 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1,2 @@ mkdocs==1.2.2 +jinja2<3.1 From c75ec760aa739ed4fada7d34bc1a69bfa68419cb Mon Sep 17 00:00:00 2001 From: h2zero Date: Mon, 23 May 2022 10:07:49 -0600 Subject: [PATCH 04/47] Fix node.compile fwrite size check. The return value from fwrite was being checked against the size of the data rather than the number of bytes written. This caused node.compile() to falsely return failure. --- components/modules/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/modules/node.c b/components/modules/node.c index 11b19077..d2ceebb2 100644 --- a/components/modules/node.c +++ b/components/modules/node.c @@ -603,7 +603,7 @@ static int writer(lua_State* L, const void* p, size_t size, void* u) if (!file) return 1; - if (size != 0 && (size != fwrite((const char *)p, size, 1, file)) ) + if (size != 0 && (fwrite((const char *)p, size, 1, file) != 1) ) return 1; return 0; From 484a7721b54100543d934da8350e542f732cb9de Mon Sep 17 00:00:00 2001 From: h2zero Date: Sun, 22 May 2022 21:51:47 -0600 Subject: [PATCH 05/47] Ensure flash address is set before obtaining memory mapped address. Before this change the struck member add_phys would contain random data and the call to spi_flash_phys2cache could return an incorrect memory address. --- components/lua/common/lfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/lua/common/lfs.c b/components/lua/common/lfs.c index 847cb8bf..00ca8721 100644 --- a/components/lua/common/lfs.c +++ b/components/lua/common/lfs.c @@ -31,8 +31,8 @@ bool lfs_get_location(lfs_location_info_t *out) return false; // Nothing to do if no LFS partition available out->size = part->size; // in bytes - out->addr_mem = spi_flash_phys2cache(out->addr_phys, SPI_FLASH_MMAP_DATA); out->addr_phys = part->address; + out->addr_mem = spi_flash_phys2cache(out->addr_phys, SPI_FLASH_MMAP_DATA); if (!out->addr_mem) { // not already mmap'd, have to do it ourselves spi_flash_mmap_handle_t ignored; esp_err_t err = spi_flash_mmap( From b8ae6ca6f809f735616238ce879a47e0ff1e9bc4 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sat, 28 May 2022 13:06:45 -0600 Subject: [PATCH 06/47] Explicitly set RMT config::flags. If this variable is not set in the config it may contain random data and set the clock source to the REF_CLK resulting in timing errors, the DHTx, DS18B20 and ws2812 devices to not communicate. --- components/platform/dht.c | 1 + components/platform/onewire.c | 2 ++ components/platform/ws2812.c | 1 + 3 files changed, 4 insertions(+) diff --git a/components/platform/dht.c b/components/platform/dht.c index 70331005..16193b83 100644 --- a/components/platform/dht.c +++ b/components/platform/dht.c @@ -86,6 +86,7 @@ static int dht_init( uint8_t gpio_num ) rmt_rx.channel = dht_rmt.channel; rmt_rx.gpio_num = gpio_num; rmt_rx.clk_div = 80; // base period is 1us + rmt_rx.flags = 0; rmt_rx.mem_block_num = 1; rmt_rx.rmt_mode = RMT_MODE_RX; rmt_rx.rx_config.filter_en = true; diff --git a/components/platform/onewire.c b/components/platform/onewire.c index 5472eeea..4bc455f6 100644 --- a/components/platform/onewire.c +++ b/components/platform/onewire.c @@ -126,6 +126,7 @@ static int onewire_rmt_init( uint8_t gpio_num ) rmt_tx.gpio_num = gpio_num; rmt_tx.mem_block_num = 1; rmt_tx.clk_div = 80; + rmt_tx.flags = 0; rmt_tx.tx_config.loop_en = false; rmt_tx.tx_config.carrier_en = false; rmt_tx.tx_config.idle_level = 1; @@ -138,6 +139,7 @@ static int onewire_rmt_init( uint8_t gpio_num ) rmt_rx.channel = ow_rmt.rx; rmt_rx.gpio_num = gpio_num; rmt_rx.clk_div = 80; + rmt_rx.flags = 0; rmt_rx.mem_block_num = 1; rmt_rx.rmt_mode = RMT_MODE_RX; rmt_rx.rx_config.filter_en = true; diff --git a/components/platform/ws2812.c b/components/platform/ws2812.c index 67fa562d..f825c9da 100644 --- a/components/platform/ws2812.c +++ b/components/platform/ws2812.c @@ -187,6 +187,7 @@ int platform_ws2812_send( void ) // common settings rmt_tx.mem_block_num = 1; rmt_tx.clk_div = WS2812_CLKDIV; + rmt_tx.flags = 0; rmt_tx.tx_config.loop_en = false; rmt_tx.tx_config.carrier_en = false; rmt_tx.tx_config.idle_level = 0; From a3712ac99093882e22000ff3e2103cf0dff953d2 Mon Sep 17 00:00:00 2001 From: Joo Aun Saw Date: Thu, 16 Jun 2022 17:47:27 +1000 Subject: [PATCH 07/47] embed_lfs.sh: handle riscv.attributes in esp32c3 map file --- tools/embed_lfs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/embed_lfs.sh b/tools/embed_lfs.sh index d63f25c3..8d9ae6d6 100755 --- a/tools/embed_lfs.sh +++ b/tools/embed_lfs.sh @@ -15,7 +15,7 @@ if [ ! -f "${LUAC_CROSS}" ]; then exit 1 fi -LFS_ADDR_SIZE=$(grep -E "0x[0-9a-f]+[ ]+0x[0-9a-f]+[ ]+esp-idf/embedded_lfs/libembedded_lfs.a\(lua.flash.store.reserved.S.obj\)" "${MAP_FILE}" | grep -v -w 0x0 | tr -s ' ') +LFS_ADDR_SIZE=$(grep -E "0x[0-9a-f]+[ ]+0x[0-9a-f]+[ ]+esp-idf/embedded_lfs/libembedded_lfs.a\(lua.flash.store.reserved.S.obj\)" "${MAP_FILE}" | grep -v -w 0x0 | grep -v -w 0x24 | tr -s ' ') if [ -z "${LFS_ADDR_SIZE}" ]; then echo "Error: LFS segment not found. Use 'make clean; make' perhaps?" exit 1 From 01b94f31dc4cc2e0c57bd1f7e1ccb800778e0105 Mon Sep 17 00:00:00 2001 From: Joo Aun Saw Date: Mon, 29 Aug 2022 16:19:46 +1000 Subject: [PATCH 08/47] uart: feed rx data to both lua console and on-data callback --- components/modules/uart.c | 4 ++++ components/platform/platform.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/components/modules/uart.c b/components/modules/uart.c index 118be37d..298a6770 100644 --- a/components/modules/uart.c +++ b/components/modules/uart.c @@ -10,6 +10,10 @@ static lua_State *gL = NULL; +bool uart_has_on_data_cb(unsigned id){ + return uart_status[id].receive_rf != LUA_NOREF; +} + bool uart_on_data_cb(unsigned id, const char *buf, size_t len){ if(!buf || len==0) return false; diff --git a/components/platform/platform.c b/components/platform/platform.c index 59228b82..09fbe757 100644 --- a/components/platform/platform.c +++ b/components/platform/platform.c @@ -65,6 +65,7 @@ uart_status_t uart_status[NUM_UART]; task_handle_t uart_event_task_id = 0; SemaphoreHandle_t sem = NULL; +extern bool uart_has_on_data_cb(unsigned id); extern bool uart_on_data_cb(unsigned id, const char *buf, size_t len); extern bool uart_on_error_cb(unsigned id, const char *buf, size_t len); @@ -81,7 +82,7 @@ void uart_event_task( task_param_t param, task_prio_t prio ) { unsigned used = feed_lua_input(post->data + i, post->size - i); i += used; } - else { + if (uart_has_on_data_cb(id)) { char ch = post->data[i]; us->line_buffer[us->line_position] = ch; us->line_position++; From ca0a2b3f476fac98b72bd4b3a702885400d71974 Mon Sep 17 00:00:00 2001 From: Joo Aun Saw Date: Mon, 29 Aug 2022 16:36:35 +1000 Subject: [PATCH 09/47] platform: platform_uart_setup handles null ptr --- components/platform/platform.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/components/platform/platform.c b/components/platform/platform.c index 09fbe757..979984cf 100644 --- a/components/platform/platform.c +++ b/components/platform/platform.c @@ -209,9 +209,11 @@ static void task_uart( void *pvParameters ){ uint32_t platform_uart_setup( unsigned id, uint32_t baud, int databits, int parity, int stopbits, uart_pins_t* pins ) { int flow_control = UART_HW_FLOWCTRL_DISABLE; - if(pins->flow_control & PLATFORM_UART_FLOW_CTS) flow_control |= UART_HW_FLOWCTRL_CTS; - if(pins->flow_control & PLATFORM_UART_FLOW_RTS) flow_control |= UART_HW_FLOWCTRL_RTS; - + if (pins != NULL) { + if(pins->flow_control & PLATFORM_UART_FLOW_CTS) flow_control |= UART_HW_FLOWCTRL_CTS; + if(pins->flow_control & PLATFORM_UART_FLOW_RTS) flow_control |= UART_HW_FLOWCTRL_RTS; + } + uart_config_t cfg = { .baud_rate = baud, .flow_ctrl = flow_control, From ddc770a6f7ccf46928cac176f00754b32aa549be Mon Sep 17 00:00:00 2001 From: Joo Aun Saw Date: Mon, 29 Aug 2022 16:45:36 +1000 Subject: [PATCH 10/47] uart: do not use uninitialized uart pins config --- components/modules/uart.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/modules/uart.c b/components/modules/uart.c index 298a6770..fa3def34 100644 --- a/components/modules/uart.c +++ b/components/modules/uart.c @@ -135,7 +135,9 @@ static int uart_setup( lua_State* L ) unsigned id, databits, parity, stopbits, echo = 1; uint32_t baud, res; uart_pins_t pins; + uart_pins_t* pins_to_use = NULL; + memset(&pins, 0, sizeof(pins)); id = luaL_checkinteger( L, 1 ); MOD_CHECK_ID( uart, id ); baud = luaL_checkinteger( L, 2 ); @@ -169,9 +171,11 @@ static int uart_setup( lua_State* L ) lua_getfield (L, 6, "flow_control"); pins.flow_control = luaL_optint(L, -1, PLATFORM_UART_FLOW_NONE); + + pins_to_use = &pins; } - res = platform_uart_setup( id, baud, databits, parity, stopbits, &pins ); + res = platform_uart_setup( id, baud, databits, parity, stopbits, pins_to_use ); lua_pushinteger( L, res ); return 1; } From 5e6cb4d1fa344f4deb77c9a5bc3753353a8a99e5 Mon Sep 17 00:00:00 2001 From: Joo Aun Saw Date: Mon, 29 Aug 2022 17:35:38 +1000 Subject: [PATCH 11/47] uart: slight clean up of uart.setup --- components/modules/uart.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/components/modules/uart.c b/components/modules/uart.c index fa3def34..e5c4fc22 100644 --- a/components/modules/uart.c +++ b/components/modules/uart.c @@ -132,7 +132,7 @@ static int uart_on( lua_State* L ) // Lua: actualbaud = setup( id, baud, databits, parity, stopbits, echo ) static int uart_setup( lua_State* L ) { - unsigned id, databits, parity, stopbits, echo = 1; + unsigned id, databits, parity, stopbits; uint32_t baud, res; uart_pins_t pins; uart_pins_t* pins_to_use = NULL; @@ -144,13 +144,14 @@ static int uart_setup( lua_State* L ) databits = luaL_checkinteger( L, 3 ); parity = luaL_checkinteger( L, 4 ); stopbits = luaL_checkinteger( L, 5 ); - if(id == CONFIG_ESP_CONSOLE_UART_NUM && lua_isnumber(L,6)){ - echo = lua_tointeger(L,6); - if(echo!=0) - input_echo = true; - else - input_echo = false; - } else if(id != CONFIG_ESP_CONSOLE_UART_NUM && lua_istable( L, 6 )) { + if(id == CONFIG_ESP_CONSOLE_UART_NUM){ + if (!lua_isnoneornil(L, 6)) { + input_echo = luaL_checkinteger(L, 6) > 0; + } + } else { + if (!lua_isnoneornil(L, 6)) { + luaL_checktable(L, 6); + lua_getfield (L, 6, "tx"); pins.tx_pin = luaL_checkint(L, -1); lua_getfield (L, 6, "rx"); @@ -173,6 +174,7 @@ static int uart_setup( lua_State* L ) pins.flow_control = luaL_optint(L, -1, PLATFORM_UART_FLOW_NONE); pins_to_use = &pins; + } } res = platform_uart_setup( id, baud, databits, parity, stopbits, pins_to_use ); From 0e71189c113bf803e34181375336288c3f17e904 Mon Sep 17 00:00:00 2001 From: Joo Aun Saw Date: Tue, 30 Aug 2022 09:53:39 +1000 Subject: [PATCH 12/47] uart: further clean up --- components/modules/uart.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/components/modules/uart.c b/components/modules/uart.c index e5c4fc22..107e2884 100644 --- a/components/modules/uart.c +++ b/components/modules/uart.c @@ -144,12 +144,10 @@ static int uart_setup( lua_State* L ) databits = luaL_checkinteger( L, 3 ); parity = luaL_checkinteger( L, 4 ); stopbits = luaL_checkinteger( L, 5 ); - if(id == CONFIG_ESP_CONSOLE_UART_NUM){ - if (!lua_isnoneornil(L, 6)) { + if (!lua_isnoneornil(L, 6)) { + if(id == CONFIG_ESP_CONSOLE_UART_NUM){ input_echo = luaL_checkinteger(L, 6) > 0; - } - } else { - if (!lua_isnoneornil(L, 6)) { + } else { luaL_checktable(L, 6); lua_getfield (L, 6, "tx"); From d24f2ae459e46133424b787eab17b3ae48df6664 Mon Sep 17 00:00:00 2001 From: Joo Aun Saw Date: Tue, 30 Aug 2022 16:53:05 +1000 Subject: [PATCH 13/47] platform uart: really feed uart data to both console and on-data cb --- components/platform/platform.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/components/platform/platform.c b/components/platform/platform.c index 979984cf..5fc404d4 100644 --- a/components/platform/platform.c +++ b/components/platform/platform.c @@ -75,14 +75,18 @@ void uart_event_task( task_param_t param, task_prio_t prio ) { uart_status_t *us = &uart_status[id]; xSemaphoreGive(sem); if(post->type == PLATFORM_UART_EVENT_DATA) { - size_t i = 0; - while (i < post->size) - { - if (id == CONFIG_ESP_CONSOLE_UART_NUM && run_input) { + if (id == CONFIG_ESP_CONSOLE_UART_NUM && run_input) { + size_t i = 0; + while (i < post->size) + { unsigned used = feed_lua_input(post->data + i, post->size - i); i += used; } - if (uart_has_on_data_cb(id)) { + } + if (uart_has_on_data_cb(id)) { + size_t i = 0; + while (i < post->size) + { char ch = post->data[i]; us->line_buffer[us->line_position] = ch; us->line_position++; From 99656356948704e4bbc7ff64a3f06bf04c0b32ea Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Thu, 29 Sep 2022 21:37:34 -0400 Subject: [PATCH 14/47] Ported the rtcmem over to ESP32 (#3544) * Ported the rtcmem over to ESP32 * Apply review comments. * Add the rtcmem string to the config option --- components/modules/CMakeLists.txt | 1 + components/modules/Kconfig | 7 ++++ components/modules/rtcmem.c | 52 ++++++++++++++++++++++++ docs/modules/rtcmem.md | 66 +++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 components/modules/rtcmem.c create mode 100644 docs/modules/rtcmem.md diff --git a/components/modules/CMakeLists.txt b/components/modules/CMakeLists.txt index aff349f9..5b960e2c 100644 --- a/components/modules/CMakeLists.txt +++ b/components/modules/CMakeLists.txt @@ -23,6 +23,7 @@ set(module_srcs "otaupgrade.c" "ow.c" "pipe.c" + "rtcmem.c" "qrcodegen.c" "sigma_delta.c" "sjson.c" diff --git a/components/modules/Kconfig b/components/modules/Kconfig index 3758bc59..7741e37b 100644 --- a/components/modules/Kconfig +++ b/components/modules/Kconfig @@ -225,6 +225,13 @@ menu "NodeMCU modules" Includes the rmt module to use the ESP32's built-in remote control hardware. + config NODEMCU_CMODULE_RTCMEM + bool "Access to a limited amount of battery backed memory (rtcmem)" + default "n" + help + Includes the rtcmem module to allow access to + the battery backed memory. + config NODEMCU_CMODULE_SDMMC depends on IDF_TARGET_ESP32 bool "SD-MMC module" diff --git a/components/modules/rtcmem.c b/components/modules/rtcmem.c new file mode 100644 index 00000000..34f03ec2 --- /dev/null +++ b/components/modules/rtcmem.c @@ -0,0 +1,52 @@ +// Module for RTC user memory access + +#include "module.h" +#include "lauxlib.h" +#include "esp_attr.h" + +#define RTC_USER_MEM_NUM_DWORDS 128 + +RTC_NOINIT_ATTR uint32_t rtc_memory[RTC_USER_MEM_NUM_DWORDS]; + +static int rtcmem_read32 (lua_State *L) +{ + int idx = luaL_checkinteger (L, 1); + int n = (lua_gettop(L) < 2) ? 1 : lua_tointeger (L, 2); + if (n == 0 || !lua_checkstack (L, n)) { + return 0; + } + + int ret = 0; + while (n > 0 && idx >= 0 && idx < RTC_USER_MEM_NUM_DWORDS) + { + lua_pushinteger (L, rtc_memory[idx++]); + --n; + ++ret; + } + return ret; +} + + +static int rtcmem_write32 (lua_State *L) +{ + int idx = luaL_checkinteger (L, 1); + int n = lua_gettop (L) - 1; + luaL_argcheck ( + L, idx + n <= RTC_USER_MEM_NUM_DWORDS, 1, "RTC mem would overrun"); + int src = 2; + while (n-- > 0) + { + rtc_memory[idx++] = (uint32_t) lua_tointeger(L, src++); + } + return 0; +} + + +// Module function map +LROT_BEGIN(rtcmem, NULL, 0) + LROT_FUNCENTRY( read32, rtcmem_read32 ) + LROT_FUNCENTRY( write32, rtcmem_write32 ) +LROT_END(rtcmem, NULL, 0) + + +NODEMCU_MODULE(RTCMEM, "rtcmem", rtcmem, NULL); diff --git a/docs/modules/rtcmem.md b/docs/modules/rtcmem.md new file mode 100644 index 00000000..bd4020dd --- /dev/null +++ b/docs/modules/rtcmem.md @@ -0,0 +1,66 @@ +# RTC User Memory Module +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2015-06-25 | [DiUS](https://github.com/DiUS), [Johny Mattsson](https://github.com/jmattsson) | [PJSG](https://github.com/pjsg) | [rtcmem.c](../../app/modules/rtcmem.c)| + +The rtcmem module provides basic access to the RTC memory. + +This memory is preserved while power is applied, making them highly useful for keeping state across sleep cycles. Some of this memory is reserved for system use, +and, for compatibility with NodeMCU on the ESP8266, 128 slots (each 32bit wide) of RTC memory are reserved by this module. +This module then provides read and write access to these slots. + +This module is 100% compatible with the ESP8266 version, and this means that, there is no mechanism for arbitrating use of particular slots. It is up to the end user to be aware of which memory is used for what, and avoid conflicts. Unlike the ESP8266 version, no other NodeMCU module uses any of these slots. + +Note that this memory is not necessary preserved across reflashing the firmware. It is the responsibility of the +developer to deal with getting inconsistent data. + +## rtcmem.read32() + +Reads one or more 32bit values from RTC user memory. + +#### Syntax +`rtcmem.read32(idx [, num])` + +#### Parameters + - `idx` zero-based index to start reading from + - `num` number of slots to read (default 1) + +#### Returns +The value(s) read from RTC user memory. + +If `idx` is outside the valid range [0,127] this function returns nothing. + +If `num` results in overstepping the end of available memory, the function only returns the data from the valid slots. + +#### Example +```lua +val = rtcmem.read32(0) -- Read the value in slot 0 +val1, val2 = rtcmem.read32(42, 2) -- Read the values in slots 42 and 43 +``` +#### See also +[`rtcmem.write32()`](#rtcmemwrite32) + +## rtcmem.write32() + +Writes one or more values to RTC user memory, starting at index `idx`. + +Writing to indices outside the valid range [0,127] has no effect. + +#### Syntax +`rtcmem.write32(idx, val [, val2, ...])` + +#### Parameters + - `idx` zero-based index to start writing to. Auto-increments if multiple values are given. + - `val` value to store (32bit) + - `val2...` additional values to store (optional) + +#### Returns +`nil` + +#### Example +```lua +rtcmem.write32(0, 53) -- Store the value 53 in slot 0 +rtcmem.write32(42, 2, 5, 7) -- Store the values 2, 5 and 7 into slots 42, 43 and 44, respectively. +``` +#### See also +[`rtcmem.read32()`](#rtcmemread32) From c7cab0aba4493c9a3b930a1f069425241d53c04f Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Thu, 29 Sep 2022 21:38:32 -0400 Subject: [PATCH 15/47] Adds support for settxpower (#3535) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adds support for settxpower * Update docs/modules/wifi.md Co-authored-by: Marcel Stör * Update docs/modules/wifi.md Co-authored-by: Marcel Stör Co-authored-by: Marcel Stör --- components/modules/wifi_sta.c | 20 ++++++++++++++++++-- docs/modules/wifi.md | 21 +++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/components/modules/wifi_sta.c b/components/modules/wifi_sta.c index acf40b76..e75a6388 100644 --- a/components/modules/wifi_sta.c +++ b/components/modules/wifi_sta.c @@ -40,6 +40,7 @@ #include "nodemcu_esp_event.h" #include #include "esp_netif.h" +#include static esp_netif_t *wifi_sta = NULL; static int scan_cb_ref = LUA_NOREF; @@ -207,6 +208,20 @@ static int wifi_sta_setip(lua_State *L) return 0; } +static int wifi_sta_settxpower(lua_State *L) +{ + lua_Number max_power = luaL_checknumber(L, 1); + + esp_err_t err = esp_wifi_set_max_tx_power(floor(max_power * 4 + 0.5)); + + if (err != ESP_OK) + return luaL_error(L, "failed to set transmit power, code %d", err); + + lua_pushboolean(L, err == ESP_OK); + + return 1; +} + static int wifi_sta_sethostname(lua_State *L) { size_t l; @@ -440,8 +455,9 @@ static int wifi_sta_scan (lua_State *L) LROT_BEGIN(wifi_sta, NULL, 0) LROT_FUNCENTRY( setip, wifi_sta_setip ) - LROT_FUNCENTRY( sethostname, wifi_sta_sethostname ) - LROT_FUNCENTRY( config, wifi_sta_config ) + LROT_FUNCENTRY( sethostname, wifi_sta_sethostname) + LROT_FUNCENTRY( settxpower, wifi_sta_settxpower) + LROT_FUNCENTRY( config, wifi_sta_config) LROT_FUNCENTRY( connect, wifi_sta_connect ) LROT_FUNCENTRY( disconnect, wifi_sta_disconnect ) LROT_FUNCENTRY( getconfig, wifi_sta_getconfig ) diff --git a/docs/modules/wifi.md b/docs/modules/wifi.md index 70860feb..b84f826e 100644 --- a/docs/modules/wifi.md +++ b/docs/modules/wifi.md @@ -233,6 +233,27 @@ Disconnects from AP in station mode. - [`wifi.sta.connect()`](#wifistaconnect) +## wifi.sta.settxpower + +Allows adjusting the maximum TX power for the WiFi. This is (unfortunately) needed for some boards which +have a badly matched antenna. + +#### Syntax +`wifi.sta.settxpower(power)` + +#### Parameters +- `power` The maximum transmit power in dBm. This must have the range 2dBm - 20dBm. This value is a float. + +#### Returns +A `boolean` where `true` is OK. + +#### Example + +``` +# Needed for the WEMOS C3 Mini +wifi.sta.settxpower(8.5) +``` + ## wifi.sta.on() Registers callbacks for WiFi station status events. From f15470e44f6f4fe11cc598ada7cae18f7a9e4b9d Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Sun, 2 Oct 2022 22:29:57 +0000 Subject: [PATCH 16/47] Include the rmt device for the esp32-c3 (and other missing devices) --- components/modules/CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/components/modules/CMakeLists.txt b/components/modules/CMakeLists.txt index 5b960e2c..88439660 100644 --- a/components/modules/CMakeLists.txt +++ b/components/modules/CMakeLists.txt @@ -59,12 +59,22 @@ if(IDF_TARGET STREQUAL "esp32") elseif(IDF_TARGET STREQUAL "esp32s2") list(APPEND module_srcs "dac.c" + "i2s.c" + "touch.c" + "rmt.c" + "pulsecnt.c" ) elseif(IDF_TARGET STREQUAL "esp32s3") list(APPEND module_srcs + "i2s.c" + "touch.c" + "rmt.c" + "pulsecnt.c" + "sdmmc.c" ) elseif(IDF_TARGET STREQUAL "esp32c3") list(APPEND module_srcs + "rmt.c" ) endif() From b9d6e2722e62393ec3847460585a4c62ec4eff09 Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Sun, 2 Oct 2022 22:38:23 +0000 Subject: [PATCH 17/47] The i2s module doesn't appear to build --- components/modules/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/modules/CMakeLists.txt b/components/modules/CMakeLists.txt index 88439660..fa21e262 100644 --- a/components/modules/CMakeLists.txt +++ b/components/modules/CMakeLists.txt @@ -59,14 +59,12 @@ if(IDF_TARGET STREQUAL "esp32") elseif(IDF_TARGET STREQUAL "esp32s2") list(APPEND module_srcs "dac.c" - "i2s.c" "touch.c" "rmt.c" "pulsecnt.c" ) elseif(IDF_TARGET STREQUAL "esp32s3") list(APPEND module_srcs - "i2s.c" "touch.c" "rmt.c" "pulsecnt.c" From fef57344db4d4cbd117164b928b90afed28f3859 Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Sun, 2 Oct 2022 22:44:56 +0000 Subject: [PATCH 18/47] Remove the touch driver as well --- components/modules/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/modules/CMakeLists.txt b/components/modules/CMakeLists.txt index fa21e262..2301e73d 100644 --- a/components/modules/CMakeLists.txt +++ b/components/modules/CMakeLists.txt @@ -59,13 +59,11 @@ if(IDF_TARGET STREQUAL "esp32") elseif(IDF_TARGET STREQUAL "esp32s2") list(APPEND module_srcs "dac.c" - "touch.c" "rmt.c" "pulsecnt.c" ) elseif(IDF_TARGET STREQUAL "esp32s3") list(APPEND module_srcs - "touch.c" "rmt.c" "pulsecnt.c" "sdmmc.c" From d8f07ddf90a278abb77a5247d4ce34201de9e56d Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Sun, 2 Oct 2022 22:52:16 +0000 Subject: [PATCH 19/47] Remove another not-compiling module --- components/modules/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/components/modules/CMakeLists.txt b/components/modules/CMakeLists.txt index 2301e73d..80455ef9 100644 --- a/components/modules/CMakeLists.txt +++ b/components/modules/CMakeLists.txt @@ -66,7 +66,6 @@ elseif(IDF_TARGET STREQUAL "esp32s3") list(APPEND module_srcs "rmt.c" "pulsecnt.c" - "sdmmc.c" ) elseif(IDF_TARGET STREQUAL "esp32c3") list(APPEND module_srcs From 38ccd7b2bad747eb8cc3f54eec47bde2b86054f9 Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Tue, 11 Oct 2022 14:43:20 +1100 Subject: [PATCH 20/47] Upgrade IDF to v4.4.2 Slightly reworked embed_lfs.sh to better cope with attribute size changes in future compiler versions, without needing to be updated again. RMT register naming changed once again... --- components/modules/node.c | 2 +- components/platform/ws2812.c | 4 +++- sdk/esp32-esp-idf | 2 +- tools/embed_lfs.sh | 26 ++++++++++++++++---------- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/components/modules/node.c b/components/modules/node.c index d2ceebb2..af25ccd3 100644 --- a/components/modules/node.c +++ b/components/modules/node.c @@ -111,9 +111,9 @@ static int node_bootreason( lua_State *L) #endif #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) case GLITCH_RTC_RESET: + case EFUSE_RESET: #endif #if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) - case EFUSE_RESET: case USB_UART_CHIP_RESET: case USB_JTAG_CHIP_RESET: case POWER_GLITCH_RESET: diff --git a/components/platform/ws2812.c b/components/platform/ws2812.c index f825c9da..94be4613 100644 --- a/components/platform/ws2812.c +++ b/components/platform/ws2812.c @@ -104,10 +104,12 @@ static void ws2812_isr(void *arg) RMT.int_clr.val = BIT(channel+24); ws2812_chain_t *chain = &(ws2812_chains[channel]); -#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) +#if defined(CONFIG_IDF_TARGET_ESP32) uint32_t data_sub_len = RMT.tx_lim_ch[channel].limit/8; #elif defined(CONFIG_IDF_TARGET_ESP32S3) uint32_t data_sub_len = RMT.chn_tx_lim[channel].tx_lim_chn/8; +#elif defined(CONFIG_IDF_TARGET_ESP32S2) + uint32_t data_sub_len = RMT.tx_lim_ch[channel].tx_lim/8; #else uint32_t data_sub_len = RMT.tx_lim[channel].limit/8; #endif diff --git a/sdk/esp32-esp-idf b/sdk/esp32-esp-idf index d83021a6..1b16ef6c 160000 --- a/sdk/esp32-esp-idf +++ b/sdk/esp32-esp-idf @@ -1 +1 @@ -Subproject commit d83021a6e8550b4d462e11d61aaab0214dc03f5a +Subproject commit 1b16ef6cfc2479a08136782f9dc57effefa86f66 diff --git a/tools/embed_lfs.sh b/tools/embed_lfs.sh index 8d9ae6d6..d0f639a2 100755 --- a/tools/embed_lfs.sh +++ b/tools/embed_lfs.sh @@ -1,6 +1,6 @@ #!/bin/bash -LUA_APP_SRC="$@" +LUA_APP_SRC=("$@") MAP_FILE=build/nodemcu.map LUAC_OUTPUT=build/luac.out @@ -15,43 +15,49 @@ if [ ! -f "${LUAC_CROSS}" ]; then exit 1 fi -LFS_ADDR_SIZE=$(grep -E "0x[0-9a-f]+[ ]+0x[0-9a-f]+[ ]+esp-idf/embedded_lfs/libembedded_lfs.a\(lua.flash.store.reserved.S.obj\)" "${MAP_FILE}" | grep -v -w 0x0 | grep -v -w 0x24 | tr -s ' ') -if [ -z "${LFS_ADDR_SIZE}" ]; then +# Extract the line containing the data size of the LFS object, filtering out +# lines for .bss/.data/.text, sorting the remaining two entries (actual LFS +# data and (optional) riscv attributes) so we can discard the latter if +# present. If the map file was a bit saner with its line breaks this would +# have been a straight forward grep for for .rodata.embedded.*lua.flash.store +LFS_SIZE_ADDR=$(grep -E "0x[0-9a-f]+[ ]+0x[0-9a-f]+[ ]+esp-idf/embedded_lfs/libembedded_lfs.a\(lua.flash.store.reserved.S.obj\)" "${MAP_FILE}" | grep -v '^ \.' | awk '{print $2,$1}' | sort -n -k 1.3 | tail -1) +if [ -z "${LFS_SIZE_ADDR}" ]; then echo "Error: LFS segment not found. Use 'make clean; make' perhaps?" exit 1 fi -LFS_ADDR=$(echo "${LFS_ADDR_SIZE}" | cut -d ' ' -f 2) +LFS_ADDR=$(echo "${LFS_SIZE_ADDR}" | cut -d ' ' -f 2) if [ -z "${LFS_ADDR}" ]; then echo "Error: LFS segment address not found" exit 1 fi # The reported size is +4 due to the length field added by the IDF -LFS_SIZE=$(( $(echo "${LFS_ADDR_SIZE}" | cut -d ' ' -f 3) - 4 )) +LFS_SIZE=$(( $(echo "${LFS_SIZE_ADDR}" | cut -d ' ' -f 1) - 4 )) if [ -z "${LFS_SIZE}" ]; then echo "Error: LFS segment size not found" exit 1 fi -echo "LFS segment address ${LFS_ADDR}, length ${LFS_SIZE}" +printf "LFS segment address %s, length %s (0x%x)\n" "${LFS_ADDR}" "${LFS_SIZE}" "${LFS_SIZE}" if ${LUAC_CROSS} -v | grep -q 'Lua 5.1' then echo "Generating Lua 5.1 LFS image..." - ${LUAC_CROSS} -a ${LFS_ADDR} -m ${LFS_SIZE} -o ${LUAC_OUTPUT} ${LUA_APP_SRC} + ${LUAC_CROSS} -a "${LFS_ADDR}" -m ${LFS_SIZE} -o ${LUAC_OUTPUT} "${LUA_APP_SRC[@]}" else set -e echo "Generating intermediate Lua 5.3 LFS image..." - ${LUAC_CROSS} -f -m ${LFS_SIZE} -o ${LUAC_OUTPUT}.tmp ${LUA_APP_SRC} + ${LUAC_CROSS} -f -m ${LFS_SIZE} -o ${LUAC_OUTPUT}.tmp "${LUA_APP_SRC[@]}" echo "Converting to absolute LFS image..." - ${LUAC_CROSS} -F ${LUAC_OUTPUT}.tmp -a ${LFS_ADDR} -o ${LUAC_OUTPUT} + ${LUAC_CROSS} -F ${LUAC_OUTPUT}.tmp -a "${LFS_ADDR}" -o ${LUAC_OUTPUT} rm ${LUAC_OUTPUT}.tmp fi +# shellcheck disable=SC2181 if [ $? != 0 ]; then echo "Error: luac.cross failed" exit 1 else - echo "Generated $(ls -l ${LUAC_OUTPUT} | cut -f5 -d' ') bytes of LFS data" + echo "Generated $(stat -c "%s" ${LUAC_OUTPUT}) bytes of LFS data" fi # cmake depencies don't seem to pick up the change to luac.out? rm -f build/lua.flash.store.reserved From cad125c47738e9f06aeaecdfe1839240cb402ca1 Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Tue, 11 Oct 2022 15:33:32 +1100 Subject: [PATCH 21/47] Update to use node16 actions per github advice. --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e9aa23b0..d10f5d1c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,14 +15,14 @@ jobs: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: recursive - name: Prepare cache key run: git rev-parse HEAD:sdk/esp32-esp-idf > idf.rev shell: bash - name: Cache Espressif tools - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.espressif key: ${{ runner.os }}-espressif-tools-${{ hashFiles('idf.rev') }} @@ -58,7 +58,7 @@ jobs: echo lua_build_opts="$(expr "$(./build/luac_cross/luac.cross -v)" : '.*\[\(.*\)\]')" >> $GITHUB_ENV shell: bash - name: Upload luac.cross - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: ${{ success() }} with: name: luac.cross-${{ env.lua_build_opts }}-${{ matrix.target }} From e3883cd27b0cb2fbdf1fb3f3e73915deae2df412 Mon Sep 17 00:00:00 2001 From: Tom Sutcliffe Date: Sun, 6 Nov 2022 13:29:03 +0000 Subject: [PATCH 22/47] Fix file.list() zero sizes. Fixes #3549. The modern spiffs backend doesn't like stat("./somefile") for something on the root of the filesystem, and instead only accepts "somefile" (it also doesn't like "/somefile"). The error from stat was being ignored which is why the file sizes all appeared to be zero. The fix is to change file.list() to pass just the filename, unless a directory was passed to list(). Also improved error handling a bit. --- components/modules/file.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/components/modules/file.c b/components/modules/file.c index e77ab54a..458d1fd1 100644 --- a/components/modules/file.c +++ b/components/modules/file.c @@ -37,20 +37,33 @@ static int file_format( lua_State* L ) // Lua: list() static int file_list( lua_State* L ) { - const char *dirname = luaL_optstring(L, 1, "."); - + const char *dirname = luaL_optstring(L, 1, NULL); + DIR *dir; - if ((dir = opendir(dirname))) { + if ((dir = opendir(dirname ? dirname : "/"))) { lua_newtable( L ); struct dirent *e; while ((e = readdir(dir))) { - char *fname; - asprintf(&fname, "%s/%s", dirname, e->d_name); - if (!fname) - return luaL_error(L, "no memory"); + char *fname = NULL; + if (dirname) { + asprintf(&fname, "%s/%s", dirname, e->d_name); + if (!fname) { + closedir(dir); + return luaL_error(L, "no memory"); + } + } else { + fname = e->d_name; + } struct stat st = { 0, }; - stat(fname, &st); - free(fname); + int err = stat(fname, &st); + if (err) { + // We historically ignored this error, so just warn (although it + // shouldn't really happen now). + NODE_ERR("Failed to stat %s err=%d\n", fname, err); + } + if (dirname) { + free(fname); + } lua_pushinteger(L, st.st_size); lua_setfield(L, -2, e->d_name); } From f592cf4ee52d285a73b99e6719f4e5ed750ff164 Mon Sep 17 00:00:00 2001 From: serg3295 <75196080+serg3295@users.noreply.github.com> Date: Fri, 11 Nov 2022 04:49:16 +0300 Subject: [PATCH 23/47] Fix ledc.md (#3557) fixes ledc.channel:fade() syntax. --- docs/modules/ledc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ledc.md b/docs/modules/ledc.md index 1237b9cc..4754ec25 100644 --- a/docs/modules/ledc.md +++ b/docs/modules/ledc.md @@ -266,7 +266,7 @@ channel:fadewithstep(1000, 10, 10); Set LEDC fade function. #### Syntax -`channel:fadewithstep(duty, direction, scale, cycleNum, stepNum [, wait])` +`channel:fade(duty, direction, scale, cycleNum, stepNum [, wait])` #### Parameters - `duty` Set the start of the gradient duty. From ed85e21a9b8ce2a0b088f1442b7a52514c38a148 Mon Sep 17 00:00:00 2001 From: Tom Sutcliffe Date: Mon, 17 Oct 2022 22:39:56 +0100 Subject: [PATCH 24/47] Allow LUA_INIT_STRING to be set in menuconfig --- components/lua/Kconfig | 31 +++++++++++++++++++++++++++++++ components/lua/lua-5.3/lua.c | 6 +----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/components/lua/Kconfig b/components/lua/Kconfig index 7e16ec1e..f41170ef 100644 --- a/components/lua/Kconfig +++ b/components/lua/Kconfig @@ -132,4 +132,35 @@ menu "Lua configuration" select NODEMCU_CMODULE_UART select LUA_BUILTIN_DEBUG + choice LUA_INIT_STRING + prompt "Boot command" + default LUA_INIT_STRING_INIT_LUA + help + Command to run on boot. This can be a .lua file, an LFS module, or + any valid Lua expression. By default init.lua is loaded and run + from the SPIFFS filesystem. + config LUA_INIT_STRING_INIT_LUA + bool "init.lua from SPIFFS" + config LUA_INIT_STRING_INIT_LFS + bool "init module from LFS" + config LUA_INIT_STRING_CUSTOM + bool "Custom" + endchoice + + config LUA_INIT_STRING_CUSTOM_STRING + string "Custom boot command" if LUA_INIT_STRING_CUSTOM + default "" + help + Run a custom command on boot. + + Specify @filename.lua to load "filename.lua" from SPIFFS. + Specify node.LFS.get('foo')() to load the module "foo" from LFS. + Or specify any other valid Lua expression to execute that on boot. + + config LUA_INIT_STRING + string + default "@init.lua" if LUA_INIT_STRING_INIT_LUA + default "node.LFS.get('init')()" if LUA_INIT_STRING_INIT_LFS + default LUA_INIT_STRING_CUSTOM_STRING if LUA_INIT_STRING_CUSTOM + endmenu diff --git a/components/lua/lua-5.3/lua.c b/components/lua/lua-5.3/lua.c index 599c850e..5427b30b 100644 --- a/components/lua/lua-5.3/lua.c +++ b/components/lua/lua-5.3/lua.c @@ -30,11 +30,7 @@ #endif #ifndef LUA_INIT_STRING -# if defined(CONFIG_NODEMCU_EMBED_LFS) -# define LUA_INIT_STRING "node.LFS.get('init')()" -# else -# define LUA_INIT_STRING "@init.lua" -# endif +# define LUA_INIT_STRING CONFIG_LUA_INIT_STRING #endif #if !defined(STARTUP_COUNT) From a40d69dbe8ab0db360ecffd0a4614016aaa4d52a Mon Sep 17 00:00:00 2001 From: Tom Sutcliffe Date: Mon, 12 Dec 2022 15:04:15 +0000 Subject: [PATCH 25/47] Make sodium.random API behave consistently on all build types By returning result as a signed 32-bit int between INT32_MIN to IN32_MAX which is represented the same regardless of 64-bit support or integral-only builds. Also updated docs, fixed links and clarified behavior of random APIs when WiFi isn't started. --- components/modules/sodium.c | 4 ++-- docs/modules/sodium.md | 18 +++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/components/modules/sodium.c b/components/modules/sodium.c index 878bd6ef..5b655041 100644 --- a/components/modules/sodium.c +++ b/components/modules/sodium.c @@ -17,7 +17,7 @@ static int l_randombytes_random(lua_State *L) { check_init(L); uint32_t ret = randombytes_random(); - lua_pushnumber(L, (lua_Number)ret); + lua_pushinteger(L, (int32_t)ret); return 1; } @@ -26,7 +26,7 @@ static int l_randombytes_uniform(lua_State *L) check_init(L); uint32_t upper_bound = (uint32_t)luaL_checkinteger(L, 1); uint32_t ret = randombytes_uniform(upper_bound); - lua_pushnumber(L, (lua_Number)ret); + lua_pushinteger(L, (int32_t)ret); return 1; } diff --git a/docs/modules/sodium.md b/docs/modules/sodium.md index c3b9097d..c43c1d01 100644 --- a/docs/modules/sodium.md +++ b/docs/modules/sodium.md @@ -9,13 +9,15 @@ In addition to the flag for enabling this module during ROM build, `Component co !!! note - Almost all functions in this module require a working random number generator. On the ESP32 this means that *WiFi must be started* otherwise ALL OF THE CRYPTOGRAPHY WILL SILENTLY BE COMPROMISED. Make sure to call `wifi.start()` before any of the functions in this module. See the [Espressif documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/system/system.html#random-number-generation) for more information. The only exception is `sodium.crypto_box.seal_open()` which does not require a random number source to operate. + Almost all functions in this module require a working random number generator. On the ESP32 this normally means that *WiFi must be started* otherwise ALL OF THE CRYPTOGRAPHY WILL SILENTLY BE COMPROMISED. Make sure to call `wifi.start()` before any of the functions in this module. See the [Espressif documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html) for more information. The only exception is `sodium.crypto_box.seal_open()` which does not require a random number source to operate. The non-crypto functions in `sodium.random` can be used with WiFi off, providing that pseudo-randomness is acceptable for the purposes they are being used for. # Random number generation -See also [https://download.libsodium.org/doc/generating_random_data](https://download.libsodium.org/doc/generating_random_data) +See also [https://download.libsodium.org/doc/generating_random_data](https://download.libsodium.org/doc/generating_random_data). On the esp32, a custom implementation `esp_random()` is used which (usually) requires WiFi being enabled to be truly random. ## sodium.random.random() -Returns a random integer between `0` and `0xFFFFFFFF` inclusive. Note that on a build using `LUA_NUMBER_INTEGRAL`, results may appear negative due to integer overflow. Wifi must be started, by calling `wifi.start()`, before calling this function. +Returns a random signed 32-bit integer between `INT32_MIN` and `INT32_MAX`. On builds with 64-bit integer support (`5.3-int64-xxx`), you can convert this to an unsigned 32-bit integer with `result % (1 << 32)`, or on builds with only 64-bit double support (`5.1-doublefp`, `5.3-int32-doublefp`), with `result % (2 ^ 32)`. + +If WiFi is not started by calling `wifi.start()` before calling this function, then the result will only be pseudo-random, not truly random. #### Syntax `sodium.random.random()` @@ -24,22 +26,24 @@ Returns a random integer between `0` and `0xFFFFFFFF` inclusive. Note that on a None #### Returns -A uniformly-distributed random integer between `0` and `0xFFFFFFFF` inclusive. +A uniformly-distributed random 32-bit integer. ## sodium.random.uniform() -Returns a random integer `0 <= result < upper_bound`. Unlike `sodium.random.random() % upper_bound`, it guarantees a uniform distribution of the possible output values even when `upper_bound` is not a power of 2. Note that on a build using `LUA_NUMBER_INTEGRAL`, if `upper_bound >= 0x80000000` the result may appear negative due to integer overflow. Wifi must be started, by calling `wifi.start()`, before calling this function. +Returns a random integer `0 <= result < upper_bound`. Unlike `sodium.random.random() % upper_bound`, it guarantees a uniform distribution of the possible output values even when `upper_bound` is not a power of 2. Specifying an `upper_bound > 0x7FFFFFFF` is not recommended because the behavior will vary depending on the Lua build configuration. + +If WiFi is not started by calling `wifi.start()` before calling this function, then the result will only be pseudo-random, not truly random. #### Syntax `sodium.random.uniform(upper_bound)` #### Parameters -- `upper_bound` must be an integer `<= 0xFFFFFFFF`. +- `upper_bound` an integer. #### Returns An integer `>= 0` and `< upper_bound` ## sodium.random.buf() -Generates `n` bytes of random data. Wifi must be started, by calling `wifi.start()`, before calling this function. +Generates `n` bytes of random data. If WiFi is not started by calling `wifi.start()` before calling this function, then the result will only be pseudo-random, not truly random. #### Syntax `sodium.random.buf(n)` From c684180dea771fea8042bd5be54cc654d79ae133 Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Wed, 14 Dec 2022 16:26:09 +1100 Subject: [PATCH 26/47] Remove defunct extmod bits. --- .gitignore | 1 - Makefile | 8 - components/base_nodemcu/include/module.h | 22 -- docs/modules/extmods.md | 134 ------------ mkdocs.yml | 1 - tools/extmod/component.mk.template | 16 -- tools/extmod/example/helloworld.c | 61 ------ tools/extmod/extmod.sh | 166 --------------- tools/extmod/read_ini.sh | 256 ----------------------- 9 files changed, 665 deletions(-) delete mode 100644 docs/modules/extmods.md delete mode 100644 tools/extmod/component.mk.template delete mode 100644 tools/extmod/example/helloworld.c delete mode 100755 tools/extmod/extmod.sh delete mode 100644 tools/extmod/read_ini.sh diff --git a/.gitignore b/.gitignore index d820c77c..b004739b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ build/ app/ components/*/.output/ tools/toolchains -extmods.ini .ccache bin diff --git a/Makefile b/Makefile index 0dea0f17..6b48014a 100644 --- a/Makefile +++ b/Makefile @@ -21,11 +21,3 @@ all: $(IDF_PATH)/tools/idf.py $(IDFPY_ARGS) "$@" endif - -# FIXME - needs updating to work in IDF4 -# -#extmod-update: -# @tools/extmod/extmod.sh update -# -#extmod-clean: -## @tools/extmod/extmod.sh clean diff --git a/components/base_nodemcu/include/module.h b/components/base_nodemcu/include/module.h index d188f06a..28e38e39 100644 --- a/components/base_nodemcu/include/module.h +++ b/components/base_nodemcu/include/module.h @@ -52,25 +52,3 @@ ROTable_entry MODULE_EXPAND_PASTE_(cfgname,MODULE_EXPAND_PASTE_(_module_selected,MODULE_PASTE_(CONFIG_NODEMCU_CMODULE_,cfgname))) \ = {luaname, LRO_ROVAL(map)} #endif - - -// helper stringing macros -#define xstr(s) str(s) -#define str(s) #s - -// EXTMODNAME is injected by the generated component.mk -#ifdef EXTMODNAME -#define MODNAME xstr(EXTMODNAME) -#else -#define MODNAME "module" -#endif - -// use NODEMCU_MODULE_METATABLE() to generate a unique metatable name for your objects: -#define NODEMCU_MODULE_METATABLE() MODULE_EXPAND_(MODNAME xstr(__COUNTER__)) - -// NODEMCU_MODULE_STD() defines the entry points for an external module: -#define NODEMCU_MODULE_STD() \ - static const LOCK_IN_SECTION(libs) \ - ROTable_entry lua_lib_module = {MODNAME, LRO_FUNCVAL(module_init)}; \ - const const LOCK_IN_SECTION(rotable) \ - ROTable_entry MODULE_EXPAND_PASTE_(EXTMODNAME, _entry) = {MODNAME, LRO_ROVAL(module_map)}; diff --git a/docs/modules/extmods.md b/docs/modules/extmods.md deleted file mode 100644 index 05730e83..00000000 --- a/docs/modules/extmods.md +++ /dev/null @@ -1,134 +0,0 @@ - -# External modules. Plugging your own C modules. **BETA** - -Note: this feature is still in beta. Configuration files / API may change. - -In order to make the most of NodeMCU, you will undoubtedly have to connect your ESP device to many different hardware modules, which will require you to write driver code in C and expose a Lua interface. - -To make this easy, we have come up with the concept of "external modules". External modules allow you to refer to an external git repository containing the module, which will be downloaded/updated and built along the firmware. It is similar to git submodules, but without the complexity and having to alter the parent repository, while also adapted to the compilation requirements of NodeMCU and Lua. - -## How to use external modules: - -To use external modules, simply create an `extmods.ini` file in the repository root, with the following syntax: - -```ini -[lua_mount_point] -url= -ref= -... -``` - -Where: -* **lua_mount_point**: Name you want the referenced module to have in Lua. -* **url**: Repository URL where to fetch the code from -* **ref**: branch, tag or commit hash of the version of the module you want to pull in. -* **disabled**: (optional) Whether or not to actually compile the module (see below) - -For example: - -```ini -[helloworld] -url=git@github.com:espore-ide/nodemcu-module-helloworld.git -ref=master -``` - -You can add further sections to `extmods.ini`, one for each module you want to add. Once this file is ready, run the update command: - -```shell -make extmod-update -``` - -This will download or update the modules to the external modules directory, `components/modules/external`. - -You can now compile the firmware with `make` as usual. The build system will find your external modules and compile them along the core modules. - -After this is flashed to the device, the module in the example will be available in lua as `helloworld`. - -### Updating to the latest code - -If your external module entry in `extmods.ini` points to a branch, e.g., `master`, you can update your local version to the latest code anytime by simply running `make extmod-update`. - -### Temporarily disabling an external module - -If you want to stop compiling and including a module in the build for any reason, without having to remove the entry from `extmods.ini`, simply add a `disabled=true` entry in the module section and run `make extmod-update`. - -Example: -```ini -[helloworld] -url=https://github.com/espore-ide/nodemcu-module-helloworld.git -ref=master -disabled=true -``` - -### Mounting different module versions - -Provided the module is well written (no global variables, etc), it is even possible to easily mount different versions of the same module simultaneously for testing: - -```ini -[helloworld] -url=https://github.com/espore-ide/nodemcu-module-helloworld.git -ref=master - -[helloworld_dev] -url=https://github.com/espore-ide/nodemcu-module-helloworld.git -ref=dev -``` - -Note that the second one points to a different branch, `dev`. Both modules will be visible in Lua under `helloworld` and `helloworld_dev` respectively. - -## How to write external modules: - -To write your own external module do the following: - -1. Create an empty repository in your favorite remote, GitHub, BitBucket, GitLab, etc, or fork the helloworld example. -2. Create an entry in `extmods.ini` as explained above, with the `url=` key pointing to your repository. For modules that you author, it is recommended to use an updateable git URL in SSH format, such as `git@github.com:espore-ide/nodemcu-module-helloworld.git`. -3. Run `make extmod-update` - -You can now change to `components/modules/external/your_module` and begin work. Since that is your own repository, you can work normally, commit, create branches, etc. - -### External module scaffolding - -External modules must follow a specific structure to declare the module in C. Please refer to the [helloworld.c](https://github.com/nodemcu/nodemcu-firmware/blob/dev-esp32/tools/example/helloworld.c) example, or use it as a template. In particular: - -1. Include `module.h` -2. Define a Module Function Map with name `module` -3. Define a `module_init` function -4. Include the module lua entries by adding a call to the `NODEMCU_MODULE_STD` macro - -Here is a bare minimum module: -```c -#include "module.h" - -// Module function map -LROT_BEGIN(module) -/* module-level functions go here*/ -LROT_END(module, NULL, 0) - -// module_init is invoked on device startup -static int module_init(lua_State* L) { - // initialize your module, register metatables, etc - return 0; -} - -NODEMCU_MODULE_STD(); // define Lua entries -``` -For a full example module boilerplate, check the [helloworld.c](https://github.com/nodemcu/nodemcu-firmware/blob/dev-esp32/tools/example/helloworld.c) file. - - -### Special makefile or compilation requirements - -If your module has special makefile requirements, you can create a `module.mk` file at the root of your module repository. This will be executed during compilation. - -### What is this "component.mk" file that appeared in my repo when running `make extmod-update` ? - -This file is ignored by your repository. Do not edit or check it in!. This file contains the necessary information to compile your module along with the others. - -Note that you don't even need to add this file to your `.gitignore`, since the `make extmod-update` operation configures your local copy to ignore it (via `.git/info/exclude`). - -## Further work: - -* Support for per-module menuconfig (`Kconfig`). This is actually possible already, but need to work around potential config variable collisions in case two module authors happen to choose the same variable names. -* Module registry: Create an official repository of known external modules. -* Move all non-essential and specific hardware-related C modules currently in the NodeMCU repository to external modules, each in their own repository. -* Create the necessary scaffolding to facilitate writing modules that will work both in ESP8266 and ESP32. -* Port this work to the ESP8266 branch. Mostly, the scripts that make this possible could work in both branches directly. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 7a7acf8e..eec8f0c0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -38,7 +38,6 @@ pages: - Lua 5.3 Support: 'lua53.md' - Lua Flash Store (LFS): 'lfs.md' - Filesystem on SD card: 'sdcard.md' - - Writing external C modules: 'modules/extmods.md' - C Modules: - 'adc': 'modules/adc.md' - 'bit': 'modules/bit.md' diff --git a/tools/extmod/component.mk.template b/tools/extmod/component.mk.template deleted file mode 100644 index 954571d8..00000000 --- a/tools/extmod/component.mk.template +++ /dev/null @@ -1,16 +0,0 @@ -# This file is autogenerated! DO NOT EDIT!! -# To add special makefile directives, create a module.mk file in your repo -# Make sure you have this file in your .gitignore to avoid checking it in your module repo - -MODNAME="%%MODNAME%%" -MODULE_DIR := $(dir $(lastword $(MAKEFILE_LIST))) - -COMPONENT_ADD_LDFLAGS=-u $(MODNAME)_entry -l$(MODNAME) - -CFLAGS += \ - -DEXTMODNAME=$(MODNAME) \ - -Werror=unused-function \ - -Werror=unused-but-set-variable \ - -Werror=unused-variable - --include $(MODULE_DIR)module.mk \ No newline at end of file diff --git a/tools/extmod/example/helloworld.c b/tools/extmod/example/helloworld.c deleted file mode 100644 index 89715438..00000000 --- a/tools/extmod/example/helloworld.c +++ /dev/null @@ -1,61 +0,0 @@ -// Helloworld sample module - -#include "esp_log.h" -#include "lauxlib.h" -#include "lnodeaux.h" -#include "module.h" - -static const char* HELLOWORLD_METATABLE = NODEMCU_MODULE_METATABLE(); - -// hello_context_t struct contains information to wrap a "hello world object" -typedef struct { - char* my_name; // pointer to the greeter's name -} hello_context_t; - -// Lua: helloworldobj:hello(text) -static int helloworld_hello(lua_State* L) { - hello_context_t* context = (hello_context_t*)luaL_checkudata(L, 1, HELLOWORLD_METATABLE); - printf("Hello, %s: %s\n", context->my_name, luaL_optstring(L, 2, "How are you?")); - return 0; -} - -// helloworld_delete is called on garbage collection -static int helloworld_delete(lua_State* L) { - hello_context_t* context = (hello_context_t*)luaL_checkudata(L, 1, HELLOWORLD_METATABLE); - printf("Helloworld object with name '%s' garbage collected\n", context->my_name); - luaX_free_string(L, context->my_name); - return 0; -} - -// Lua: modulename.new(string) -static int helloworld_new(lua_State* L) { - //create a new lua userdata object and initialize to 0. - hello_context_t* context = (hello_context_t*)lua_newuserdata(L, sizeof(hello_context_t)); - - context->my_name = luaX_alloc_string(L, 1, 100); - - luaL_getmetatable(L, HELLOWORLD_METATABLE); - lua_setmetatable(L, -2); - - return 1; //one object returned, the helloworld context wrapped in a lua userdata object -} - -// object function map: -LROT_BEGIN(helloworld_metatable) -LROT_FUNCENTRY(hello, helloworld_hello) -LROT_FUNCENTRY(__gc, helloworld_delete) -LROT_TABENTRY(__index, helloworld_metatable) -LROT_END(helloworld_metatable, NULL, 0) - -// Module function map -LROT_BEGIN(module) -LROT_FUNCENTRY(new, helloworld_new) -LROT_END(module, NULL, 0) - -// module_init is invoked on device startup -static int module_init(lua_State* L) { - luaL_rometatable(L, HELLOWORLD_METATABLE, (void*)helloworld_metatable_map); // create metatable for helloworld - return 0; -} - -NODEMCU_MODULE_STD(); // define Lua entries \ No newline at end of file diff --git a/tools/extmod/extmod.sh b/tools/extmod/extmod.sh deleted file mode 100755 index 789d0fd8..00000000 --- a/tools/extmod/extmod.sh +++ /dev/null @@ -1,166 +0,0 @@ -#!/bin/bash - -# External modules update script. - -# This script parses the repository root extmods.ini file to locate -# external modules to download - -export EXTMOD_DIR="components/modules/external" # Location where to store external modules: -EXTMOD_BIN_PATH="./tools/extmod" # Location of this script -TEMPLATE_MK="$EXTMOD_BIN_PATH/component.mk.template" # Location of the template component.mk - -# Include the ini file reader script -. $EXTMOD_BIN_PATH/read_ini.sh - -# Returns the given value in the INI file, passing section and value -function sectionVar() { - local varname="INI__$1__$2" - echo "${!varname}" -} - -# helper pushd to make it silent -function pushd() { - command pushd "$@" >/dev/null -} - -# helper popd to make it silent -function popd() { - command popd >/dev/null -} - -function usage() { - - echo "" - echo "extmod.sh - Manages external modules" - echo "Usage:" - echo "extmod.sh " - echo "update : Parses extmods.ini and updates all modules" - echo "clean : Effectively cleans the contents of the external modules directory ($EXTMOD_DIR)" - echo "" - -} - -# Generic command line parser -function readCommandLine() { - while test ${#} -gt 0; do - case "$1" in - "clean") - CLEAN=1 - ;; - "update") - UPDATE=1 - ;; - *) - echo -e "Error: Unrecognized parameter\n" - usage - exit 1 - ;; - esac - shift - done -} - -function updateMod() { - local modname="$1" - local url="$(sectionVar "$modname" "url")" - local ref="$(sectionVar "$modname" "ref")" - local disabled="$(sectionVar "$modname" "disabled")" - local path="$EXTMOD_DIR/$modname" - local component_mk="$path/component.mk" - - if [[ ! -d "$path" ]]; then - echo "$modname not present. Downloading from $url ..." - if ! git clone --quiet "$url" -b "$ref" "$path"; then - echo "Error cloning $modname in $url" - return 1 - fi - # Add "component.mk" to local repo gitignore - echo "component.mk" >>"$path/.git/info/exclude" - fi - echo "Updating $modname ..." - - if ! pushd "$path"; then - echo "Cannot change to $path". - return 1 - fi - if ! git status >/dev/null; then - echo "Error processing $modname. Error reading git repo status." - popd - return 1 - fi - if [ "$(git status --short)" == "" ]; then - if ! git fetch --quiet; then - echo "Error fetching $modname" - popd - return 1 - fi - if ! git checkout "$ref" --quiet; then - echo "Error setting ref $ref in $modname. Does $ref exist?" - popd - return 1 - fi - if ! git clean -d -f --quiet; then - echo "Error repo $modname after checkout." - popd - return 1 - fi - # check if HEAD was detached (like when checking out a tag or commit) - if git symbolic-ref HEAD 2>/dev/null; then - # This is a branch. Update it. - if ! git pull --quiet; then - echo "Error pulling $ref from $url." - popd - return 1 - fi - fi - - else - echo "$modname working directory in $path is not clean. Skipping..." - popd - return 0 - fi - popd - - if [ "$disabled" != "1" ]; then - if ! sed 's/%%MODNAME%%/'"$modname"'/g' "$TEMPLATE_MK" >"$component_mk"; then - echo "Error generating $component_mk" - return 1 - fi - else - echo "Warning: Module $modname is disabled and won't be included in build" - [ -f "$component_mk" ] && rm "$component_mk" - fi - echo "Successfully updated $modname." - return 0 -} - -function update() { - - if ! read_ini "extmods.ini"; then - echo "Error reading extmods.ini" - fi - - mkdir -p "$EXTMOD_DIR" - - for modname in $INI__ALL_SECTIONS; do - if ! updateMod "$modname"; then - echo "Error updating $modname" - return 1 - fi - done - echo "Successfully updated all modules" -} - -function main() { - if [ "$CLEAN" == "1" ]; then - echo "Cleaning ${EXTMOD_DIR:?} ..." - rm -rf "${EXTMOD_DIR:?}/"* - fi - - if [ "$UPDATE" == "1" ]; then - update - fi -} - -readCommandLine "$@" -main diff --git a/tools/extmod/read_ini.sh b/tools/extmod/read_ini.sh deleted file mode 100644 index 618bbf9c..00000000 --- a/tools/extmod/read_ini.sh +++ /dev/null @@ -1,256 +0,0 @@ -#!/bin/bash -# Copyright (c) 2009 Kevin Porter / Advanced Web Construction Ltd -# (http://coding.tinternet.info, http://webutils.co.uk) -# Copyright (c) 2010-2014 Ruediger Meier -# (https://github.com/rudimeier/) -# -# License: BSD-3-Clause, see LICENSE file -# -# Simple INI file parser. -# -# See README for usage. -# -# - -function read_ini() { - # Be strict with the prefix, since it's going to be run through eval - function check_prefix() { - if ! [[ "${VARNAME_PREFIX}" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then - echo "read_ini: invalid prefix '${VARNAME_PREFIX}'" >&2 - return 1 - fi - } - - function check_ini_file() { - if [ ! -r "$INI_FILE" ]; then - echo "read_ini: '${INI_FILE}' doesn't exist or not" \ - "readable" >&2 - return 1 - fi - } - - # enable some optional shell behavior (shopt) - function pollute_bash() { - if ! shopt -q extglob; then - SWITCH_SHOPT="${SWITCH_SHOPT} extglob" - fi - if ! shopt -q nocasematch; then - SWITCH_SHOPT="${SWITCH_SHOPT} nocasematch" - fi - shopt -q -s ${SWITCH_SHOPT} - } - - # unset all local functions and restore shopt settings before returning - # from read_ini() - function cleanup_bash() { - shopt -q -u ${SWITCH_SHOPT} - unset -f check_prefix check_ini_file pollute_bash cleanup_bash - } - - local INI_FILE="" - local INI_SECTION="" - - # {{{ START Deal with command line args - - # Set defaults - local BOOLEANS=1 - local VARNAME_PREFIX=INI - local CLEAN_ENV=0 - - # {{{ START Options - - # Available options: - # --boolean Whether to recognise special boolean values: ie for 'yes', 'true' - # and 'on' return 1; for 'no', 'false' and 'off' return 0. Quoted - # values will be left as strings - # Default: on - # - # --prefix=STRING String to begin all returned variables with (followed by '__'). - # Default: INI - # - # First non-option arg is filename, second is section name - - while [ $# -gt 0 ]; do - - case $1 in - - --clean | -c) - CLEAN_ENV=1 - ;; - - --booleans | -b) - shift - BOOLEANS=$1 - ;; - - --prefix | -p) - shift - VARNAME_PREFIX=$1 - ;; - - *) - if [ -z "$INI_FILE" ]; then - INI_FILE=$1 - else - if [ -z "$INI_SECTION" ]; then - INI_SECTION=$1 - fi - fi - ;; - - esac - - shift - done - - if [ -z "$INI_FILE" ] && [ "${CLEAN_ENV}" = 0 ]; then - echo -e "Usage: read_ini [-c] [-b 0| -b 1]] [-p PREFIX] FILE" \ - "[SECTION]\n or read_ini -c [-p PREFIX]" >&2 - cleanup_bash - return 1 - fi - - if ! check_prefix; then - cleanup_bash - return 1 - fi - - local INI_ALL_VARNAME="${VARNAME_PREFIX}__ALL_VARS" - local INI_ALL_SECTION="${VARNAME_PREFIX}__ALL_SECTIONS" - local INI_NUMSECTIONS_VARNAME="${VARNAME_PREFIX}__NUMSECTIONS" - if [ "${CLEAN_ENV}" = 1 ]; then - eval unset "\$${INI_ALL_VARNAME}" - fi - unset ${INI_ALL_VARNAME} - unset ${INI_ALL_SECTION} - unset ${INI_NUMSECTIONS_VARNAME} - - if [ -z "$INI_FILE" ]; then - cleanup_bash - return 0 - fi - - if ! check_ini_file; then - cleanup_bash - return 1 - fi - - # Sanitise BOOLEANS - interpret "0" as 0, anything else as 1 - if [ "$BOOLEANS" != "0" ]; then - BOOLEANS=1 - fi - - # }}} END Options - - # }}} END Deal with command line args - - local LINE_NUM=0 - local SECTIONS_NUM=0 - local SECTION="" - - # IFS is used in "read" and we want to switch it within the loop - local IFS=$' \t\n' - local IFS_OLD="${IFS}" - - # we need some optional shell behavior (shopt) but want to restore - # current settings before returning - local SWITCH_SHOPT="" - pollute_bash - - while read -r line || [ -n "$line" ]; do - #echo line = "$line" - - ((LINE_NUM++)) - - # Skip blank lines and comments - if [ -z "$line" -o "${line:0:1}" = ";" -o "${line:0:1}" = "#" ]; then - continue - fi - - # Section marker? - if [[ "${line}" =~ ^\[[a-zA-Z0-9_]{1,}\]$ ]]; then - - # Set SECTION var to name of section (strip [ and ] from section marker) - SECTION="${line#[}" - SECTION="${SECTION%]}" - eval "${INI_ALL_SECTION}=\"\${${INI_ALL_SECTION}# } $SECTION\"" - ((SECTIONS_NUM++)) - - continue - fi - - # Are we getting only a specific section? And are we currently in it? - if [ ! -z "$INI_SECTION" ]; then - if [ "$SECTION" != "$INI_SECTION" ]; then - continue - fi - fi - - # Valid var/value line? (check for variable name and then '=') - if ! [[ "${line}" =~ ^[a-zA-Z0-9._]{1,}[[:space:]]*= ]]; then - echo "Error: Invalid line:" >&2 - echo " ${LINE_NUM}: $line" >&2 - cleanup_bash - return 1 - fi - - # split line at "=" sign - IFS="=" - read -r VAR VAL <<<"${line}" - IFS="${IFS_OLD}" - - # delete spaces around the equal sign (using extglob) - VAR="${VAR%%+([[:space:]])}" - VAL="${VAL##+([[:space:]])}" - VAR=$(echo $VAR) - - # Construct variable name: - # ${VARNAME_PREFIX}__$SECTION__$VAR - # Or if not in a section: - # ${VARNAME_PREFIX}__$VAR - # In both cases, full stops ('.') are replaced with underscores ('_') - if [ -z "$SECTION" ]; then - VARNAME=${VARNAME_PREFIX}__${VAR//./_} - else - VARNAME=${VARNAME_PREFIX}__${SECTION}__${VAR//./_} - fi - eval "${INI_ALL_VARNAME}=\"\${${INI_ALL_VARNAME}# } ${VARNAME}\"" - - if [[ "${VAL}" =~ ^\".*\"$ ]]; then - # remove existing double quotes - VAL="${VAL##\"}" - VAL="${VAL%%\"}" - elif [[ "${VAL}" =~ ^\'.*\'$ ]]; then - # remove existing single quotes - VAL="${VAL##\'}" - VAL="${VAL%%\'}" - elif [ "$BOOLEANS" = 1 ]; then - # Value is not enclosed in quotes - # Booleans processing is switched on, check for special boolean - # values and convert - - # here we compare case insensitive because - # "shopt nocasematch" - case "$VAL" in - yes | true | on) - VAL=1 - ;; - no | false | off) - VAL=0 - ;; - esac - fi - - # enclose the value in single quotes and escape any - # single quotes and backslashes that may be in the value - VAL="${VAL//\\/\\\\}" - VAL="\$'${VAL//\'/\'}'" - - eval "$VARNAME=$VAL" - done <"${INI_FILE}" - - # return also the number of parsed sections - eval "$INI_NUMSECTIONS_VARNAME=$SECTIONS_NUM" - - cleanup_bash -} From 4c510a5a6975dcb240f9585c8b4368c8a52b85ff Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Wed, 14 Dec 2022 16:57:30 +1100 Subject: [PATCH 27/47] Initial support for using external components/modules. --- CMakeLists.txt | 7 +++++++ docs/build.md | 25 ++++++++++++++++++++++ docs/nodemcu-pil.md | 10 ++++----- extcomp-template/CMakeLists.txt | 37 +++++++++++++++++++++++++++++++++ extcomp-template/Kconfig | 13 ++++++++++++ extcomp-template/mymod.c | 21 +++++++++++++++++++ 6 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 extcomp-template/CMakeLists.txt create mode 100644 extcomp-template/Kconfig create mode 100644 extcomp-template/mymod.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0448dec5..d219a56e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,11 @@ cmake_minimum_required(VERSION 3.5) + +if(DEFINED ENV{EXTRA_COMPONENT_DIRS}) + # Turn space-separated, quote-aware environment var into CMake list + separate_arguments( + EXTRA_COMPONENT_DIRS UNIX_COMMAND "$ENV{EXTRA_COMPONENT_DIRS}") +endif() + include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(nodemcu) diff --git a/docs/build.md b/docs/build.md index 2b75f6cd..07b33225 100644 --- a/docs/build.md +++ b/docs/build.md @@ -121,3 +121,28 @@ Partition Table ---> (components/platform/partitions-2MB.csv) Custom partition CSV file (0x10000) Factory app partition offset ``` + +### Using external components + +It is possible, and relatively easy, to include external components and modules in NodeMCU. It is not uncommon to have one or more custom modules one wishes to include in the firmware. To enable this NodeMCU leverages the standard IDF `EXTRA_COMPONENT_DIRS` functionality. As such, it is possible to not only add extra Lua C modules, but also other components such as libraries. + +To include one (or more) additional IDF components, simply set the `EXTRA_COMPONENT_DIRS` environment variable to the space-separated list of directories of said components. E.g. + +``` +export EXTRA_COMPONENT_DIRS="/path/to/mymod /path/to/mylib" +make menuconfig +make +``` + +To get started, a template directory structure is provided in [extcomp-template/](../extcomp-template) which provides a skeleton for a simple Lua C module, including the build logic in `CMakeLists.txt`, the configuration option in `Kconfig` and the Lua C module code in `mymod.c`. A detailed discussion on the specifics is beyond this document, but the first two are described comprehensively in the [official IDF documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html), and module development is covered in [Programming in NodeMCU](nodemcu-pil.md). + +In fact, to quickly try it out it's even possible to include the template itself, as-is: + + +``` +export EXTRA_COMPONENT_DIRS="$PWD/extcomp-template" +make menuconfig +make +``` + +after which the command `mymod.hello()` is available in the Lua environment. diff --git a/docs/nodemcu-pil.md b/docs/nodemcu-pil.md index 2943284d..712a2a25 100644 --- a/docs/nodemcu-pil.md +++ b/docs/nodemcu-pil.md @@ -6,7 +6,7 @@ NodeMCU developers are also able to develop and incorporate their own C modules Those developers who wish to develop or to modify existing C modules should have access to the LRM, PiL and NRM and familiarise themselves with these references. These are the primary references; and this document does not repeat this content, but rather provide some NodeMCU-specific information to supplement it. -From a perspective of developing C modules, there is very little difference from that of developing modules in standard Lua. All of the standard Lua library modules (`bit`, `coroutine`, `debug`, `math`, `string`, `table`, `utf8`) use the C API for Lua and the NodeMCU versions have been updated to use NRM extensions. so their source code is available for browsing and using as style template (see the corresponding `lXXXlib.c` file in GitHub [NodeMCU lua53](../app/lua53) folder). +From a perspective of developing C modules, there is very little difference from that of developing modules in standard Lua. All of the standard Lua library modules (`bit`, `coroutine`, `debug`, `math`, `string`, `table`, `utf8`) use the C API for Lua and the NodeMCU versions have been updated to use NRM extensions. so their source code is available for browsing and using as style template (see the corresponding `lXXXlib.c` file in GitHub [NodeMCU lua53](../components/lua/lua-5.3) folder). The main functional change is that NodeMCU supports a read-only subclass of the `Table` type, known as a **`ROTable`**, which can be statically declared within the module source using static `const` declarations. There are also limitations on the valid types for ROTable keys and value in order to ensure that these are consistent with static declaration; and hence ROTables are stored in code space (and therefore in flash memory on the IoT device). Hence unlike standard Lua tables, ROTables do not take up RAM resources. @@ -18,9 +18,9 @@ The `NODEMCU_MODULE` macro is used in each module to register it in an entry in - All `ROM` entries will resolve globally - The Lua runtime scans the `ROMentry` ROTable during its start up, and it will execute any non-NULL `CFunction` values in this table. This enables C modules to hook in any one-time start-up functions if they are needed. -Note that the standard `make` will include any modules found in the `app/modules` folder within a firmware build _if_ the corresponding `LUA_USE_MODULES_modname` macro has been defined. These defines are conventionally set in a common include file `user_modules.h`, and this practice is mandated for any user-submitted modules that are added to to the NodeMCU distribution. However, this does not prevent developers adding their own local modules to the `app/modules` folder and simply defining the corresponding `LUA_USE_MODULES_modname` inline. +For a module to be included in the build, it has to be enabled in the sdkconfig file (e.g. via running `make menuconfig`). Some modules are enabled by default. Between compile time macros based on the sdkconfig and linker processing only the enabled modules are actually included into the firmware. -This macro + linker approach renders the need for `luaL_reg` declarations and use of `luaL_openlib()` unnecessary, and these are not permitted in project-adopted `app/modules` files. +This macro + linker approach renders the need for `luaL_reg` declarations and use of `luaL_openlib()` unnecessary, and these are not permitted in project-adopted `components/modules` files. Hence a NodeMCU C library module typically has a standard layout that parallels that of the standard Lua library modules and uses the same C API to access the Lua runtime: @@ -33,10 +33,10 @@ Hence a NodeMCU C library module typically has a standard layout that parallels - Whilst the ROTable search algorithm is a simply linear scan of the ROTable entries, the runtime also maintains a LRU cache of ROTable accesses, so typically over 95% of ROTable accesses bypass the linear scan and do a direct access to the appropriate entry. - ROTables are also reasonable lightweight and well integrated into the Lua runtime, so the normal metamethod processing works well. This means that developers can use the `__index` method to implement other key and value typed entries through an index function. - NodeMCU modules are intended to be compilable against both our Lua 5.1 and Lua 5.3 runtimes. The NRM discusses the implications and constraints here. However note that: - - We have back-ported many new Lua 5.3 features into the NodeMCU Lua 5.1 API, so in general you can use the 5.3 API to code your modules. Again the NRM notes the exceptions where you will either need variant code or to decide to limit yourself to the the 5.3 runtime. In this last case the simplest approach is to `#if LUA_VERSION_NUM != 503` to disable the 5.3 content so that 5.1 build can compile and link. Note that all modules currently in the `app/modules` folder will compile against and execute within both the Lua 5.1 and the 5.3 environments. + - We have back-ported many new Lua 5.3 features into the NodeMCU Lua 5.1 API, so in general you can use the 5.3 API to code your modules. Again the NRM notes the exceptions where you will either need variant code or to decide to limit yourself to the the 5.3 runtime. In this last case the simplest approach is to `#if LUA_VERSION_NUM != 503` to disable the 5.3 content so that 5.1 build can compile and link. Note that all modules currently in the `components/modules` folder will compile against and execute within both the Lua 5.1 and the 5.3 environments. - Lua 5.3 uses a 32-bit representation for all numerics with separate subtypes for integer (stored as a 32 bit signed integer) and float (stored as 32bit single precision float). This achieves the same RAM storage density as Lua 5.1 integer builds without the loss of use of floating point when convenient. We have therefore decided that there is no benefit in having a separate Integer 5.3 build variant. - We recommend that developers make use of the full set of `luaL_` API calls to minimise code verbosity. We have also added a couple of registry access optimisations that both simply and improve runtime performance when using the Lua registry for callback support. - `luaL_reref()` replaces an existing registry reference in place (or creates a new one if needed). Less code and faster execution than a `luaL_unref()` plus `luaL_ref()` construct. - `luaL_unref2()` does the unref and set the static int hook to `LUA_NOREF`. -Rather than include simple examples of module templates, we suggest that you review the modules in our GitHub repository, such as the [`utf8`](../app/lua53/lutf8lib.c) library. Note that whilst all of the existing modules in `app/modules` folder compile and work, we plan to do a clean up of the core modules to ensure that they conform to best practice. +Rather than include simple examples of module templates, we suggest that you review the modules in our GitHub repository, such as the [`utf8`](../components/lua/lua-5.3/lutf8lib.c) library. Note that whilst all of the existing modules in `components/modules` folder compile and work, we plan to do a clean up of the core modules to ensure that they conform to best practice. diff --git a/extcomp-template/CMakeLists.txt b/extcomp-template/CMakeLists.txt new file mode 100644 index 00000000..118e730f --- /dev/null +++ b/extcomp-template/CMakeLists.txt @@ -0,0 +1,37 @@ +# Modify this list as necessary to include all your source files. +set(extmod_srcs + "mymod.c" +) + +# If necessary, add items to the PRIV_REQUIRES list below +idf_component_register( + SRCS ${extmod_srcs} + INCLUDE_DIRS "." "${CMAKE_CURRENT_BINARY_DIR}" + PRIV_REQUIRES + "base_nodemcu" + "lua" + "platform" +) + +# The remainder is boiler-plate glue to get the linker to actually include +# the modules enabled in Kconfig. No user-serviceable parts inside. + +# Match up all the extmod source files with their corresponding Kconfig +# option in the form NODEMCU_CMODULE_ and if enabled, add a +# "-u _module_selected1" option to force the linker to include +# the module. See components/core/include/module.h for further details on +# how this works. +set(extmods_enabled) +foreach(extmod_src ${extmod_srcs}) + string(REPLACE ".c" "" module_name ${extmod_src}) + string(TOUPPER ${module_name} module_ucase) + set(mod_opt "CONFIG_NODEMCU_CMODULE_${module_ucase}") + if (${${mod_opt}}) + list(APPEND extmods_enabled ${module_ucase}) + endif() +endforeach() +message("Including the following modules: ${extmods_enabled}") + +foreach(mod ${extmods_enabled}) + target_link_libraries(${COMPONENT_LIB} "-u ${mod}_module_selected1") +endforeach() diff --git a/extcomp-template/Kconfig b/extcomp-template/Kconfig new file mode 100644 index 00000000..87bdda3a --- /dev/null +++ b/extcomp-template/Kconfig @@ -0,0 +1,13 @@ +menu "External modules" + + config NODEMCU_CMODULE_MYMOD + bool "Mymod module" + default "y" + help + Includes the mymod module. This module is only an example for + showing how to use external modules. Note that the config option + name has to be prefixed with NODEMCU_CMODULE_ and the suffix + has to match the first argument in the NODEMCU_MODULE() macro + in the .c file. + +endmenu diff --git a/extcomp-template/mymod.c b/extcomp-template/mymod.c new file mode 100644 index 00000000..c99761e8 --- /dev/null +++ b/extcomp-template/mymod.c @@ -0,0 +1,21 @@ +#include "module.h" + +static int lmymod_hello(lua_State *L) +{ + if (lua_isnoneornil(L, 1)) + lua_pushliteral(L, "world"); + + lua_getglobal(L, "print"); + lua_pushliteral(L, "Hello,"); + lua_pushvalue(L, 1); + lua_call(L, 2, 0); + + return 0; +} + + +LROT_BEGIN(mymod, NULL, 0) + LROT_FUNCENTRY(hello, lmymod_hello) +LROT_END(mymod, NULL, 0) + +NODEMCU_MODULE(MYMOD, "mymod", mymod, NULL); From ccb3b5002d74ff1551e35009e476f8f657ac3f50 Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Thu, 29 Dec 2022 14:16:28 +1100 Subject: [PATCH 28/47] Improvements to external component/module support. Based on feedback from @tomsci - thanks! --- components/modules/project_include.cmake | 22 ++++++++++++ extcomp-template/CMakeLists.txt | 43 +++++++----------------- extcomp-template/Kconfig | 21 +++++------- 3 files changed, 43 insertions(+), 43 deletions(-) create mode 100644 components/modules/project_include.cmake diff --git a/components/modules/project_include.cmake b/components/modules/project_include.cmake new file mode 100644 index 00000000..46c87de8 --- /dev/null +++ b/components/modules/project_include.cmake @@ -0,0 +1,22 @@ +# Helper functions for external module registration. +# - extmod_register_conditional() for modules with a Kconfig option +# - extmod_register_unconditional() for always-enabled modules + +function(extmod_register_conditional confname) + if (${CONFIG_NODEMCU_CMODULE_${confname}}) + # If the module is enabled in menuconfig, add the linker option + # "-u _module_selected1" to make the linker include this + # module. See components/core/include/module.h for further details + # on how this works. + message("Including external module ${confname}") + target_link_libraries(${COMPONENT_LIB} "-u ${confname}_module_selected1") + endif() +endfunction() + +function(extmod_register_unconditional confname) + message("Including external module ${confname}") + # The module macros rely on the presence of a CONFIG_NODEMCU_CMODULE_XXX + # def, so we have to add it explicitly as it won't be coming from Kconfig + target_compile_options(${COMPONENT_LIB} PRIVATE "-DCONFIG_NODEMCU_CMODULE_${confname}") + target_link_libraries(${COMPONENT_LIB} "-u ${confname}_module_selected1") +endfunction() diff --git a/extcomp-template/CMakeLists.txt b/extcomp-template/CMakeLists.txt index 118e730f..846e58fb 100644 --- a/extcomp-template/CMakeLists.txt +++ b/extcomp-template/CMakeLists.txt @@ -1,37 +1,20 @@ -# Modify this list as necessary to include all your source files. -set(extmod_srcs - "mymod.c" -) - -# If necessary, add items to the PRIV_REQUIRES list below +# Update source files and includes as necessary idf_component_register( - SRCS ${extmod_srcs} - INCLUDE_DIRS "." "${CMAKE_CURRENT_BINARY_DIR}" + SRCS + "mymod.c" + PRIV_INCLUDE_DIRS + "${CMAKE_CURRENT_BINARY_DIR}" PRIV_REQUIRES "base_nodemcu" "lua" "platform" ) -# The remainder is boiler-plate glue to get the linker to actually include -# the modules enabled in Kconfig. No user-serviceable parts inside. - -# Match up all the extmod source files with their corresponding Kconfig -# option in the form NODEMCU_CMODULE_ and if enabled, add a -# "-u _module_selected1" option to force the linker to include -# the module. See components/core/include/module.h for further details on -# how this works. -set(extmods_enabled) -foreach(extmod_src ${extmod_srcs}) - string(REPLACE ".c" "" module_name ${extmod_src}) - string(TOUPPER ${module_name} module_ucase) - set(mod_opt "CONFIG_NODEMCU_CMODULE_${module_ucase}") - if (${${mod_opt}}) - list(APPEND extmods_enabled ${module_ucase}) - endif() -endforeach() -message("Including the following modules: ${extmods_enabled}") - -foreach(mod ${extmods_enabled}) - target_link_libraries(${COMPONENT_LIB} "-u ${mod}_module_selected1") -endforeach() +# To register the module with NodeMCU, use one of the below functions. +# The name given MUST match the first argument to the NODEMCU_MODULE() in +# the module's C file. +# +# For modules with a Kconfig option, use: +extmod_register_conditional(MYMOD) +# ...and for modules without a Kconfig option, instead use: +#extmod_register_unconditional(MYMOD) diff --git a/extcomp-template/Kconfig b/extcomp-template/Kconfig index 87bdda3a..7415c4a7 100644 --- a/extcomp-template/Kconfig +++ b/extcomp-template/Kconfig @@ -1,13 +1,8 @@ -menu "External modules" - - config NODEMCU_CMODULE_MYMOD - bool "Mymod module" - default "y" - help - Includes the mymod module. This module is only an example for - showing how to use external modules. Note that the config option - name has to be prefixed with NODEMCU_CMODULE_ and the suffix - has to match the first argument in the NODEMCU_MODULE() macro - in the .c file. - -endmenu +config NODEMCU_CMODULE_MYMOD + bool "External NodeMCU module: mymod" + default "y" + help + Includes the mymod module. This module is only an example for + showing how to use external modules. Note that the config option + name has to be prefixed with NODEMCU_CMODULE_ and the module + registered with extmod_register_conditional(MYMOD) in CMakeLists.txt From 55dbcc79d54fefa2c98456a90046f568f4502c76 Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Sat, 1 Jan 2022 17:36:48 +1100 Subject: [PATCH 29/47] Added mkdir/rmdir support to file module. --- components/modules/file.c | 26 ++++++++++++++++++++++++++ docs/modules/file.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/components/modules/file.c b/components/modules/file.c index 458d1fd1..fae1c0bc 100644 --- a/components/modules/file.c +++ b/components/modules/file.c @@ -11,6 +11,7 @@ #include #include #include +#include static const char *default_fs_label = ((CONFIG_NODEMCU_DEFAULT_SPIFFS_LABEL && @@ -129,6 +130,29 @@ static int file_fsinfo( lua_State* L ) } +static int file_mkdir( lua_State *L ) +{ + const char *name = luaL_checkstring(L, 1); + unsigned mode = luaL_optint(L, 2, 0777); + if (mkdir(name, mode) != 0) { + return + luaL_error(L, "failed to create directory '%s'; code %d", name, errno); + } + return 0; +} + + +static int file_rmdir( lua_State *L ) +{ + const char *name = luaL_checkstring(L, 1); + if (rmdir(name) != 0) { + return + luaL_error(L, "failed to remove directory '%s'; code %d", name, errno); + } + return 0; +} + + // Module function map LROT_BEGIN(file, NULL, 0) LROT_FUNCENTRY( list, file_list ) @@ -137,6 +161,8 @@ LROT_BEGIN(file, NULL, 0) LROT_FUNCENTRY( rename, file_rename ) LROT_FUNCENTRY( exists, file_exists ) LROT_FUNCENTRY( fsinfo, file_fsinfo ) + LROT_FUNCENTRY( mkdir, file_mkdir ) + LROT_FUNCENTRY( rmdir, file_rmdir ) LROT_END(file, NULL, 0) diff --git a/docs/modules/file.md b/docs/modules/file.md index 095ffc7f..0dd347b1 100644 --- a/docs/modules/file.md +++ b/docs/modules/file.md @@ -115,6 +115,39 @@ for k,v in pairs(l) do end ``` +## file.mkdir() + +Creates a directory, provided the underlying file system supports directories. SPIFFS does not, but FAT (which you may have on an attached SD card) does. + +#### Syntax +`file.mkdir(path [, mode]) +``` + +#### Parameters +- `path` the full path name of the directory to create. E.g. "/SD0/MYDIR". +- `mode` optional, only used for file systems which use mode permissions. Defaults to 0777 (octal). + +#### Returns +`nil` + +Throws an error if the directory could not be created. Error code 134 (at the +time of writing) indicates that the filesystem at the given path does not +support directories. + +## file.rmdir() + +Removes an empty directory, provided the underlying file system supports directories. SPIFFS does not, but FAT (which you may have on an attached SD card) does. + +#### Syntax +`file.rmdir(path)` + +#### Parameters +- `path` the path to the directory to remove. The directory must be empty. + +#### Returns +`nil` + +Throws an error if the directory could not be removed. ## file.remove() From 1d173c818b0a506609814663ac939c0e27890afa Mon Sep 17 00:00:00 2001 From: serg3295 <75196080+serg3295@users.noreply.github.com> Date: Tue, 17 Jan 2023 18:44:59 +0300 Subject: [PATCH 30/47] Fix typo in `file.md` (#3566) --- docs/modules/file.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/modules/file.md b/docs/modules/file.md index 0dd347b1..9dda5a6f 100644 --- a/docs/modules/file.md +++ b/docs/modules/file.md @@ -120,8 +120,7 @@ end Creates a directory, provided the underlying file system supports directories. SPIFFS does not, but FAT (which you may have on an attached SD card) does. #### Syntax -`file.mkdir(path [, mode]) -``` +`file.mkdir(path [, mode])` #### Parameters - `path` the full path name of the directory to create. E.g. "/SD0/MYDIR". From 4379dd9d31583feebefa58ab675c1b4c19847ab0 Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Thu, 10 Nov 2022 17:03:07 +1100 Subject: [PATCH 31/47] Update to IDF v4.4.3. Rewrote the embed_lfs.sh section addr/size handling since it was still having issues between compiler versions. --- sdk/esp32-esp-idf | 2 +- tools/embed_lfs.sh | 30 +++++++++--------------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/sdk/esp32-esp-idf b/sdk/esp32-esp-idf index 1b16ef6c..6407ecb3 160000 --- a/sdk/esp32-esp-idf +++ b/sdk/esp32-esp-idf @@ -1 +1 @@ -Subproject commit 1b16ef6cfc2479a08136782f9dc57effefa86f66 +Subproject commit 6407ecb3f8d2cc07c4c230e7e64f2046af5c86f7 diff --git a/tools/embed_lfs.sh b/tools/embed_lfs.sh index d0f639a2..b032a037 100755 --- a/tools/embed_lfs.sh +++ b/tools/embed_lfs.sh @@ -15,28 +15,16 @@ if [ ! -f "${LUAC_CROSS}" ]; then exit 1 fi -# Extract the line containing the data size of the LFS object, filtering out -# lines for .bss/.data/.text, sorting the remaining two entries (actual LFS -# data and (optional) riscv attributes) so we can discard the latter if -# present. If the map file was a bit saner with its line breaks this would -# have been a straight forward grep for for .rodata.embedded.*lua.flash.store -LFS_SIZE_ADDR=$(grep -E "0x[0-9a-f]+[ ]+0x[0-9a-f]+[ ]+esp-idf/embedded_lfs/libembedded_lfs.a\(lua.flash.store.reserved.S.obj\)" "${MAP_FILE}" | grep -v '^ \.' | awk '{print $2,$1}' | sort -n -k 1.3 | tail -1) -if [ -z "${LFS_SIZE_ADDR}" ]; then - echo "Error: LFS segment not found. Use 'make clean; make' perhaps?" - exit 1 -fi - -LFS_ADDR=$(echo "${LFS_SIZE_ADDR}" | cut -d ' ' -f 2) -if [ -z "${LFS_ADDR}" ]; then - echo "Error: LFS segment address not found" - exit 1 -fi -# The reported size is +4 due to the length field added by the IDF -LFS_SIZE=$(( $(echo "${LFS_SIZE_ADDR}" | cut -d ' ' -f 1) - 4 )) -if [ -z "${LFS_SIZE}" ]; then - echo "Error: LFS segment size not found" - exit 1 +# Extract the start/end symbols of the LFS object, then calculate the +# available size from that. +LFS_ADDR=$(grep -E '0x[0-9a-f]+ +_binary_lua_flash_store_reserved_start' "${MAP_FILE}" | awk '{print $1}') +LFS_ADDR_END=$(grep -E '0x[0-9a-f]+ +_binary_lua_flash_store_reserved_end' "${MAP_FILE}" | awk '{print $1}') +if [ "${LFS_ADDR}" = "" ] +then + echo "Error: LFS segment address not found" + exit 1 fi +LFS_SIZE=$((LFS_ADDR_END - LFS_ADDR)) printf "LFS segment address %s, length %s (0x%x)\n" "${LFS_ADDR}" "${LFS_SIZE}" "${LFS_SIZE}" From bc3aaf6e4b9325ed3c76ca88df3efdbfa04494db Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Wed, 21 Dec 2022 14:50:27 +1100 Subject: [PATCH 32/47] Update wifi.sta to support new IDF functionality. In addition to exposing additional configuration settings, it now also provides access to the wifi station power-saving control. Documentation updated, and wifi.sta.getconfig() has been documented. Not sure why that wasn't already the case. --- components/modules/wifi.c | 5 + components/modules/wifi_sta.c | 177 +++++++++++++++++++++++++++++++--- docs/modules/wifi.md | 69 ++++++++++++- 3 files changed, 236 insertions(+), 15 deletions(-) diff --git a/components/modules/wifi.c b/components/modules/wifi.c index 489c804a..b8d9fc5f 100644 --- a/components/modules/wifi.c +++ b/components/modules/wifi.c @@ -137,6 +137,11 @@ LROT_BEGIN(wifi, NULL, 0) LROT_NUMENTRY ( AUTH_WPA2_WPA3_PSK, WIFI_AUTH_WPA2_WPA3_PSK ) LROT_NUMENTRY ( AUTH_WAPI_PSK, WIFI_AUTH_WAPI_PSK ) + LROT_NUMENTRY ( SAE_PWE_UNSPECIFIED, WPA3_SAE_PWE_UNSPECIFIED ) + LROT_NUMENTRY ( SAE_PWE_HUNT_AND_PECK, WPA3_SAE_PWE_HUNT_AND_PECK ) + LROT_NUMENTRY ( SAE_PWE_HASH_TO_ELEMENT, WPA3_SAE_PWE_HASH_TO_ELEMENT ) + LROT_NUMENTRY ( SAE_PWE_BOTH, WPA3_SAE_PWE_BOTH ) + LROT_NUMENTRY ( STR_WIFI_SECOND_CHAN_NONE, WIFI_SECOND_CHAN_NONE ) LROT_NUMENTRY ( STR_WIFI_SECOND_CHAN_ABOVE, WIFI_SECOND_CHAN_ABOVE ) LROT_NUMENTRY ( STR_WIFI_SECOND_CHAN_BELOW, WIFI_SECOND_CHAN_BELOW ) diff --git a/components/modules/wifi_sta.c b/components/modules/wifi_sta.c index e75a6388..d23f0171 100644 --- a/components/modules/wifi_sta.c +++ b/components/modules/wifi_sta.c @@ -42,6 +42,9 @@ #include "esp_netif.h" #include +#define PMF_VAL_AVAILABLE 1 +#define PMF_VAL_REQUIRED 2 + static esp_netif_t *wifi_sta = NULL; static int scan_cb_ref = LUA_NOREF; @@ -252,16 +255,18 @@ static int wifi_sta_config (lua_State *L) if (len > sizeof (cfg.sta.ssid)) len = sizeof (cfg.sta.ssid); strncpy ((char *)cfg.sta.ssid, str, len); + lua_pop(L, 1); lua_getfield (L, 1, "pwd"); str = luaL_optlstring (L, -1, "", &len); if (len > sizeof (cfg.sta.password)) len = sizeof (cfg.sta.password); strncpy ((char *)cfg.sta.password, str, len); + lua_pop(L, 1); lua_getfield (L, 1, "bssid"); cfg.sta.bssid_set = false; - if (lua_isstring (L, -1)) + if (!lua_isnoneornil(L, -1)) { const char *bssid = luaL_checklstring (L, -1, &len); const char *fmts[] = { @@ -284,18 +289,76 @@ static int wifi_sta_config (lua_State *L) if (!cfg.sta.bssid_set) return luaL_error (L, "invalid BSSID: %s", bssid); } + lua_pop(L, 1); lua_getfield(L, 1, "pmf"); - if (lua_isnumber(L, -1)) + if (!lua_isnoneornil(L, -1)) { - int pmf_mode = lua_tointeger(L, -1); - if (pmf_mode) - cfg.sta.pmf_cfg.capable = true; - if (pmf_mode == 2) - cfg.sta.pmf_cfg.required = true; + int pmf_mode = luaL_checkinteger(L, -1); + cfg.sta.pmf_cfg.required = (pmf_mode == PMF_VAL_REQUIRED); } else - cfg.sta.pmf_cfg.capable = true; + cfg.sta.pmf_cfg.required = false; + lua_pop(L, 1); + + lua_getfield(L, 1, "channel"); + if (!lua_isnoneornil(L, -1)) + cfg.sta.channel = luaL_checkinteger(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "scan_method"); + if (!lua_isnoneornil(L, -1)) + { + static const wifi_scan_method_t vals[] = { + WIFI_FAST_SCAN, WIFI_ALL_CHANNEL_SCAN, + }; + static const char *keys[] = { "fast", "all", }; + cfg.sta.scan_method = vals[luaL_checkoption(L, -1, NULL, keys)]; + } + lua_pop(L, 1); + + lua_getfield(L, 1, "listen_interval"); + if (!lua_isnoneornil(L, -1)) + cfg.sta.listen_interval = luaL_checkinteger(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "sort_by"); + if (!lua_isnoneornil(L, -1)) + { + static const wifi_sort_method_t vals[] = { + WIFI_CONNECT_AP_BY_SIGNAL, WIFI_CONNECT_AP_BY_SECURITY, + }; + static const char *keys[] = { "rssi", "authmode", }; + cfg.sta.sort_method = vals[luaL_checkoption(L, -1, NULL, keys)]; + } + lua_pop(L, 1); + + lua_getfield(L, 1, "threshold_rssi"); + if (!lua_isnoneornil(L, -1)) + cfg.sta.threshold.rssi = luaL_checkinteger(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "threshold_authmode"); + if (!lua_isnoneornil(L, -1)) + cfg.sta.threshold.authmode = luaL_checkinteger(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "rm"); + cfg.sta.rm_enabled = lua_tointeger(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "btm"); + cfg.sta.btm_enabled = lua_tointeger(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "mbo"); + cfg.sta.mbo_enabled = lua_tointeger(L, -1); + lua_pop(L, 1); + + lua_getfield(L, 1, "sae_pwe"); + if (!lua_isnoneornil(L, -1)) + cfg.sta.sae_pwe_h2e = luaL_checkinteger(L, -1); + lua_pop(L, 1); SET_SAVE_MODE(save); esp_err_t err = esp_wifi_set_config (WIFI_IF_STA, &cfg); @@ -323,6 +386,7 @@ static int wifi_sta_disconnect (lua_State *L) static int wifi_sta_getconfig (lua_State *L) { wifi_config_t cfg; + memset(&cfg, 0, sizeof(cfg)); esp_err_t err = esp_wifi_get_config (WIFI_IF_STA, &cfg); if (err != ESP_OK) return luaL_error (L, "failed to get config, code %d", err); @@ -344,6 +408,59 @@ static int wifi_sta_getconfig (lua_State *L) lua_setfield (L, -2, "bssid"); } + lua_pushinteger(L, + cfg.sta.pmf_cfg.required ? PMF_VAL_REQUIRED : PMF_VAL_AVAILABLE); + lua_setfield(L, -2, "pmf"); + + const char *tmp; + switch(cfg.sta.scan_method) + { + case WIFI_FAST_SCAN: tmp = "fast"; break; + case WIFI_ALL_CHANNEL_SCAN: tmp = "all"; break; + default: tmp = NULL; break; + } + if (tmp) + { + lua_pushstring(L, tmp); + lua_setfield(L, -2, "scan_method"); + } + + lua_pushinteger(L, cfg.sta.channel); + lua_setfield(L, -2, "channel"); + + lua_pushinteger(L, cfg.sta.listen_interval); + lua_setfield(L, -2, "listen_interval"); + + switch(cfg.sta.sort_method) + { + case WIFI_CONNECT_AP_BY_SIGNAL: tmp = "rssi"; break; + case WIFI_CONNECT_AP_BY_SECURITY: tmp = "authmode"; break; + default: tmp = NULL; break; + } + if (tmp) + { + lua_pushstring(L, tmp); + lua_setfield(L, -2, "sort_by"); + } + + lua_pushinteger(L, cfg.sta.threshold.rssi); + lua_setfield(L, -2, "threshold_rssi"); + + lua_pushinteger(L, cfg.sta.threshold.authmode); + lua_setfield(L, -2, "threshold_authmode"); + + lua_pushinteger(L, cfg.sta.rm_enabled); + lua_setfield(L, -2, "rm"); + + lua_pushinteger(L, cfg.sta.btm_enabled); + lua_setfield(L, -2, "btm"); + + lua_pushinteger(L, cfg.sta.mbo_enabled); + lua_setfield(L, -2, "mbo"); + + lua_pushinteger(L, cfg.sta.sae_pwe_h2e); + lua_setfield(L, -2, "sae_pwe"); + return 1; } @@ -453,6 +570,43 @@ static int wifi_sta_scan (lua_State *L) } +static int wifi_sta_powersave(lua_State *L) +{ + static const wifi_ps_type_t vals[] = { + WIFI_PS_NONE, WIFI_PS_MIN_MODEM, WIFI_PS_MAX_MODEM, + }; + static const char *keys[] = { "none", "min", "max" }; + + esp_err_t ret = esp_wifi_set_ps(vals[luaL_checkoption(L, 1, NULL, keys)]); + if (ret != ESP_OK) + return luaL_error(L, "set powersave failed, code %d", ret); + + return 0; +} + + +static int wifi_sta_getpowersave(lua_State *L) +{ + wifi_ps_type_t ps; + esp_err_t ret = esp_wifi_get_ps(&ps); + if (ret != ESP_OK) + return luaL_error(L, "get powersave failed, code %d", ret); + + const char *mode; + switch(ps) + { + case WIFI_PS_NONE: mode = "none"; break; + case WIFI_PS_MIN_MODEM: mode = "min"; break; + case WIFI_PS_MAX_MODEM: mode = "max"; break; + default: + return luaL_error(L, "unknown powersave mode??"); + } + lua_pushstring(L, mode); + + return 1; +} + + LROT_BEGIN(wifi_sta, NULL, 0) LROT_FUNCENTRY( setip, wifi_sta_setip ) LROT_FUNCENTRY( sethostname, wifi_sta_sethostname) @@ -464,10 +618,11 @@ LROT_BEGIN(wifi_sta, NULL, 0) LROT_FUNCENTRY( getmac, wifi_sta_getmac ) LROT_FUNCENTRY( on, wifi_sta_on ) LROT_FUNCENTRY( scan, wifi_sta_scan ) + LROT_FUNCENTRY( powersave, wifi_sta_powersave ) + LROT_FUNCENTRY( getpowersave,wifi_sta_getpowersave ) - LROT_NUMENTRY( PMF_OFF, 0 ) - LROT_NUMENTRY( PMF_AVAILABLE, 1 ) - LROT_NUMENTRY( PMF_REQUIRED, 2 ) + LROT_NUMENTRY( PMF_AVAILABLE, PMF_VAL_AVAILABLE ) + LROT_NUMENTRY( PMF_REQUIRED, PMF_VAL_REQUIRED ) LROT_END(wifi_sta, NULL, 0) NODEMCU_ESP_EVENT(WIFI_EVENT, WIFI_EVENT_SCAN_DONE, on_scan_done); diff --git a/docs/modules/wifi.md b/docs/modules/wifi.md index b84f826e..1ccafb55 100644 --- a/docs/modules/wifi.md +++ b/docs/modules/wifi.md @@ -149,11 +149,22 @@ being removed in the SDK/IDF. After start-up it is necessary to call - "AcDc0123c0DE" - `pmf` an optional setting to control whether Protected Management Frames are supported and/or required. One of: - - `wifi.sta.PMF_OFF` - `wifi.sta.PMF_AVAILABLE` - `wifi.sta.PMF_REQUIRED`. Defaults to `wifi.sta.PMF_AVAILABLE`. PMF is required when joining to - WPA3-Personal access points. + WPA3-Personal access points. The value `wifi.sta.PMF_OFF` is no longer + available as it is no longer supported by the wifi stack. + + - `channel` optional integer value, the channel number to start scanning for the AP from, if known. + - `scan_method` optional string value, one of `"fast"` or `"all"` do either do a fast scan or all channel scan when looking for the AP to connect to. With fast scan, the first found matching AP is used even if it is not the best/closest one. + - `listen_interval` optional listen interval to receive beacon if max wifi power saving mode is enabled. Units is in AP beacon intervals. Defaults to 3. + - `sort_by` optional string value for preferential selection of AP. Must be one of `"rssi"` or `"authmode"` if present. + - `threshold_rssi` optional integer value to limit APs to only those which have a signal stronger than this value. + - `threshold_authmode` optional value to limit APs to those with an authentication mode of at least this settings. One of `wifi.AUTH_OPEN`, `wifi.AUTH_WEP`, `wifi.AUTH_WPA_PSK`, `wifi.AUTH_WPA2_PSK`, `wifi.AUTH_WPA_WPA2_PSK`, `wifi.AUTH_WPA2_ENTERPRISE`, `wifi.AUTH_WPA3_PSK`, `wifi.AUTH_WPA2_WPA3_PSK`, `wifi.AUTH_WAPI_PSK`. + - `rm` optional integer value, set to 1 to enable Radio Measurements + - `btm` optional integer value, set to 1 to enable BSS Transition Management + - `mbo` optional integer value, set to 1 to enable Multi-Band Operation + - `sae_pwe` optional, configures WPA3 SAE Password Element setting. One of `wifi.SAE_PWE_UNSPECIFIED`, `wifi.SAE_PWE_HUNT_AND_PECK`, `wifi.SAE_PWE_HASH_TO_ELEMENT` or `wifi.SAE_PWE_BOTH`. - `save` Save station configuration to flash. - `true` configuration **will** be retained through power cycle. @@ -190,6 +201,21 @@ wifi.sta.config(station_cfg) - [`wifi.sta.connect()`](#wifistaconnect) - [`wifi.sta.disconnect()`](#wifistadisconnect) + +## wifi.sta.getconfig() + +Returns the current station configuration. + +#### Syntax +`wifi.sta.getconfig()` + +#### Parameters +`nil` + +#### Returns +A table with the configuration settings. Refer to [`wifi.sta.config()`](#wifistaconfig) for field details. + + ## wifi.sta.connect() Connects to the configured AP in station mode. You will want to call this @@ -254,6 +280,41 @@ A `boolean` where `true` is OK. wifi.sta.settxpower(8.5) ``` +## wifi.sta.powersave + +Configures power-saving setting in station mode. + +#### Syntax +`wifi.sta.powersave(setting)` + +#### Parameters +- `setting` one of `"none"`, `"min"` or `"max"`. In `"min"` mode, the station wakes up every DTIM period to receive the beacon. In `"max"` mode, the station wakes up at the interval configured in `listen_interval` (see [`wifi.sta.config()`](#wifistaconfig). When set to `"none"` the station does not go to sleep and can receive frames immediately. + +#### Returns +`nil` + +#### See also +- [`wifi.sta.getpowersave()`](#wifistagetpowersave) + + +## wifi.sta.getpowersave + +Returns the configured station power-saving mode. + +#### Syntax +`wifi.sta.getpowersave()` + +#### Parameters +`nil` + +#### Returns + +One of `"none"`, `"min"` or `"max"`. Refer to [`wifi.sta.powersave()`](#wifistapowersave) for details. + +#### See also +- [`wifi.sta.powersave()`](#wifistapowersave) + + ## wifi.sta.on() Registers callbacks for WiFi station status events. @@ -282,7 +343,7 @@ Event information provided for each event is as follows: - `ssid`: the SSID of the network - `bssid`: the BSSID of the AP - `channel`: the primary channel of the network - - `auth` authentication method, one of `wifi.AUTH_OPEN`, `wifi.AUTH_WPA_PSK`, `wifi.AUTH_WPA2_PSK`, `wifi.WPA_WPA2_PSK`, `wifi.AUTH_WPA3_PSK`, `wifi.AUTH_WAPI_PSK` + - `auth` authentication method, one of `wifi.AUTH_OPEN`, `wifi.AUTH_WEP`, `wifi.AUTH_WPA_PSK`, `wifi.AUTH_WPA2_PSK`, `wifi.AUTH_WPA_WPA2_PSK`, `wifi.AUTH_WPA2_ENTERPRISE`, `wifi.AUTH_WPA3_PSK`, `wifi.AUTH_WPA2_WPA3_PSK`, `wifi.AUTH_WAPI_PSK` - `disconnected`: information about the network/AP that was disconnected from: - `ssid`: the SSID of the network - `bssid`: the BSSID of the AP @@ -374,7 +435,7 @@ The following fields are provided for each scanned AP: - `bssid`: the BSSID of the AP - `channel`: primary WiFi channel of the AP - `rssi`: Received Signal Strength Indicator value -- `auth` authentication method, one of `wifi.AUTH_OPEN`, `wifi.AUTH_WPA_PSK`, `wifi.AUTH_WPA2_PSK`, `wifi.AUTH_WPA_WPA2_PSK`, `wifi.AUTH_WPA2_ENTERPRISE`, `wifi.AUTH_WPA2_WPA3_PSK`, `wifi.AUTH_WPA3_PSK`, `wifi.AUTH_WAPI_PSK` +- `auth` authentication method, one of `wifi.AUTH_OPEN`, `wifi.AUTH_WEP`, `wifi.AUTH_WPA_PSK`, `wifi.AUTH_WPA2_PSK`, `wifi.AUTH_WPA_WPA2_PSK`, `wifi.AUTH_WPA2_ENTERPRISE`, `wifi.AUTH_WPA3_PSK`, `wifi.AUTH_WPA2_WPA3_PSK`, `wifi.AUTH_WAPI_PSK` - `bandwidth`: one of the following constants: - `wifi.HT20` - `wifi.HT40_ABOVE` From 32d03a21da4fdfddf153d118e2e32a064d6d0003 Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Sat, 21 Jan 2023 14:30:34 +1100 Subject: [PATCH 33/47] Prefer Lua booleans over 1/nil or 1/0 Document preference and also provide easy-to-use backwards compatible interface. --- components/lua/lua-5.1/lauxlib.h | 1 + components/lua/lua-5.1/lnodemcu.c | 17 +++++++++++++++++ components/lua/lua-5.3/lauxlib.c | 17 +++++++++++++++++ components/lua/lua-5.3/lauxlib.h | 1 + components/modules/wifi_sta.c | 12 ++++++------ docs/modules/wifi.md | 6 +++--- docs/nodemcu-lrm.md | 11 +++++++++++ 7 files changed, 56 insertions(+), 9 deletions(-) diff --git a/components/lua/lua-5.1/lauxlib.h b/components/lua/lua-5.1/lauxlib.h index 92cdf590..dde6aebe 100644 --- a/components/lua/lua-5.1/lauxlib.h +++ b/components/lua/lua-5.1/lauxlib.h @@ -179,6 +179,7 @@ LUALIB_API int (luaL_posttask) ( lua_State* L, int prio ); #define LUA_TASK_LOW 0 #define LUA_TASK_MEDIUM 1 #define LUA_TASK_HIGH 2 +LUALIB_API int (luaL_totoggle) (lua_State *L, int idx); /* }====================================================== */ diff --git a/components/lua/lua-5.1/lnodemcu.c b/components/lua/lua-5.1/lnodemcu.c index fe87010a..2e0a507d 100644 --- a/components/lua/lua-5.1/lnodemcu.c +++ b/components/lua/lua-5.1/lnodemcu.c @@ -257,3 +257,20 @@ LUA_API int lua_pushlfsindex (lua_State *L) { return p ? LUA_TFUNCTION : LUA_TNIL; } #endif + + +/* luaL_totoggle provides lenient boolean interpretation for feature toggles + * true, 1 => true + * false, 0, nil => false + */ +LUALIB_API int luaL_totoggle(lua_State *L, int idx) +{ + if (lua_isboolean(L, idx)) + return lua_toboolean(L, idx); + else if (lua_isnoneornil(L, idx)) + return 0; + else if (lua_isnumber(L, idx)) + return lua_tonumber(L, idx) != 0; + else + return luaL_error(L, "unexpected type"); +} diff --git a/components/lua/lua-5.3/lauxlib.c b/components/lua/lua-5.3/lauxlib.c index 03727d5a..81a4c8a2 100644 --- a/components/lua/lua-5.3/lauxlib.c +++ b/components/lua/lua-5.3/lauxlib.c @@ -1177,3 +1177,20 @@ LUALIB_API int luaL_pcallx (lua_State *L, int narg, int nres) { return status; } #endif + + +/* luaL_totoggle provides lenient boolean interpretation for feature toggles + * true, 1 => true + * false, 0, nil => false + */ +LUALIB_API bool luaL_totoggle(lua_State *L, int idx) +{ + if (lua_isboolean(L, idx)) + return lua_toboolean(L, idx); + else if (lua_isnoneornil(L, idx)) + return false; + else if (lua_isnumber(L, idx)) + return lua_tonumber(L, idx) != 0; + else + return luaL_error(L, "unexpected type"); +} diff --git a/components/lua/lua-5.3/lauxlib.h b/components/lua/lua-5.3/lauxlib.h index 7a0ce1ee..1088fed1 100644 --- a/components/lua/lua-5.3/lauxlib.h +++ b/components/lua/lua-5.3/lauxlib.h @@ -295,6 +295,7 @@ LUALIB_API void (luaL_lfsreload) (lua_State *L); LUALIB_API int (luaL_posttask) (lua_State* L, int prio); LUALIB_API int (luaL_pcallx) (lua_State *L, int narg, int nres); #define luaL_pushlfsmodule(l) lua_pushlfsfunc(L) +LUALIB_API bool (luaL_totoggle) (lua_State *L, int idx); /* }============================================================ */ diff --git a/components/modules/wifi_sta.c b/components/modules/wifi_sta.c index d23f0171..120c507e 100644 --- a/components/modules/wifi_sta.c +++ b/components/modules/wifi_sta.c @@ -344,15 +344,15 @@ static int wifi_sta_config (lua_State *L) lua_pop(L, 1); lua_getfield(L, 1, "rm"); - cfg.sta.rm_enabled = lua_tointeger(L, -1); + cfg.sta.rm_enabled = luaL_totoggle(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "btm"); - cfg.sta.btm_enabled = lua_tointeger(L, -1); + cfg.sta.btm_enabled = luaL_totoggle(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "mbo"); - cfg.sta.mbo_enabled = lua_tointeger(L, -1); + cfg.sta.mbo_enabled = luaL_totoggle(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "sae_pwe"); @@ -449,13 +449,13 @@ static int wifi_sta_getconfig (lua_State *L) lua_pushinteger(L, cfg.sta.threshold.authmode); lua_setfield(L, -2, "threshold_authmode"); - lua_pushinteger(L, cfg.sta.rm_enabled); + lua_pushboolean(L, cfg.sta.rm_enabled); lua_setfield(L, -2, "rm"); - lua_pushinteger(L, cfg.sta.btm_enabled); + lua_pushboolean(L, cfg.sta.btm_enabled); lua_setfield(L, -2, "btm"); - lua_pushinteger(L, cfg.sta.mbo_enabled); + lua_pushboolean(L, cfg.sta.mbo_enabled); lua_setfield(L, -2, "mbo"); lua_pushinteger(L, cfg.sta.sae_pwe_h2e); diff --git a/docs/modules/wifi.md b/docs/modules/wifi.md index 1ccafb55..18edacd3 100644 --- a/docs/modules/wifi.md +++ b/docs/modules/wifi.md @@ -161,9 +161,9 @@ being removed in the SDK/IDF. After start-up it is necessary to call - `sort_by` optional string value for preferential selection of AP. Must be one of `"rssi"` or `"authmode"` if present. - `threshold_rssi` optional integer value to limit APs to only those which have a signal stronger than this value. - `threshold_authmode` optional value to limit APs to those with an authentication mode of at least this settings. One of `wifi.AUTH_OPEN`, `wifi.AUTH_WEP`, `wifi.AUTH_WPA_PSK`, `wifi.AUTH_WPA2_PSK`, `wifi.AUTH_WPA_WPA2_PSK`, `wifi.AUTH_WPA2_ENTERPRISE`, `wifi.AUTH_WPA3_PSK`, `wifi.AUTH_WPA2_WPA3_PSK`, `wifi.AUTH_WAPI_PSK`. - - `rm` optional integer value, set to 1 to enable Radio Measurements - - `btm` optional integer value, set to 1 to enable BSS Transition Management - - `mbo` optional integer value, set to 1 to enable Multi-Band Operation + - `rm` optional boolean, set to `true` to enable Radio Measurements + - `btm` optional boolean, set to `true` to enable BSS Transition Management + - `mbo` optional boolean, set to `true` to enable Multi-Band Operation - `sae_pwe` optional, configures WPA3 SAE Password Element setting. One of `wifi.SAE_PWE_UNSPECIFIED`, `wifi.SAE_PWE_HUNT_AND_PECK`, `wifi.SAE_PWE_HASH_TO_ELEMENT` or `wifi.SAE_PWE_BOTH`. - `save` Save station configuration to flash. diff --git a/docs/nodemcu-lrm.md b/docs/nodemcu-lrm.md index 1cc5557e..d278897f 100644 --- a/docs/nodemcu-lrm.md +++ b/docs/nodemcu-lrm.md @@ -231,6 +231,17 @@ Equivalent to `luaL_newmetatable()` for ROTable metatables. Adds key / ROTable This macro executes `luaL_unref(L, t, r)` and then assigns `r = LUA_NOREF`. +#### luaL_totoggle + +` bool luaL_totoggle(lua_State *L, int idx)` + +There are several ways of indicating a configuration toggle value: + - The modern Lua way, with a boolean (`true`/`false`) + - The "classic" Lua way, with `1`/`nil` + - The "C" way, with `1`/`0` + +When implementing C modules for NodeMCU and needing to indicate an on/off setting, the preference is to do it as a boolean. In the interest of ease of use on the other hand, it is however nice to also support the other styles. The `luaL_totoggle` function provides just that. + ### Declaring modules and ROTables in NodeMCU All NodeMCU C library modules should include the standard header "`module.h`". This internally includes `lnodemcu.h` and these together provide the macros to enable declaration of NodeMCU modules and ROTables within them. All ROtable support macros are either prefixed by `LRO_` (Lua Read Only) or in the case of table entries `LROT_`. From 1b38d8e6ebc520973de4818546fd7b0c2484f7a6 Mon Sep 17 00:00:00 2001 From: Tom Sutcliffe Date: Sun, 30 Apr 2023 18:46:07 +0100 Subject: [PATCH 34/47] Fix ws2812 on later esp32 variants By not relying on the internal details of the RMT registers. For example on the esp32s2, the threshold event bits start at bit 12 not bit 24, and on the esp32s3 the memory buffers are only 48 words not 64. Also the esp-idf recommends against hooking the rmt ISR in the first place. Instead, use the published rmt APIs (specifically rmt_write_sample and rmt_translator_init) and the default rmt ISR, which should be much more resilient against future esp32 SoC changes. --- components/platform/ws2812.c | 131 +++++++++++++++-------------------- 1 file changed, 57 insertions(+), 74 deletions(-) diff --git a/components/platform/ws2812.c b/components/platform/ws2812.c index 94be4613..4a4aace2 100644 --- a/components/platform/ws2812.c +++ b/components/platform/ws2812.c @@ -33,6 +33,11 @@ #undef WS2812_DEBUG +// If either of these fails, the reset logic in ws2812_sample_to_rmt will need revisiting. +_Static_assert(SOC_RMT_MEM_WORDS_PER_CHANNEL % 8 == 0, + "SOC_RMT_MEM_WORDS_PER_CHANNEL is assumed to be a multiple of 8"); +_Static_assert(SOC_RMT_MEM_WORDS_PER_CHANNEL >= 16, + "SOC_RMT_MEM_WORDS_PER_CHANNEL is assumed to be >= 16"); // divider to generate 100ns base period from 80MHz APB clock #define WS2812_CLKDIV (100 * 80 /1000) @@ -58,84 +63,67 @@ const rmt_item32_t ws2812_rmt_bit1 = { .duration1 = WS2812_DURATION_T1L }; -#define ws2812_rmt_reset {.level0 = 0, .duration0 = 4, .level1 = 0, .duration1 = 4} -// reset signal, spans one complete buffer block -const rmt_item32_t ws2812_rmt_reset_block[64] = { [0 ... 63] = ws2812_rmt_reset }; - +// This is one eighth of 512 * 100ns, ie in total a bit above the requisite 50us +const rmt_item32_t ws2812_rmt_reset = { .level0 = 0, .duration0 = 32, .level1 = 0, .duration1 = 32 }; // descriptor for a ws2812 chain typedef struct { bool valid; + bool needs_reset; uint8_t gpio; const uint8_t *data; size_t len; - size_t tx_offset; } ws2812_chain_t; - // chain descriptor array static ws2812_chain_t ws2812_chains[RMT_CHANNEL_MAX]; -// interrupt handler for ws2812 ISR -static intr_handle_t ws2812_intr_handle; +#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -static void ws2812_fill_memory_encoded( rmt_channel_t channel, const uint8_t *data, size_t len, size_t tx_offset ) +static void ws2812_sample_to_rmt(const void *src, rmt_item32_t *dest, size_t src_size, size_t wanted_num, size_t *translated_size, size_t *item_num) { - for (size_t idx = 0; idx < len; idx++) { + // Note: enabling these commented-out logs will ruin the timing so nothing + // will actually work when they're enabled. But I've kept them in as comments + // because they were useful in debugging the buffer management. + // ESP_DRAM_LOGW("ws2812", "ws2812_sample_to_rmt wanted=%u src_size=%u", wanted_num, src_size); + + void *ctx; + rmt_translator_get_context(item_num, &ctx); + ws2812_chain_t *chain = (ws2812_chain_t *)ctx; + + size_t reset_num = 0; + if (chain->needs_reset) { + // Haven't sent reset yet + + // We split the reset into 8 even though it would fit in a single + // rmt_item32_t, simply so that dest stays 8-item aligned which means we + // don't have to worry about having to split a byte of src across multiple + // blocks (assuming the static asserts at the top of this file are true). + for (int i = 0; i < 8; i++) { + dest[i] = ws2812_rmt_reset; + } + dest += 8; + wanted_num -= 8; + reset_num = 8; + chain->needs_reset = false; + } + + // Now write the actual data from src + const uint8_t *data = (const uint8_t *)src; + size_t data_num = MIN(wanted_num, src_size * 8) / 8; + for (size_t idx = 0; idx < data_num; idx++) { uint8_t byte = data[idx]; - for (uint8_t i = 0; i < 8; i++) { - RMTMEM.chan[channel].data32[tx_offset + idx*8 + i].val = byte & 0x80 ? ws2812_rmt_bit1.val : ws2812_rmt_bit0.val; - + dest[idx * 8 + i] = (byte & 0x80) ? ws2812_rmt_bit1 : ws2812_rmt_bit0; byte <<= 1; } } + + *translated_size = data_num; + *item_num = reset_num + data_num * 8; + // ESP_DRAM_LOGW("ws2812", "src bytes consumed: %u total rmt items: %u", *translated_size, *item_num); } - -static void ws2812_isr(void *arg) -{ - uint32_t intr_st = RMT.int_st.val; - - for (rmt_channel_t channel = 0; channel < RMT_CHANNEL_MAX; channel++) { - - if ((intr_st & (BIT(channel+24))) && (ws2812_chains[channel].valid)) { - RMT.int_clr.val = BIT(channel+24); - - ws2812_chain_t *chain = &(ws2812_chains[channel]); -#if defined(CONFIG_IDF_TARGET_ESP32) - uint32_t data_sub_len = RMT.tx_lim_ch[channel].limit/8; -#elif defined(CONFIG_IDF_TARGET_ESP32S3) - uint32_t data_sub_len = RMT.chn_tx_lim[channel].tx_lim_chn/8; -#elif defined(CONFIG_IDF_TARGET_ESP32S2) - uint32_t data_sub_len = RMT.tx_lim_ch[channel].tx_lim/8; -#else - uint32_t data_sub_len = RMT.tx_lim[channel].limit/8; -#endif - - if (chain->len >= data_sub_len) { - ws2812_fill_memory_encoded( channel, chain->data, data_sub_len, chain->tx_offset ); - chain->data += data_sub_len; - chain->len -= data_sub_len; - } else if (chain->len == 0) { - RMTMEM.chan[channel].data32[chain->tx_offset].val = 0; - } else { - ws2812_fill_memory_encoded( channel, chain->data, chain->len, chain->tx_offset ); - RMTMEM.chan[channel].data32[chain->tx_offset + chain->len*8].val = 0; - chain->data += chain->len; - chain->len = 0; - } - if (chain->tx_offset == 0) { - chain->tx_offset = data_sub_len * 8; - } else { - chain->tx_offset = 0; - } - } - } -} - - int platform_ws2812_setup( uint8_t gpio_num, uint8_t num_mem, const uint8_t *data, size_t len ) { int channel; @@ -147,7 +135,7 @@ int platform_ws2812_setup( uint8_t gpio_num, uint8_t num_mem, const uint8_t *dat chain->gpio = gpio_num; chain->len = len; chain->data = data; - chain->tx_offset = 0; + chain->needs_reset = true; #ifdef WS2812_DEBUG ESP_LOGI("ws2812", "Setup done for gpio %d on RMT channel %d", gpio_num, channel); @@ -209,23 +197,23 @@ int platform_ws2812_send( void ) res = PLATFORM_ERR; break; } + if (rmt_translator_init(channel, ws2812_sample_to_rmt) != ESP_OK) { + res = PLATFORM_ERR; + break; + } + if (rmt_translator_set_context(channel, &ws2812_chains[channel]) != ESP_OK) { + res = PLATFORM_ERR; + break; + } } } - - // hook-in our shared ISR - esp_intr_alloc( ETS_RMT_INTR_SOURCE, PLATFORM_RMT_INTR_FLAGS, ws2812_isr, NULL, &ws2812_intr_handle ); - - // start selected channels one by one for (rmt_channel_t channel = 0; channel < RMT_CHANNEL_MAX && res == PLATFORM_OK; channel++) { if (ws2812_chains[channel].valid) { - // we just feed a single block for generating the reset to the rmt driver - // the actual payload data is handled by our private shared ISR - if (rmt_write_items( channel, - (rmt_item32_t *)ws2812_rmt_reset_block, - 64, - false ) != ESP_OK) { + // ws2812_sample_to_rmt takes care of translating the data to rmt_item32_t + // format, as well as prepending the reset sequence. + if (rmt_write_sample(channel, ws2812_chains[channel].data, ws2812_chains[channel].len, false) != ESP_OK) { res = PLATFORM_ERR; break; } @@ -239,11 +227,6 @@ int platform_ws2812_send( void ) } } - - // un-hook our ISR - esp_intr_free( ws2812_intr_handle ); - - return res; } From 7155d7197a5a5661ed162d0fe9d5734576ca6edb Mon Sep 17 00:00:00 2001 From: Tom Sutcliffe Date: Wed, 10 May 2023 18:12:15 +0100 Subject: [PATCH 35/47] Fix platform_rmt_allocate for esp32-S3 By not relying on ifdefing for specific targets, but instead using the SOC_RMT_xxx macros to figure it out. --- components/platform/platform_rmt.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/components/platform/platform_rmt.c b/components/platform/platform_rmt.c index e189c431..1c568220 100644 --- a/components/platform/platform_rmt.c +++ b/components/platform/platform_rmt.c @@ -24,19 +24,19 @@ static bool rmt_channel_check( uint8_t channel, uint8_t num_mem ) return rmt_channel_check( channel-1, num_mem-1); } -#if defined(CONFIG_IDF_TARGET_ESP32C3) int platform_rmt_allocate( uint8_t num_mem, rmt_mode_t mode ) -#else -int platform_rmt_allocate( uint8_t num_mem, rmt_mode_t mode __attribute__((unused))) -#endif { int channel; int alloc_min; int alloc_max; uint8_t tag = 1; -#if defined(CONFIG_IDF_TARGET_ESP32C3) - /* The ESP32-C3 is limited to TX on channel 0-1 and RX on channel 2-3. */ +#if SOC_RMT_TX_CANDIDATES_PER_GROUP == SOC_RMT_CHANNELS_PER_GROUP + alloc_min = 0; + alloc_max = RMT_CHANNEL_MAX - 1; +#else + /* On platforms where channels cannot do both TX and RX, the TX ones always + seem to start at index 0, and are then followed by the RX channels. */ if( mode==RMT_MODE_TX ) { alloc_min = 0; alloc_max = SOC_RMT_TX_CANDIDATES_PER_GROUP - 1; @@ -44,10 +44,6 @@ int platform_rmt_allocate( uint8_t num_mem, rmt_mode_t mode __attribute__((unuse alloc_min = RMT_CHANNEL_MAX - SOC_RMT_RX_CANDIDATES_PER_GROUP; alloc_max = RMT_CHANNEL_MAX - 1; } -#else - /* The other ESP32 devices can do RX and TX on all channels. */ - alloc_min = 0; - alloc_max = RMT_CHANNEL_MAX - 1; #endif for (channel = alloc_max; channel >= alloc_min; channel--) { From 4523bbb36d2bf2107ecaaa594306ad6fa5b112f5 Mon Sep 17 00:00:00 2001 From: Tom Sutcliffe Date: Mon, 1 May 2023 13:59:23 +0100 Subject: [PATCH 36/47] Fix i2c.setup() for pins >= 32 Which includes SDA and SCL on esp32s2. --- components/driver_i2c/i2c_sw_master.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/driver_i2c/i2c_sw_master.c b/components/driver_i2c/i2c_sw_master.c index c42b2284..4ee15a8b 100644 --- a/components/driver_i2c/i2c_sw_master.c +++ b/components/driver_i2c/i2c_sw_master.c @@ -112,7 +112,7 @@ void i2c_sw_master_gpio_init(uint8_t sda, uint8_t scl) gpio_config_t cfg; - cfg.pin_bit_mask = 1 << sda | 1 << scl; + cfg.pin_bit_mask = 1ULL << sda | 1ULL << scl; cfg.mode = GPIO_MODE_INPUT_OUTPUT_OD; cfg.pull_up_en = GPIO_PULLUP_ENABLE; cfg.pull_down_en = GPIO_PULLDOWN_DISABLE; From f125b2d059d88374a6a568333d440ddf8cc9caae Mon Sep 17 00:00:00 2001 From: Felix Penzlin Date: Mon, 7 Feb 2022 12:04:21 +0100 Subject: [PATCH 37/47] configure ledc to auto select clock --- components/modules/ledc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/modules/ledc.c b/components/modules/ledc.c index 9f0fc9e5..e695df9c 100644 --- a/components/modules/ledc.c +++ b/components/modules/ledc.c @@ -30,6 +30,8 @@ static int lledc_new_channel( lua_State *L ) ledc_timer.timer_num = opt_checkint_range(L, "timer", -1, 0, LEDC_TIMER_MAX-1); + ledc_timer.clk_cfg = LEDC_AUTO_CLK; + /* Setup channel */ ledc_channel_config_t channel_config = { .speed_mode = ledc_timer.speed_mode, From bf84d4aafc4981ca7896b0dad23a30c5e03f43f4 Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Tue, 23 Jan 2024 09:17:15 -0500 Subject: [PATCH 38/47] Updated to IDF 4.4.6 (#3621) * Updated to IDF 4.4.6 * Use the version that doesn't have my monitor fix in it --- components/modules/spi.c | 2 ++ sdk/esp32-esp-idf | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/components/modules/spi.c b/components/modules/spi.c index 3424e173..a6751780 100644 --- a/components/modules/spi.c +++ b/components/modules/spi.c @@ -17,7 +17,9 @@ LROT_BEGIN(lspi, NULL, 0) #endif LROT_NUMENTRY( SPI1, SPI1_HOST ) LROT_NUMENTRY( SPI2, SPI2_HOST ) +#ifdef SPI3_HOST LROT_NUMENTRY( SPI3, SPI3_HOST ) +#endif LROT_END(lspi, NULL, 0) int luaopen_spi( lua_State *L ) { diff --git a/sdk/esp32-esp-idf b/sdk/esp32-esp-idf index 6407ecb3..35729009 160000 --- a/sdk/esp32-esp-idf +++ b/sdk/esp32-esp-idf @@ -1 +1 @@ -Subproject commit 6407ecb3f8d2cc07c4c230e7e64f2046af5c86f7 +Subproject commit 357290093430e41e7e3338227a61ef5162f2deed From 2b8e32713178b496098edc50af6f934762f482de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Tue, 23 Jan 2024 23:01:12 +0100 Subject: [PATCH 39/47] Fix the RTD configuration --- .readthedocs.yaml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 983ee9b1..b67bdf03 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,15 +1,19 @@ -# .readthedocs.yaml -# Read the Docs configuration file +# Read the Docs configuration file for MkDocs projects # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.12" + mkdocs: configuration: mkdocs.yml -# Optionally set the version of Python and requirements required to build your docs +# Optionally declare the Python requirements required to build your docs python: - version: "3.7" install: - - requirements: docs/requirements.txt + - requirements: docs/requirements.txt \ No newline at end of file From f126b6fe91848cd930ec4938235d346815bd5851 Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Tue, 31 Jan 2023 17:07:54 +1100 Subject: [PATCH 40/47] Initial migration to IDFv5.0 Plenty of dependency adjustments, printf format specificier updates, FreeRTOS type and macro name modernisation, not to mention API changes. Still plenty of legacy/deprecated drivers in use which will need updating. The following features have been removed due to no longer being available from the IDF: - ADC hall effect sensor reading - Configuration of SD SPI host via sdmmc module (now must be done first via the spimaster module) - FAT partition selection on external SD cards; only the first FAT partition is supported by the IDF now On the other hand, the eth module now supports the following new chipsets: - KSZ8001 - KSZ8021 - KSZ8031 - KSZ8051 - KSZ8061 - KSZ8091 - Possibly additional models in the LAN87xx series (the IDF docs aren't clear on precisely which models are handled) Further, the sdmmc module is now available on the ESP32-S3 as well. --- .gitignore | 3 + components/base_nodemcu/CMakeLists.txt | 2 +- components/base_nodemcu/user_main.c | 4 +- components/driver_can/CAN.c | 3 + components/driver_can/CMakeLists.txt | 1 + components/driver_i2c/CMakeLists.txt | 3 +- .../driver_i2c/include/driver/i2c_sw_master.h | 4 +- components/lua/Kconfig | 2 +- components/lua/lua-5.1/lobject.c | 4 +- components/lua/lua-5.3/lnodemcu.c | 8 +- components/modules/CMakeLists.txt | 11 +- components/modules/adc.c | 6 +- components/modules/can.c | 2 +- components/modules/eth.c | 35 ++-- components/modules/heaptrace.c | 3 + components/modules/i2c_hw_master.c | 11 +- components/modules/i2s.c | 14 +- components/modules/idf_component.yml | 7 + components/modules/mqtt.c | 104 ++++++---- components/modules/node.c | 1 + components/modules/otaupgrade.c | 1 + components/modules/pulsecnt.c | 6 +- components/modules/sdmmc.c | 76 ++++---- components/modules/touch.c | 6 +- components/platform/CMakeLists.txt | 29 ++- components/platform/flash_api.c | 179 ------------------ components/platform/include/cpu_esp32.h | 10 +- components/platform/include/flash_api.h | 25 --- components/platform/include/platform.h | 6 +- components/platform/onewire.c | 6 +- components/platform/platform.c | 35 ++-- components/platform/platform_flash.c | 179 ++---------------- components/platform/platform_partition.c | 49 +---- components/platform/platform_rmt.c | 1 - components/platform/u8x8_nodemcu_hal.c | 12 +- components/platform/ucg_nodemcu_hal.c | 2 +- components/platform/wdt.c | 25 ++- components/platform/ws2812.c | 2 + components/rtos_dbg/rtos_dbg.c | 4 +- components/task/task.c | 4 +- docs/modules/adc.md | 19 -- docs/modules/eth.md | 14 +- docs/modules/sdmmc.md | 6 +- sdk/esp32-esp-idf | 2 +- 44 files changed, 307 insertions(+), 619 deletions(-) create mode 100644 components/modules/idf_component.yml delete mode 100644 components/platform/flash_api.c delete mode 100644 components/platform/include/flash_api.h diff --git a/.gitignore b/.gitignore index b004739b..e161118d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ components/*/.output/ tools/toolchains .ccache bin +version.txt +managed_components/ +dependencies.lock #ignore Eclipse project files .cproject diff --git a/components/base_nodemcu/CMakeLists.txt b/components/base_nodemcu/CMakeLists.txt index 72072637..50114b1f 100644 --- a/components/base_nodemcu/CMakeLists.txt +++ b/components/base_nodemcu/CMakeLists.txt @@ -2,6 +2,6 @@ idf_component_register( SRCS "ip_fmt.c" "user_main.c" INCLUDE_DIRS "include" REQUIRES "lua" - PRIV_REQUIRES "nvs_flash" "spiffs" + PRIV_REQUIRES "nvs_flash" "spiffs" "esp_netif" "driver" LDFRAGMENTS "nodemcu.lf" ) diff --git a/components/base_nodemcu/user_main.c b/components/base_nodemcu/user_main.c index adb8832f..2e8bca56 100644 --- a/components/base_nodemcu/user_main.c +++ b/components/base_nodemcu/user_main.c @@ -17,8 +17,8 @@ #include "esp_system.h" #include "esp_event.h" #include "esp_spiffs.h" +#include "esp_netif.h" #include "nvs_flash.h" -#include "flash_api.h" #include "task/task.h" #include "sections.h" @@ -64,7 +64,7 @@ static void relay_default_loop_events( if (task_post_medium(relayed_event_task, (intptr_t)&event)) xSemaphoreTake(relayed_event_handled, portMAX_DELAY); else - printf("ERROR: failed to forward esp event %s/%d", base, id); + printf("ERROR: failed to forward esp event %s/%ld", base, id); } diff --git a/components/driver_can/CAN.c b/components/driver_can/CAN.c index ddd6b619..d3ca24ba 100644 --- a/components/driver_can/CAN.c +++ b/components/driver_can/CAN.c @@ -32,6 +32,9 @@ #include "freertos/FreeRTOS.h" #include "freertos/queue.h" +#include "soc/gpio_sig_map.h" +#include "rom/gpio.h" + #include "esp_intr_alloc.h" #include "soc/dport_reg.h" #include diff --git a/components/driver_can/CMakeLists.txt b/components/driver_can/CMakeLists.txt index 44bde7b9..35ffaee3 100644 --- a/components/driver_can/CMakeLists.txt +++ b/components/driver_can/CMakeLists.txt @@ -3,6 +3,7 @@ if(IDF_TARGET STREQUAL "esp32") idf_component_register( SRCS "CAN.c" INCLUDE_DIRS "include" + PRIV_REQUIRES "driver" "esp_rom" ) endif() diff --git a/components/driver_i2c/CMakeLists.txt b/components/driver_i2c/CMakeLists.txt index 99b83882..64ae54bd 100644 --- a/components/driver_i2c/CMakeLists.txt +++ b/components/driver_i2c/CMakeLists.txt @@ -1,5 +1,6 @@ idf_component_register( SRCS "i2c_sw_master.c" INCLUDE_DIRS "include" - REQUIRES "esp32" + REQUIRES "esp_rom" + PRIV_REQUIRES "driver" ) diff --git a/components/driver_i2c/include/driver/i2c_sw_master.h b/components/driver_i2c/include/driver/i2c_sw_master.h index ac8b8d30..5cb6d801 100644 --- a/components/driver_i2c/include/driver/i2c_sw_master.h +++ b/components/driver_i2c/include/driver/i2c_sw_master.h @@ -1,14 +1,14 @@ #ifndef __I2C_SW_MASTER_H__ #define __I2C_SW_MASTER_H__ -#include "rom/ets_sys.h" +#include "esp_rom_sys.h" #define I2C_NUM_MAX 1 void i2c_sw_master_gpio_init(uint8_t sda, uint8_t scl); void i2c_sw_master_init(void); -#define i2c_sw_master_wait ets_delay_us +#define i2c_sw_master_wait esp_rom_delay_us void i2c_sw_master_stop(void); void i2c_sw_master_start(void); void i2c_sw_master_setAck(uint8_t level); diff --git a/components/lua/Kconfig b/components/lua/Kconfig index f41170ef..695101a5 100644 --- a/components/lua/Kconfig +++ b/components/lua/Kconfig @@ -2,7 +2,7 @@ menu "Lua configuration" choice LUA_VERSION prompt "Lua version" - default LUA_VERSION_51 + default LUA_VERSION_53 help The version of Lua interpreter to use. diff --git a/components/lua/lua-5.1/lobject.c b/components/lua/lua-5.1/lobject.c index 39512b8c..2a0b0530 100644 --- a/components/lua/lua-5.1/lobject.c +++ b/components/lua/lua-5.1/lobject.c @@ -20,9 +20,7 @@ #include "lstate.h" #include "lstring.h" #include "lvm.h" -#ifndef LUA_CROSS_COMPILER -#include "flash_api.h" -#else +#ifdef LUA_CROSS_COMPILER #include #endif const TValue luaO_nilobject_ = {LUA_TVALUE_NIL}; diff --git a/components/lua/lua-5.3/lnodemcu.c b/components/lua/lua-5.3/lnodemcu.c index f9cba0f5..7eb70fcd 100644 --- a/components/lua/lua-5.3/lnodemcu.c +++ b/components/lua/lua-5.3/lnodemcu.c @@ -177,7 +177,7 @@ static void platform_flash_erase_sector(lu_int32 i) { lockFlashWrite(); } -static void platform_s_flash_write(const void *from, lu_int32 to, lu_int32 len) { +static void platform_flash_write(const void *from, lu_int32 to, lu_int32 len) { lua_assert(to >= LFSbase && to + len < LFSbase + LFS_SIZE); /* DEBUG */ unlockFlashWrite(); memcpy(byteptr(LFSregion) + (to-LFSbase), from, len); @@ -449,7 +449,7 @@ static const char *readF (lua_State *L, void *ud, size_t *size) { static void eraseLFS(LFSflashState *F) { lu_int32 i; #ifdef LUA_USE_ESP - printf("\nErasing LFS from flash addr 0x%06x", F->addrPhys); + printf("\nErasing LFS from flash addr 0x%06lx", F->addrPhys); #endif unlockFlashWrite(); for (i = 0; i < F->size; i += FLASH_PAGE_SIZE) { @@ -464,7 +464,7 @@ static void eraseLFS(LFSflashState *F) { platform_flash_erase_sector(s); } #ifdef LUA_USE_ESP - printf(" to 0x%06x\n", F->addrPhys + F->size-1); + printf(" to 0x%06lx\n", F->addrPhys + F->size-1); #endif flush_icache(F); lockFlashWrite(); @@ -483,7 +483,7 @@ LUAI_FUNC void luaN_flushFlash(void *vF) { lu_int32 size = F->oNdx * WORDSIZE; lua_assert(start + size < F->addrPhys + F->size); /* is write in bounds? */ //printf("Flush Buf: %6x (%u)\n", F->oNdx, size); //DEBUG - platform_s_flash_write(F->oBuff, start, size); + platform_flash_write(F->oBuff, start, size); F->oChunkNdx += F->oNdx; F->oNdx = 0; } diff --git a/components/modules/CMakeLists.txt b/components/modules/CMakeLists.txt index 80455ef9..c5e77a2a 100644 --- a/components/modules/CMakeLists.txt +++ b/components/modules/CMakeLists.txt @@ -59,13 +59,14 @@ if(IDF_TARGET STREQUAL "esp32") elseif(IDF_TARGET STREQUAL "esp32s2") list(APPEND module_srcs "dac.c" - "rmt.c" "pulsecnt.c" + "rmt.c" ) elseif(IDF_TARGET STREQUAL "esp32s3") list(APPEND module_srcs - "rmt.c" "pulsecnt.c" + "rmt.c" + "sdmmc.c" ) elseif(IDF_TARGET STREQUAL "esp32c3") list(APPEND module_srcs @@ -81,9 +82,11 @@ idf_component_register( "base_nodemcu" "bt" "driver_can" + "esp_eth" "esp_http_client" - "fatfs" "esp_http_server" + "esp_hw_support" + "fatfs" "libsodium" "lua" "mbedtls" @@ -91,10 +94,12 @@ idf_component_register( "platform" "qrcodegen" "sdmmc" + "spi_flash" "sjson" "soc" "u8g2" "ucg" + "vfs" ) # Match up all the module source files with their corresponding Kconfig diff --git a/components/modules/adc.c b/components/modules/adc.c index 86af249c..33f334a9 100644 --- a/components/modules/adc.c +++ b/components/modules/adc.c @@ -54,9 +54,9 @@ static int adc_read( lua_State *L ) // Lua: read_hall_sensor( ) static int read_hall_sensor( lua_State *L ) { - int sample = platform_adc_read_hall_sensor( ); - lua_pushinteger( L, ( lua_Integer ) sample ); - return 1; + const char *msg = "ADC hall sensor no longer supported in IDF, sorry"; + platform_print_deprecation_note("msg", "IDFv5"); + return luaL_error(L, msg); } // Module function map diff --git a/components/modules/can.c b/components/modules/can.c index 6f748057..3816bafa 100644 --- a/components/modules/can.c +++ b/components/modules/can.c @@ -29,7 +29,7 @@ CAN_device_t CAN_cfg = { static task_handle_t can_data_task_id; static int can_on_received = LUA_NOREF; -static xTaskHandle xCanTaskHandle = NULL; +static TaskHandle_t xCanTaskHandle = NULL; // LUA static void can_data_task( task_param_t param, task_prio_t prio ) { diff --git a/components/modules/eth.c b/components/modules/eth.c index 7ccefad5..7aaed53f 100644 --- a/components/modules/eth.c +++ b/components/modules/eth.c @@ -14,6 +14,7 @@ #include "esp_eth.h" #include "esp_eth_phy.h" #include "driver/gpio.h" +#include "esp_mac.h" typedef esp_eth_phy_t *(*new_eth_phy_fn)(const eth_phy_config_t *config); @@ -21,9 +22,8 @@ typedef esp_eth_phy_t *(*new_eth_phy_fn)(const eth_phy_config_t *config); typedef enum { ETH_PHY_DP83848, ETH_PHY_IP101, - ETH_PHY_KSZ8041, - ETH_PHY_KSZ8081, - ETH_PHY_LAN8720, + ETH_PHY_KSZ80XX, + ETH_PHY_LAN87XX, ETH_PHY_RTL8201, ETH_PHY_MAX } eth_phy_t; @@ -31,9 +31,8 @@ typedef enum { static const new_eth_phy_fn new_eth_phy[] = { [ETH_PHY_DP83848] = esp_eth_phy_new_dp83848, [ETH_PHY_IP101] = esp_eth_phy_new_ip101, - [ETH_PHY_KSZ8041] = esp_eth_phy_new_ksz8041, - [ETH_PHY_KSZ8081] = esp_eth_phy_new_ksz8081, - [ETH_PHY_LAN8720] = esp_eth_phy_new_lan8720, + [ETH_PHY_KSZ80XX] = esp_eth_phy_new_ksz80xx, + [ETH_PHY_LAN87XX] = esp_eth_phy_new_lan87xx, [ETH_PHY_RTL8201] = esp_eth_phy_new_rtl8201, }; _Static_assert(sizeof(new_eth_phy) == (sizeof(new_eth_phy[0]) * ETH_PHY_MAX), @@ -273,13 +272,15 @@ static int leth_init( lua_State *L ) // temporarily copy option table to top of stack for opt_ functions lua_pushvalue( L, stack ); - eth_mac_config_t mac_cfg = ETH_MAC_DEFAULT_CONFIG(); + eth_esp32_emac_config_t emac_cfg = ETH_ESP32_EMAC_DEFAULT_CONFIG(); - mac_cfg.smi_mdc_gpio_num = + emac_cfg.smi_mdc_gpio_num = opt_checkint_range( L, "mdc", -1, GPIO_NUM_0, GPIO_NUM_MAX-1 ); - mac_cfg.smi_mdio_gpio_num = + emac_cfg.smi_mdio_gpio_num = opt_checkint_range( L, "mdio", -1, GPIO_NUM_0, GPIO_NUM_MAX-1 ); + eth_mac_config_t mac_cfg = ETH_MAC_DEFAULT_CONFIG(); + eth_phy_config_t phy_cfg = ETH_PHY_DEFAULT_CONFIG(); phy_cfg.phy_addr = opt_checkint_range( L, "addr", -1, -1, 31 ); @@ -290,7 +291,7 @@ static int leth_init( lua_State *L ) lua_settop( L, top ); - esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_cfg); + esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&emac_cfg, &mac_cfg); esp_eth_phy_t *phy = new_eth_phy[phy_type](&phy_cfg); esp_eth_config_t eth_cfg = ETH_DEFAULT_CONFIG(mac, phy); @@ -302,10 +303,6 @@ static int leth_init( lua_State *L ) esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); esp_netif_t *new_eth = esp_netif_new(&netif_cfg); - err = esp_eth_set_default_handlers(new_eth); - if (err != ESP_OK) - goto cleanup_netif; - void *glue = esp_eth_new_netif_glue(eth_handle); err = esp_netif_attach(new_eth, glue); if (err != ESP_OK) @@ -320,8 +317,6 @@ static int leth_init( lua_State *L ) cleanup_glue: esp_eth_del_netif_glue(glue); - -cleanup_netif: esp_netif_destroy(new_eth); cleanup_mac_phy: @@ -354,9 +349,11 @@ LROT_BEGIN(eth, NULL, 0) LROT_NUMENTRY( PHY_DP83848, ETH_PHY_DP83848 ) LROT_NUMENTRY( PHY_IP101, ETH_PHY_IP101 ) - LROT_NUMENTRY( PHY_KSZ8041, ETH_PHY_KSZ8041 ) - LROT_NUMENTRY( PHY_KSZ8081, ETH_PHY_KSZ8081 ) - LROT_NUMENTRY( PHY_LAN8720, ETH_PHY_LAN8720 ) + LROT_NUMENTRY( PHY_KSZ80XX, ETH_PHY_KSZ80XX ) + LROT_NUMENTRY( PHY_KSZ8041, ETH_PHY_KSZ80XX ) // deprecated in favour of 80XX + LROT_NUMENTRY( PHY_KSZ8081, ETH_PHY_KSZ80XX ) // deprecated in favour of 80XX + LROT_NUMENTRY( PHY_LAN87XX, ETH_PHY_LAN87XX ) + LROT_NUMENTRY( PHY_LAN8720, ETH_PHY_LAN87XX ) // deprecated in favour of 87XX LROT_NUMENTRY( PHY_RTL8201, ETH_PHY_RTL8201 ) LROT_END(eth, NULL, 0) diff --git a/components/modules/heaptrace.c b/components/modules/heaptrace.c index 8a5966f4..d8f023ee 100644 --- a/components/modules/heaptrace.c +++ b/components/modules/heaptrace.c @@ -1,7 +1,10 @@ #include "module.h" #include "lauxlib.h" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcpp" #include "esp_heap_trace.h" +#pragma GCC diagnostic pop static heap_trace_record_t *buffer = NULL; diff --git a/components/modules/i2c_hw_master.c b/components/modules/i2c_hw_master.c index f0709292..e4055fc4 100644 --- a/components/modules/i2c_hw_master.c +++ b/components/modules/i2c_hw_master.c @@ -1,10 +1,11 @@ - #include "module.h" #include "lauxlib.h" #include "lmem.h" +#include "platform.h" #include "driver/i2c.h" #include "soc/i2c_reg.h" #include "hal/i2c_ll.h" +#include "rom/gpio.h" #include "i2c_common.h" @@ -73,7 +74,7 @@ static void vTransferTask( void *pvParameters ) xQueueReceive( xQueue, job, portMAX_DELAY ); job->err = i2c_master_cmd_begin( job->port, job->cmd, - job->to_ms > 0 ? job->to_ms / portTICK_RATE_MS : portMAX_DELAY ); + job->to_ms > 0 ? job->to_ms / portTICK_PERIOD_MS : portMAX_DELAY ); task_post_medium( i2c_transfer_task_id, (task_param_t)job ); } @@ -202,11 +203,11 @@ int li2c_hw_master_setup( lua_State *L, unsigned id, unsigned sda, unsigned scl, memset( &cfg, 0, sizeof( cfg ) ); cfg.mode = I2C_MODE_MASTER; - luaL_argcheck( L, GPIO_IS_VALID_OUTPUT_GPIO(sda), 2, "invalid sda pin" ); + luaL_argcheck( L, platform_gpio_output_exists(sda), 2, "invalid sda pin" ); cfg.sda_io_num = sda; cfg.sda_pullup_en = GPIO_PULLUP_ENABLE; - luaL_argcheck( L, GPIO_IS_VALID_OUTPUT_GPIO(scl), 3, "invalid scl pin" ); + luaL_argcheck( L, platform_gpio_output_exists(scl), 3, "invalid scl pin" ); cfg.scl_io_num = scl; cfg.scl_pullup_en = GPIO_PULLUP_ENABLE; @@ -357,7 +358,7 @@ int li2c_hw_master_transfer( lua_State *L ) // note that i2c_master_cmd_begin() implements mutual exclusive access // if it is currently in progress from the transfer task, it will block here until esp_err_t err = i2c_master_cmd_begin( job->port, job->cmd, - job->to_ms > 0 ? job->to_ms / portTICK_RATE_MS : portMAX_DELAY ); + job->to_ms > 0 ? job->to_ms / portTICK_PERIOD_MS : portMAX_DELAY ); switch (err) { case ESP_OK: diff --git a/components/modules/i2s.c b/components/modules/i2s.c index 88fab401..cd213c57 100644 --- a/components/modules/i2s.c +++ b/components/modules/i2s.c @@ -30,11 +30,11 @@ typedef struct { typedef struct { struct { - xTaskHandle taskHandle; + TaskHandle_t taskHandle; QueueHandle_t queue; } tx; struct { - xTaskHandle taskHandle; + TaskHandle_t taskHandle; QueueHandle_t queue; } rx; int cb; @@ -152,8 +152,8 @@ static int node_i2s_start( lua_State *L ) // i2s_config.channel_format = opt_checkint(L, "channel", I2S_CHANNEL_FMT_RIGHT_LEFT); i2s_config.communication_format = opt_checkint(L, "format", I2S_COMM_FORMAT_STAND_I2S); - i2s_config.dma_buf_count = opt_checkint_range(L, "buffer_count", 2, 2, 128); - i2s_config.dma_buf_len = opt_checkint_range(L, "buffer_len", i2s_config.sample_rate / 100, 8, 1024); + i2s_config.dma_desc_num = opt_checkint_range(L, "buffer_count", 2, 2, 128); + i2s_config.dma_frame_num = opt_checkint_range(L, "buffer_len", i2s_config.sample_rate / 100, 8, 1024); i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; // pin_config.bck_io_num = opt_checkint(L, "bck_pin", I2S_PIN_NO_CHANGE); @@ -176,9 +176,9 @@ static int node_i2s_start( lua_State *L ) esp_err_t err; if (i2s_config.mode & I2S_MODE_RX) { - err = i2s_driver_install(i2s_id, &i2s_config, i2s_config.dma_buf_count, &is->rx.queue); + err = i2s_driver_install(i2s_id, &i2s_config, i2s_config.dma_desc_num, &is->rx.queue); } else { - err = i2s_driver_install(i2s_id, &i2s_config, i2s_config.dma_buf_count, NULL); + err = i2s_driver_install(i2s_id, &i2s_config, i2s_config.dma_desc_num, NULL); } if (err != ESP_OK) luaL_error( L, "i2s can not start" ); @@ -265,7 +265,7 @@ static int node_i2s_read( lua_State *L ) int wait_ms = luaL_optint(L, 3, 0); char * data = luaM_malloc( L, bytes ); size_t read; - if (i2s_read(i2s_id, data, bytes, &read, wait_ms / portTICK_RATE_MS) != ESP_OK) + if (i2s_read(i2s_id, data, bytes, &read, wait_ms / portTICK_PERIOD_MS) != ESP_OK) return luaL_error( L, "I2S driver error" ); lua_pushlstring(L, data, read); diff --git a/components/modules/idf_component.yml b/components/modules/idf_component.yml new file mode 100644 index 00000000..ae9c5301 --- /dev/null +++ b/components/modules/idf_component.yml @@ -0,0 +1,7 @@ +## IDF Component Manager Manifest File +dependencies: + ## Required IDF version + idf: + version: ">=5.0.0" + # Component dependencies + libsodium: "~1.0.20" diff --git a/components/modules/mqtt.c b/components/modules/mqtt.c index ecc8aca4..3c4909aa 100644 --- a/components/modules/mqtt.c +++ b/components/modules/mqtt.c @@ -50,6 +50,13 @@ typedef struct { }; } mqtt_context_t; + +typedef struct { + mqtt_context_t *ctx; + esp_mqtt_event_t event; +} event_info_t; + + // event_handler_t is the function signature for all events typedef void (*event_handler_t)(lua_State* L, mqtt_context_t* mqtt_context, esp_mqtt_event_handle_t event); @@ -64,10 +71,12 @@ task_handle_t event_handler_task_id = 0; // event_clone makes a copy of the mqtt event received so we can pass it on // and the mqtt library can discard it. -static esp_mqtt_event_handle_t event_clone(esp_mqtt_event_handle_t ev) { +static event_info_t *event_clone(esp_mqtt_event_handle_t ev, mqtt_context_t *ctx) { // allocate memory for the copy - esp_mqtt_event_handle_t ev1 = (esp_mqtt_event_handle_t)malloc(sizeof(esp_mqtt_event_t)); + event_info_t *clone = (event_info_t *)malloc(sizeof(event_info_t)); ESP_LOGD(TAG, "event_clone(): event %p, event id %d, msg %d", ev, ev->event_id, ev->msg_id); + clone->ctx = ctx; + esp_mqtt_event_handle_t ev1 = &clone->event; // make a shallow copy: *ev1 = *ev; @@ -95,11 +104,12 @@ static esp_mqtt_event_handle_t event_clone(esp_mqtt_event_handle_t ev) { ev1->topic = NULL; } } - return ev1; + return clone; } // event_free deallocates all the memory associated with a cloned event -static void event_free(esp_mqtt_event_handle_t ev) { +static void event_free(event_info_t *clone) { + esp_mqtt_event_handle_t ev = &clone->event; if (ev->data != NULL) { ESP_LOGD(TAG, "event_free():free: event %p, msg %d, data %p", ev, ev->msg_id, ev->data); free(ev->data); @@ -108,7 +118,7 @@ static void event_free(esp_mqtt_event_handle_t ev) { ESP_LOGD(TAG, "event_free():free: event %p, msg %d, topic %p", ev, ev->msg_id, ev->topic); free(ev->topic); } - free(ev); + free(clone); } // event_connected is run when the mqtt client connected @@ -235,10 +245,9 @@ static void event_data_received(lua_State* L, mqtt_context_t* mqtt_context, esp_ // event_task_handler takes a nodemcu task message and dispatches it to the appropriate event_xxx callback above. static void event_task_handler(task_param_t param, task_prio_t prio) { // extract the event data out of the task param - esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)param; - - // recover the mqtt context from the event user_context field: - mqtt_context_t* mqtt_context = (mqtt_context_t*)event->user_context; + event_info_t *info = (event_info_t *)param; + esp_mqtt_event_handle_t event = &info->event; + mqtt_context_t* mqtt_context = info->ctx; // Check if this event is about an object that is in the process of garbage collection: if (!luaX_valid_ref(mqtt_context->self)) { @@ -290,14 +299,17 @@ static void event_task_handler(task_param_t param, task_prio_t prio) { lua_settop(L, top); // leave the stack as it was task_handler_end: - event_free(event); // free the event copy memory + event_free(info); // free the cloned event info } // mqtt_event_handler receives all events from the esp mqtt library and converts them // to a task message -static esp_err_t mqtt_event_handler(esp_mqtt_event_handle_t event) { - task_post_medium(event_handler_task_id, (task_param_t)event_clone(event)); - return ESP_OK; +static void mqtt_event_handler(void *handler_arg, esp_event_base_t base, int32_t event_id, void *event_data) { + (void)base; + (void)event_id; + event_info_t *clone = event_clone(event_data, handler_arg); + if (!task_post_medium(event_handler_task_id, (task_param_t)clone)) + event_free(clone); } // Lua: on() @@ -330,7 +342,15 @@ static int mqtt_connect(lua_State* L) { memset(&config, 0, sizeof(esp_mqtt_client_config_t)); // process function parameters populating the mqtt config structure - config.host = luaL_checkstring(L, 2); + const char *host = luaL_checkstring(L, 2); + bool is_mqtt_uri = strncmp(host, "mqtt://", 7) == 0; + bool is_mqtts_uri = strncmp(host, "mqtts://", 8) == 0; + bool is_ws_uri = strncmp(host, "ws://", 5) == 0; + bool is_wss_uri = strncmp(host, "wss://", 6) == 0; + if (is_mqtt_uri || is_mqtts_uri || is_ws_uri || is_wss_uri) + config.broker.address.uri = host; + else + config.broker.address.hostname = host; // set defaults: int secure = 0; @@ -340,14 +360,20 @@ static int mqtt_connect(lua_State* L) { const char * cert_pem = NULL; const char * client_cert_pem = NULL; const char * client_key_pem = NULL; - + size_t cert_pem_len = 0; + size_t client_cert_pem_len = 0; + size_t client_key_pem_len = 0; if (lua_isnumber(L, n)) { + if (is_mqtt_uri || is_mqtts_uri) + return luaL_error(L, "port arg must be nil if giving full uri"); port = luaL_checknumber(L, n); n++; } if (lua_isnumber(L, n)) { + if (is_mqtt_uri || is_mqtts_uri) + return luaL_error(L, "secure on/off determined by uri"); secure = !!luaL_checkinteger(L, n); n++; @@ -355,19 +381,19 @@ static int mqtt_connect(lua_State* L) { if (lua_istable(L, n)) { secure = true; lua_getfield(L, n, "ca_cert"); - if ((cert_pem = luaL_optstring(L, -1, NULL)) != NULL) { + if ((cert_pem = luaL_optlstring(L, -1, NULL, &cert_pem_len)) != NULL) { luaX_set_ref(L, -1, &mqtt_context->cert_pem); } lua_pop(L, 1); // lua_getfield(L, n, "client_cert"); - if ((client_cert_pem = luaL_optstring(L, -1, NULL)) != NULL) { + if ((client_cert_pem = luaL_optlstring(L, -1, NULL, &client_cert_pem_len)) != NULL) { luaX_set_ref(L, -1, &mqtt_context->client_cert_pem); } lua_pop(L, 1); // lua_getfield(L, n, "client_key"); - if ((client_key_pem = luaL_optstring(L, -1, NULL)) != NULL) { + if ((client_key_pem = luaL_optlstring(L, -1, NULL, &client_key_pem_len)) != NULL) { luaX_set_ref(L, -1, &mqtt_context->client_key_pem); } lua_pop(L, 1); @@ -393,24 +419,26 @@ static int mqtt_connect(lua_State* L) { ESP_LOGD(TAG, "connect: mqtt_context*: %p", mqtt_context); - config.user_context = mqtt_context; // store a pointer to our context in the mqtt client user context field - // this will be useful to identify to which instance events belong to - config.event_handle = mqtt_event_handler; // set the function that will be called by the mqtt client everytime something - // happens - - config.client_id = mqtt_context->client_id; - config.lwt_msg = mqtt_context->lwt_msg; - config.lwt_topic = mqtt_context->lwt_topic; - config.username = mqtt_context->username; - config.password = mqtt_context->password; - config.keepalive = mqtt_context->keepalive; - config.disable_clean_session = mqtt_context->disable_clean_session; - config.port = port; - config.disable_auto_reconnect = (reconnect == 0); - config.transport = secure ? MQTT_TRANSPORT_OVER_SSL : MQTT_TRANSPORT_OVER_TCP; - config.cert_pem = cert_pem; - config.client_cert_pem = client_cert_pem; - config.client_key_pem = client_key_pem; + if (config.broker.address.uri != NULL) + { + config.broker.address.port = port; + config.broker.address.transport = + secure ? MQTT_TRANSPORT_OVER_SSL : MQTT_TRANSPORT_OVER_TCP; + } + config.broker.verification.certificate = cert_pem; + config.broker.verification.certificate_len = cert_pem_len; + config.credentials.authentication.certificate = client_cert_pem; + config.credentials.authentication.certificate_len = client_cert_pem_len; + config.credentials.authentication.key = client_key_pem; + config.credentials.authentication.key_len = client_key_pem_len; + config.credentials.authentication.password = mqtt_context->password; + config.credentials.client_id = mqtt_context->client_id; + config.credentials.username = mqtt_context->username; + config.network.disable_auto_reconnect = (reconnect == 0); + config.session.disable_clean_session = mqtt_context->disable_clean_session; + config.session.keepalive = mqtt_context->keepalive; + config.session.last_will.msg = mqtt_context->lwt_msg; + config.session.last_will.topic = mqtt_context->lwt_topic; // create a mqtt client instance mqtt_context->client = esp_mqtt_client_init(&config); @@ -419,6 +447,10 @@ static int mqtt_connect(lua_State* L) { return 0; } + // register the event handler with mqtt_context as the handler arg + esp_mqtt_client_register_event( + mqtt_context->client, ESP_EVENT_ANY_ID, mqtt_event_handler, mqtt_context); + // actually start the mqtt client and connect esp_err_t err = esp_mqtt_client_start(mqtt_context->client); if (err != ESP_OK) { diff --git a/components/modules/node.c b/components/modules/node.c index af25ccd3..b16c67f0 100644 --- a/components/modules/node.c +++ b/components/modules/node.c @@ -5,6 +5,7 @@ #include "platform.h" #include "task/task.h" #include "esp_system.h" +#include "esp_timer.h" #include "esp_log.h" #include "esp_sleep.h" #include "driver/rtc_io.h" diff --git a/components/modules/otaupgrade.c b/components/modules/otaupgrade.c index 2444ebcf..3c206f2f 100644 --- a/components/modules/otaupgrade.c +++ b/components/modules/otaupgrade.c @@ -37,6 +37,7 @@ #include "esp_system.h" #include "esp_ota_ops.h" #include "esp_partition.h" +#include "spi_flash_mmap.h" static esp_ota_handle_t ota_handle; static const esp_partition_t *next; diff --git a/components/modules/pulsecnt.c b/components/modules/pulsecnt.c index 4f4df34b..62030e61 100644 --- a/components/modules/pulsecnt.c +++ b/components/modules/pulsecnt.c @@ -114,7 +114,7 @@ static void pulsecnt_task(task_param_t param, task_prio_t prio) (void)prio; // we bit packed the unit number and status into 1 int in the IRAM interrupt so need to unpack here - uint32_t unit = (uint32_t)param & 0xffu; + uint8_t unit = (uint8_t)param & 0xffu; int status = ((uint32_t)param >> 8); // int16_t cur_count, evt_count = 0; @@ -217,7 +217,7 @@ static void pulsecnt_task(task_param_t param, task_prio_t prio) // lua_pushinteger (L, status); luaL_pcallx (L, 6, 0); } else { - if (pc->is_debug) ESP_LOGI("pulsecnt", "Could not find cb for unit %d with ptr %d", unit, pc->cb_ref); + if (pc->is_debug) ESP_LOGI("pulsecnt", "Could not find cb for unit %d with ptr %ld", unit, pc->cb_ref); } } @@ -599,7 +599,7 @@ static int pulsecnt_create( lua_State *L ) { pc->is_debug = true; } - if (pc->is_debug) ESP_LOGI("pulsecnt", "Created obj for unit %d with callback ref of %d", pc->unit, pc->cb_ref ); + if (pc->is_debug) ESP_LOGI("pulsecnt", "Created obj for unit %d with callback ref of %ld", pc->unit, pc->cb_ref ); return 1; } diff --git a/components/modules/sdmmc.c b/components/modules/sdmmc.c index e4cd94cb..d800ba14 100644 --- a/components/modules/sdmmc.c +++ b/components/modules/sdmmc.c @@ -13,6 +13,7 @@ #include "driver/sdspi_host.h" #include "common.h" +#include "platform.h" // We're limiting ourselves to the number of FAT volumes configured. #define NUM_CARDS FF_VOLUMES @@ -20,8 +21,12 @@ sdmmc_card_t *lsdmmc_card[NUM_CARDS]; // local definition for SDSPI host #define LSDMMC_HOST_SDSPI 100 +#ifdef CONFIG_IDF_TARGET_ESP32 #define LSDMMC_HOST_HSPI (LSDMMC_HOST_SDSPI + HSPI_HOST) #define LSDMMC_HOST_VSPI (LSDMMC_HOST_SDSPI + VSPI_HOST) +#endif +#define LSDMMC_HOST_SPI2 (LSDMMC_HOST_SDSPI + SPI2_HOST) +#define LSDMMC_HOST_SPI3 (LSDMMC_HOST_SDSPI + SPI3_HOST) typedef struct { sdmmc_card_t *card; @@ -31,25 +36,21 @@ typedef struct { } lsdmmc_ud_t; -static void choose_partition(uint8_t pdrv, uint8_t part) +static bool is_field_present(lua_State *L, int idx, const char *field) { - // Update the volume->partition mapping in FatFS - for (unsigned i = 0; i < FF_VOLUMES; ++i) - { - if (VolToPart[i].pd == pdrv) - VolToPart[i].pt = part; - } + lua_getfield(L, idx, field); + bool present = !lua_isnoneornil(L, -1); + lua_pop(L, 1); + return present; } -static esp_err_t sdmmc_mount_fat(lsdmmc_ud_t *ud, const char *base_path, uint8_t partition) +static esp_err_t sdmmc_mount_fat(lsdmmc_ud_t *ud, const char *base_path) { esp_err_t err = ff_diskio_get_drive(&ud->pdrv); if (err != ESP_OK) return err; - choose_partition(ud->pdrv, partition); - ff_diskio_register_sdmmc(ud->pdrv, ud->card); char drv[3] = { (char)('0' + ud->pdrv), ':', 0 }; @@ -73,7 +74,6 @@ fail: ud->fs = NULL; } esp_vfs_fat_unregister_path(base_path); - choose_partition(ud->pdrv, 0); ff_diskio_unregister(ud->pdrv); return err; @@ -101,7 +101,10 @@ static int lsdmmc_init( lua_State *L ) int slot = luaL_checkint( L, ++stack ); luaL_argcheck( L, slot == SDMMC_HOST_SLOT_0 || slot == SDMMC_HOST_SLOT_1 || - slot == LSDMMC_HOST_HSPI || slot == LSDMMC_HOST_VSPI, +#ifdef CONFIG_IDF_TARGET_ESP32 + slot == LSDMMC_HOST_HSPI || slot == LSDMMC_HOST_VSPI || +#endif + slot == LSDMMC_HOST_SPI2 || slot == LSDMMC_HOST_SPI3, stack, "invalid slot" ); bool is_sdspi = false; @@ -115,10 +118,7 @@ static int lsdmmc_init( lua_State *L ) int wp_pin = SDMMC_SLOT_NO_WP; int freq_khz = SDMMC_FREQ_DEFAULT; int width = SDMMC_HOST_FLAG_1BIT; - // additional entries for SDSPI configuration - int sck_pin = -1; - int mosi_pin = -1; - int miso_pin = -1; + // additional cs for SDSPI configuration int cs_pin = -1; if (lua_type( L, ++stack ) == LUA_TTABLE) { @@ -131,9 +131,12 @@ static int lsdmmc_init( lua_State *L ) // mandatory entries for SDSPI configuration if (is_sdspi) { - sck_pin = opt_checkint_range( L, "sck_pin", -1, 0, GPIO_NUM_MAX ); - mosi_pin = opt_checkint_range( L, "mosi_pin", -1, 0, GPIO_NUM_MAX ); - miso_pin = opt_checkint_range( L, "miso_pin", -1, 0, GPIO_NUM_MAX ); + if (is_field_present(L, -1, "sck_pin") || + is_field_present(L, -1, "mosi_pin") || + is_field_present(L, -1, "miso_pin")) + { + platform_print_deprecation_note("SCK/MOSI/MISO ignored; please configure via spimaster instead", "IDFv5"); + } cs_pin = opt_checkint_range( L, "cs_pin", -1, 0, GPIO_NUM_MAX ); } @@ -166,16 +169,16 @@ static int lsdmmc_init( lua_State *L ) } if (res == ESP_OK || res == ESP_ERR_INVALID_STATE) { + sdmmc_host_t sdspi_host_config = SDSPI_HOST_DEFAULT(); + sdmmc_host_t sdmmc_host_config = SDMMC_HOST_DEFAULT(); + if (is_sdspi) { // configure SDSPI slot - sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT(); - slot_config.gpio_miso = miso_pin; - slot_config.gpio_mosi = mosi_pin; - slot_config.gpio_sck = sck_pin; - slot_config.gpio_cs = cs_pin; - slot_config.gpio_cd = cd_pin; - slot_config.gpio_wp = wp_pin; - res = sdspi_host_init_slot( slot, &slot_config ); + sdspi_device_config_t dev_config = SDSPI_DEVICE_CONFIG_DEFAULT(); + dev_config.gpio_cs = cs_pin; + dev_config.gpio_cd = cd_pin; + dev_config.gpio_wp = wp_pin; + res = sdspi_host_init_device(&dev_config, &sdspi_host_config.slot); } else { // configure SDMMC slot sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); @@ -184,10 +187,7 @@ static int lsdmmc_init( lua_State *L ) res = sdmmc_host_init_slot( slot, &slot_config ); } if (res == ESP_OK) { - // initialize card - sdmmc_host_t sdspi_host_config = SDSPI_HOST_DEFAULT(); - sdmmc_host_t sdmmc_host_config = SDMMC_HOST_DEFAULT(); sdmmc_host_t *host_config = is_sdspi ? &sdspi_host_config : &sdmmc_host_config; host_config->slot = slot; host_config->flags &= ~SDMMC_HOST_FLAG_8BIT; @@ -352,13 +352,21 @@ static int lsdmmc_mount( lua_State *L ) (void)card; const char *ldrv = luaL_checkstring(L, 2); - int part = luaL_optint(L, 3, 0); + if (!lua_isnoneornil(L, 3)) + { + // Warn that this feature isn't around + platform_print_deprecation_note( + "partition selection not supported", "IDFv5"); + // ...and error if used to select something we can no longer do + if (luaL_optint(L, 3, 0) > 1) + return luaL_error(L, "only first partition supported since IDFv5"); + } lua_settop(L, 2); if (ud->fs == NULL) { - esp_err_t err = sdmmc_mount_fat(ud, ldrv, part); + esp_err_t err = sdmmc_mount_fat(ud, ldrv); if (err == ESP_OK) { // We need this when we unmount @@ -408,8 +416,12 @@ LROT_BEGIN(sdmmc, NULL, 0) LROT_FUNCENTRY( init, lsdmmc_init ) LROT_NUMENTRY( HS1, SDMMC_HOST_SLOT_0 ) LROT_NUMENTRY( HS2, SDMMC_HOST_SLOT_1 ) +#ifdef CONFIG_IDF_TARGET_ESP32 LROT_NUMENTRY( HSPI, LSDMMC_HOST_HSPI ) LROT_NUMENTRY( VSPI, LSDMMC_HOST_VSPI ) +#endif + LROT_NUMENTRY( SPI2, LSDMMC_HOST_SPI2 ) + LROT_NUMENTRY( SPI3, LSDMMC_HOST_SPI3 ) LROT_NUMENTRY( W1BIT, SDMMC_HOST_FLAG_1BIT ) LROT_NUMENTRY( W4BIT, SDMMC_HOST_FLAG_1BIT | SDMMC_HOST_FLAG_4BIT ) diff --git a/components/modules/touch.c b/components/modules/touch.c index a1d2137b..e8bb2abd 100644 --- a/components/modules/touch.c +++ b/components/modules/touch.c @@ -190,7 +190,7 @@ static int touch_create( lua_State *L ) { tp->is_intr = opt_checkbool(L, "intrInitAtStart", true); tp->thresTrigger = opt_checkint_range(L, "thresTrigger", TOUCH_TRIGGER_BELOW, TOUCH_TRIGGER_BELOW, TOUCH_TRIGGER_MAX); - if (tp->is_debug) ESP_LOGI(TAG, "isDebug: %d, filterMs: %d, lvolt: %d, hvolt: %d, atten: %d, slope: %d, intrInitAtStart: %d, thresTrigger: %d", + if (tp->is_debug) ESP_LOGI(TAG, "isDebug: %d, filterMs: %lu, lvolt: %d, hvolt: %d, atten: %d, slope: %d, intrInitAtStart: %d, thresTrigger: %d", tp->is_debug, tp->filterMs, tp->lvolt, tp->hvolt, tp->atten, tp->slope, tp->is_intr, tp->thresTrigger); // get the field pad. this can be passed in as int or table of ints. pad = 0 || {0,1,2,3,4,5,6,7,8,9} @@ -277,7 +277,7 @@ static int touch_create( lua_State *L ) { // Initialize and start a software filter to detect slight change of capacitance. if (tp->filterMs > 0) { touch_pad_filter_start(tp->filterMs); - if (tp->is_debug) ESP_LOGI(TAG, "Set filter period to %d ms", tp->filterMs ); + if (tp->is_debug) ESP_LOGI(TAG, "Set filter period to %lu ms", tp->filterMs ); } // Register touch interrupt ISR @@ -320,7 +320,7 @@ static int touch_create( lua_State *L ) { // prevent false triggering when detecting slight change of capacitance. Need to call // touch_pad_filter_start before all touch filter APIs if (tp->filterMs > 0) { - if (tp->is_debug) ESP_LOGI(TAG, "You provided a filter so turning on filter mode. filterMs: %d", tp->filterMs); + if (tp->is_debug) ESP_LOGI(TAG, "You provided a filter so turning on filter mode. filterMs: %lu", tp->filterMs); esp_err_t err = touch_pad_filter_start(tp->filterMs); if (err == ESP_ERR_INVALID_ARG) { ESP_LOGI(TAG, "Filter start parameter error"); diff --git a/components/platform/CMakeLists.txt b/components/platform/CMakeLists.txt index 1e7148e8..955f8dbb 100644 --- a/components/platform/CMakeLists.txt +++ b/components/platform/CMakeLists.txt @@ -1,8 +1,27 @@ idf_component_register( - SRCS "dht.c" "flash_api.c" "onewire.c" "platform.c" - "platform_flash.c" "platform_partition.c" "platform_rmt.c" - "u8x8_nodemcu_hal.c" "ucg_nodemcu_hal.c" "wdt.c" "ws2812.c" + SRCS + "dht.c" + "onewire.c" + "platform.c" + "platform_flash.c" + "platform_partition.c" + "platform_rmt.c" + "u8x8_nodemcu_hal.c" + "ucg_nodemcu_hal.c" + "wdt.c" + "ws2812.c" INCLUDE_DIRS "include" - REQUIRES "spiffs" "u8g2" "ucg" "driver_i2c" "task" - PRIV_REQUIRES "bootloader_support" "lua" "esp32" + REQUIRES + "driver" + "driver_i2c" + "spi_flash" + "spiffs" + "task" + "u8g2" + "ucg" + PRIV_REQUIRES + "bootloader_support" + "esp_rom" + "lua" + "soc" ) diff --git a/components/platform/flash_api.c b/components/platform/flash_api.c deleted file mode 100644 index 9b6383a4..00000000 --- a/components/platform/flash_api.c +++ /dev/null @@ -1,179 +0,0 @@ -/****************************************************************************** - * Flash api for NodeMCU - * NodeMCU Team - * 2014-12-31 -*******************************************************************************/ -#include "flash_api.h" -#include -#include -#include -#include "rom/spi_flash.h" -#include "platform_wdt.h" -#include "esp_image_format.h" -#include "esp_flash_partitions.h" - -#define FLASH_HDR_ADDR 0x1000 - -static inline esp_image_header_t flash_load_rom_header (void) -{ - esp_image_header_t hdr; - if (ESP_OK != - spi_flash_read (FLASH_HDR_ADDR, (uint32_t *)&hdr, sizeof (hdr))) - { - NODE_ERR("Failed to load flash header block!\n"); - abort(); - } - return hdr; -} - -#define IRAM_SECTION __attribute__((section(".iram1"))) -static void IRAM_SECTION update_flash_chip_size (uint32_t sz) -{ - esp_rom_spiflash_config_param ( - g_rom_flashchip.device_id, - sz, - g_rom_flashchip.block_size, - g_rom_flashchip.sector_size, - g_rom_flashchip.page_size, - g_rom_flashchip.status_mask); -} - -static uint32_t __attribute__((section(".iram1"))) flash_detect_size_byte(void) -{ -#define DETECT_SZ 32 - uint32_t detected_size = FLASH_SIZE_1MBYTE; - uint8_t data_orig[DETECT_SZ] PLATFORM_ALIGNMENT = {0}; - uint8_t data_new[DETECT_SZ] PLATFORM_ALIGNMENT = {0}; - // Ensure we read something which isn't just 0xff... - const uint32_t offs = ESP_PARTITION_TABLE_OFFSET; - // Detect read failure or wrap-around on flash read to find end of flash - if (ESP_OK == spi_flash_read (offs, (uint32_t *)data_orig, DETECT_SZ)) - { - update_flash_chip_size (FLASH_SIZE_16MBYTE); - while ((detected_size < FLASH_SIZE_16MBYTE) && - (ESP_OK == spi_flash_read ( - detected_size + offs, (uint32_t *)data_new, DETECT_SZ)) && - (0 != memcmp(data_orig, data_new, DETECT_SZ))) - { - detected_size *= 2; - } - - update_flash_chip_size (detected_size); - }; - return detected_size; -#undef FLASH_BUFFER_SIZE_DETECT -} - -uint32_t flash_safe_get_size_byte(void) -{ - static uint32_t flash_size = 0; - if (flash_size == 0) - { - flash_size = flash_detect_size_byte(); - } - return flash_size; -} - - -uint16_t flash_safe_get_sec_num(void) -{ - return (flash_safe_get_size_byte() / (SPI_FLASH_SEC_SIZE)); -} - -uint32_t flash_rom_get_size_byte(void) -{ - static uint32_t flash_size = 0; - if (flash_size == 0) - { - switch (flash_load_rom_header ().spi_size) - { - default: // Unknown flash size, fall back mode. - case ESP_IMAGE_FLASH_SIZE_1MB: flash_size = FLASH_SIZE_1MBYTE; break; - case ESP_IMAGE_FLASH_SIZE_2MB: flash_size = FLASH_SIZE_2MBYTE; break; - case ESP_IMAGE_FLASH_SIZE_4MB: flash_size = FLASH_SIZE_4MBYTE; break; - case ESP_IMAGE_FLASH_SIZE_8MB: flash_size = FLASH_SIZE_8MBYTE; break; - case ESP_IMAGE_FLASH_SIZE_16MB: flash_size = FLASH_SIZE_16MBYTE; break; - } - } - return flash_size; -} - -static bool flash_rom_set_size_type(uint8_t size_code) -{ - // Dangerous, here are dinosaur infested!!!!! - // Reboot required!!! - // If you don't know what you're doing, your nodemcu may turn into stone ... - NODE_DBG("\nBEGIN SET FLASH HEADER\n"); - esp_image_header_t *hdr = (esp_image_header_t *)malloc (SPI_FLASH_SEC_SIZE); - if (!hdr) - return false; - - if (ESP_OK == spi_flash_read (FLASH_HDR_ADDR, (uint32_t *)hdr, SPI_FLASH_SEC_SIZE)) - { - hdr->spi_size = size_code; - if (ESP_OK == spi_flash_erase_sector (FLASH_HDR_ADDR / SPI_FLASH_SEC_SIZE)) - { - NODE_DBG("\nERASE SUCCESS\n"); - } - if (ESP_OK == spi_flash_write(FLASH_HDR_ADDR, (uint32_t *)hdr, SPI_FLASH_SEC_SIZE)) - { - NODE_DBG("\nWRITE SUCCESS, %u\n", size_code); - } - } - free (hdr); - NODE_DBG("\nEND SET FLASH HEADER\n"); - return true; -} - - -bool flash_rom_set_size_byte(uint32_t size) -{ - // Dangerous, here are dinosaur infested!!!!! - // Reboot required!!! - bool ok = true; - uint8_t size_code = 0; - switch (size) - { - case FLASH_SIZE_1MBYTE: size_code = ESP_IMAGE_FLASH_SIZE_1MB; break; - case FLASH_SIZE_2MBYTE: size_code = ESP_IMAGE_FLASH_SIZE_2MB; break; - case FLASH_SIZE_4MBYTE: size_code = ESP_IMAGE_FLASH_SIZE_4MB; break; - case FLASH_SIZE_8MBYTE: size_code = ESP_IMAGE_FLASH_SIZE_8MB; break; - case FLASH_SIZE_16MBYTE: size_code = ESP_IMAGE_FLASH_SIZE_16MB; break; - default: ok = false; break; - } - if (ok) - ok = flash_rom_set_size_type (size_code); - return ok; -} - - -uint16_t flash_rom_get_sec_num(void) -{ - return ( flash_rom_get_size_byte() / (SPI_FLASH_SEC_SIZE) ); -} - - -uint8_t flash_rom_get_mode(void) -{ - return flash_load_rom_header ().spi_mode; -} - - -uint32_t flash_rom_get_speed(void) -{ - switch (flash_load_rom_header ().spi_speed) - { - case ESP_IMAGE_SPI_SPEED_40M: return 40000000; - case ESP_IMAGE_SPI_SPEED_26M: return 26700000; // TODO: verify 26.7MHz - case ESP_IMAGE_SPI_SPEED_20M: return 20000000; - case ESP_IMAGE_SPI_SPEED_80M: return 80000000; - default: break; - } - return 0; -} - -esp_err_t flash_erase(size_t sector) -{ - platform_wdt_feed(); - return spi_flash_erase_sector(sector); -} diff --git a/components/platform/include/cpu_esp32.h b/components/platform/include/cpu_esp32.h index f8ae6968..6a8fe104 100644 --- a/components/platform/include/cpu_esp32.h +++ b/components/platform/include/cpu_esp32.h @@ -2,7 +2,7 @@ #define _CPU_ESP32_H_ #include "sdkconfig.h" -#include "esp_spi_flash.h" +#include "spi_flash_mmap.h" #define NUM_UART SOC_UART_NUM @@ -10,12 +10,4 @@ #define INTERNAL_FLASH_WRITE_UNIT_SIZE 4 #define INTERNAL_FLASH_READ_UNIT_SIZE 4 -#define FLASH_SEC_NUM (flash_safe_get_sec_num()) - -// Determine whether an address is in the flash-cache range -static inline bool is_cache_flash_addr (uint32_t addr) -{ - return addr >= 0x3F400000 && addr < 0x3FC00000; -} - #endif diff --git a/components/platform/include/flash_api.h b/components/platform/include/flash_api.h deleted file mode 100644 index fb703d44..00000000 --- a/components/platform/include/flash_api.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef __FLASH_API_H__ -#define __FLASH_API_H__ -#include "platform.h" -#include "esp_spi_flash.h" - -uint32_t flash_safe_get_size_byte(void); -uint16_t flash_safe_get_sec_num(void); - -uint32_t flash_rom_get_size_byte(void); - bool flash_rom_set_size_byte(uint32_t); -uint16_t flash_rom_get_sec_num(void); - uint8_t flash_rom_get_mode(void); -uint32_t flash_rom_get_speed(void); - -#define FLASH_SIZE_1MBYTE ( 1 * 1024 * 1024) -#define FLASH_SIZE_2MBYTE ( 2 * 1024 * 1024) -#define FLASH_SIZE_4MBYTE ( 4 * 1024 * 1024) -#define FLASH_SIZE_8MBYTE ( 8 * 1024 * 1024) -#define FLASH_SIZE_16MBYTE (16 * 1024 * 1024) - -#define flash_write spi_flash_write -esp_err_t flash_erase(size_t sector); -#define flash_read spi_flash_read - -#endif // __FLASH_API_H__ diff --git a/components/platform/include/platform.h b/components/platform/include/platform.h index 786e9ab5..5c0929a4 100644 --- a/components/platform/include/platform.h +++ b/components/platform/include/platform.h @@ -90,7 +90,7 @@ typedef struct { typedef struct { QueueHandle_t queue; - xTaskHandle taskHandle; + TaskHandle_t taskHandle; int receive_rf; int error_rf; char *line_buffer; @@ -137,7 +137,6 @@ int platform_adc_channel_exists( uint8_t adc, uint8_t channel ); uint8_t platform_adc_set_width( uint8_t adc, int bits ); uint8_t platform_adc_setup( uint8_t adc, uint8_t channel, uint8_t attn ); int platform_adc_read( uint8_t adc, uint8_t channel ); -int platform_adc_read_hall_sensor( ); enum { PLATFORM_ADC_ATTEN_0db = 0, PLATFORM_ADC_ATTEN_2_5db = 1, @@ -220,9 +219,6 @@ int platform_ws2812_send( void ); uint32_t platform_flash_get_sector_of_address( uint32_t addr ); uint32_t platform_flash_write( const void *from, uint32_t toaddr, uint32_t size ); uint32_t platform_flash_read( void *to, uint32_t fromaddr, uint32_t size ); -uint32_t platform_s_flash_write( const void *from, uint32_t toaddr, uint32_t size ); -uint32_t platform_s_flash_read( void *to, uint32_t fromaddr, uint32_t size ); -uint32_t platform_flash_get_num_sectors(void); int platform_flash_erase_sector( uint32_t sector_id ); diff --git a/components/platform/onewire.c b/components/platform/onewire.c index 4bc455f6..4d5888e5 100644 --- a/components/platform/onewire.c +++ b/components/platform/onewire.c @@ -63,6 +63,8 @@ sample code bearing this copyright. #include "driver/rmt.h" #include "driver/gpio.h" +#include "rom/gpio.h" // for gpio_matrix_out() +#include "soc/gpio_periph.h" #include "esp_log.h" #define TRUE (1==1) @@ -108,7 +110,7 @@ static const uint8_t owDefaultPower = 0; static int onewire_rmt_init( uint8_t gpio_num ) { - if(!GPIO_IS_VALID_GPIO(gpio_num)) { + if(!platform_gpio_exists(gpio_num)) { return PLATFORM_ERR; } @@ -187,7 +189,7 @@ static void onewire_flush_rmt_rx_buf( void ) // check rmt TX&RX channel assignment and eventually attach them to the requested pin static int onewire_rmt_attach_pin( uint8_t gpio_num ) { - if(!GPIO_IS_VALID_GPIO(gpio_num)) { + if(!platform_gpio_exists(gpio_num)) { return PLATFORM_ERR; } diff --git a/components/platform/platform.c b/components/platform/platform.c index 5fc404d4..ea759c34 100644 --- a/components/platform/platform.c +++ b/components/platform/platform.c @@ -5,7 +5,9 @@ #include "soc/uart_reg.h" #include #include -#include +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" #include "lua.h" #include "rom/uart.h" #include "esp_log.h" @@ -22,8 +24,24 @@ int platform_init (void) // ***************************************************************************** // GPIO subsection -int platform_gpio_exists( unsigned gpio ) { return GPIO_IS_VALID_GPIO(gpio); } -int platform_gpio_output_exists( unsigned gpio ) { return GPIO_IS_VALID_OUTPUT_GPIO(gpio); } +int platform_gpio_exists(unsigned gpio) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" + // Suppress ">= is always true" due to unsigned type here + return GPIO_IS_VALID_GPIO(gpio); +#pragma GCC diagnostic pop +} + + +int platform_gpio_output_exists(unsigned gpio) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" + // Suppress ">= is always true" due to unsigned type here + return GPIO_IS_VALID_OUTPUT_GPIO(gpio); +#pragma GCC diagnostic pop +} // **************************************************************************** @@ -136,7 +154,7 @@ static void task_uart( void *pvParameters ){ uart_event_t event; for(;;) { - if(xQueueReceive(uart_status[id].queue, (void * )&event, (portTickType)portMAX_DELAY)) { + if(xQueueReceive(uart_status[id].queue, (void * )&event, (TickType_t)portMAX_DELAY)) { switch(event.type) { case UART_DATA: { // Attempt to coalesce received bytes to reduce risk of overrunning @@ -222,6 +240,7 @@ uint32_t platform_uart_setup( unsigned id, uint32_t baud, int databits, int pari .baud_rate = baud, .flow_ctrl = flow_control, .rx_flow_ctrl_thresh = UART_FIFO_LEN - 16, + .source_clk = UART_SCLK_DEFAULT, }; switch (databits) @@ -523,14 +542,6 @@ int platform_adc_read( uint8_t adc, uint8_t channel ) { return value; } -int platform_adc_read_hall_sensor( ) { -#if defined(CONFIG_IDF_TARGET_ESP32) - int value = hall_sensor_read( ); - return value; -#else - return -1; -#endif -} // ***************************************************************************** // I2C platform interface diff --git a/components/platform/platform_flash.c b/components/platform/platform_flash.c index d6e00b36..acabdccd 100644 --- a/components/platform/platform_flash.c +++ b/components/platform/platform_flash.c @@ -1,5 +1,6 @@ #include "platform.h" -#include "flash_api.h" +#include "platform_wdt.h" +#include "esp_flash.h" #include #include #include @@ -21,183 +22,43 @@ static uint32_t flashh_find_sector( uint32_t address, uint32_t *pstart, uint32_t return sect_id; } + uint32_t platform_flash_get_sector_of_address( uint32_t addr ) { return flashh_find_sector( addr, NULL, NULL ); } -uint32_t platform_flash_get_num_sectors(void) -{ - return flash_safe_get_sec_num (); -} uint32_t platform_flash_write( const void *from, uint32_t toaddr, uint32_t size ) { -#ifndef INTERNAL_FLASH_WRITE_UNIT_SIZE - return platform_s_flash_write( from, toaddr, size ); -#else // #ifindef INTERNAL_FLASH_WRITE_UNIT_SIZE - uint32_t temp, rest, ssize = size; - unsigned i; - char tmpdata[ INTERNAL_FLASH_WRITE_UNIT_SIZE ]; - const uint8_t *pfrom = ( const uint8_t* )from; - const uint32_t blksize = INTERNAL_FLASH_WRITE_UNIT_SIZE; - const uint32_t blkmask = INTERNAL_FLASH_WRITE_UNIT_SIZE - 1; - - // Align the start - if( toaddr & blkmask ) - { - rest = toaddr & blkmask; - temp = toaddr & ~blkmask; // this is the actual aligned address - // memcpy( tmpdata, ( const void* )temp, blksize ); - platform_s_flash_read( tmpdata, temp, blksize ); - for( i = rest; size && ( i < blksize ); i ++, size --, pfrom ++ ) - tmpdata[ i ] = *pfrom; - platform_s_flash_write( tmpdata, temp, blksize ); - if( size == 0 ) - return ssize; - toaddr = temp + blksize; - } - // The start address is now a multiple of blksize - // Compute how many bytes we can write as multiples of blksize - rest = size & blkmask; - temp = size & ~blkmask; - // Program the blocks now - if( temp ) - { - platform_s_flash_write( pfrom, toaddr, temp ); - toaddr += temp; - pfrom += temp; - } - // And the final part of a block if needed - if( rest ) - { - // memcpy( tmpdata, ( const void* )toaddr, blksize ); - platform_s_flash_read( tmpdata, toaddr, blksize ); - for( i = 0; size && ( i < rest ); i ++, size --, pfrom ++ ) - tmpdata[ i ] = *pfrom; - platform_s_flash_write( tmpdata, toaddr, blksize ); - } - return ssize; -#endif // #ifndef INTERNAL_FLASH_WRITE_UNIT_SIZE + esp_err_t err = esp_flash_write(NULL, from, toaddr, size); + if (err != ESP_OK) + return 0; + else + return size; } + uint32_t platform_flash_read( void *to, uint32_t fromaddr, uint32_t size ) { -#ifndef INTERNAL_FLASH_READ_UNIT_SIZE - return platform_s_flash_read( to, fromaddr, size ); -#else // #ifindef INTERNAL_FLASH_READ_UNIT_SIZE - uint32_t temp, rest, ssize = size; - unsigned i; - char tmpdata[ INTERNAL_FLASH_READ_UNIT_SIZE ] __attribute__ ((aligned(INTERNAL_FLASH_READ_UNIT_SIZE))); - uint8_t *pto = ( uint8_t* )to; - const uint32_t blksize = INTERNAL_FLASH_READ_UNIT_SIZE; - const uint32_t blkmask = INTERNAL_FLASH_READ_UNIT_SIZE - 1; - - // Align the start - if( fromaddr & blkmask ) - { - rest = fromaddr & blkmask; - temp = fromaddr & ~blkmask; // this is the actual aligned address - platform_s_flash_read( tmpdata, temp, blksize ); - for( i = rest; size && ( i < blksize ); i ++, size --, pto ++ ) - *pto = tmpdata[ i ]; - - if( size == 0 ) - return ssize; - fromaddr = temp + blksize; - } - // The start address is now a multiple of blksize - // Compute how many bytes we can read as multiples of blksize - rest = size & blkmask; - temp = size & ~blkmask; - // Program the blocks now - if( temp ) - { - platform_s_flash_read( pto, fromaddr, temp ); - fromaddr += temp; - pto += temp; - } - // And the final part of a block if needed - if( rest ) - { - platform_s_flash_read( tmpdata, fromaddr, blksize ); - for( i = 0; size && ( i < rest ); i ++, size --, pto ++ ) - *pto = tmpdata[ i ]; - } - return ssize; -#endif // #ifndef INTERNAL_FLASH_READ_UNIT_SIZE -} - - -/* - * Assumptions: - * > toaddr is INTERNAL_FLASH_WRITE_UNIT_SIZE aligned - * > size is a multiple of INTERNAL_FLASH_WRITE_UNIT_SIZE - */ -uint32_t platform_s_flash_write( const void *from, uint32_t toaddr, uint32_t size ) -{ - esp_err_t r; - const uint32_t blkmask = INTERNAL_FLASH_WRITE_UNIT_SIZE - 1; - uint32_t *apbuf = NULL; - uint32_t fromaddr = (uint32_t)from; - if( (fromaddr & blkmask ) || is_cache_flash_addr(fromaddr)) { - apbuf = (uint32_t *)malloc(size); - if(!apbuf) - return 0; - memcpy(apbuf, from, size); - } - r = flash_write(toaddr, apbuf?(uint32_t *)apbuf:(uint32_t *)from, size); - if(apbuf) - free(apbuf); - if(ESP_OK == r) - return size; - else{ - NODE_ERR( "ERROR in flash_write: r=%d at %08X\n", ( int )r, ( unsigned )toaddr); + esp_err_t err = esp_flash_write(NULL, to, fromaddr, size); + if (err != ESP_OK) return 0; - } -} - - -/* - * Assumptions: - * > fromaddr is INTERNAL_FLASH_READ_UNIT_SIZE aligned - * > size is a multiple of INTERNAL_FLASH_READ_UNIT_SIZE - */ -uint32_t platform_s_flash_read( void *to, uint32_t fromaddr, uint32_t size ) -{ - if (size==0) - return 0; - - esp_err_t r; - - const uint32_t blkmask = (INTERNAL_FLASH_READ_UNIT_SIZE - 1); - if( ((uint32_t)to) & blkmask ) - { - uint32_t size2=size-INTERNAL_FLASH_READ_UNIT_SIZE; - uint32_t* to2=(uint32_t*)((((uint32_t)to)&(~blkmask))+INTERNAL_FLASH_READ_UNIT_SIZE); - r = flash_read(fromaddr, to2, size2); - if(ESP_OK == r) - { - memmove(to,to2,size2); - char back[ INTERNAL_FLASH_READ_UNIT_SIZE ] __attribute__ ((aligned(INTERNAL_FLASH_READ_UNIT_SIZE))); - r=flash_read(fromaddr+size2,(uint32_t*)back,INTERNAL_FLASH_READ_UNIT_SIZE); - memcpy((uint8_t*)to+size2,back,INTERNAL_FLASH_READ_UNIT_SIZE); - } - } else - r = flash_read(fromaddr, (uint32_t *)to, size); - - if(ESP_OK == r) return size; - else{ - NODE_ERR( "ERROR in flash_read: r=%d at %08X sz %x\n", ( int )r, ( unsigned )fromaddr, size); - return 0; - } } int platform_flash_erase_sector( uint32_t sector_id ) { - return flash_erase( sector_id ) == ESP_OK ? PLATFORM_OK : PLATFORM_ERR; + platform_wdt_feed(); + + uint32_t addr = sector_id * INTERNAL_FLASH_SECTOR_SIZE; + esp_err_t err = + esp_flash_erase_region(NULL, addr, INTERNAL_FLASH_SECTOR_SIZE); + if (err == ESP_OK) + return PLATFORM_OK; + else + return PLATFORM_ERR; } diff --git a/components/platform/platform_partition.c b/components/platform/platform_partition.c index 46cedca6..78fc2eff 100644 --- a/components/platform/platform_partition.c +++ b/components/platform/platform_partition.c @@ -36,7 +36,7 @@ #include #include #include "esp_flash_partitions.h" -#include "esp_spi_flash.h" +#include "esp_flash.h" static inline bool possible_idx (uint8_t idx) { @@ -50,8 +50,12 @@ bool platform_partition_info (uint8_t idx, platform_partition_t *info) return false; esp_partition_info_t pi; - esp_err_t err = spi_flash_read ( - ESP_PARTITION_TABLE_OFFSET + idx * sizeof(pi), (uint32_t *)&pi, sizeof (pi)); + esp_err_t err = esp_flash_read( + NULL, + (uint32_t *)&pi, + ESP_PARTITION_TABLE_OFFSET + idx * sizeof(pi), + sizeof (pi)); + if (err != ESP_OK) return false; @@ -66,42 +70,3 @@ bool platform_partition_info (uint8_t idx, platform_partition_t *info) return true; } - - -bool platform_partition_add (const platform_partition_t *info) -{ - esp_partition_info_t *part_table = - (esp_partition_info_t *)malloc(SPI_FLASH_SEC_SIZE); - if (!part_table) - return false; - esp_err_t err = spi_flash_read ( - ESP_PARTITION_TABLE_OFFSET, (uint32_t *)part_table, SPI_FLASH_SEC_SIZE); - if (err != ESP_OK) - goto out; - - uint8_t idx = 0; - for (; possible_idx (idx); ++idx) - if (part_table[idx].magic != ESP_PARTITION_MAGIC) - break; - - if (possible_idx (idx)) - { - esp_partition_info_t *slot = &part_table[idx]; - slot->magic = ESP_PARTITION_MAGIC; - slot->type = info->type; - slot->subtype = info->subtype; - slot->pos.offset = info->offs; - slot->pos.size = info->size; - memcpy (slot->label, info->label, sizeof (slot->label)); - slot->flags = 0; - err = spi_flash_erase_sector ( - ESP_PARTITION_TABLE_OFFSET / SPI_FLASH_SEC_SIZE); - if (err == ESP_OK) - err = spi_flash_write ( - ESP_PARTITION_TABLE_OFFSET, (uint32_t *)part_table, SPI_FLASH_SEC_SIZE); - } - -out: - free (part_table); - return err == ESP_OK; -} diff --git a/components/platform/platform_rmt.c b/components/platform/platform_rmt.c index 1c568220..45560fa9 100644 --- a/components/platform/platform_rmt.c +++ b/components/platform/platform_rmt.c @@ -1,4 +1,3 @@ - #include #include "platform_rmt.h" diff --git a/components/platform/u8x8_nodemcu_hal.c b/components/platform/u8x8_nodemcu_hal.c index e66b9477..2b51750d 100644 --- a/components/platform/u8x8_nodemcu_hal.c +++ b/components/platform/u8x8_nodemcu_hal.c @@ -3,7 +3,7 @@ #include "driver/gpio.h" #include "driver/i2c.h" #include "driver/spi_master.h" -#include "rom/ets_sys.h" +#include "esp_rom_sys.h" #include "esp_heap_caps.h" #include @@ -38,27 +38,27 @@ uint8_t u8x8_gpio_and_delay_nodemcu(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, break; case U8X8_MSG_DELAY_NANO: // delay arg_int * 1 nano second - ets_delay_us( 1 ); + esp_rom_delay_us( 1 ); break; case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds temp = arg_int * 100; temp /= 1000; - ets_delay_us( temp > 0 ? temp : 1 ); + esp_rom_delay_us( temp > 0 ? temp : 1 ); break; case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds - ets_delay_us( arg_int * 10 ); + esp_rom_delay_us( arg_int * 10 ); break; case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second - ets_delay_us( arg_int * 1000 ); + esp_rom_delay_us( arg_int * 1000 ); break; case U8X8_MSG_DELAY_I2C: // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz temp = 5000 / arg_int; // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us temp /= 1000; - ets_delay_us( temp > 0 ? temp : 1 ); + esp_rom_delay_us( temp > 0 ? temp : 1 ); break; case U8X8_MSG_GPIO_D0: // D0 or SPI clock pin: Output level in arg_int diff --git a/components/platform/ucg_nodemcu_hal.c b/components/platform/ucg_nodemcu_hal.c index 5cbded35..b8b407c3 100644 --- a/components/platform/ucg_nodemcu_hal.c +++ b/components/platform/ucg_nodemcu_hal.c @@ -87,7 +87,7 @@ int16_t ucg_com_nodemcu_hw_spi(ucg_t *ucg, int16_t msg, uint16_t arg, uint8_t *d break; case UCG_COM_MSG_DELAY: - ets_delay_us(arg); + esp_rom_delay_us(arg); break; case UCG_COM_MSG_CHANGE_RESET_LINE: diff --git a/components/platform/wdt.c b/components/platform/wdt.c index c815268e..4603011b 100644 --- a/components/platform/wdt.c +++ b/components/platform/wdt.c @@ -7,23 +7,20 @@ #include "platform.h" #include "esp_task_wdt.h" -#ifdef CONFIG_TASK_WDT -static uint32_t task_wdt_timeout = CONFIG_TASK_WDT_TIMEOUT_S; - -static bool task_wdt_panic = -#ifdef CONFIG_TASK_WDT_PANIC - true; -#else -false; -#endif - -#endif - - int platform_wdt_feed( void ) { #ifdef CONFIG_TASK_WDT - return esp_task_wdt_init(task_wdt_timeout, task_wdt_panic) == ESP_OK ? PLATFORM_OK : PLATFORM_ERR; + esp_task_wdt_config_t cfg = { + .timeout_ms = CONFIG_TASK_WDT_TIMEOUT_S * 1000, + .idle_core_mask = (uint32_t)-1, + .trigger_panic = +#ifdef CONFIG_TASK_WDT_PANIC + true, +#else + false, +#endif + }; + return esp_task_wdt_init(&cfg) == ESP_OK ? PLATFORM_OK : PLATFORM_ERR; #else return PLATFORM_OK; #endif diff --git a/components/platform/ws2812.c b/components/platform/ws2812.c index 4a4aace2..d00510cb 100644 --- a/components/platform/ws2812.c +++ b/components/platform/ws2812.c @@ -30,6 +30,8 @@ #include "driver/gpio.h" #include "esp_log.h" #include "soc/periph_defs.h" +#include "rom/gpio.h" // for gpio_matrix_out() +#include "soc/gpio_periph.h" #undef WS2812_DEBUG diff --git a/components/rtos_dbg/rtos_dbg.c b/components/rtos_dbg/rtos_dbg.c index 4ca8f666..50aed4cb 100644 --- a/components/rtos_dbg/rtos_dbg.c +++ b/components/rtos_dbg/rtos_dbg.c @@ -13,7 +13,7 @@ extern struct { void rtos_dbg_task_print (const char *file, uint32_t line) { - printf(">>dbg: %s:%d in RTOS task \"%s\": prio %d\n", + printf(">>dbg: %s:%lu in RTOS task \"%s\": prio %u\n", file, line, pxCurrentTCB->pcTaskName, @@ -30,6 +30,6 @@ void rtos_dbg_stack_print (const char *file, uint32_t line) for (;p < pxCurrentTCB->pxTopOfStack && *p == fill; ++p) ++nwords; - printf(">>dbg: %s:%d in RTOS task \"%s\": %u stack untouched\n", + printf(">>dbg: %s:%lu in RTOS task \"%s\": %lu stack untouched\n", file, line, pxCurrentTCB->pcTaskName, nwords * 4); } diff --git a/components/task/task.c b/components/task/task.c index 0de0caab..12c0f77b 100644 --- a/components/task/task.c +++ b/components/task/task.c @@ -29,11 +29,11 @@ typedef struct /* * Private arrays to hold the 3 event task queues and the dispatch callbacks */ -static xQueueHandle task_Q[TASK_PRIORITY_COUNT]; +static QueueHandle_t task_Q[TASK_PRIORITY_COUNT]; /* Rather than using a QueueSet (which requires queues to be empty when created) * we use a binary semaphore to unblock the pump whenever something is posted */ -static xSemaphoreHandle pending; +static SemaphoreHandle_t pending; static task_callback_t *task_func; static int task_count; diff --git a/docs/modules/adc.md b/docs/modules/adc.md index 152e44e5..c719999c 100644 --- a/docs/modules/adc.md +++ b/docs/modules/adc.md @@ -60,22 +60,3 @@ the sampled value (number) ```lua val = adc.read(adc.ADC1, 0) ``` - -## adc.read_hall_sensor() - -Read Hall sensor (GPIO36, GPIO39). We recommend using 12-bits width on ADC1. - -#### Syntax -`adc.read_hall_sensor()` - -#### Parameters -none - -#### Returns -the sampled value (number) - -#### Example -```lua -val = adc.read_hall_sensor() -``` - diff --git a/docs/modules/eth.md b/docs/modules/eth.md index 3802586c..1512af19 100644 --- a/docs/modules/eth.md +++ b/docs/modules/eth.md @@ -8,8 +8,10 @@ The eth module provides access to the ethernet PHY chip configuration. Your board must contain one of the PHY chips that are supported by ESP-IDF: - IP101 -- LAN8720 -- TLK110 +- RTL8201 +- LAN8720 (possibly others in the LAN87xx family) +- DP83848 +- KSZ8001 / KSZ8021 / KSZ8031 / KSZ8041 / KSZ8051 / KSZ8061 / KSZ8081 / KSZ8091 ## eth.get_mac() Get MAC address. @@ -67,9 +69,11 @@ eth.init(cfg) - `phy` PHY chip model, one of - `PHY_DP83848` - `PHY_IP101` - - `PHY_KSZ8041` - - `PHY_KSZ8081` - - `PHY_LAN8720` + - `PHY_KSZ80XX` + - `PHY_KSZ8041` (deprecated, use `PHY_KSZ80XX` instead) + - `PHY_KSZ8081` (deprecated, use `PHY_KSZ80XX` instead) + - `PHY_LAN87XX` + - `PHY_LAN8720` (deprecated, use `PHY_LAN87XX` instead) - `PHY_RTL8201` - `power` power enable pin, optional diff --git a/docs/modules/sdmmc.md b/docs/modules/sdmmc.md index 89e7728a..d6005998 100644 --- a/docs/modules/sdmmc.md +++ b/docs/modules/sdmmc.md @@ -77,7 +77,7 @@ Initialize the SDMMC and probe the attached SD card. - `sdmmc.W8BIT`, not supported yet #### Parameters SD SPI Mode -- `slot` SD SPI slot, one of `sdmmc.HSPI` or `sdmmc.VSPI` +- `slot` SD SPI slot, one of `sdmmc.SPI2` or `sdmmc.SPI3` (on ESP32 the names`sdmmc.HSPI` or `sdmmc.VSPI` are still available, but deprecated) - `cfg` mandatory table containing slot configuration: - `sck_pin` SPI SCK pin, mandatory - `mosi_pin`, SPI MOSI pin, mandatory @@ -128,12 +128,10 @@ Table containing the card's OCR, CID, CSD, SCR, and RCA with elements: Mount filesystem on SD card. #### Syntax -`card:mount(ldrv[, partition])` +`card:mount(ldrv)` #### Parameters - `ldrv` name of logical drive, "/SD0", "/SD1", etc. -- `partition` the partition number, default is 0, meaning the first available - FAT partition. #### Returns `true` if successful, `false` otherwise diff --git a/sdk/esp32-esp-idf b/sdk/esp32-esp-idf index 35729009..5181de8a 160000 --- a/sdk/esp32-esp-idf +++ b/sdk/esp32-esp-idf @@ -1 +1 @@ -Subproject commit 357290093430e41e7e3338227a61ef5162f2deed +Subproject commit 5181de8ac5ec5e18f04f634da8ce173b7ef5ab73 From 1400d7bc6e69e96a40cc0f228a124e26d6d761ac Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Mon, 6 Feb 2023 11:12:49 +1100 Subject: [PATCH 41/47] Remove old cryptography package lock. This workaround is no longer needed nor desired. --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a8747b68..ce3a0c1d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ ziglang>=0.8.0 -cryptography>=2.1.4,<35.0.0 From 12035a767835d16712443b51e8564133b91906c7 Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Wed, 17 May 2023 16:44:23 +1000 Subject: [PATCH 42/47] Upgrade to IDF v5.0.2 With minor update required to our spi module. --- docs/modules/spi.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/modules/spi.md b/docs/modules/spi.md index caa2fd92..7404cfe4 100644 --- a/docs/modules/spi.md +++ b/docs/modules/spi.md @@ -6,6 +6,8 @@ # SPI Bus The ESP32 contains 4 SPI bus hosts called `SPI`, `SPI1`, `HSPI`, and `VSPI`. `SPI` is locked to flash communication and is not available for the application. `SPI1` is currently also tied to flash support, but might be available in the future. Applications can currently only use the `HSPI` and `VSPI` hosts. +On later models in the ESP32 series, the SPI hosts are simply referred to by number, and are available here as `spi.SPI1`, `spi.SPI2` and (if present) `spi.SPI3`. + The host signals can be mapped to any suitable GPIO pins. !!! note From 9633b03af315da897a2f65996f14e9ad29fdd95e Mon Sep 17 00:00:00 2001 From: serg3295 Date: Sun, 16 Jul 2023 10:19:16 +0300 Subject: [PATCH 43/47] fix: mqtt module --- components/modules/mqtt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/modules/mqtt.c b/components/modules/mqtt.c index 3c4909aa..0f59a965 100644 --- a/components/modules/mqtt.c +++ b/components/modules/mqtt.c @@ -419,7 +419,7 @@ static int mqtt_connect(lua_State* L) { ESP_LOGD(TAG, "connect: mqtt_context*: %p", mqtt_context); - if (config.broker.address.uri != NULL) + if (config.broker.address.uri == NULL) { config.broker.address.port = port; config.broker.address.transport = From 9a9548b0b865fe3fc5266fc2a880dc14da4c00d6 Mon Sep 17 00:00:00 2001 From: serg3295 Date: Mon, 17 Jul 2023 09:22:09 +0300 Subject: [PATCH 44/47] fix(mqtt): select next parameter in connect() --- components/modules/mqtt.c | 6 ++---- docs/modules/mqtt.md | 18 +----------------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/components/modules/mqtt.c b/components/modules/mqtt.c index 0f59a965..7e7f534b 100644 --- a/components/modules/mqtt.c +++ b/components/modules/mqtt.c @@ -368,14 +368,13 @@ static int mqtt_connect(lua_State* L) { if (is_mqtt_uri || is_mqtts_uri) return luaL_error(L, "port arg must be nil if giving full uri"); port = luaL_checknumber(L, n); - n++; } + n++; if (lua_isnumber(L, n)) { if (is_mqtt_uri || is_mqtts_uri) return luaL_error(L, "secure on/off determined by uri"); secure = !!luaL_checkinteger(L, n); - n++; } else { if (lua_istable(L, n)) { @@ -397,10 +396,9 @@ static int mqtt_connect(lua_State* L) { luaX_set_ref(L, -1, &mqtt_context->client_key_pem); } lua_pop(L, 1); - // - n++; } } + n++; if (lua_isnumber(L, n)) { reconnect = !!luaL_checkinteger(L, n); diff --git a/docs/modules/mqtt.md b/docs/modules/mqtt.md index 8f325fc8..08f7ca2a 100644 --- a/docs/modules/mqtt.md +++ b/docs/modules/mqtt.md @@ -86,7 +86,7 @@ Closes connection to the broker. none #### Returns -`true` on success, `false` otherwise +`nil` ## mqtt.client:connect() @@ -139,22 +139,6 @@ This is the description of how the `autoreconnect` functionality may (or may not > is considered a success if the client connects to a server and gets back a good response packet in response to its MQTT connection request. > This implies (for example) that the username and password are correct. -#### Connection failure callback reason codes: - -| Constant | Value | Description | -|----------|-------|-------------| -|`mqtt.CONN_FAIL_SERVER_NOT_FOUND`|-5|There is no broker listening at the specified IP Address and Port| -|`mqtt.CONN_FAIL_NOT_A_CONNACK_MSG`|-4|The response from the broker was not a CONNACK as required by the protocol| -|`mqtt.CONN_FAIL_DNS`|-3|DNS Lookup failed| -|`mqtt.CONN_FAIL_TIMEOUT_RECEIVING`|-2|Timeout waiting for a CONNACK from the broker| -|`mqtt.CONN_FAIL_TIMEOUT_SENDING`|-1|Timeout trying to send the Connect message| -|`mqtt.CONNACK_ACCEPTED`|0|No errors. _Note: This will not trigger a failure callback._| -|`mqtt.CONNACK_REFUSED_PROTOCOL_VER`|1|The broker is not a 3.1.1 MQTT broker.| -|`mqtt.CONNACK_REFUSED_ID_REJECTED`|2|The specified ClientID was rejected by the broker. (See `mqtt.Client()`)| -|`mqtt.CONNACK_REFUSED_SERVER_UNAVAILABLE`|3|The server is unavailable.| -|`mqtt.CONNACK_REFUSED_BAD_USER_OR_PASS`|4|The broker refused the specified username or password.| -|`mqtt.CONNACK_REFUSED_NOT_AUTHORIZED`|5|The username is not authorized.| - ## mqtt.client:lwt() Setup [Last Will and Testament](http://www.hivemq.com/blog/mqtt-essentials-part-9-last-will-and-testament) (optional). A broker will publish a message with qos = 0, retain = 0, data = "offline" to topic "/lwt" if client does not send keepalive packet. From 096164a75c7ee5123df4585b68dcc842873fe157 Mon Sep 17 00:00:00 2001 From: serg3295 Date: Mon, 17 Jul 2023 12:39:31 +0300 Subject: [PATCH 45/47] fix(mqtt): adds parameter checking to nil --- components/modules/mqtt.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/modules/mqtt.c b/components/modules/mqtt.c index 7e7f534b..2a456ce6 100644 --- a/components/modules/mqtt.c +++ b/components/modules/mqtt.c @@ -368,14 +368,19 @@ static int mqtt_connect(lua_State* L) { if (is_mqtt_uri || is_mqtts_uri) return luaL_error(L, "port arg must be nil if giving full uri"); port = luaL_checknumber(L, n); + n++; + } else if (lua_type(L, n) == LUA_TNIL) { + n++; } - n++; if (lua_isnumber(L, n)) { if (is_mqtt_uri || is_mqtts_uri) return luaL_error(L, "secure on/off determined by uri"); secure = !!luaL_checkinteger(L, n); + n++; + } else if (lua_type(L, n) == LUA_TNIL) { + n++; } else { if (lua_istable(L, n)) { secure = true; @@ -396,9 +401,10 @@ static int mqtt_connect(lua_State* L) { luaX_set_ref(L, -1, &mqtt_context->client_key_pem); } lua_pop(L, 1); + // + n++; } } - n++; if (lua_isnumber(L, n)) { reconnect = !!luaL_checkinteger(L, n); From 796fd7ad0e0c54e998f6f2cc699cafdecc8236cc Mon Sep 17 00:00:00 2001 From: serg3295 Date: Tue, 25 Jul 2023 12:13:33 +0300 Subject: [PATCH 46/47] Fix time.get() for 5.3-int32-singlefp firmware on IDF v5.0.2 --- components/modules/time.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/modules/time.c b/components/modules/time.c index 155fb409..9c72dd62 100644 --- a/components/modules/time.c +++ b/components/modules/time.c @@ -32,8 +32,8 @@ static int time_get(lua_State *L) { struct timeval tv; gettimeofday (&tv, NULL); - lua_pushnumber (L, tv.tv_sec); - lua_pushnumber (L, tv.tv_usec); + lua_pushinteger (L, tv.tv_sec); + lua_pushinteger (L, tv.tv_usec); return 2; } From 3cc1d48b83447a86f69c015a0fbd464c4635fc27 Mon Sep 17 00:00:00 2001 From: Mike_Went Date: Mon, 29 Aug 2022 14:02:24 +0300 Subject: [PATCH 47/47] Fix example pins in docs/modules/uart.md --- docs/modules/uart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/uart.md b/docs/modules/uart.md index d8febdbd..c31905a4 100644 --- a/docs/modules/uart.md +++ b/docs/modules/uart.md @@ -102,7 +102,7 @@ uart.setup(0, 9600, 8, uart.PARITY_NONE, uart.STOPBITS_1, 1) ``` ```lua -uart.setup(2, 115200, 8, uart.PARITY_NONE, uart.STOPBITS_1, {tx = 16, rx = 17}) +uart.setup(2, 115200, 8, uart.PARITY_NONE, uart.STOPBITS_1, {tx = 17, rx = 16}) ``` ## uart.getconfig()