From cdaf6344457ae427d8c06ac28a645047f9e0f588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Fri, 3 Jun 2016 23:24:37 +0200 Subject: [PATCH 01/11] Fix RTD badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cac99fde..f665690a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Join the chat at https://gitter.im/nodemcu/nodemcu-firmware](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/nodemcu/nodemcu-firmware?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/nodemcu/nodemcu-firmware.svg)](https://travis-ci.org/nodemcu/nodemcu-firmware) -[![Documentation Status](https://readthedocs.com/projects/nodemcu/badge/?version=dev)](http://nodemcu.readthedocs.io/) +[![Documentation Status](https://img.shields.io/badge/docs-master-yellow.svg?style=flat)](http://nodemcu.readthedocs.io/) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/nodemcu/nodemcu-firmware/blob/master/LICENSE) ### A Lua based firmware for ESP8266 WiFi SOC From b580bfe79e6e73020c2bd7cd92a6afe01a8bc867 Mon Sep 17 00:00:00 2001 From: Harald Kubota Date: Sat, 2 Jul 2016 23:21:33 +0900 Subject: [PATCH 02/11] Update sntp.md (#1378) Extra character in the doc --- docs/en/modules/sntp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/modules/sntp.md b/docs/en/modules/sntp.md index 9b495961..9560d06a 100644 --- a/docs/en/modules/sntp.md +++ b/docs/en/modules/sntp.md @@ -17,7 +17,7 @@ Attempts to obtain time synchronization. #### Parameters - `server_ip` if non-`nil`, that server is used. If `nil`, then the last contacted server is used. This ties in with the NTP anycast mode, where the first responding server is remembered for future synchronization requests. The easiest way to use anycast is to always pass nil for the server argument. -- `callback` Iif provided it will be invoked on a successful synchronization, with three parameters: seconds, microseconds, and server. Note that when the [rtctime](rtctime.md) module is available, there is no need to explicitly call [`rtctime.set()`](rtctime.md#rtctimeset) - this module takes care of doing so internally automatically, for best accuracy. +- `callback` if provided it will be invoked on a successful synchronization, with three parameters: seconds, microseconds, and server. Note that when the [rtctime](rtctime.md) module is available, there is no need to explicitly call [`rtctime.set()`](rtctime.md#rtctimeset) - this module takes care of doing so internally automatically, for best accuracy. - `errcallback` failure callback with a single integer parameter describing the type of error. The module automatically performs a number of retries before giving up and reporting the error. Error codes: - 1: DNS lookup failed - 2: Memory allocation failure From d55900011fbdf951ea453fbd330c1baad8e56a02 Mon Sep 17 00:00:00 2001 From: dirkju Date: Wed, 3 Aug 2016 22:23:32 +0200 Subject: [PATCH 03/11] Fixed typo in bme280 module doc (#1437) --- docs/en/modules/bme280.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/modules/bme280.md b/docs/en/modules/bme280.md index 92fe92ad..2e2a067d 100644 --- a/docs/en/modules/bme280.md +++ b/docs/en/modules/bme280.md @@ -23,8 +23,8 @@ altitude in meters of measurement point ## bme280.baro() -Reads the sensor and returns the air temperature in hectopascals as an integer multiplied with 1000 or `nil` when readout is not successful. -Current temperature is needed to calculate the air pressure so temperature reading is performed prior reading pressure data. Second returned variable is therefore current temperature. +Reads the sensor and returns the air pressure in hectopascals as an integer multiplied with 1000 or `nil` when readout is not successful. +Current temperature is needed to calculate the air pressure so temperature reading is performed prior reading pressure data. Second returned variable is therefore current air temperature. #### Syntax `bme280.baro()` @@ -196,4 +196,4 @@ none #### Returns - `T` temperature in celsius as an integer multiplied with 100 or `nil` when readout is not successful -- `t_fine` temperature measure used in pressure and humidity compensation formulas (generally no need to use this value) \ No newline at end of file +- `t_fine` temperature measure used in pressure and humidity compensation formulas (generally no need to use this value) From 98d3b46e2c11068f361154a564ce75a048c9dadc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Wed, 3 Aug 2016 22:24:20 +0200 Subject: [PATCH 04/11] Revert "documentation-only change: fixed wrong word in bme280 module doc" (#1438) --- docs/en/modules/bme280.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/modules/bme280.md b/docs/en/modules/bme280.md index 2e2a067d..92fe92ad 100644 --- a/docs/en/modules/bme280.md +++ b/docs/en/modules/bme280.md @@ -23,8 +23,8 @@ altitude in meters of measurement point ## bme280.baro() -Reads the sensor and returns the air pressure in hectopascals as an integer multiplied with 1000 or `nil` when readout is not successful. -Current temperature is needed to calculate the air pressure so temperature reading is performed prior reading pressure data. Second returned variable is therefore current air temperature. +Reads the sensor and returns the air temperature in hectopascals as an integer multiplied with 1000 or `nil` when readout is not successful. +Current temperature is needed to calculate the air pressure so temperature reading is performed prior reading pressure data. Second returned variable is therefore current temperature. #### Syntax `bme280.baro()` @@ -196,4 +196,4 @@ none #### Returns - `T` temperature in celsius as an integer multiplied with 100 or `nil` when readout is not successful -- `t_fine` temperature measure used in pressure and humidity compensation formulas (generally no need to use this value) +- `t_fine` temperature measure used in pressure and humidity compensation formulas (generally no need to use this value) \ No newline at end of file From 6e25b747817d98ee943e7127038ac0ed0721298c Mon Sep 17 00:00:00 2001 From: bert hubert Date: Sat, 20 Aug 2016 22:04:35 +0200 Subject: [PATCH 05/11] Make DNS engine RFC 5452 compliant (#1324) The original code did not deal with UDP binding failures, this new code does one random port attempt and then sets port to 0 to get 'OS default' behaviour, and ASSERTs that that has to work. Patch also moves the local UDP port range definition to udp.h from udp.c. --- app/include/lwip/udp.h | 5 +++++ app/lwip/core/dns.c | 10 ++++++++-- app/lwip/core/udp.c | 4 ---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/include/lwip/udp.h b/app/include/lwip/udp.h index cb53d33e..760b24cf 100644 --- a/app/include/lwip/udp.h +++ b/app/include/lwip/udp.h @@ -156,6 +156,11 @@ void udp_input (struct pbuf *p, struct netif *inp)ICACHE_FLASH_ #define udp_init() /* Compatibility define, not init needed. */ +#ifndef UDP_LOCAL_PORT_RANGE_START +#define UDP_LOCAL_PORT_RANGE_START 4096 +#define UDP_LOCAL_PORT_RANGE_END 0x7fff +#endif + #if UDP_DEBUG void udp_debug_print(struct udp_hdr *udphdr)ICACHE_FLASH_ATTR; #else diff --git a/app/lwip/core/dns.c b/app/lwip/core/dns.c index c0d616f2..72a31995 100644 --- a/app/lwip/core/dns.c +++ b/app/lwip/core/dns.c @@ -254,8 +254,14 @@ dns_init() LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0", DNS_STATE_UNUSED == 0); - /* initialize DNS client */ - udp_bind(dns_pcb, IP_ADDR_ANY, 0); + /* initialize DNS client, try to get RFC 5452 random source port */ + u16_t port = UDP_LOCAL_PORT_RANGE_START + (os_random() % (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)); + for(;;) { + if(udp_bind(dns_pcb, IP_ADDR_ANY, port) == ERR_OK) + break; + LWIP_ASSERT("Unable to get a PCB for DNS", port != 0); + port=0; // try again with random source + } udp_recv(dns_pcb, dns_recv, NULL); /* initialize default DNS primary server */ diff --git a/app/lwip/core/udp.c b/app/lwip/core/udp.c index e44d7f38..2d794818 100644 --- a/app/lwip/core/udp.c +++ b/app/lwip/core/udp.c @@ -758,10 +758,6 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) /* no port specified? */ if (port == 0) { -#ifndef UDP_LOCAL_PORT_RANGE_START -#define UDP_LOCAL_PORT_RANGE_START 4096 -#define UDP_LOCAL_PORT_RANGE_END 0x7fff -#endif port = UDP_LOCAL_PORT_RANGE_START; ipcb = udp_pcbs; while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) { From 95e85c74e7310a595aa6bd57dbbc69ec3516e9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Sat, 20 Aug 2016 22:06:40 +0200 Subject: [PATCH 06/11] =?UTF-8?q?Revert=20"with=20this=20small=20fix,=20th?= =?UTF-8?q?e=20DNS=20engine=20is=20RFC=205452=20compliant=20because=20it?= =?UTF-8?q?=20=E2=80=A6"=20(#1465)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/include/lwip/udp.h | 5 ----- app/lwip/core/dns.c | 10 ++-------- app/lwip/core/udp.c | 4 ++++ 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/app/include/lwip/udp.h b/app/include/lwip/udp.h index 760b24cf..cb53d33e 100644 --- a/app/include/lwip/udp.h +++ b/app/include/lwip/udp.h @@ -156,11 +156,6 @@ void udp_input (struct pbuf *p, struct netif *inp)ICACHE_FLASH_ #define udp_init() /* Compatibility define, not init needed. */ -#ifndef UDP_LOCAL_PORT_RANGE_START -#define UDP_LOCAL_PORT_RANGE_START 4096 -#define UDP_LOCAL_PORT_RANGE_END 0x7fff -#endif - #if UDP_DEBUG void udp_debug_print(struct udp_hdr *udphdr)ICACHE_FLASH_ATTR; #else diff --git a/app/lwip/core/dns.c b/app/lwip/core/dns.c index 72a31995..c0d616f2 100644 --- a/app/lwip/core/dns.c +++ b/app/lwip/core/dns.c @@ -254,14 +254,8 @@ dns_init() LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0", DNS_STATE_UNUSED == 0); - /* initialize DNS client, try to get RFC 5452 random source port */ - u16_t port = UDP_LOCAL_PORT_RANGE_START + (os_random() % (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)); - for(;;) { - if(udp_bind(dns_pcb, IP_ADDR_ANY, port) == ERR_OK) - break; - LWIP_ASSERT("Unable to get a PCB for DNS", port != 0); - port=0; // try again with random source - } + /* initialize DNS client */ + udp_bind(dns_pcb, IP_ADDR_ANY, 0); udp_recv(dns_pcb, dns_recv, NULL); /* initialize default DNS primary server */ diff --git a/app/lwip/core/udp.c b/app/lwip/core/udp.c index 2d794818..e44d7f38 100644 --- a/app/lwip/core/udp.c +++ b/app/lwip/core/udp.c @@ -758,6 +758,10 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) /* no port specified? */ if (port == 0) { +#ifndef UDP_LOCAL_PORT_RANGE_START +#define UDP_LOCAL_PORT_RANGE_START 4096 +#define UDP_LOCAL_PORT_RANGE_END 0x7fff +#endif port = UDP_LOCAL_PORT_RANGE_START; ipcb = udp_pcbs; while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) { From 8e48483c825dea9c12b37a4db3d034fccbcba0bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Fri, 9 Sep 2016 17:01:28 +0200 Subject: [PATCH 07/11] Change RTD links to point to `master` Fixes #1490 --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index dd7f3553..dc626d71 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Join the chat at https://gitter.im/nodemcu/nodemcu-firmware](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/nodemcu/nodemcu-firmware?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/nodemcu/nodemcu-firmware.svg)](https://travis-ci.org/nodemcu/nodemcu-firmware) -[![Documentation Status](https://img.shields.io/badge/docs-latest-yellow.svg?style=flat)](http://nodemcu.readthedocs.io/en/latest/) +[![Documentation Status](https://img.shields.io/badge/docs-master-yellow.svg?style=flat)](http://nodemcu.readthedocs.io/en/master/) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/nodemcu/nodemcu-firmware/blob/master/LICENSE) ### A Lua based firmware for ESP8266 WiFi SOC @@ -45,15 +45,15 @@ wifi.sta.config("SSID", "password") The entire [NodeMCU documentation](https://nodemcu.readthedocs.io) is maintained right in this repository at [/docs](docs). The fact that the API documentation is mainted in the same repository as the code that *provides* the API ensures consistency between the two. With every commit the documentation is rebuilt by Read the Docs and thus transformed from terse Markdown into a nicely browsable HTML site at [https://nodemcu.readthedocs.io](https://nodemcu.readthedocs.io). -- How to [build the firmware](https://nodemcu.readthedocs.io/en/dev/en/build/) -- How to [build the filesystem](https://nodemcu.readthedocs.io/en/dev/en/spiffs/) -- How to [flash the firmware](https://nodemcu.readthedocs.io/en/dev/en/flash/) -- How to [upload code and NodeMCU IDEs](https://nodemcu.readthedocs.io/en/dev/en/upload/) +- How to [build the firmware](https://nodemcu.readthedocs.io/en/master/en/build/) +- How to [build the filesystem](https://nodemcu.readthedocs.io/en/master/en/spiffs/) +- How to [flash the firmware](https://nodemcu.readthedocs.io/en/master/en/flash/) +- How to [upload code and NodeMCU IDEs](https://nodemcu.readthedocs.io/en/master/en/upload/) - API documentation for every module # Releases -Due to the ever-growing number of modules available within NodeMCU, pre-built binaries are no longer made available. Use the automated [custom firmware build service](http://nodemcu-build.com/) to get the specific firmware configuration you need, or consult the [documentation](http://nodemcu.readthedocs.io/en/dev/en/build/) for other options to build your own firmware. +Due to the ever-growing number of modules available within NodeMCU, pre-built binaries are no longer made available. Use the automated [custom firmware build service](http://nodemcu-build.com/) to get the specific firmware configuration you need, or consult the [documentation](http://nodemcu.readthedocs.io/en/master/en/build/) for other options to build your own firmware. This project uses two main branches, `master` and `dev`. `dev` is actively worked on and it's also where PRs should be created against. `master` thus can be considered "stable" even though there are no automated regression tests. The goal is to merge back to `master` roughly every 2 months. Depending on the current "heat" (issues, PRs) we accept changes to `dev` for 5-6 weeks and then hold back for 2-3 weeks before the next snap is completed. @@ -61,7 +61,7 @@ A new tag is created every time `dev` is merged back to `master`. They are liste # Support -See [https://nodemcu.readthedocs.io/en/dev/en/support/](https://nodemcu.readthedocs.io/en/dev/en/support/). +See [https://nodemcu.readthedocs.io/en/master/en/support/](https://nodemcu.readthedocs.io/en/master/en/support/). # License @@ -69,7 +69,7 @@ See [https://nodemcu.readthedocs.io/en/dev/en/support/](https://nodemcu.readthed # Build Options -The following sections explain some of the options you have if you want to [build your own NodeMCU firmware](http://nodemcu.readthedocs.io/en/dev/en/build/). +The following sections explain some of the options you have if you want to [build your own NodeMCU firmware](http://nodemcu.readthedocs.io/en/master/en/build/). ### Select Modules From 04ce0adf6fa7ccda242f1235b7916252cd1ad543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Thu, 1 Dec 2016 21:37:24 +0100 Subject: [PATCH 08/11] Next 1.5.4.1 master drop (#1627) * add u8g.fb_rle display * move comm drivers to u8g_glue.c * disable fb_rle per default * implement file.size for spiffs (#1516) Another bug squashed! * Fix start-up race between UART & start_lua. (#1522) Input during startup (especially while doing initial filesystem format) ran the risk of filling up the task queue, preventing the start_lua task from being queued, and hence NodeMCU would not start up that time. * Reimplemented esp_init_data_default. To work around the pesky "rf_cal[0] !=0x05" hang when booting on a chip which doesn't have esp_init_data written to it. It is no longer possible to do the writing of the esp_init_data_default from within nodemcu_init(), as the SDK now hangs long before it gets there. As such, I've had to reimplement this in our user_start_trampoline and get it all done before the SDK has a chance to look for the init data. It's unfortunate that we have to spend IRAM on this, but I see no better alternative at this point. * Replace hardcoded init data with generated data from SDK The esp_init_data_default.bin is now extracted from the SDK (and its patch file, if present), and the contents are automatically embedded into user_main.o. * Rework flashing instructions Clarifies issues around SDK init data and hopefully clears up some confusion, when paired with the esp_init_data_default changes in NodeMCU. * Fix typo * Fixes the gpio.serout problem from #1534 (#1535) * Fix some issues in gpio.serout * Minor cleanup * fix dereferencing NULL pointer in vfs_errno() (#1539) * add map ids for flash sizes 32m-c2, 64m, 128m in user_rf_cal_sector_set() (#1529) * Somfy/TELIS driver (#1521) * Reduced LUAL_BUFFERSIZE to 256. Should free up some stack (#1530) * avoid task queue overrun for serial input (#1540) Thank you. * Increase irom0_0_seg size for PR build * Improve reliability of FS detection. (#1528) * Version to make filesystem detection more reliable * Improve bad fs detection * Version of printf that doesn't suffer from buffer overflows (#1564) * Small improvement to http client (#1558) * Remove luaL_buffer from file_g_read() (#1541) * remove luaL_buffer from file_g_read() - avoid memory leak when function gets terminated by lua_error - skip scanning for end_char when reading until EOF * attempt to free memory in any case * Change HTTP failures from debug to error messages (#1568) * Change HTTP failures from debug to error messages * Add tag to HTTP error messages * Create macro for error msg and improve dbg msg * Add ssd1306_128x32 for U8G (#1571) * Update CONTRIBUTING.md * Add support to mix ws2812.buffer objects. (#1575) * Add load/dump/mix/power operations on the buffer object * Calculate the pixel value in mix and then clip to the range. * Fixed the two wrong userdata types * Added a couple more useful methods * Add support for shifting a piece of the buffer. * Fix a minor bug with offset shifts * Update to the wifi module (#1497) * Removed inline documentation for several functions and update comments Since documentation is now part of the repository, the inline documentation just adds to the already huge wifi.c * Wifi module: add new functionality, update documentation Functions Added: wifi.getdefaultmode(): returns default wifi opmode wifi.sta.apchange(): select alternate cached AP wifi.sta.apinfo(): get cached AP list wifi.sta.aplimit(): set cached AP limit wifi.sta.getapindex(): get index of currently configured AP wifi.sta.getdefaultconfig(): get default station configuration wifi.ap.getdefaultconfig(): get default AP configuration functions modified: wifi.setmode: saving mode to flash is now optional wifi.sta.config: now accepts table as an argument and save config to flash is now optional wifi.sta.getconfig: added option to return table wifi.ap.config: save config to flash is now optional wifi.ap.getconfig: added option to return table Documentation changes: - Modified documentation to reflect above changes - Removed unnecessary inline documentation from `wifi.c` - Updated documentation for `wifi.sta.disconnect`to address issue #1480 - Fixed inaccurate documentation for function `wifi.sleeptype` - Added more details to `wifi.nullmodesleep()` * Move function `wifi.sleeptype()` to `wifi.sta.sleeptype()` * Fixed problem where wifi.x.getconfig() returned invalid strings when ssid or password were set to maximum length. * fix error in documentation for `wifi.sta.getapindex` * Renamed some wifi functions wifi.sta.apinfo -> getapinfo wifi.sta.aplimit -> setaplimit wifi.sta.apchange -> changeap also organized the wifi_station_map array * Make the MQTT PING functionality work better. (#1557) Deal with flow control stopped case * Implement object model for files (#1532) * Eus channelfix (#1583) Squashed commits included: Bug fixes and final implementation - Added Content-Length: 0 to all headers - Endpoint name checks not using trailing space so cache-busting techniques can be used (i.e., append a nonce to the URL) - Track when connecting so APList scan doesn't take place during (which changes the channel) - More debugging output added to assist in tracking down some issues Added /status.json endpoint for phone apps/XHR to get JSON response Station Status caching for wifi channel workaround + AJAX/CORS - During checkstation poll, cache the last station status - Shut down the station if status = 2,3,4 and channel is different than SoftAP - Add Access-Control-Allow-Origin: * to endpoint responses used by a service - Add a /setwifi GET endpoint for phone apps/XHR to use (same parameters as /update endpoint). Returns a JSON response containing chip id and status code. - Add handler for OPTIONS verb (needed for CORS support) Wi-Fi Channel Issue Workaround - Do a site survey upon startup, set SoftAP channel to the strongest rssi's channel - Compare successful station connect channel to SoftAP's. If different, then defer the Lua success callback to the end. Shut down Station and start the SoftAP back up with original channel. - After the 10 second shutdown timer fires, check to see if success callback was already called. If not, then call it while starting the Station back up. HTTP Response and DNS enhancements - If DNS's UDP buffer fills up, keep going as non-fatal. It's UDP and not guaranteed anyways. I've seen this occur when connecting a PC to the SoftAP and every open program tries to phone home at the same time, overwhelming the EUS DNS server. - Support for detecting/handling pre-gzipped `enduser_setup.html` (and `http_html_backup`) payload. Nice for keeping the size of the `state->http_payload_data` as small as possible (also makes minimization not as critical) - Corrected misuse of HTTP 401 response status (changed one occurrence to 400/Bad Request, and changed another to 405/Method Not Allowed) * Normalized formatting (tabs-to-spaces) * Added documentation * Corrected misuse of strlen for binary (gzip) data. * Added NULL check after malloc * fix vfs_lseek() result checking in enduser_setup and clarify SPIFFS_lseek() return value (#1570) * Fix link * Overhaul flashing docs once again (#1587) * Add chapter about determine flash size plus small fixes * Rewrite esptool.py chapter, move flash size chapter to end * i2c - allow slave stretching SCL (just loop and check) (#1589) * Add note on dev board usage of SPI bus 0 (#1591) * Turn SPI busses note to admonition note * support for custom websocket headers (#1573) Looks good to me. Thank you. Also: - allow for '\0's in received messages * add client:config for setting websocket headers Also: - headers are case-insensitive now * fix docs * fix typo * remove unnecessary luaL_argcheck calls * replace os_sprintf with simple string copy * Handle error condition in file.read() (#1599) * handle error condition in file.read() * simplify loop initialization * Fix macro as suggested in #1548 * Extract and hoist net receive callbacks This is done to avoid the accidental upval binding * Fix typo at rtctime.md rtctime.dsleep -> rtctime.dsleep_aligned --- CONTRIBUTING.md | 24 +- Makefile | 4 +- app/driver/i2c_master.c | 7 + app/driver/uart.c | 16 +- app/http/httpclient.c | 74 ++- app/http/httpclient.h | 19 +- app/include/driver/uart.h | 2 +- app/include/rom.h | 2 + app/include/sections.h | 1 + app/include/u8g_config.h | 6 + app/include/user_config.h | 7 +- app/include/user_modules.h | 1 + app/libc/c_stdio.h | 2 + app/libc/dbg_printf.c | 164 +++++ app/lua/lauxlib.c | 2 +- app/lua/liolib.c | 25 +- app/lua/luaconf.h | 4 +- app/modules/enduser_setup.c | 602 ++++++++++------- app/modules/eus/enduser_setup.html | 315 +++++++++ app/modules/eus/enduser_setup.html.gz | Bin 0 -> 2393 bytes app/modules/eus/http_html_backup.def | 203 ++++++ app/modules/eus/prepare.sh | 7 + app/modules/file.c | 246 +++++-- app/modules/gpio.c | 20 +- app/modules/http.c | 6 +- app/modules/mqtt.c | 94 +-- app/modules/ow.c | 2 + app/modules/sntp.c | 2 +- app/modules/somfy.c | 248 +++++++ app/modules/u8g.c | 293 ++------- app/modules/u8g_glue.c | 347 ++++++++++ app/modules/u8g_glue.h | 22 + app/modules/websocket.c | 64 +- app/modules/wifi.c | 868 +++++++++++++++---------- app/modules/wifi_common.h | 11 +- app/modules/ws2812.c | 256 ++++++-- app/net/nodemcu_mdns.c | 2 +- app/platform/vfs.c | 3 +- app/platform/vfs.h | 2 +- app/spiffs/spiffs.c | 34 +- app/spiffs/spiffs_config.h | 10 +- app/spiffs/spiffs_nucleus.c | 59 +- app/spiffs/spiffs_nucleus.h | 2 +- app/u8glib/u8g.h | 3 - app/user/Makefile | 2 +- app/user/user_main.c | 80 ++- app/websocket/websocketclient.c | 92 ++- app/websocket/websocketclient.h | 8 +- docs/en/flash.md | 111 ++-- docs/en/index.md | 2 +- docs/en/modules/enduser-setup.md | 27 +- docs/en/modules/file.md | 326 ++++++---- docs/en/modules/gpio.md | 17 +- docs/en/modules/net.md | 67 +- docs/en/modules/ow.md | 2 +- docs/en/modules/rtctime.md | 2 +- docs/en/modules/sntp.md | 2 +- docs/en/modules/somfy.md | 44 ++ docs/en/modules/spi.md | 4 + docs/en/modules/u8g.md | 28 +- docs/en/modules/websocket.md | 25 +- docs/en/modules/wifi.md | 556 ++++++++++++---- docs/en/modules/ws2812.md | 134 +++- lua_examples/somfy.lua | 145 +++++ sdk-overrides/include/user_interface.h | 6 + tools/pr-build.sh | 2 +- tools/spiffsimg/Makefile | 2 +- 67 files changed, 4341 insertions(+), 1424 deletions(-) create mode 100644 app/libc/dbg_printf.c create mode 100644 app/modules/eus/enduser_setup.html create mode 100644 app/modules/eus/enduser_setup.html.gz create mode 100644 app/modules/eus/http_html_backup.def create mode 100644 app/modules/eus/prepare.sh create mode 100644 app/modules/somfy.c create mode 100644 app/modules/u8g_glue.c create mode 100644 app/modules/u8g_glue.h create mode 100644 docs/en/modules/somfy.md create mode 100644 lua_examples/somfy.lua diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 996162da..42ee59bc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,16 +4,22 @@ The following is a set of guidelines for contributing to NodeMCU on GitHub. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request. -It is appreciated but optional if you raise an issue _before_ you start changing NodeMCU, discussing the proposed change; emphasing that the you are proposing to develop the patch yourself, and outlining the strategy for implementation. This type of discussion is what we should be doing on the issues list and it is better to do this before or in parallel to developing the patch rather than having "you should have done it this way" type of feedback on the PR itself. +It is appreciated if you raise an issue _before_ you start changing NodeMCU, discussing the proposed change; emphasing that the you are proposing to develop the patch yourself, and outlining the strategy for implementation. This type of discussion is what we should be doing on the issues list and it is better to do this before or in parallel to developing the patch rather than having "you should have done it this way" type of feedback on the PR itself. ### Table Of Contents - +* [General remarks](#general-remarks) * [Development environment setup](#development-environment-setup) * [Writing Documentation](#writing-documentation) * [Working with Git and GitHub](#working-with-git-and-github) * [General flow](#general-flow) * [Keeping your fork in sync](#keeping-your-fork-in-sync) * [Commit messages](#commit-messages) +* [For collaborators](#for-collaborators) + * [Handling releases](#handling-release) + +## General remarks +We are a friendly and welcoming community and look forward to your contributions. Once your contribution is integrated into this repository we feel responsible for it. Therefore, be prepared for constructive feedback. Before we merge anything we need to ensure that it fits in and is consistent with the rest of NodeMCU. +If you made something really cool but won't spend time to integrate it into this upstream project please still share it in your fork on GitHub. If you mention it in an issues we'll take a look at it anyway. ## Development environment setup Use the platform and tools you feel most comfortable with. There are no constraints imposed by this project. You have (at least) two options to set up the toolchain to build the NodeMCU firmware: @@ -52,7 +58,7 @@ Avoid intermediate merge commits. [Rebase](https://www.atlassian.com/git/tutoria 1. `git checkout ` 1. Make changes to the code base and commit them using e.g. `git commit -a -m 'Look ma, I did it'` 1. When you're done: - 1. [Squash your commits](http://www.andrewconnell.com/blog/squash-multiple-git-commits-into-one). There are [several ways](http://stackoverflow.com/a/5201642/131929) of doing this. + 1. Think about [squashing (some of) your commits](http://www.andrewconnell.com/blog/squash-multiple-git-commits-into-one). There are [several ways](http://stackoverflow.com/a/5201642/131929) to do this. There's no need to squash everything into a single commit as GitHub offers to do this when we merge your changes. However, you might want to trim your commit history to relevant chunks. 1. Bring your fork up-to-date with the NodeMCU upstream repo ([see below](#keeping-your-fork-in-sync)). Then rebase your branch on `dev` running `git rebase dev`. 1. `git push` 1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/) (PR) on GitHub. @@ -90,3 +96,15 @@ Further paragraphs come after blank lines. Don't forget to [reference affected issues](https://help.github.com/articles/closing-issues-via-commit-messages/) in the commit message to have them closed automatically on GitHub. [Amend](https://help.github.com/articles/changing-a-commit-message/) your commit messages if necessary to make sure what the world sees on GitHub is as expressive and meaningful as possible. + +## For collaborators + +### Handling releases +- Create a [milestone](https://github.com/nodemcu/nodemcu-firmware/milestones) right after you cut a new release. Give it a meaningful name if you already have an idea what the scope of the upcoming release is going to be. Also set the due date to ~2 months in the future. +- Add this milestone to every PR before you merge it. Also add the milestone to PRs you want to see land in this milestone. +- Add notes to the description of the milestone in the course of the ~2 months it lives. +- Be careful and reluctant to merge PRs once we're past the 6-weeks mark of a milestone. Ideally we don't merge anything in the last 2 weeks. +- Cutting a release + - Create an annotated tag like so: `git tag -a -master_ -m ""`, `git push --tags` + - Create a new [release](https://github.com/nodemcu/nodemcu-firmware/releases) based on the tag you just pushed. The version name is the same as the tag name. + - Write release notes. Mention breaking changes explicitly. Since every PR that went into this release is linked to from the milestone it should be fairly easy to include important changes in the release notes. diff --git a/Makefile b/Makefile index f3e8bc45..2e3f3900 100644 --- a/Makefile +++ b/Makefile @@ -207,14 +207,14 @@ sdk_patched: sdk_extracted $(TOP_DIR)/sdk/.patched-$(SDK_VER) $(TOP_DIR)/sdk/.extracted-$(SDK_BASE_VER): $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip mkdir -p "$(dir $@)" - (cd "$(dir $@)" && rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip ESP8266_NONOS_SDK/lib/* ESP8266_NONOS_SDK/ld/eagle.rom.addr.v6.ld ESP8266_NONOS_SDK/include/* ) + (cd "$(dir $@)" && rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip ESP8266_NONOS_SDK/lib/* ESP8266_NONOS_SDK/ld/eagle.rom.addr.v6.ld ESP8266_NONOS_SDK/include/* ESP8266_NONOS_SDK/bin/esp_init_data_default.bin) mv $(dir $@)/ESP8266_NONOS_SDK $(dir $@)/esp_iot_sdk_v$(SDK_VER) rm -f $(SDK_DIR)/lib/liblwip.a touch $@ $(TOP_DIR)/sdk/.patched-$(SDK_VER): $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VER).zip mkdir -p "$(dir $@)/patch" - (cd "$(dir $@)/patch" && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VER)*.zip *.a && mv *.a $(SDK_DIR)/lib/) + (cd "$(dir $@)/patch" && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VER)*.zip *.a esp_init_data_default.bin && mv *.a $(SDK_DIR)/lib/ && mv esp_init_data_default.bin $(SDK_DIR)/bin/) rmdir $(dir $@)/patch rm -f $(SDK_DIR)/lib/liblwip.a touch $@ diff --git a/app/driver/i2c_master.c b/app/driver/i2c_master.c index 772be691..f8de4bc4 100644 --- a/app/driver/i2c_master.c +++ b/app/driver/i2c_master.c @@ -33,6 +33,8 @@ LOCAL uint8 pinSCL = 15; LOCAL void ICACHE_FLASH_ATTR i2c_master_setDC(uint8 SDA, uint8 SCL) { + uint8 sclLevel; + SDA &= 0x01; SCL &= 0x01; m_nLastSDA = SDA; @@ -47,6 +49,11 @@ i2c_master_setDC(uint8 SDA, uint8 SCL) } else { I2C_MASTER_SDA_HIGH_SCL_HIGH(); } + if(1 == SCL) { + do { + sclLevel = GPIO_INPUT_GET(GPIO_ID_PIN(I2C_MASTER_SCL_GPIO)); + } while(sclLevel == 0); + } } /****************************************************************************** diff --git a/app/driver/uart.c b/app/driver/uart.c index 688dcd2d..39969b45 100755 --- a/app/driver/uart.c +++ b/app/driver/uart.c @@ -30,6 +30,8 @@ // For event signalling static task_handle_t sig = 0; +static uint8 *sig_flag; +static uint8 isr_flag = 0; // UartDev is defined and initialized in rom code. extern UartDevice UartDev; @@ -277,8 +279,12 @@ uart0_rx_intr_handler(void *para) got_input = true; } - if (got_input && sig) - task_post_low (sig, false); + if (got_input && sig) { + if (isr_flag == *sig_flag) { + isr_flag ^= 0x01; + task_post_low (sig, 0x8000 | isr_flag << 14 | false); + } + } } static void @@ -316,21 +322,21 @@ uart_stop_autobaud() * Description : user interface for init uart * Parameters : UartBautRate uart0_br - uart0 bautrate * UartBautRate uart1_br - uart1 bautrate - * uint8 task_prio - task priority to signal on input * os_signal_t sig_input - signal to post + * uint8 *flag_input - flag of consumer task * Returns : NONE *******************************************************************************/ void ICACHE_FLASH_ATTR -uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input) +uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input, uint8 *flag_input) { sig = sig_input; + sig_flag = flag_input; // rom use 74880 baut_rate, here reinitialize UartDev.baut_rate = uart0_br; uart_config(UART0); UartDev.baut_rate = uart1_br; uart_config(UART1); - ETS_UART_INTR_ENABLE(); #ifdef BIT_RATE_AUTOBAUD uart_init_autobaud(0); #endif diff --git a/app/http/httpclient.c b/app/http/httpclient.c index 5e0fe555..42e5b8f1 100644 --- a/app/http/httpclient.c +++ b/app/http/httpclient.c @@ -13,6 +13,7 @@ */ #include "osapi.h" +#include "../libc/c_stdio.h" #include "user_interface.h" #include "espconn.h" #include "mem.h" @@ -94,7 +95,7 @@ static int ICACHE_FLASH_ATTR http_chunked_decode( const char * chunked, char * d char * endstr; /* [chunk-size] */ i = strtoul( str + j, NULL, 16 ); - HTTPCLIENT_DEBUG( "Chunk Size:%d\r\n", i ); + HTTPCLIENT_DEBUG( "Chunk Size:%d", i ); if ( i <= 0 ) break; /* [chunk-size-end-ptr] */ @@ -137,7 +138,7 @@ static void ICACHE_FLASH_ATTR http_receive_callback( void * arg, char * buf, uns char * new_buffer; if ( new_size > BUFFER_SIZE_MAX || NULL == (new_buffer = (char *) os_malloc( new_size ) ) ) { - HTTPCLIENT_DEBUG( "Response too long (%d)\n", new_size ); + HTTPCLIENT_ERR( "Response too long (%d)", new_size ); req->buffer[0] = '\0'; /* Discard the buffer to avoid using an incomplete response. */ if ( req->secure ) espconn_secure_disconnect( conn ); @@ -163,12 +164,12 @@ static void ICACHE_FLASH_ATTR http_send_callback( void * arg ) if ( req->post_data == NULL ) { - HTTPCLIENT_DEBUG( "All sent\n" ); + HTTPCLIENT_DEBUG( "All sent" ); } else { /* The headers were sent, now send the contents. */ - HTTPCLIENT_DEBUG( "Sending request body\n" ); + HTTPCLIENT_DEBUG( "Sending request body" ); if ( req->secure ) espconn_secure_send( conn, (uint8_t *) req->post_data, strlen( req->post_data ) ); else @@ -181,7 +182,7 @@ static void ICACHE_FLASH_ATTR http_send_callback( void * arg ) static void ICACHE_FLASH_ATTR http_connect_callback( void * arg ) { - HTTPCLIENT_DEBUG( "Connected\n" ); + HTTPCLIENT_DEBUG( "Connected" ); struct espconn * conn = (struct espconn *) arg; request_args_t * req = (request_args_t *) conn->reverse; espconn_regist_recvcb( conn, http_receive_callback ); @@ -250,7 +251,7 @@ static void ICACHE_FLASH_ATTR http_connect_callback( void * arg ) } req->headers = NULL; - HTTPCLIENT_DEBUG( "Sending request header\n" ); + HTTPCLIENT_DEBUG( "Sending request header" ); } static void http_free_req( request_args_t * req) @@ -272,7 +273,7 @@ static void http_free_req( request_args_t * req) static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) { - HTTPCLIENT_DEBUG( "Disconnected\n" ); + HTTPCLIENT_DEBUG( "Disconnected" ); struct espconn *conn = (struct espconn *) arg; if ( conn == NULL ) @@ -295,7 +296,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) if ( req->buffer == NULL ) { - HTTPCLIENT_DEBUG( "Buffer probably shouldn't be NULL\n" ); + HTTPCLIENT_DEBUG( "Buffer probably shouldn't be NULL" ); } else if ( req->buffer[0] != '\0' ) { @@ -305,7 +306,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) if (( os_strncmp( req->buffer, version_1_0, strlen( version_1_0 ) ) != 0 ) && ( os_strncmp( req->buffer, version_1_1, strlen( version_1_1 ) ) != 0 )) { - HTTPCLIENT_DEBUG( "Invalid version in %s\n", req->buffer ); + HTTPCLIENT_ERR( "Invalid version in %s", req->buffer ); } else { @@ -326,7 +327,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) char *locationOffsetEnd = (char *) os_strstr(locationOffset, "\r\n"); if ( locationOffsetEnd == NULL ) { - HTTPCLIENT_DEBUG( "Found Location header but was incomplete\n" ); + HTTPCLIENT_ERR( "Found Location header but was incomplete" ); http_status = -1; } else { *locationOffsetEnd = '\0'; @@ -371,7 +372,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) return; } } else { - HTTPCLIENT_DEBUG("Too many redirections\n"); + HTTPCLIENT_ERR("Too many redirections"); http_status = -1; } } else { @@ -379,7 +380,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) if (NULL == body) { /* Find missing body */ - HTTPCLIENT_DEBUG("Body shouldn't be NULL\n"); + HTTPCLIENT_ERR("Body shouldn't be NULL"); /* To avoid NULL body */ body = ""; } else { @@ -411,16 +412,9 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) } -static void ICACHE_FLASH_ATTR http_error_callback( void *arg, sint8 errType ) -{ - HTTPCLIENT_DEBUG( "Disconnected with error\n" ); - http_disconnect_callback( arg ); -} - - static void ICACHE_FLASH_ATTR http_timeout_callback( void *arg ) { - HTTPCLIENT_DEBUG( "Connection timeout\n" ); + HTTPCLIENT_ERR( "Connection timeout" ); struct espconn * conn = (struct espconn *) arg; if ( conn == NULL ) { @@ -439,13 +433,20 @@ static void ICACHE_FLASH_ATTR http_timeout_callback( void *arg ) } +static void ICACHE_FLASH_ATTR http_error_callback( void *arg, sint8 errType ) +{ + HTTPCLIENT_ERR( "Disconnected with error: %d", errType ); + http_timeout_callback( arg ); +} + + static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_t * addr, void * arg ) { request_args_t * req = (request_args_t *) arg; if ( addr == NULL ) { - HTTPCLIENT_DEBUG( "DNS failed for %s\n", hostname ); + HTTPCLIENT_ERR( "DNS failed for %s", hostname ); if ( req->callback_handle != NULL ) { req->callback_handle( "", -1, "" ); @@ -454,7 +455,7 @@ static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_ } else { - HTTPCLIENT_DEBUG( "DNS found %s " IPSTR "\n", hostname, IP2STR( addr ) ); + HTTPCLIENT_DEBUG( "DNS found %s " IPSTR, hostname, IP2STR( addr ) ); struct espconn * conn = (struct espconn *) os_zalloc( sizeof(struct espconn) ); conn->type = ESPCONN_TCP; @@ -489,7 +490,7 @@ static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_ void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool secure, const char * method, const char * path, const char * headers, const char * post_data, http_callback_t callback_handle, int redirect_follow_count ) { - HTTPCLIENT_DEBUG( "DNS request\n" ); + HTTPCLIENT_DEBUG( "DNS request" ); request_args_t * req = (request_args_t *) os_zalloc( sizeof(request_args_t) ); req->hostname = esp_strdup( hostname ); @@ -512,7 +513,7 @@ void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool s if ( error == ESPCONN_INPROGRESS ) { - HTTPCLIENT_DEBUG( "DNS pending\n" ); + HTTPCLIENT_DEBUG( "DNS pending" ); } else if ( error == ESPCONN_OK ) { @@ -523,9 +524,9 @@ void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool s { if ( error == ESPCONN_ARG ) { - HTTPCLIENT_DEBUG( "DNS arg error %s\n", hostname ); + HTTPCLIENT_ERR( "DNS arg error %s", hostname ); }else { - HTTPCLIENT_DEBUG( "DNS error code %d\n", error ); + HTTPCLIENT_ERR( "DNS error code %d", error ); } http_dns_callback( hostname, NULL, req ); /* Handle all DNS errors the same way. */ } @@ -561,7 +562,7 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons } else { - HTTPCLIENT_DEBUG( "URL is not HTTP or HTTPS %s\n", url ); + HTTPCLIENT_ERR( "URL is not HTTP or HTTPS %s", url ); return; } @@ -578,7 +579,7 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons } if (path - url >= sizeof(hostname)) { - HTTPCLIENT_DEBUG( "hostname is too long %s\n", url ); + HTTPCLIENT_ERR( "hostname is too long %s", url ); return; } @@ -592,7 +593,7 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons port = atoi( colon + 1 ); if ( port == 0 ) { - HTTPCLIENT_DEBUG( "Port error %s\n", url ); + HTTPCLIENT_ERR( "Port error %s", url ); return; } @@ -606,10 +607,10 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons path = "/"; } - HTTPCLIENT_DEBUG( "hostname=%s\n", hostname ); - HTTPCLIENT_DEBUG( "port=%d\n", port ); - HTTPCLIENT_DEBUG( "method=%s\n", method ); - HTTPCLIENT_DEBUG( "path=%s\n", path ); + HTTPCLIENT_DEBUG( "hostname=%s", hostname ); + HTTPCLIENT_DEBUG( "port=%d", port ); + HTTPCLIENT_DEBUG( "method=%s", method ); + HTTPCLIENT_DEBUG( "path=%s", path ); http_raw_request( hostname, port, secure, method, path, headers, post_data, callback_handle, redirect_follow_count); } @@ -645,10 +646,11 @@ void ICACHE_FLASH_ATTR http_put( const char * url, const char * headers, const c void ICACHE_FLASH_ATTR http_callback_example( char * response, int http_status, char * full_response ) { - os_printf( "http_status=%d\n", http_status ); + dbg_printf( "http_status=%d\n", http_status ); if ( http_status != HTTP_STATUS_GENERIC_ERROR ) { - os_printf( "strlen(full_response)=%d\n", strlen( full_response ) ); - os_printf( "response=%s\n", response ); + dbg_printf( "strlen(full_response)=%d\n", strlen( full_response ) ); + dbg_printf( "response=%s\n", response ); } } + diff --git a/app/http/httpclient.h b/app/http/httpclient.h index bb718f03..98c214d9 100644 --- a/app/http/httpclient.h +++ b/app/http/httpclient.h @@ -10,18 +10,25 @@ #ifndef __HTTPCLIENT_H__ #define __HTTPCLIENT_H__ -#if defined(GLOBAL_DEBUG_ON) -#define HTTPCLIENT_DEBUG_ON +static const char log_prefix[] = "HTTP client: "; + +#if defined(DEVELOP_VERSION) + #define HTTPCLIENT_DEBUG_ON #endif #if defined(HTTPCLIENT_DEBUG_ON) -#define HTTPCLIENT_DEBUG(format, ...) os_printf(format, ##__VA_ARGS__) + #define HTTPCLIENT_DEBUG(format, ...) dbg_printf("%s"format"\n", log_prefix, ##__VA_ARGS__) #else -#define HTTPCLIENT_DEBUG(format, ...) + #define HTTPCLIENT_DEBUG(...) +#endif +#if defined(NODE_ERROR) + #define HTTPCLIENT_ERR(format, ...) NODE_ERR("%s"format"\n", log_prefix, ##__VA_ARGS__) +#else + #define HTTPCLIENT_ERR(...) #endif #if defined(USES_SDK_BEFORE_V140) -#define espconn_send espconn_sent -#define espconn_secure_send espconn_secure_sent + #define espconn_send espconn_sent + #define espconn_secure_send espconn_secure_sent #endif /* diff --git a/app/include/driver/uart.h b/app/include/driver/uart.h index f9eada98..e4007e1c 100755 --- a/app/include/driver/uart.h +++ b/app/include/driver/uart.h @@ -101,7 +101,7 @@ typedef struct { int buff_uart_no; //indicate which uart use tx/rx buffer } UartDevice; -void uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input); +void uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input, uint8 *flag_input); void uart0_alt(uint8 on); void uart0_sendStr(const char *str); void uart0_putc(const char c); diff --git a/app/include/rom.h b/app/include/rom.h index 7de1c5e0..c2ad7d3d 100644 --- a/app/include/rom.h +++ b/app/include/rom.h @@ -149,5 +149,7 @@ void uart_div_modify(int no, unsigned int freq); /* Returns 0 on success, 1 on failure */ uint8_t SPIRead(uint32_t src_addr, uint32_t *des_addr, uint32_t size); +uint8_t SPIWrite(uint32_t dst_addr, const uint32_t *src, uint32_t size); +uint8_t SPIEraseSector(uint32_t sector); #endif diff --git a/app/include/sections.h b/app/include/sections.h index d09c576e..58613c90 100644 --- a/app/include/sections.h +++ b/app/include/sections.h @@ -2,5 +2,6 @@ #define _SECTIONS_H_ #define TEXT_SECTION_ATTR __attribute__((section(".text"))) +#define RAM_CONST_SECTION_ATTR __attribute((section(".data"))) #endif diff --git a/app/include/u8g_config.h b/app/include/u8g_config.h index 09866f1b..99a137e4 100644 --- a/app/include/u8g_config.h +++ b/app/include/u8g_config.h @@ -24,6 +24,7 @@ // // I2C based displays go into here: // U8G_DISPLAY_TABLE_ENTRY(sh1106_128x64_i2c) \ +// U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x32_i2c) \ // U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_i2c) \ // U8G_DISPLAY_TABLE_ENTRY(ssd1306_64x48_i2c) \ // U8G_DISPLAY_TABLE_ENTRY(ssd1309_128x64_i2c) \ @@ -40,6 +41,7 @@ // U8G_DISPLAY_TABLE_ENTRY(pcd8544_84x48_hw_spi) \ // U8G_DISPLAY_TABLE_ENTRY(pcf8812_96x65_hw_spi) \ // U8G_DISPLAY_TABLE_ENTRY(sh1106_128x64_hw_spi) \ +// U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x32_hw_spi) \ // U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_hw_spi) \ // U8G_DISPLAY_TABLE_ENTRY(ssd1306_64x48_hw_spi) \ // U8G_DISPLAY_TABLE_ENTRY(ssd1309_128x64_hw_spi) \ @@ -75,6 +77,10 @@ U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_hw_spi) \ #undef U8G_DISPLAY_TABLE_ENTRY + +// Special display device to provide run-length encoded framebuffer contents +// to a Lua callback: +//#define U8G_DISPLAY_FB_RLE // // *************************************************************************** diff --git a/app/include/user_config.h b/app/include/user_config.h index 2a375e08..c786e909 100644 --- a/app/include/user_config.h +++ b/app/include/user_config.h @@ -36,13 +36,13 @@ extern void luaL_assertfail(const char *file, int line, const char *message); #define NODE_ERROR #ifdef NODE_DEBUG -#define NODE_DBG c_printf +#define NODE_DBG dbg_printf #else #define NODE_DBG #endif /* NODE_DEBUG */ #ifdef NODE_ERROR -#define NODE_ERR c_printf +#define NODE_ERR dbg_printf #else #define NODE_ERR #endif /* NODE_ERROR */ @@ -76,6 +76,9 @@ extern void luaL_assertfail(const char *file, int line, const char *message); // maximum length of a filename #define FS_OBJ_NAME_LEN 31 +// maximum number of open files for SPIFFS +#define SPIFFS_MAX_OPEN_FILES 4 + // Uncomment this next line for fastest startup // It reduces the format time dramatically // #define SPIFFS_MAX_FILESYSTEM_SIZE 32768 diff --git a/app/include/user_modules.h b/app/include/user_modules.h index 40b6dbb6..f34285f7 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -53,6 +53,7 @@ //#define LUA_USE_MODULES_RTCTIME //#define LUA_USE_MODULES_SIGMA_DELTA //#define LUA_USE_MODULES_SNTP +//#define LUA_USE_MODULES_SOMFY #define LUA_USE_MODULES_SPI //#define LUA_USE_MODULES_STRUCT //#define LUA_USE_MODULES_SWITEC diff --git a/app/libc/c_stdio.h b/app/libc/c_stdio.h index 41391bd7..4c9a9bf3 100644 --- a/app/libc/c_stdio.h +++ b/app/libc/c_stdio.h @@ -63,6 +63,8 @@ extern void output_redirect(const char *str); int c_sprintf(char* s,const char *fmt, ...); #endif +extern void dbg_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); + #define c_vsprintf ets_vsprintf #define c_printf(...) do { \ unsigned char __print_buf[BUFSIZ]; \ diff --git a/app/libc/dbg_printf.c b/app/libc/dbg_printf.c new file mode 100644 index 00000000..92f3668a --- /dev/null +++ b/app/libc/dbg_printf.c @@ -0,0 +1,164 @@ +/* $NetBSD: printf.c,v 1.12 1997/06/26 19:11:48 drochner Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)printf.c 8.1 (Berkeley) 6/11/93 + */ + +// This version uses almost no stack, and does not suffer from buffer +// overflows. The downside is that it does not implement a wide range +// of formatting characters. + +#include +#include +#include +#include "driver/uart.h" + +static void kprintn (void (*)(const char), uint32_t, int, int, char); +static void kdoprnt (void (*)(const char), const char *, va_list); + +void +dbg_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + kdoprnt(uart0_putc, fmt, ap); + va_end(ap); +} + +void +dbg_vprintf(const char *fmt, va_list ap) +{ + kdoprnt(uart0_putc, fmt, ap); +} + +void +kdoprnt(void (*put)(const char), const char *fmt, va_list ap) +{ + register char *p; + register int ch, n; + unsigned long ul; + int lflag, set; + char zwidth; + char width; + + for (;;) { + while ((ch = *fmt++) != '%') { + if (ch == '\0') + return; + put(ch); + } + lflag = 0; + width = 0; + zwidth = ' '; +reswitch: switch (ch = *fmt++) { + case '\0': + /* XXX print the last format character? */ + return; + case 'l': + lflag = 1; + goto reswitch; + case 'c': + ch = va_arg(ap, int); + put(ch & 0x7f); + break; + case 's': + p = va_arg(ap, char *); + if (p == 0) { + p = ""; + } + while ((ch = *p++)) + put(ch); + break; + case 'd': + ul = lflag ? + va_arg(ap, long) : va_arg(ap, int); + if ((long)ul < 0) { + put('-'); + ul = -(long)ul; + } + kprintn(put, ul, 10, width, zwidth); + break; + case 'o': + ul = lflag ? + va_arg(ap, uint32_t) : va_arg(ap, uint32_t); + kprintn(put, ul, 8, width, zwidth); + break; + case 'u': + ul = lflag ? + va_arg(ap, uint32_t) : va_arg(ap, uint32_t); + kprintn(put, ul, 10, width, zwidth); + break; + case 'x': + ul = lflag ? + va_arg(ap, uint32_t) : va_arg(ap, uint32_t); + kprintn(put, ul, 16, width, zwidth); + break; + default: + if (ch >= '0' && ch <= '9') { + if (ch == '0' && width == 0 && zwidth == ' ') { + zwidth = '0'; + } else { + width = width * 10 + ch - '0'; + } + goto reswitch; + } + put('%'); + if (lflag) + put('l'); + put(ch); + } + } + va_end(ap); +} + +static void +kprintn(void (*put)(const char), unsigned long ul, int base, int width, char padchar) +{ + /* hold a long in base 8 */ + char *p, buf[(sizeof(long) * 8 / 3) + 2]; + + p = buf; + do { + *p++ = "0123456789abcdef"[ul % base]; + } while (ul /= base); + + while (p - buf < width--) { + put(padchar); + } + + do { + put(*--p); + } while (p > buf); +} diff --git a/app/lua/lauxlib.c b/app/lua/lauxlib.c index 2161c27e..bc9010fa 100644 --- a/app/lua/lauxlib.c +++ b/app/lua/lauxlib.c @@ -807,7 +807,7 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { } LUALIB_API void luaL_assertfail(const char *file, int line, const char *message) { - c_printf("ASSERT@%s(%d): %s\n", file, line, message); + dbg_printf("ASSERT@%s(%d): %s\n", file, line, message); } static int panic (lua_State *L) { diff --git a/app/lua/liolib.c b/app/lua/liolib.c index 03e67beb..4ac31fe4 100644 --- a/app/lua/liolib.c +++ b/app/lua/liolib.c @@ -343,28 +343,19 @@ static int read_line (lua_State *L, int f) { static int read_line (lua_State *L, int f) { luaL_Buffer b; luaL_buffinit(L, &b); - char *p = luaL_prepbuffer(&b); - signed char c = EOF; - int i = 0; - do{ + signed char c; + do { c = (signed char)vfs_getc(f); - if(c==EOF){ + if (c==EOF) { break; } - p[i++] = c; - }while((c!=EOF) && (c!='\n') && (i0 && p[i-1] == '\n') - i--; /* do not include `eol' */ - - if(i==0){ - luaL_pushresult(&b); /* close buffer */ - return (lua_objlen(L, -1) > 0); /* check whether read something */ - } - - luaL_addsize(&b, i); luaL_pushresult(&b); /* close buffer */ - return 1; /* read at least an `eol' */ + return (lua_objlen(L, -1) > 0); /* check whether read something */ } #endif diff --git a/app/lua/luaconf.h b/app/lua/luaconf.h index 01c052a2..d039b18f 100644 --- a/app/lua/luaconf.h +++ b/app/lua/luaconf.h @@ -346,7 +346,7 @@ extern int readline4lua(const char *prompt, char *buffer, int length); ** (A format string with one argument is enough for Lua...) */ #if !defined(LUA_USE_STDIO) -#define luai_writestringerror(s,p) c_printf((s), (p)) +#define luai_writestringerror(s,p) dbg_printf((s), (p)) #endif // defined(LUA_USE_STDIO) @@ -556,7 +556,7 @@ extern int readline4lua(const char *prompt, char *buffer, int length); ** For example: If set to 4K a call to string.gsub will need more than ** 5k C stack space. */ -#define LUAL_BUFFERSIZE BUFSIZ +#define LUAL_BUFFERSIZE 256 /* }================================================================== */ diff --git a/app/modules/enduser_setup.c b/app/modules/enduser_setup.c index 39a9975b..19af1ac5 100644 --- a/app/modules/enduser_setup.c +++ b/app/modules/enduser_setup.c @@ -31,6 +31,7 @@ * @author Robert Foss * * Additions & fixes: Johny Mattsson + * Jason Follas */ #include "module.h" @@ -50,6 +51,8 @@ #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #define LITLEN(strliteral) (sizeof (strliteral) -1) +#define STRINGIFY(x) #x +#define NUMLEN(x) (sizeof(STRINGIFY(x)) - 1) #define ENDUSER_SETUP_ERR_FATAL (1 << 0) #define ENDUSER_SETUP_ERR_NONFATAL (1 << 1) @@ -59,7 +62,7 @@ #define ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND 2 #define ENDUSER_SETUP_ERR_UNKOWN_ERROR 3 #define ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN 4 - +#define ENDUSER_SETUP_ERR_MAX_NUMBER 5 /** * DNS Response Packet: @@ -78,159 +81,19 @@ static const char dns_body[] = { 0x00, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x04 }; static const char http_html_filename[] = "enduser_setup.html"; -static const char http_header_200[] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nContent-Type: text/html\r\n"; /* Note single \r\n here! */ -static const char http_header_204[] = "HTTP/1.1 204 No Content\r\n\r\n"; -static const char http_header_302[] = "HTTP/1.1 302 Moved\r\nLocation: /\r\n\r\n"; -static const char http_header_401[] = "HTTP/1.1 401 Bad request\r\n\r\n"; -static const char http_header_404[] = "HTTP/1.1 404 Not found\r\n\r\n"; -static const char http_header_500[] = "HTTP/1.1 500 Internal Error\r\n\r\n"; +static const char http_header_200[] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nConnection:close\r\nContent-Type:text/html\r\n"; /* Note single \r\n here! */ +static const char http_header_204[] = "HTTP/1.1 204 No Content\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; +static const char http_header_302[] = "HTTP/1.1 302 Moved\r\nLocation: /\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; +static const char http_header_400[] = "HTTP/1.1 400 Bad request\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; +static const char http_header_404[] = "HTTP/1.1 404 Not found\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; +static const char http_header_405[] = "HTTP/1.1 405 Method Not Allowed\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; +static const char http_header_500[] = "HTTP/1.1 500 Internal Error\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; -/* The below is the un-minified version of the http_html_backup[] string. - * Minified using https://kangax.github.io/html-minifier/ - * Note: using method="get" due to iOS not always sending body in same - * packet as the HTTP header, and us thus missing it in that case - */ -#if 0 - - - - - - - WiFi Login - - - -
-
-
- -

WiFi Login

-

Connect gadget to your WiFi network

- - - - -

Status: Updating...

-
-

-
-
- - - -#endif -static const char http_html_backup[] = -"WiFi Login

WiFi Login

Connect gadget to your WiFi network

Status: Updating...

"; +static const char http_header_content_len_fmt[] = "Content-length:%5d\r\n\r\n"; +static const char http_html_gzip_contentencoding[] = "Content-Encoding: gzip\r\n"; +// Externally defined: static const char http_html_backup[] = ... +#include "eus/http_html_backup.def" typedef struct scan_listener { @@ -250,9 +113,15 @@ typedef struct int lua_err_cb_ref; int lua_dbg_cb_ref; scan_listener_t *scan_listeners; + uint8_t softAPchannel; + uint8_t success; + uint8_t callbackDone; + uint8_t lastStationStatus; + uint8_t connecting; } enduser_setup_state_t; static enduser_setup_state_t *state; + static bool manual = false; static task_handle_t do_station_cfg_handle; @@ -340,7 +209,7 @@ static void enduser_setup_check_station_start(void) ENDUSER_SETUP_DEBUG("enduser_setup_check_station_start"); os_timer_setfn(&(state->check_station_timer), enduser_setup_check_station, NULL); - os_timer_arm(&(state->check_station_timer), 1*1000, TRUE); + os_timer_arm(&(state->check_station_timer), 3*1000, TRUE); } @@ -377,13 +246,60 @@ static void enduser_setup_check_station(void *p) has_ip |= ((char *) &ip)[i]; } + uint8_t currChan = wifi_get_channel(); + if (has_ip == 0) { - return; + // No IP Address yet, so check the reported status + uint8_t curr_status = wifi_station_get_connect_status(); + char buf[20]; + c_sprintf(buf, "status=%d,chan=%d", curr_status, currChan); + ENDUSER_SETUP_DEBUG(buf); + + if (curr_status == 2 || curr_status == 3 || curr_status == 4) + { + state->connecting = 0; + + // If the status is an error status and the channel changed, then cache the + // status to state since the Station won't be able to report the same status + // after switching the channel back to the SoftAP's + if (currChan != state->softAPchannel) { + state->lastStationStatus = curr_status; + + ENDUSER_SETUP_DEBUG("Turning off Station due to different channel than AP"); + + wifi_station_disconnect(); + wifi_set_opmode(SOFTAP_MODE); + enduser_setup_ap_start(); + } + } + return; } + state->success = 1; + state->lastStationStatus = 5; // We have an IP Address, so the status is 5 (as of SDK 1.5.1) + state->connecting = 0; + +#if ENDUSER_SETUP_DEBUG_ENABLE + char debuginfo[100]; + c_sprintf(debuginfo, "AP_CHAN: %d, STA_CHAN: %d", state->softAPchannel, currChan); + ENDUSER_SETUP_DEBUG(debuginfo); +#endif + + if (currChan == state->softAPchannel) + { + enduser_setup_connected_callback(); + state->callbackDone = 1; + } + else + { + ENDUSER_SETUP_DEBUG("Turning off Station due to different channel than AP"); + wifi_station_disconnect(); + wifi_set_opmode(SOFTAP_MODE); + enduser_setup_ap_start(); + } + enduser_setup_check_station_stop(); - enduser_setup_connected_callback(); /* Trigger shutdown, but allow time for HTTP client to fetch last status. */ if (!manual) @@ -411,6 +327,8 @@ static void enduser_setup_check_station(void *p) /* Callback on timeout to hard-close a connection */ static err_t force_abort (void *arg, struct tcp_pcb *pcb) { + ENDUSER_SETUP_DEBUG("force_abort"); + (void)arg; tcp_poll (pcb, 0, 0); tcp_abort (pcb); @@ -420,6 +338,8 @@ static err_t force_abort (void *arg, struct tcp_pcb *pcb) /* Callback to detect a remote-close of a connection */ static err_t handle_remote_close (void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { + ENDUSER_SETUP_DEBUG("handle_remote_close"); + (void)arg; (void)err; if (p) /* server sent us data, just ACK and move on */ { @@ -438,6 +358,8 @@ static err_t handle_remote_close (void *arg, struct tcp_pcb *pcb, struct pbuf *p /* Set up a deferred close of a connection, as discussed above. */ static inline void deferred_close (struct tcp_pcb *pcb) { + ENDUSER_SETUP_DEBUG("deferred_close"); + tcp_poll (pcb, force_abort, 15); /* ~3sec from now */ tcp_recv (pcb, handle_remote_close); tcp_sent (pcb, 0); @@ -446,6 +368,8 @@ static inline void deferred_close (struct tcp_pcb *pcb) /* Convenience function to queue up a close-after-send. */ static err_t close_once_sent (void *arg, struct tcp_pcb *pcb, u16_t len) { + ENDUSER_SETUP_DEBUG("close_once_sent"); + (void)arg; (void)len; deferred_close (pcb); return ERR_OK; @@ -473,7 +397,6 @@ static int enduser_setup_srch_str(const char *str, const char *srch_str) } } - /** * Load HTTP Payload * @@ -495,14 +418,26 @@ static int enduser_setup_http_load_payload(void) err2 = vfs_lseek(f, 0, VFS_SEEK_SET); } - const char cl_hdr[] = "Content-length:%5d\r\n\r\n"; - const size_t cl_len = LITLEN(cl_hdr) + 3; /* room to expand %4d */ + char cl_hdr[30]; + size_t ce_len = 0; + + c_sprintf(cl_hdr, http_header_content_len_fmt, file_len); + size_t cl_len = c_strlen(cl_hdr); - if (!f || err != VFS_RES_OK || err2 != VFS_RES_OK) + if (!f || err == VFS_RES_ERR || err2 == VFS_RES_ERR) { ENDUSER_SETUP_DEBUG("enduser_setup_http_load_payload unable to load file enduser_setup.html, loading backup HTML."); - int payload_len = LITLEN(http_header_200) + cl_len + LITLEN(http_html_backup); + c_sprintf(cl_hdr, http_header_content_len_fmt, sizeof(http_html_backup)); + cl_len = c_strlen(cl_hdr); + + if (http_html_backup[0] == 0x1f && http_html_backup[1] == 0x8b) + { + ce_len = c_strlen(http_html_gzip_contentencoding); + ENDUSER_SETUP_DEBUG("Content is gzipped"); + } + + int payload_len = LITLEN(http_header_200) + cl_len + ce_len + LITLEN(http_html_backup); state->http_payload_len = payload_len; state->http_payload_data = (char *) c_malloc(payload_len); if (state->http_payload_data == NULL) @@ -513,13 +448,25 @@ static int enduser_setup_http_load_payload(void) int offset = 0; c_memcpy(&(state->http_payload_data[offset]), &(http_header_200), LITLEN(http_header_200)); offset += LITLEN(http_header_200); - offset += c_sprintf(state->http_payload_data + offset, cl_hdr, LITLEN(http_html_backup)); - c_memcpy(&(state->http_payload_data[offset]), &(http_html_backup), LITLEN(http_html_backup)); - - return 1; + if (ce_len > 0) + offset += c_sprintf(state->http_payload_data + offset, http_html_gzip_contentencoding, ce_len); + c_memcpy(&(state->http_payload_data[offset]), &(cl_hdr), cl_len); + offset += cl_len; + c_memcpy(&(state->http_payload_data[offset]), &(http_html_backup), sizeof(http_html_backup)); + + return 1; } - int payload_len = LITLEN(http_header_200) + cl_len + file_len; + char magic[2]; + vfs_read(f, magic, 2); + + if (magic[0] == 0x1f && magic[1] == 0x8b) + { + ce_len = c_strlen(http_html_gzip_contentencoding); + ENDUSER_SETUP_DEBUG("Content is gzipped"); + } + + int payload_len = LITLEN(http_header_200) + cl_len + ce_len + file_len; state->http_payload_len = payload_len; state->http_payload_data = (char *) c_malloc(payload_len); if (state->http_payload_data == NULL) @@ -527,13 +474,19 @@ static int enduser_setup_http_load_payload(void) return 2; } + vfs_lseek(f, 0, VFS_SEEK_SET); + int offset = 0; + c_memcpy(&(state->http_payload_data[offset]), &(http_header_200), LITLEN(http_header_200)); offset += LITLEN(http_header_200); - offset += c_sprintf(state->http_payload_data + offset, cl_hdr, file_len); + if (ce_len > 0) + offset += c_sprintf(state->http_payload_data + offset, http_html_gzip_contentencoding, ce_len); + c_memcpy(&(state->http_payload_data[offset]), &(cl_hdr), cl_len); + offset += cl_len; vfs_read(f, &(state->http_payload_data[offset]), file_len); - vfs_close(f); - + vfs_close(f); + return 0; } @@ -605,6 +558,9 @@ static int enduser_setup_http_urldecode(char *dst, const char *src, int src_len, */ static void do_station_cfg (task_param_t param, uint8_t prio) { + ENDUSER_SETUP_DEBUG("do_station_cfg"); + + state->connecting = 1; struct station_config *cnf = (struct station_config *)param; (void)prio; @@ -635,6 +591,9 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data { ENDUSER_SETUP_DEBUG("enduser_setup_http_handle_credentials"); + state->success = 0; + state->lastStationStatus = 0; + char *name_str = (char *) ((uint32_t)strstr(&(data[6]), "wifi_ssid=")); char *pwd_str = (char *) ((uint32_t)strstr(&(data[6]), "wifi_password=")); if (name_str == NULL || pwd_str == NULL) @@ -663,7 +622,7 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data int err; err = enduser_setup_http_urldecode(cnf->ssid, name_str_start, name_str_len, sizeof(cnf->ssid)); err |= enduser_setup_http_urldecode(cnf->password, pwd_str_start, pwd_str_len, sizeof(cnf->password)); - if (err != 0) + if (err != 0 || c_strlen(cnf->ssid) == 0) { ENDUSER_SETUP_DEBUG("Unable to decode HTTP parameter to valid password or SSID"); return 1; @@ -694,7 +653,7 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data static int enduser_setup_http_serve_header(struct tcp_pcb *http_client, const char *header, uint32_t header_len) { ENDUSER_SETUP_DEBUG("enduser_setup_http_serve_header"); - + err_t err = tcp_write (http_client, header, header_len, TCP_WRITE_FLAG_COPY); if (err != ERR_OK) { @@ -708,8 +667,9 @@ static int enduser_setup_http_serve_header(struct tcp_pcb *http_client, const ch static err_t streamout_sent (void *arg, struct tcp_pcb *pcb, u16_t len) { - (void)len; + ENDUSER_SETUP_DEBUG("streamout_sent"); + (void)len; unsigned offs = (unsigned)arg; if (!state || !state->http_payload_data) @@ -754,6 +714,7 @@ static err_t streamout_sent (void *arg, struct tcp_pcb *pcb, u16_t len) static int enduser_setup_http_serve_html(struct tcp_pcb *http_client) { ENDUSER_SETUP_DEBUG("enduser_setup_http_serve_html"); + if (state->http_payload_data == NULL) { enduser_setup_http_load_payload(); @@ -774,7 +735,7 @@ static int enduser_setup_http_serve_html(struct tcp_pcb *http_client) } -static void serve_status(struct tcp_pcb *conn) +static void enduser_setup_serve_status(struct tcp_pcb *conn) { ENDUSER_SETUP_DEBUG("enduser_setup_serve_status"); @@ -782,11 +743,12 @@ static void serve_status(struct tcp_pcb *conn) "HTTP/1.1 200 OK\r\n" "Cache-control:no-cache\r\n" "Connection:close\r\n" + "Access-Control-Allow-Origin: *\r\n" "Content-type:text/plain\r\n" "Content-length: %d\r\n" "\r\n" - "%s%s"; - const char *state[] = + "%s"; + const char *states[] = { "Idle.", "Connecting to \"%s\".", @@ -796,8 +758,8 @@ static void serve_status(struct tcp_pcb *conn) "Connected to \"%s\" (%s)." }; - const size_t num_states = sizeof(state)/sizeof(state[0]); - uint8_t curr_state = wifi_station_get_connect_status (); + const size_t num_states = sizeof(states)/sizeof(states[0]); + uint8_t curr_state = state->lastStationStatus > 0 ? state->lastStationStatus : wifi_station_get_connect_status (); if (curr_state < num_states) { switch (curr_state) @@ -807,22 +769,21 @@ static void serve_status(struct tcp_pcb *conn) case STATION_NO_AP_FOUND: case STATION_GOT_IP: { - const char *s = state[curr_state]; + const char *s = states[curr_state]; struct station_config config; wifi_station_get_config(&config); config.ssid[31] = '\0'; - struct ip_info ip_info; + struct ip_info ip_info; - wifi_get_ip_info(STATION_IF , &ip_info); - - char ip_addr[16]; - ip_addr[0] = '\0'; - if (curr_state == STATION_GOT_IP) - { - c_sprintf (ip_addr, "%d.%d.%d.%d", IP2STR(&ip_info.ip.addr)); - } + wifi_get_ip_info(STATION_IF , &ip_info); + char ip_addr[16]; + ip_addr[0] = '\0'; + if (curr_state == STATION_GOT_IP) + { + c_sprintf (ip_addr, "%d.%d.%d.%d", IP2STR(&ip_info.ip.addr)); + } int state_len = c_strlen(s); int ip_len = c_strlen(ip_addr); @@ -844,7 +805,7 @@ static void serve_status(struct tcp_pcb *conn) /* Handle non-formatted strings */ default: { - const char *s = state[curr_state]; + const char *s = states[curr_state]; int status_len = c_strlen(s); int buf_len = sizeof(fmt) + status_len + 10; //10 = (9+1), 1 byte is '\0' and 9 are reserved for length field char buf[buf_len]; @@ -862,12 +823,76 @@ static void serve_status(struct tcp_pcb *conn) } } +static void enduser_setup_serve_status_as_json (struct tcp_pcb *http_client) +{ + ENDUSER_SETUP_DEBUG("enduser_setup_serve_status_as_json"); + + // If the station is currently shut down because of wi-fi channel issue, use the cached status + uint8_t curr_status = state->lastStationStatus > 0 ? state->lastStationStatus : wifi_station_get_connect_status (); + + char json_payload[64]; + c_sprintf(json_payload, "{\"deviceid\":\"%06X\", \"status\":%d}", system_get_chip_id(), curr_status); + + const char fmt[] = + "HTTP/1.1 200 OK\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Content-Type: application/json\r\n" + "Content-Length: %d\r\n" + "\r\n" + "%s"; + + int len = c_strlen(json_payload); + char buf[c_strlen(fmt) + NUMLEN(len) + len - 4]; + len = c_sprintf (buf, fmt, len, json_payload); + enduser_setup_http_serve_header (http_client, buf, len); +} + + +static void enduser_setup_handle_OPTIONS (struct tcp_pcb *http_client, char *data, unsigned short data_len) +{ + ENDUSER_SETUP_DEBUG("enduser_setup_handle_OPTIONS"); + + const char json[] = + "HTTP/1.1 200 OK\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Type: application/json\r\n" + "Content-Length: 0\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Access-Control-Allow-Methods: GET\r\n" + "Access-Control-Allow-Age: 300\r\n" + "\r\n"; + + const char others[] = + "HTTP/1.1 200 OK\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Length: 0\r\n" + "\r\n"; + + int type = 0; + + if (c_strncmp(data, "GET ", 4) == 0) + { + if (c_strncmp(data + 4, "/aplist", 7) == 0 || c_strncmp(data + 4, "/setwifi?", 9) == 0 || c_strncmp(data + 4, "/status.json", 12) == 0) + { + enduser_setup_http_serve_header (http_client, json, c_strlen(json)); + return; + } + } + enduser_setup_http_serve_header (http_client, others, c_strlen(others)); + return; +} /* --- WiFi AP scanning support -------------------------------------------- */ static void free_scan_listeners (void) { + ENDUSER_SETUP_DEBUG("free_scan_listeners"); + if (!state || !state->scan_listeners) { return; @@ -886,6 +911,8 @@ static void free_scan_listeners (void) static void remove_scan_listener (scan_listener_t *l) { + ENDUSER_SETUP_DEBUG("remove_scan_listener"); + if (state) { scan_listener_t **sl = &state->scan_listeners; @@ -921,6 +948,8 @@ static char *escape_ssid (char *dst, const char *src) static void notify_scan_listeners (const char *payload, size_t sz) { + ENDUSER_SETUP_DEBUG("notify_scan_listeners"); + if (!state) { return; @@ -944,6 +973,8 @@ static void notify_scan_listeners (const char *payload, size_t sz) static void on_scan_done (void *arg, STATUS status) { + ENDUSER_SETUP_DEBUG("on_scan_done"); + if (!state || !state->scan_listeners) { return; @@ -961,13 +992,14 @@ static void on_scan_done (void *arg, STATUS status) "HTTP/1.1 200 OK\r\n" "Connection:close\r\n" "Cache-control:no-cache\r\n" + "Access-Control-Allow-Origin: *\r\n" "Content-type:application/json\r\n" "Content-length:%4d\r\n" "\r\n"; const size_t hdr_sz = sizeof (header_fmt) +1 -1; /* +expand %4d, -\0 */ /* To be able to safely escape a pathological SSID, we need 2*32 bytes */ - const size_t max_entry_sz = sizeof("{\"ssid\":\"\",\"rssi\":},") + 2*32 + 6; + const size_t max_entry_sz = 27 + 2*32 + 6; // {"ssid":"","rssi":,"chan":} const size_t alloc_sz = hdr_sz + num_nets * max_entry_sz + 3; char *http = os_zalloc (alloc_sz); if (!http) @@ -996,6 +1028,12 @@ static void on_scan_done (void *arg, STATUS status) p += sizeof (entry_mid) -1; p += c_sprintf (p, "%d", wn->rssi); + + const char entry_chan[] = ",\"chan\":"; + strcpy (p, entry_chan); + p += sizeof (entry_chan) -1; + + p += c_sprintf (p, "%d", wn->channel); *p++ = '}'; } @@ -1006,6 +1044,8 @@ static void on_scan_done (void *arg, STATUS status) http[hdr_sz] = '['; /* Rewrite the \0 with the correct start of body */ notify_scan_listeners (http, hdr_sz + body_sz); + ENDUSER_SETUP_DEBUG(http + hdr_sz); + c_free (http); return; } @@ -1048,6 +1088,9 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s pbuf_free (p); err_t ret = ERR_OK; + + ENDUSER_SETUP_DEBUG(data); + if (c_strncmp(data, "GET ", 4) == 0) { if (c_strncmp(data + 4, "/ ", 2) == 0) @@ -1061,38 +1104,52 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s goto free_out; /* streaming now in progress */ } } - else if (c_strncmp(data + 4, "/aplist ", 8) == 0) + else if (c_strncmp(data + 4, "/aplist", 7) == 0) { - scan_listener_t *l = os_malloc (sizeof (scan_listener_t)); - if (!l) + // Don't do an AP Scan while station is trying to connect to Wi-Fi + if (state->connecting == 0) { - ENDUSER_SETUP_ERROR("out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); - } - - bool already = (state->scan_listeners != NULL); - - tcp_arg (http_client, l); - /* TODO: check if also need a tcp_err() cb, or if recv() is enough */ - l->conn = http_client; - l->next = state->scan_listeners; - state->scan_listeners = l; - - if (!already) - { - if (!wifi_station_scan(NULL, on_scan_done)) + scan_listener_t *l = os_malloc (sizeof (scan_listener_t)); + if (!l) { - enduser_setup_http_serve_header(http_client, http_header_500, LITLEN(http_header_500)); - deferred_close (l->conn); - l->conn = 0; - free_scan_listeners(); + ENDUSER_SETUP_ERROR("out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); } + + bool already = (state->scan_listeners != NULL); + + tcp_arg (http_client, l); + /* TODO: check if also need a tcp_err() cb, or if recv() is enough */ + l->conn = http_client; + l->next = state->scan_listeners; + state->scan_listeners = l; + + if (!already) + { + if (!wifi_station_scan(NULL, on_scan_done)) + { + enduser_setup_http_serve_header(http_client, http_header_500, LITLEN(http_header_500)); + deferred_close (l->conn); + l->conn = 0; + free_scan_listeners(); + } + } + goto free_out; /* request queued */ + } + else + { + // Return No Content status to the caller + enduser_setup_http_serve_header(http_client, http_header_204, LITLEN(http_header_204)); } - goto free_out; /* request queued */ } - else if (c_strncmp(data + 4, "/status ", 8) == 0) + else if (c_strncmp(data + 4, "/status.json", 12) == 0) { - serve_status(http_client); + enduser_setup_serve_status_as_json(http_client); + } + else if (c_strncmp(data + 4, "/status", 7) == 0) + { + enduser_setup_serve_status(http_client); } + else if (c_strncmp(data + 4, "/update?", 8) == 0) { switch (enduser_setup_http_handle_credentials(data, data_len)) @@ -1101,14 +1158,29 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s enduser_setup_http_serve_header(http_client, http_header_302, LITLEN(http_header_302)); break; case 1: - enduser_setup_http_serve_header(http_client, http_header_401, LITLEN(http_header_401)); + enduser_setup_http_serve_header(http_client, http_header_400, LITLEN(http_header_400)); break; default: ENDUSER_SETUP_ERROR("http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); break; } } - else if (c_strncmp(data + 4, "/generate_204 ", 14) == 0) + else if (c_strncmp(data + 4, "/setwifi?", 9) == 0) + { + switch (enduser_setup_http_handle_credentials(data, data_len)) + { + case 0: + enduser_setup_serve_status_as_json(http_client); + break; + case 1: + enduser_setup_http_serve_header(http_client, http_header_400, LITLEN(http_header_400)); + break; + default: + ENDUSER_SETUP_ERROR("http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); + break; + } + } + else if (c_strncmp(data + 4, "/generate_204", 13) == 0) { /* Convince Android devices that they have internet access to avoid pesky dialogues. */ enduser_setup_http_serve_header(http_client, http_header_204, LITLEN(http_header_204)); @@ -1116,13 +1188,16 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s else { ENDUSER_SETUP_DEBUG("serving 404"); - ENDUSER_SETUP_DEBUG(data + 4); enduser_setup_http_serve_header(http_client, http_header_404, LITLEN(http_header_404)); } } - else /* not GET */ + else if (c_strncmp(data, "OPTIONS ", 8) == 0) { - enduser_setup_http_serve_header(http_client, http_header_401, LITLEN(http_header_401)); + enduser_setup_handle_OPTIONS(http_client, data, data_len); + } + else /* not GET or OPTIONS */ + { + enduser_setup_http_serve_header(http_client, http_header_405, LITLEN(http_header_405)); } deferred_close (http_client); @@ -1135,6 +1210,7 @@ free_out: static err_t enduser_setup_http_connectcb(void *arg, struct tcp_pcb *pcb, err_t err) { ENDUSER_SETUP_DEBUG("enduser_setup_http_connectcb"); + if (!state) { ENDUSER_SETUP_DEBUG("connect callback but no state?!"); @@ -1208,7 +1284,7 @@ static void enduser_setup_http_stop(void) static void enduser_setup_ap_stop(void) { - ENDUSER_SETUP_DEBUG("enduser_setup_station_stop"); + ENDUSER_SETUP_DEBUG("enduser_setup_ap_stop"); wifi_set_opmode(~SOFTAP_MODE & wifi_get_opmode()); } @@ -1234,15 +1310,47 @@ static void enduser_setup_ap_start(void) cnf.ssid[ssid_name_len] = '_'; c_sprintf(cnf.ssid + ssid_name_len + 1, "%02X%02X%02X", mac[3], mac[4], mac[5]); cnf.ssid_len = ssid_name_len + 7; - cnf.channel = 1; + cnf.channel = state == NULL? 1 : state->softAPchannel; cnf.authmode = AUTH_OPEN; cnf.ssid_hidden = 0; cnf.max_connection = 5; cnf.beacon_interval = 100; wifi_set_opmode(STATIONAP_MODE); wifi_softap_set_config(&cnf); + +#if ENDUSER_SETUP_DEBUG_ENABLE + char debuginfo[100]; + c_sprintf(debuginfo, "SSID: %s, CHAN: %d", cnf.ssid, cnf.channel); + ENDUSER_SETUP_DEBUG(debuginfo); +#endif } +static void on_initial_scan_done (void *arg, STATUS status) +{ + ENDUSER_SETUP_DEBUG("on_initial_scan_done"); + + if (!state) + { + return; + } + + int8_t rssi = -100; + + if (status == OK) + { + for (struct bss_info *wn = arg; wn; wn = wn->next.stqe_next) + { + if (wn->rssi > rssi) + { + state->softAPchannel = wn->channel; + rssi = wn->rssi; + } + } + } + + enduser_setup_ap_start(); + enduser_setup_check_station_start(); +} static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned short recv_len) { @@ -1255,6 +1363,31 @@ static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned uint32_t dns_reply_static_len = (uint32_t) sizeof(dns_header) + (uint32_t) sizeof(dns_body) + 2 + 4; // dns_id=2bytes, ip=4bytes uint32_t dns_reply_len = dns_reply_static_len + qname_len; +#if ENDUSER_SETUP_DEBUG_ENABLE + char *qname = c_malloc(qname_len + 12); + if (qname != NULL) + { + c_sprintf(qname, "DNS QUERY = %s", &(recv_data[12])); + + uint32_t p; + int i, j; + + for(i=12;i<(int)strlen(qname);i++) + { + p=qname[i]; + for(j=0;j<(int)p;j++) + { + qname[i]=qname[i+1]; + i=i+1; + } + qname[i]='.'; + } + qname[i-1]='\0'; + ENDUSER_SETUP_DEBUG(qname); + c_free(qname); + } +#endif + uint8_t if_mode = wifi_get_opmode(); if ((if_mode & SOFTAP_MODE) == 0) { @@ -1305,6 +1438,14 @@ static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned { ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Can't execute transmission.", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_FATAL); } + else if (err == ESPCONN_MAXNUM) + { + ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Buffer full. Discarding...", ENDUSER_SETUP_ERR_MAX_NUMBER, ENDUSER_SETUP_ERR_NONFATAL); + } + else if (err == ESPCONN_IF) + { + ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Send UDP data failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); + } else if (err != 0) { ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. espconn_send failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); @@ -1408,6 +1549,7 @@ static void enduser_setup_dns_stop(void) static int enduser_setup_init(lua_State *L) { + // Note: Normal to not see this debug message on first invocation because debug callback is set below ENDUSER_SETUP_DEBUG("enduser_setup_init"); if (state != NULL) @@ -1452,12 +1594,19 @@ static int enduser_setup_init(lua_State *L) { lua_pushvalue (L, 3); state->lua_dbg_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); + ENDUSER_SETUP_DEBUG("enduser_setup_init: Debug callback has been defined"); } else { state->lua_dbg_cb_ref = LUA_NOREF; } + state->softAPchannel = 1; + state->success = 0; + state->callbackDone = 0; + state->lastStationStatus = 0; + state->connecting = 0; + return 0; } @@ -1475,6 +1624,7 @@ static int enduser_setup_manual(lua_State *L) static int enduser_setup_start(lua_State *L) { + // Note: The debug callback is set in enduser_setup_init. It's normal to not see this debug message on first invocation. ENDUSER_SETUP_DEBUG("enduser_setup_start"); if (!do_station_cfg_handle) @@ -1487,10 +1637,14 @@ static int enduser_setup_start(lua_State *L) goto failed; } - enduser_setup_check_station_start(); if (!manual) { - enduser_setup_ap_start(); + ENDUSER_SETUP_DEBUG("Performing AP Scan to identify likely AP's channel"); + wifi_station_scan(NULL, on_initial_scan_done); + } + else + { + enduser_setup_check_station_start(); } if(enduser_setup_dns_start()) @@ -1529,6 +1683,12 @@ static int enduser_setup_stop(lua_State* L) { enduser_setup_ap_stop(); } + if (state->success && !state->callbackDone) + { + wifi_set_opmode(STATION_MODE | wifi_get_opmode()); + wifi_station_connect(); + enduser_setup_connected_callback(); + } enduser_setup_dns_stop(); enduser_setup_http_stop(); enduser_setup_free(); diff --git a/app/modules/eus/enduser_setup.html b/app/modules/eus/enduser_setup.html new file mode 100644 index 00000000..b1e0de09 --- /dev/null +++ b/app/modules/eus/enduser_setup.html @@ -0,0 +1,315 @@ + + + + + + WiFi Login + + + + +
+
+
+
+

Connect device to your Wi-Fi

+ + + + + +
+
+

Success!

+
+

Your device has successfully connected to the Wi-Fi network.

+
+
+
+

Trying...

+ +
+
+

Updating Status...

+
+ + + + \ No newline at end of file diff --git a/app/modules/eus/enduser_setup.html.gz b/app/modules/eus/enduser_setup.html.gz new file mode 100644 index 0000000000000000000000000000000000000000..3abbaa13a7d8a321c7fa1825d827d2576d28d278 GIT binary patch literal 2393 zcmV-f38wZRiwFP!000021C?3*a@#f%|GPJH@30>CLb;)2eb{Nn64lA&k|sAv+Ns^? zbuyU_M1m$_3g7{tWnJobKP&-ShCqk4T2}!+6&zyJIS3cW6ufB)OU zed7})nR)tVv8I{;Cu^ES*Nko|A$0_);F@xsL>rdrBFX4AOX;v##K1VyjFdx_l9DEq z(Ku=ZX{JlM{EfY4@J?JZzBsl!c&7BWq@cGIO*FmH$Ei}d|IK&s1;4&o6KOckU_5V@ z70EKjFWdDk{s;fi{1_irBHKFFg0jm(pTT52{)gqxjq$^rtXa7|14XzRDk@oS4^jP) zl+Wi$tuP&vid3FYeCnqP_$%(J~(8a{5t%PEl2_uE9lqhRWoqSQB7rsL{n?zwgH9Jjn6ky;DiHpZ)3+rZRD32z=1Eos`CJKWIK z6*Hb!6(y4Jl%4?>oVKRPjLM-T8LL$g^_<Q)hFVIqx zKbFB^nx^+94BLcFoM@LO2en>jQc((N?43@hJ6sy(XGY^%aG@%aQr~Y3o^P|C#cm0H zgoaM9Hd!_Vl5*Xr#G%G1vHUL1LBa#D%&OMkS}J6$1lwJ!jH#+5TN9m9q*p;4I&Wu2 zQz41HTn?k2`G}3vPH4;~tCQ33!(4SZdzOo|R`I=!LR?b`UphfOhN)4CA&Ko8-QMZ* zYSiprvJf29`s#z?*|TSzShFzp$a(0xINW`oz zT03rrE@a=I+K71`%&Kl!w;ot4f&$(YgYJ9m{0NP9sqwZRpPW$sVJ1aoxOm*G@~Qit z+3(fW)G2VFb?EDs5wrnGOQP8|eV|gH{U!|?SH8U(f9fh#cjfW*ld5E@rwN7H!zQUk z6||zDP4(1CuhDu1DY&koA5x0V9q9i(!=7ZO7ayb}+TuxNlO^ zh-~7%d~$d*Jzb@96NHNFh-|Y#G6yr93*EEPWRD%S^VUAw&T9CSV%cuaYp|XQDGgg9 z^4zXT#WX2RSxNAIK~<8J7NX3kOrjOn)-q7%Kh{%_mky$ou*W&1C!^;UaFW~Ta5gUh;RTYhyrE# z^13XykXiuI46&mN3N~5}B_qU8n4B)l?#PPi@;b=4gleDdzlqBubvk5qtq@OpzQik=AR<_2|DfmD_Viu%n#A?c5hhRz~KxgXplmfYDJ5 zq;i|~K14@*OiGLDMOmUXq*OEpNz9)zRCjbcJ5?bzgFJ?5`*7x^Ra$Z}Ji7K7+mSxkLag8#si2%f@w zqKlCvJQM4INpAGxtBWWGPB-T|hH0NMX)VK;*`!8?`NT-dorvIY`nAIu9`-1=Mh;XA zSztjk*0s@!hi zXxsLlFWJZ*uq+5pd(qQ|tRf7pdyoo4{juBb=xtWgD1PA3jS03WKS=eOu##plgpCv? z@NHA$UN+-Fy|wXA7C)OD{&hb#wd-?Sx6`V5;y51_Vt*;x?mgc24lZ*r1ysH%^Nz)7A$&-c|4%}SU)4-6{>tU^cQAzowE?^1c z2P*>_<99aX?5R0{VzK&6QyukbL57~T>9LHIkb1BK8i`@$>j)>l)krGEUt2rDEO#;4Yx>T+uNIq_wOJ<6&-wJg)=ur;7Sgg0bnzNnlW5bm-V) zjDAmeO-gvhwcJLJ%8;`@>@`7LD3NJl${+^k4n^kfI(^;159hJq`m|RQTCOFgfE!I= zVR-aAqkYZ)+UufiEcgohKnL0|!?1Bv!M*Vc5=BLz|xgkRFW@nC=M^MCAc zo1U86K&c%>tx{*6-3lmIS5uFy)3k6@_SEB|`^|PdIt? http_html_backup.def + diff --git a/app/modules/file.c b/app/modules/file.c index 049f2a83..3e9de55d 100644 --- a/app/modules/file.c +++ b/app/modules/file.c @@ -2,15 +2,24 @@ #include "module.h" #include "lauxlib.h" +#include "lmem.h" #include "platform.h" #include "c_types.h" #include "vfs.h" #include "c_string.h" +#include + +#define FILE_READ_CHUNK 1024 + static int file_fd = 0; +static int file_fd_ref = LUA_NOREF; static int rtc_cb_ref = LUA_NOREF; +typedef struct _file_fd_ud { + int fd; +} file_fd_ud; static void table2tm( lua_State *L, vfs_time *tm ) { @@ -91,13 +100,48 @@ static int file_on(lua_State *L) // Lua: close() static int file_close( lua_State* L ) { - if(file_fd){ - vfs_close(file_fd); - file_fd = 0; + int need_pop = FALSE; + file_fd_ud *ud; + + if (lua_type( L, 1 ) != LUA_TUSERDATA) { + // fall back to last opened file + if (file_fd_ref != LUA_NOREF) { + lua_rawgeti( L, LUA_REGISTRYINDEX, file_fd_ref ); + // top of stack is now default file descriptor + ud = (file_fd_ud *)luaL_checkudata(L, -1, "file.obj"); + lua_pop( L, 1 ); + } else { + // no default file currently opened + return 0; + } + } else { + ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj"); + } + + // unref default file descriptor + luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref ); + file_fd_ref = LUA_NOREF; + + if(ud->fd){ + vfs_close(ud->fd); + // mark as closed + ud->fd = 0; } return 0; } +static int file_obj_free( lua_State *L ) +{ + file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj"); + if (ud->fd) { + // close file if it's still open + vfs_close(ud->fd); + ud->fd = 0; + } + + return 0; +} + // Lua: format() static int file_format( lua_State* L ) { @@ -130,10 +174,10 @@ static int file_fscfg (lua_State *L) static int file_open( lua_State* L ) { size_t len; - if(file_fd){ - vfs_close(file_fd); - file_fd = 0; - } + + // unref last file descriptor to allow gc'ing if not kept by user script + luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref ); + file_fd_ref = LUA_NOREF; const char *fname = luaL_checklstring( L, 1, &len ); const char *basename = vfs_basename( fname ); @@ -146,7 +190,14 @@ static int file_open( lua_State* L ) if(!file_fd){ lua_pushnil(L); } else { - lua_pushboolean(L, 1); + file_fd_ud *ud = (file_fd_ud *) lua_newuserdata( L, sizeof( file_fd_ud ) ); + ud->fd = file_fd; + luaL_getmetatable( L, "file.obj" ); + lua_setmetatable( L, -2 ); + + // store reference to opened file + lua_pushvalue( L, -1 ); + file_fd_ref = luaL_ref( L, LUA_REGISTRYINDEX ); } return 1; } @@ -170,19 +221,36 @@ static int file_list( lua_State* L ) return 0; } -static int file_seek (lua_State *L) +static int get_file_obj( lua_State *L, int *argpos ) { + if (lua_type( L, 1 ) == LUA_TUSERDATA) { + file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj"); + *argpos = 2; + return ud->fd; + } else { + *argpos = 1; + return file_fd; + } +} + +#define GET_FILE_OBJ int argpos; \ + int fd = get_file_obj( L, &argpos ); + +static int file_seek (lua_State *L) +{ + GET_FILE_OBJ; + static const int mode[] = {VFS_SEEK_SET, VFS_SEEK_CUR, VFS_SEEK_END}; static const char *const modenames[] = {"set", "cur", "end", NULL}; - if(!file_fd) + if(!fd) return luaL_error(L, "open a file first"); - int op = luaL_checkoption(L, 1, "cur", modenames); - long offset = luaL_optlong(L, 2, 0); - op = vfs_lseek(file_fd, offset, mode[op]); + int op = luaL_checkoption(L, argpos, "cur", modenames); + long offset = luaL_optlong(L, ++argpos, 0); + op = vfs_lseek(fd, offset, mode[op]); if (op < 0) lua_pushnil(L); /* error */ else - lua_pushinteger(L, vfs_tell(file_fd)); + lua_pushinteger(L, vfs_tell(fd)); return 1; } @@ -210,7 +278,6 @@ static int file_remove( lua_State* L ) const char *fname = luaL_checklstring( L, 1, &len ); const char *basename = vfs_basename( fname ); luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid"); - file_close(L); vfs_remove((char *)fname); return 0; } @@ -218,9 +285,11 @@ static int file_remove( lua_State* L ) // Lua: flush() static int file_flush( lua_State* L ) { - if(!file_fd) + GET_FILE_OBJ; + + if(!fd) return luaL_error(L, "open a file first"); - if(vfs_flush(file_fd) == 0) + if(vfs_flush(fd) == 0) lua_pushboolean(L, 1); else lua_pushnil(L); @@ -231,10 +300,6 @@ static int file_flush( lua_State* L ) static int file_rename( lua_State* L ) { size_t len; - if(file_fd){ - vfs_close(file_fd); - file_fd = 0; - } const char *oldname = luaL_checklstring( L, 1, &len ); const char *basename = vfs_basename( oldname ); @@ -253,38 +318,64 @@ static int file_rename( lua_State* L ) } // g_read() -static int file_g_read( lua_State* L, int n, int16_t end_char ) +static int file_g_read( lua_State* L, int n, int16_t end_char, int fd ) { - if(n <= 0 || n > LUAL_BUFFERSIZE) - n = LUAL_BUFFERSIZE; - if(end_char < 0 || end_char >255) - end_char = EOF; - - luaL_Buffer b; - if(!file_fd) - return luaL_error(L, "open a file first"); - - luaL_buffinit(L, &b); - char *p = luaL_prepbuffer(&b); - int i; - - n = vfs_read(file_fd, p, n); - for (i = 0; i < n; ++i) - if (p[i] == end_char) - { - ++i; - break; - } - - if(i==0){ - luaL_pushresult(&b); /* close buffer */ - return (lua_objlen(L, -1) > 0); /* check whether read something */ + static char *heap_mem = NULL; + // free leftover memory + if (heap_mem) { + luaM_free(L, heap_mem); + heap_mem = NULL; } - vfs_lseek(file_fd, -(n - i), VFS_SEEK_CUR); - luaL_addsize(&b, i); - luaL_pushresult(&b); /* close buffer */ - return 1; /* read at least an `eol' */ + if(n <= 0) + n = FILE_READ_CHUNK; + + if(end_char < 0 || end_char >255) + end_char = EOF; + + + if(!fd) + return luaL_error(L, "open a file first"); + + char *p; + int i; + + if (n > LUAL_BUFFERSIZE) { + // get buffer from heap + p = heap_mem = luaM_malloc(L, n); + } else { + // small chunks go onto the stack + p = alloca(n); + } + + n = vfs_read(fd, p, n); + // bypass search if no end character provided + if (n > 0 && end_char != EOF) { + for (i = 0; i < n; ++i) + if (p[i] == end_char) + { + ++i; + break; + } + } else { + i = n; + } + + if (i == 0 || n == VFS_RES_ERR) { + if (heap_mem) { + luaM_free(L, heap_mem); + heap_mem = NULL; + } + return 0; + } + + vfs_lseek(fd, -(n - i), VFS_SEEK_CUR); + lua_pushlstring(L, p, i); + if (heap_mem) { + luaM_free(L, heap_mem); + heap_mem = NULL; + } + return 1; } // Lua: read() @@ -293,42 +384,46 @@ static int file_g_read( lua_State* L, int n, int16_t end_char ) // file.read('q') will read until 'q' or EOF is reached. static int file_read( lua_State* L ) { - unsigned need_len = LUAL_BUFFERSIZE; + unsigned need_len = FILE_READ_CHUNK; int16_t end_char = EOF; size_t el; - if( lua_type( L, 1 ) == LUA_TNUMBER ) + + GET_FILE_OBJ; + + if( lua_type( L, argpos ) == LUA_TNUMBER ) { - need_len = ( unsigned )luaL_checkinteger( L, 1 ); - if( need_len > LUAL_BUFFERSIZE ){ - need_len = LUAL_BUFFERSIZE; - } + need_len = ( unsigned )luaL_checkinteger( L, argpos ); } - else if(lua_isstring(L, 1)) + else if(lua_isstring(L, argpos)) { - const char *end = luaL_checklstring( L, 1, &el ); + const char *end = luaL_checklstring( L, argpos, &el ); if(el!=1){ return luaL_error( L, "wrong arg range" ); } end_char = (int16_t)end[0]; } - return file_g_read(L, need_len, end_char); + return file_g_read(L, need_len, end_char, fd); } // Lua: readline() static int file_readline( lua_State* L ) { - return file_g_read(L, LUAL_BUFFERSIZE, '\n'); + GET_FILE_OBJ; + + return file_g_read(L, LUAL_BUFFERSIZE, '\n', fd); } // Lua: write("string") static int file_write( lua_State* L ) { - if(!file_fd) + GET_FILE_OBJ; + + if(!fd) return luaL_error(L, "open a file first"); size_t l, rl; - const char *s = luaL_checklstring(L, 1, &l); - rl = vfs_write(file_fd, s, l); + const char *s = luaL_checklstring(L, argpos, &l); + rl = vfs_write(fd, s, l); if(rl==l) lua_pushboolean(L, 1); else @@ -339,13 +434,15 @@ static int file_write( lua_State* L ) // Lua: writeline("string") static int file_writeline( lua_State* L ) { - if(!file_fd) + GET_FILE_OBJ; + + if(!fd) return luaL_error(L, "open a file first"); size_t l, rl; - const char *s = luaL_checklstring(L, 1, &l); - rl = vfs_write(file_fd, s, l); + const char *s = luaL_checklstring(L, argpos, &l); + rl = vfs_write(fd, s, l); if(rl==l){ - rl = vfs_write(file_fd, "\n", 1); + rl = vfs_write(fd, "\n", 1); if(rl==1) lua_pushboolean(L, 1); else @@ -420,6 +517,20 @@ static int file_vol_umount( lua_State *L ) } +static const LUA_REG_TYPE file_obj_map[] = +{ + { LSTRKEY( "close" ), LFUNCVAL( file_close ) }, + { LSTRKEY( "read" ), LFUNCVAL( file_read ) }, + { LSTRKEY( "readline" ), LFUNCVAL( file_readline ) }, + { LSTRKEY( "write" ), LFUNCVAL( file_write ) }, + { LSTRKEY( "writeline" ), LFUNCVAL( file_writeline ) }, + { LSTRKEY( "seek" ), LFUNCVAL( file_seek ) }, + { LSTRKEY( "flush" ), LFUNCVAL( file_flush ) }, + { LSTRKEY( "__gc" ), LFUNCVAL( file_obj_free ) }, + { LSTRKEY( "__index" ), LROVAL( file_obj_map ) }, + { LNILKEY, LNILVAL } +}; + static const LUA_REG_TYPE file_vol_map[] = { { LSTRKEY( "umount" ), LFUNCVAL( file_vol_umount )}, @@ -459,6 +570,7 @@ static const LUA_REG_TYPE file_map[] = { int luaopen_file( lua_State *L ) { luaL_rometatable( L, "file.vol", (void *)file_vol_map ); + luaL_rometatable( L, "file.obj", (void *)file_obj_map ); return 0; } diff --git a/app/modules/gpio.c b/app/modules/gpio.c index 75634429..de2fbd47 100644 --- a/app/modules/gpio.c +++ b/app/modules/gpio.c @@ -190,19 +190,20 @@ static void seroutasync_done (task_param_t arg) { lua_State *L = lua_getstate(); luaM_freearray(L, serout.delay_table, serout.tablelen, uint32); - if (serout.lua_done_ref != LUA_REFNIL) { // we're here so serout.lua_done_ref != LUA_NOREF + serout.delay_table = NULL; + if (serout.lua_done_ref != LUA_NOREF) { lua_rawgeti (L, LUA_REGISTRYINDEX, serout.lua_done_ref); + luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref); + serout.lua_done_ref = LUA_NOREF; if (lua_pcall(L, 0, 0, 0)) { // Uncaught Error. Print instead of sudden reset luaL_error(L, "error: %s", lua_tostring(L, -1)); } - luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref); } } static void ICACHE_RAM_ATTR seroutasync_cb(os_param_t p) { (void) p; - NODE_DBG("%d\t%d\t%d\t%d\t%d\t%d\t%d\n", serout.repeats, serout.index, serout.level, serout.pin, serout.tablelen, serout.delay_table[serout.index], system_get_time()); // timing is delayed for short timings when debug output is enabled if (serout.index < serout.tablelen) { GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[serout.pin]), serout.level); serout.level = serout.level==LOW ? HIGH : LOW; @@ -220,17 +221,25 @@ static int lgpio_serout( lua_State* L ) serout.pin = luaL_checkinteger( L, 1 ); serout.level = luaL_checkinteger( L, 2 ); serout.repeats = luaL_optint( L, 4, 1 )-1; + luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref); + uint8_t is_async = FALSE; if (!lua_isnoneornil(L, 5)) { if (lua_isnumber(L, 5)) { - serout.lua_done_ref = LUA_REFNIL; + serout.lua_done_ref = LUA_NOREF; } else { lua_pushvalue(L, 5); serout.lua_done_ref = luaL_ref(L, LUA_REGISTRYINDEX); } + is_async = TRUE; } else { serout.lua_done_ref = LUA_NOREF; } + if (serout.delay_table) { + luaM_freearray(L, serout.delay_table, serout.tablelen, uint32); + serout.delay_table = NULL; + } + luaL_argcheck(L, platform_gpio_exists(serout.pin), 1, "Invalid pin"); luaL_argcheck(L, serout.level==HIGH || serout.level==LOW, 2, "Wrong level type" ); luaL_argcheck(L, lua_istable( L, 3 ) && @@ -244,7 +253,7 @@ static int lgpio_serout( lua_State* L ) lua_pop( L, 1 ); } - if (serout.lua_done_ref != LUA_NOREF) { // async version for duration above 15 mSec + if (is_async) { // async version for duration above 15 mSec if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, TRUE)) { // Failed to init the timer luaL_error(L, "Unable to initialize timer"); @@ -296,6 +305,7 @@ int luaopen_gpio( lua_State *L ) { platform_gpio_init(task_get_id(gpio_intr_callback_task)); #endif serout.done_taskid = task_get_id((task_callback_t) seroutasync_done); + serout.lua_done_ref = LUA_NOREF; return 0; } diff --git a/app/modules/http.c b/app/modules/http.c index 2b7ee5e6..dc4c9b4e 100644 --- a/app/modules/http.c +++ b/app/modules/http.c @@ -14,13 +14,13 @@ static int http_callback_registry = LUA_NOREF; static void http_callback( char * response, int http_status, char * full_response ) { #if defined(HTTPCLIENT_DEBUG_ON) - c_printf( "http_status=%d\n", http_status ); + dbg_printf( "http_status=%d\n", http_status ); if ( http_status != HTTP_STATUS_GENERIC_ERROR ) { if (full_response != NULL) { - c_printf( "strlen(full_response)=%d\n", strlen( full_response ) ); + dbg_printf( "strlen(full_response)=%d\n", strlen( full_response ) ); } - c_printf( "response=%s\n", response ); + dbg_printf( "response=%s\n", response ); } #endif if (http_callback_registry != LUA_NOREF) diff --git a/app/modules/mqtt.c b/app/modules/mqtt.c index ef604ac7..c9813af2 100644 --- a/app/modules/mqtt.c +++ b/app/modules/mqtt.c @@ -72,6 +72,7 @@ typedef struct lmqtt_userdata uint8_t secure; #endif bool connected; // indicate socket connected, not mqtt prot connected. + bool keepalive_sent; ETSTimer mqttTimer; tConnState connState; }lmqtt_userdata; @@ -212,9 +213,9 @@ static void mqtt_connack_fail(lmqtt_userdata * mud, int reason_code) { return; } - - lua_State *L = lua_getstate(); - + + lua_State *L = lua_getstate(); + lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_connect_fail_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata(client) to callback func in lua lua_pushinteger(L, reason_code); @@ -241,12 +242,12 @@ static sint8 mqtt_send_if_possible(struct espconn *pesp_conn) #ifdef CLIENT_SSL_ENABLE if( mud->secure ) { - espconn_status = espconn_secure_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length ); + espconn_status = espconn_secure_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length ); } else #endif { - espconn_status = espconn_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length ); + espconn_status = espconn_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length ); } mud->keep_alive_tick = 0; } @@ -275,7 +276,7 @@ static void mqtt_socket_received(void *arg, char *pdata, unsigned short len) READPACKET: if(length > MQTT_BUF_SIZE || length <= 0) - return; + return; // c_memcpy(in_buffer, pdata, length); uint8_t temp_buffer[MQTT_BUF_SIZE]; @@ -287,7 +288,7 @@ READPACKET: case MQTT_CONNECT_SENDING: case MQTT_CONNECT_SENT: mud->event_timeout = 0; - + if(mqtt_get_type(in_buffer) != MQTT_MSG_TYPE_CONNACK){ NODE_DBG("MQTT: Invalid packet\r\n"); mud->connState = MQTT_INIT; @@ -301,16 +302,16 @@ READPACKET: { espconn_disconnect(pesp_conn); } - + mqtt_connack_fail(mud, MQTT_CONN_FAIL_NOT_A_CONNACK_MSG); - + break; - + } else if (mqtt_get_connect_ret_code(in_buffer) != MQTT_CONNACK_ACCEPTED) { NODE_DBG("MQTT: CONNACK REFUSED (CODE: %d)\n", mqtt_get_connect_ret_code(in_buffer)); - + mud->connState = MQTT_INIT; - + #ifdef CLIENT_SSL_ENABLE if(mud->secure) { @@ -320,12 +321,12 @@ READPACKET: #endif { espconn_disconnect(pesp_conn); - } - + } + mqtt_connack_fail(mud, mqtt_get_connect_ret_code(in_buffer)); - + break; - + } else { mud->connState = MQTT_DATA; NODE_DBG("MQTT: Connected\r\n"); @@ -454,6 +455,7 @@ READPACKET: break; case MQTT_MSG_TYPE_PINGRESP: // Ignore + mud->keepalive_sent = 0; NODE_DBG("MQTT: PINGRESP received\r\n"); break; } @@ -585,7 +587,7 @@ void mqtt_socket_timer(void *arg) NODE_DBG("timer, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q))); if(mud->event_timeout > 0){ NODE_DBG("event_timeout: %d.\n", mud->event_timeout); - mud->event_timeout --; + mud->event_timeout --; if(mud->event_timeout > 0){ return; } else { @@ -605,7 +607,7 @@ void mqtt_socket_timer(void *arg) NODE_DBG("sSend MQTT_CONNECT failed.\n"); mud->connState = MQTT_INIT; mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_SENDING); - + #ifdef CLIENT_SSL_ENABLE if(mud->secure) { @@ -620,7 +622,7 @@ void mqtt_socket_timer(void *arg) } else if(mud->connState == MQTT_CONNECT_SENT) { // wait for CONACK time out. NODE_DBG("MQTT_CONNECT timeout.\n"); mud->connState = MQTT_INIT; - + #ifdef CLIENT_SSL_ENABLE if(mud->secure) { @@ -630,7 +632,7 @@ void mqtt_socket_timer(void *arg) #endif { espconn_disconnect(mud->pesp_conn); - } + } mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_RECEIVING); } else if(mud->connState == MQTT_DATA){ msg_queue_t *pending_msg = msg_peek(&(mud->mqtt_state.pending_msg_q)); @@ -640,13 +642,20 @@ void mqtt_socket_timer(void *arg) // no queued event. mud->keep_alive_tick ++; if(mud->keep_alive_tick > mud->mqtt_state.connect_info->keepalive){ - uint8_t temp_buffer[MQTT_BUF_SIZE]; - mqtt_msg_init(&mud->mqtt_state.mqtt_connection, temp_buffer, MQTT_BUF_SIZE); - NODE_DBG("\r\nMQTT: Send keepalive packet\r\n"); - mqtt_message_t* temp_msg = mqtt_msg_pingreq(&mud->mqtt_state.mqtt_connection); - msg_queue_t *node = msg_enqueue( &(mud->mqtt_state.pending_msg_q), temp_msg, - 0, MQTT_MSG_TYPE_PINGREQ, (int)mqtt_get_qos(temp_msg->data) ); - mqtt_send_if_possible(mud->pesp_conn); + if (mud->keepalive_sent) { + // Oh dear -- keepalive timer expired and still no ack of previous message + mqtt_socket_reconnected(mud->pesp_conn, 0); + } else { + uint8_t temp_buffer[MQTT_BUF_SIZE]; + mqtt_msg_init(&mud->mqtt_state.mqtt_connection, temp_buffer, MQTT_BUF_SIZE); + NODE_DBG("\r\nMQTT: Send keepalive packet\r\n"); + mqtt_message_t* temp_msg = mqtt_msg_pingreq(&mud->mqtt_state.mqtt_connection); + msg_queue_t *node = msg_enqueue( &(mud->mqtt_state.pending_msg_q), temp_msg, + 0, MQTT_MSG_TYPE_PINGREQ, (int)mqtt_get_qos(temp_msg->data) ); + mud->keepalive_sent = 1; + mud->keep_alive_tick = 0; // Need to reset to zero in case flow control stopped. + mqtt_send_if_possible(mud->pesp_conn); + } } } } @@ -675,6 +684,7 @@ static int mqtt_socket_client( lua_State* L ) // create a object mud = (lmqtt_userdata *)lua_newuserdata(L, sizeof(lmqtt_userdata)); + c_memset(mud, 0, sizeof(*mud)); // pre-initialize it, in case of errors mud->self_ref = LUA_NOREF; mud->cb_connect_ref = LUA_NOREF; @@ -685,18 +695,8 @@ static int mqtt_socket_client( lua_State* L ) mud->cb_suback_ref = LUA_NOREF; mud->cb_unsuback_ref = LUA_NOREF; mud->cb_puback_ref = LUA_NOREF; - mud->pesp_conn = NULL; -#ifdef CLIENT_SSL_ENABLE - mud->secure = 0; -#endif - mud->keep_alive_tick = 0; - mud->event_timeout = 0; mud->connState = MQTT_INIT; - mud->connected = false; - c_memset(&mud->mqttTimer, 0, sizeof(ETSTimer)); - c_memset(&mud->mqtt_state, 0, sizeof(mqtt_state_t)); - c_memset(&mud->connect_info, 0, sizeof(mqtt_connect_info_t)); // set its metatable luaL_getmetatable(L, "mqtt.socket"); @@ -761,7 +761,7 @@ static int mqtt_socket_client( lua_State* L ) c_free(mud->connect_info.password); mud->connect_info.password = NULL; } - return luaL_error(L, "not enough memory"); + return luaL_error(L, "not enough memory"); } c_memcpy(mud->connect_info.client_id, clientId, idl); @@ -819,8 +819,8 @@ static int mqtt_delete( lua_State* L ) // ---- alloc-ed in mqtt_socket_lwt() if(mud->connect_info.will_topic){ - c_free(mud->connect_info.will_topic); - mud->connect_info.will_topic = NULL; + c_free(mud->connect_info.will_topic); + mud->connect_info.will_topic = NULL; } if(mud->connect_info.will_message){ @@ -926,7 +926,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) if( dns_reconn_count >= 5 ){ NODE_ERR( "DNS Fail!\n" ); // Note: should delete the pesp_conn or unref self_ref here. - + struct espconn *pesp_conn = arg; if(pesp_conn != NULL) { lmqtt_userdata *mud = (lmqtt_userdata *)pesp_conn->reverse; @@ -934,7 +934,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) mqtt_connack_fail(mud, MQTT_CONN_FAIL_DNS); } } - + mqtt_socket_disconnected(arg); // although not connected, but fire disconnect callback to release every thing. return -1; } @@ -1070,7 +1070,7 @@ static int mqtt_socket_connect( lua_State* L ) } stack++; - + // call back function when a connection fails if ((stack<=top) && (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION)){ lua_pushvalue(L, stack); // copy argument (func) to the top of stack @@ -1135,7 +1135,7 @@ static int mqtt_socket_close( lua_State* L ) // Send disconnect message mqtt_message_t* temp_msg = mqtt_msg_disconnect(&mud->mqtt_state.mqtt_connection); NODE_DBG("Send MQTT disconnect infomation, data len: %d, d[0]=%d \r\n", temp_msg->length, temp_msg->data[0]); - + #ifdef CLIENT_SSL_ENABLE if(mud->secure) { espconn_status = espconn_secure_send(mud->pesp_conn, temp_msg->data, temp_msg->length); @@ -1150,7 +1150,7 @@ static int mqtt_socket_close( lua_State* L ) } } mud->connected = 0; - + while (mud->mqtt_state.pending_msg_q) { msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); } @@ -1595,7 +1595,7 @@ static const LUA_REG_TYPE mqtt_socket_map[] = { { LNILKEY, LNILVAL } }; - + static const LUA_REG_TYPE mqtt_map[] = { { LSTRKEY( "Client" ), LFUNCVAL( mqtt_socket_client ) }, @@ -1609,7 +1609,7 @@ static const LUA_REG_TYPE mqtt_map[] = { { LSTRKEY( "CONNACK_REFUSED_ID_REJECTED" ), LNUMVAL( MQTT_CONNACK_REFUSED_ID_REJECTED ) }, { LSTRKEY( "CONNACK_REFUSED_SERVER_UNAVAILABLE" ), LNUMVAL( MQTT_CONNACK_REFUSED_SERVER_UNAVAILABLE ) }, { LSTRKEY( "CONNACK_REFUSED_BAD_USER_OR_PASS" ), LNUMVAL( MQTT_CONNACK_REFUSED_BAD_USER_OR_PASS ) }, - { LSTRKEY( "CONNACK_REFUSED_NOT_AUTHORIZED" ), LNUMVAL( MQTT_CONNACK_REFUSED_NOT_AUTHORIZED ) }, + { LSTRKEY( "CONNACK_REFUSED_NOT_AUTHORIZED" ), LNUMVAL( MQTT_CONNACK_REFUSED_NOT_AUTHORIZED ) }, { LSTRKEY( "__metatable" ), LROVAL( mqtt_map ) }, { LNILKEY, LNILVAL } diff --git a/app/modules/ow.c b/app/modules/ow.c index 6ce773bf..9fec9bc9 100644 --- a/app/modules/ow.c +++ b/app/modules/ow.c @@ -134,6 +134,8 @@ static int ow_read_bytes( lua_State *L ) if( size == 0 ) return 0; + luaL_argcheck(L, size <= LUAL_BUFFERSIZE, 2, "Attempt to read too many characters"); + luaL_Buffer b; luaL_buffinit( L, &b ); char *p = luaL_prepbuffer(&b); diff --git a/app/modules/sntp.c b/app/modules/sntp.c index 14695395..af65d4ab 100644 --- a/app/modules/sntp.c +++ b/app/modules/sntp.c @@ -53,7 +53,7 @@ #define MAX_ATTEMPTS 5 #if 0 -# define sntp_dbg(...) c_printf(__VA_ARGS__) +# define sntp_dbg(...) dbg_printf(__VA_ARGS__) #else # define sntp_dbg(...) #endif diff --git a/app/modules/somfy.c b/app/modules/somfy.c new file mode 100644 index 00000000..4266aa6e --- /dev/null +++ b/app/modules/somfy.c @@ -0,0 +1,248 @@ +// *************************************************************************** +// Somfy module for ESP8266 with NodeMCU +// +// Written by Lukas Voborsky, @voborsky +// based on https://github.com/Nickduino/Somfy_Remote +// Somfy protocol description: https://pushstack.wordpress.com/somfy-rts-protocol/ +// and discussion: https://forum.arduino.cc/index.php?topic=208346.0 +// +// MIT license, http://opensource.org/licenses/MIT +// *************************************************************************** + +//#define NODE_DEBUG + +#include "os_type.h" +#include "osapi.h" +#include "sections.h" + +#include "module.h" +#include "lauxlib.h" +#include "lmem.h" +#include "platform.h" +#include "hw_timer.h" +#include "user_interface.h" + +#define SYMBOL 640 // symbol width in microseconds +#define SOMFY_UP 0x2 +#define SOMFY_STOP 0x1 +#define SOMFY_DOWN 0x4 +#define SOMFY_PROG 0x8 + +#define DIRECT_WRITE_LOW(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 0)) +#define DIRECT_WRITE_HIGH(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 1)) + +static const os_param_t TIMER_OWNER = 0x736f6d66; // "somf" +static task_handle_t done_taskid; + +static uint8_t pin; +static uint8_t frame[7]; +static uint8_t sync; +static uint8_t repeat; + +//static uint32_t delay[10] = {9415, 89565, 4*SYMBOL, 4*SYMBOL, 4*SYMBOL, 4550, SYMBOL, SYMBOL, SYMBOL, 30415}; // in us +// the `delay` array of constants must be in RAM as it is accessed from the timer interrupt +static const RAM_CONST_SECTION_ATTR uint32_t delay[10] = {US_TO_RTC_TIMER_TICKS(9415), US_TO_RTC_TIMER_TICKS(89565), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4550), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(30415)}; // in ticks (no need to recalculate) + +static uint8_t repeatindex; +static uint8_t signalindex; +static uint8_t subindex; +static uint8_t bitcondition; + +int lua_done_ref; // callback when transmission is done + +void buildFrame(uint8_t *frame, uint64_t remote, uint8_t button, uint16_t code) { + // NODE_DBG("remote: %x\n", remote); + // NODE_DBG("button: %x\n", button); + // NODE_DBG("rolling code: %x\n", code); + frame[0] = 0xA7; // Encryption key. Doesn't matter much + frame[1] = button << 4; // Which button did you press? The 4 LSB will be the checksum + frame[2] = code >> 8; // Rolling code (big endian) + frame[3] = code; // Rolling code + frame[4] = remote >> 16; // Remote address + frame[5] = remote >> 8; // Remote address + frame[6] = remote; // Remote address + // frame[7] = 0x80; + // frame[8] = 0x0; + // frame[9] = 0x0; + + // NODE_DBG("Frame:\t\t\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]); + // Checksum calculation: a XOR of all the nibbles + uint8_t checksum = 0; + for(uint8_t i = 0; i < 7; i++) { + checksum = checksum ^ frame[i] ^ (frame[i] >> 4); + } + checksum &= 0b1111; // We keep the last 4 bits only + + //Checksum integration + frame[1] |= checksum; // If a XOR of all the nibbles is equal to 0, the blinds will consider the checksum ok. + // NODE_DBG("With checksum:\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]); + + // Obfuscation: a XOR of all the uint8_ts + for(uint8_t i = 1; i < 7; i++) { + frame[i] ^= frame[i-1]; + } + // NODE_DBG("Obfuscated:\t\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]); +} + +static void somfy_transmissionDone (task_param_t arg) +{ + lua_State *L = lua_getstate(); + lua_rawgeti (L, LUA_REGISTRYINDEX, lua_done_ref); + luaL_unref (L, LUA_REGISTRYINDEX, lua_done_ref); + lua_done_ref = LUA_NOREF; + lua_call (L, 0, 0); +} + +static void ICACHE_RAM_ATTR sendCommand(os_param_t p) { + (void) p; + // NODE_DBG("%d\t%d\n", signalindex, subindex); + switch (signalindex) { + case 0: + subindex = 0; + if(sync == 2) { // Only with the first frame. + //Wake-up pulse & Silence + DIRECT_WRITE_HIGH(pin); + signalindex++; + // delayMicroseconds(9415); + break; + } else { + signalindex++; signalindex++; //no break means: go directly to step 3 + } + case 1: + //Wake-up pulse & Silence + DIRECT_WRITE_LOW(pin); + signalindex++; + // delayMicroseconds(89565); + break; + case 2: + signalindex++; + // no break means go directly to step 3 + // a "useless" step to allow repeating the hardware sync w/o the silence after wake-up pulse + case 3: + // Hardware sync: two sync for the first frame, seven for the following ones. + DIRECT_WRITE_HIGH(pin); + signalindex++; + // delayMicroseconds(4*SYMBOL); + break; + case 4: + DIRECT_WRITE_LOW(pin); + subindex++; + if (subindex < sync) {signalindex--;} else {signalindex++;} + // delayMicroseconds(4*SYMBOL); + break; + case 5: + // Software sync + DIRECT_WRITE_HIGH(pin); + signalindex++; + // delayMicroseconds(4550); + break; + case 6: + DIRECT_WRITE_LOW(pin); + signalindex++; + subindex=0; + // delayMicroseconds(SYMBOL); + break; + case 7: + //Data: bits are sent one by one, starting with the MSB. + bitcondition = ((frame[subindex/8] >> (7 - (subindex%8))) & 1) == 1; + if(bitcondition) { + DIRECT_WRITE_LOW(pin); + } + else { + DIRECT_WRITE_HIGH(pin); + } + signalindex++; + // delayMicroseconds(SYMBOL); + break; + case 8: + //Data: bits are sent one by one, starting with the MSB. + if(bitcondition) { + DIRECT_WRITE_HIGH(pin); + } + else { + DIRECT_WRITE_LOW(pin); + } + + if (subindex<56) { + subindex++; + signalindex--; + } + else { + signalindex++; + } + // delayMicroseconds(SYMBOL); + break; + case 9: + DIRECT_WRITE_LOW(pin); + signalindex++; + // delayMicroseconds(30415); // Inter-frame silence + break; + case 10: + repeatindex++; + if (repeatindexu8g)) - // helper function: retrieve and check userdata argument static lu8g_userdata_t *get_lud( lua_State *L ) @@ -780,214 +769,6 @@ static int lu8g_setFontLineSpacingFactor( lua_State *L ) } -// ------------------------------------------------------------ -// comm functions -// -#define I2C_CMD_MODE 0x000 -#define I2C_DATA_MODE 0x040 - -#define ESP_I2C_ID 0 - - -static uint8_t do_i2c_start(uint8_t id, uint8_t sla) -{ - platform_i2c_send_start( id ); - - // ignore return value -> tolerate missing ACK - platform_i2c_send_address( id, sla, PLATFORM_I2C_DIRECTION_TRANSMITTER ); - - return 1; -} - -static uint8_t u8g_com_esp8266_ssd_start_sequence(u8g_t *u8g) -{ - /* are we requested to set the a0 state? */ - if ( u8g->pin_list[U8G_PI_SET_A0] == 0 ) - return 1; - - /* setup bus, might be a repeated start */ - if ( do_i2c_start( ESP_I2C_ID, u8g->i2c_addr ) == 0 ) - return 0; - if ( u8g->pin_list[U8G_PI_A0_STATE] == 0 ) - { - // ignore return value -> tolerate missing ACK - if ( platform_i2c_send_byte( ESP_I2C_ID, I2C_CMD_MODE ) == 0 ) - ; //return 0; - } - else - { - platform_i2c_send_byte( ESP_I2C_ID, I2C_DATA_MODE ); - } - - u8g->pin_list[U8G_PI_SET_A0] = 0; - return 1; -} - - -static void lu8g_digital_write( u8g_t *u8g, uint8_t pin_index, uint8_t value ) -{ - uint8_t pin; - - pin = u8g->pin_list[pin_index]; - if ( pin != U8G_PIN_NONE ) - platform_gpio_write( pin, value ); -} - -void u8g_Delay(u8g_t *u8g, uint16_t msec) -{ - const uint16_t chunk = 50; - - if (u8g->use_delay == 0) - return; - - while (msec > chunk) - { - os_delay_us( chunk*1000 ); - msec -= chunk; - } - if (msec > 0) - os_delay_us( msec*1000 ); -} -void u8g_MicroDelay(void) -{ - os_delay_us( 1 ); -} -void u8g_10MicroDelay(void) -{ - os_delay_us( 10 ); -} - - -uint8_t u8g_com_esp8266_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) -{ - switch(msg) - { - case U8G_COM_MSG_STOP: - break; - - case U8G_COM_MSG_INIT: - // we assume that the SPI interface was already initialized - // just care for the /CS and D/C pins - lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_HIGH ); - platform_gpio_mode( u8g->pin_list[U8G_PI_CS], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT ); - platform_gpio_mode( u8g->pin_list[U8G_PI_A0], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT ); - break; - - case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */ - lu8g_digital_write( u8g, U8G_PI_A0, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH ); - break; - - case U8G_COM_MSG_CHIP_SELECT: - if (arg_val == 0) - { - /* disable */ - lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_HIGH ); - } - else - { - /* enable */ - //u8g_com_arduino_digital_write(u8g, U8G_PI_SCK, LOW); - lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_LOW ); - } - break; - - case U8G_COM_MSG_RESET: - if ( u8g->pin_list[U8G_PI_RESET] != U8G_PIN_NONE ) - lu8g_digital_write( u8g, U8G_PI_RESET, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH ); - break; - - case U8G_COM_MSG_WRITE_BYTE: - platform_spi_send( 1, 8, arg_val ); - break; - - case U8G_COM_MSG_WRITE_SEQ: - case U8G_COM_MSG_WRITE_SEQ_P: - { - register uint8_t *ptr = arg_ptr; - while( arg_val > 0 ) - { - platform_spi_send( 1, 8, *ptr++ ); - arg_val--; - } - } - break; - } - return 1; -} - - -uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) -{ - switch(msg) - { - case U8G_COM_MSG_INIT: - // we assume that the i2c bus was already initialized - //u8g_i2c_init(u8g->pin_list[U8G_PI_I2C_OPTION]); - - break; - - case U8G_COM_MSG_STOP: - break; - - case U8G_COM_MSG_RESET: - /* Currently disabled, but it could be enable. Previous restrictions have been removed */ - /* u8g_com_arduino_digital_write(u8g, U8G_PI_RESET, arg_val); */ - break; - - case U8G_COM_MSG_CHIP_SELECT: - u8g->pin_list[U8G_PI_A0_STATE] = 0; - u8g->pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again, also forces start condition */ - if ( arg_val == 0 ) - { - /* disable chip, send stop condition */ - platform_i2c_send_stop( ESP_I2C_ID ); - } - else - { - /* enable, do nothing: any byte writing will trigger the i2c start */ - } - break; - - case U8G_COM_MSG_WRITE_BYTE: - //u8g->pin_list[U8G_PI_SET_A0] = 1; - if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 ) - return platform_i2c_send_stop( ESP_I2C_ID ), 0; - // ignore return value -> tolerate missing ACK - if ( platform_i2c_send_byte( ESP_I2C_ID, arg_val) == 0 ) - ; //return platform_i2c_send_stop( ESP_I2C_ID ), 0; - // platform_i2c_send_stop( ESP_I2C_ID ); - break; - - case U8G_COM_MSG_WRITE_SEQ: - case U8G_COM_MSG_WRITE_SEQ_P: - //u8g->pin_list[U8G_PI_SET_A0] = 1; - if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 ) - return platform_i2c_send_stop( ESP_I2C_ID ), 0; - { - register uint8_t *ptr = arg_ptr; - while( arg_val > 0 ) - { - // ignore return value -> tolerate missing ACK - if ( platform_i2c_send_byte( ESP_I2C_ID, *ptr++) == 0 ) - ; //return platform_i2c_send_stop( ESP_I2C_ID ), 0; - arg_val--; - } - } - // platform_i2c_send_stop( ESP_I2C_ID ); - break; - - case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */ - u8g->pin_list[U8G_PI_A0_STATE] = arg_val; - u8g->pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again */ - - break; - } - return 1; -} - - - - // device destructor static int lu8g_close_display( lua_State *L ) { @@ -996,6 +777,19 @@ static int lu8g_close_display( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; + if (lud->cb_ref != LUA_NOREF) { + // this is the fb_rle device + u8g_dev_t *fb_dev = LU8G->dev; + u8g_pb_t *fb_dev_pb = (u8g_pb_t *)(fb_dev->dev_mem); + uint8_t *fb_dev_buf = fb_dev_pb->buf; + + luaM_free( L, fb_dev_buf ); + luaM_free( L, fb_dev_pb ); + luaM_free( L, fb_dev ); + + luaL_unref( L, lud->cb_ref, LUA_REGISTRYINDEX ); + } + return 0; } @@ -1016,9 +810,10 @@ static int lu8g_close_display( lua_State *L ) return luaL_error( L, "i2c address required" ); \ \ lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \ + lud->cb_ref = LUA_NOREF; \ \ - lud->u8g.i2c_addr = (uint8_t)addr; \ - lud->u8g.use_delay = del > 0 ? 1 : 0; \ + lud->i2c_addr = (uint8_t)addr; \ + lud->use_delay = del > 0 ? 1 : 0; \ \ u8g_InitI2C( LU8G, &u8g_dev_ ## device, U8G_I2C_OPT_NONE); \ \ @@ -1049,8 +844,9 @@ U8G_DISPLAY_TABLE_I2C unsigned del = luaL_optinteger( L, 4, 0 ); \ \ lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \ + lud->cb_ref = LUA_NOREF; \ \ - lud->u8g.use_delay = del > 0 ? 1 : 0; \ + lud->use_delay = del > 0 ? 1 : 0; \ \ u8g_InitHWSPI( LU8G, &u8g_dev_ ## device, cs, dc, res ); \ \ @@ -1064,6 +860,53 @@ U8G_DISPLAY_TABLE_I2C // Unroll the display table and insert binding functions for SPI based displays. U8G_DISPLAY_TABLE_SPI // +// +// +// This display forwards the framebuffer contents as run-length encoded chunks to a Lua callback +static int lu8g_fb_rle( lua_State *L ) { + lu8g_userdata_t *lud; + + if ((lua_type( L, 1 ) != LUA_TFUNCTION) && + (lua_type( L, 1 ) != LUA_TLIGHTFUNCTION)) { + luaL_typerror( L, 1, "function" ); + } + + int width = luaL_checkint( L, 2 ); + int height = luaL_checkint( L, 3 ); + + luaL_argcheck( L, (width > 0) && (width < 256) && ((width % 8) == 0), 2, "invalid width" ); + luaL_argcheck( L, (height > 0) && (height < 256) && ((height % 8) == 0), 3, "invalid height" ); + + // construct display device structures manually because width and height are configurable + uint8_t *fb_dev_buf = (uint8_t *)luaM_malloc( L, width ); + + u8g_pb_t *fb_dev_pb = (u8g_pb_t *)luaM_malloc( L, sizeof( u8g_pb_t ) ); + fb_dev_pb->p.page_height = 8; + fb_dev_pb->p.total_height = height; + fb_dev_pb->p.page_y0 = 0; + fb_dev_pb->p.page_y1 = 0; + fb_dev_pb->p.page = 0; + fb_dev_pb->width = width; + fb_dev_pb->buf = fb_dev_buf; + + u8g_dev_t *fb_dev = (u8g_dev_t *)luaM_malloc( L, sizeof( u8g_dev_t ) ); + fb_dev->dev_fn = u8g_dev_gen_fb_fn; + fb_dev->dev_mem = fb_dev_pb; + fb_dev->com_fn = u8g_com_esp8266_fbrle_fn; + + lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); + lua_pushvalue( L, 1 ); // copy argument (func) to the top of stack + lud->cb_ref = luaL_ref( L, LUA_REGISTRYINDEX ); + + /* set metatable for userdata */ + luaL_getmetatable(L, "u8g.display"); + lua_setmetatable(L, -2); + + u8g_Init8BitFixedPort( LU8G, fb_dev, width, height, 0, 0, 0); + + return 1; +} +// // *************************************************************************** @@ -1137,6 +980,8 @@ static const LUA_REG_TYPE lu8g_map[] = { #define U8G_FONT_TABLE_ENTRY(font) \ { LSTRKEY( #font ), LUDATA( (void *)(u8g_ ## font) ) }, U8G_FONT_TABLE + // + { LSTRKEY( "fb_rle" ), LFUNCVAL( lu8g_fb_rle ) }, // Options for circle/ ellipse drawing { LSTRKEY( "DRAW_UPPER_RIGHT" ), LNUMVAL( U8G_DRAW_UPPER_RIGHT ) }, { LSTRKEY( "DRAW_UPPER_LEFT" ), LNUMVAL( U8G_DRAW_UPPER_LEFT ) }, diff --git a/app/modules/u8g_glue.c b/app/modules/u8g_glue.c new file mode 100644 index 00000000..41fe2b72 --- /dev/null +++ b/app/modules/u8g_glue.c @@ -0,0 +1,347 @@ +/* + Functions for integrating U8glib into the nodemcu platform. +*/ + +#include "lauxlib.h" +#include "platform.h" + +#include "c_stdlib.h" + +#include "u8g.h" +#include "u8g_glue.h" + + +// ------------------------------------------------------------ +// comm functions +// +#define I2C_CMD_MODE 0x000 +#define I2C_DATA_MODE 0x040 + +#define ESP_I2C_ID 0 + + +static uint8_t do_i2c_start(uint8_t id, uint8_t sla) +{ + platform_i2c_send_start( id ); + + // ignore return value -> tolerate missing ACK + platform_i2c_send_address( id, sla, PLATFORM_I2C_DIRECTION_TRANSMITTER ); + + return 1; +} + +static uint8_t u8g_com_esp8266_ssd_start_sequence(struct _lu8g_userdata_t *lu8g) +{ + /* are we requested to set the a0 state? */ + if ( lu8g->u8g.pin_list[U8G_PI_SET_A0] == 0 ) + return 1; + + /* setup bus, might be a repeated start */ + if ( do_i2c_start( ESP_I2C_ID, lu8g->i2c_addr ) == 0 ) + return 0; + if ( lu8g->u8g.pin_list[U8G_PI_A0_STATE] == 0 ) + { + // ignore return value -> tolerate missing ACK + if ( platform_i2c_send_byte( ESP_I2C_ID, I2C_CMD_MODE ) == 0 ) + ; //return 0; + } + else + { + platform_i2c_send_byte( ESP_I2C_ID, I2C_DATA_MODE ); + } + + lu8g->u8g.pin_list[U8G_PI_SET_A0] = 0; + return 1; +} + + +static void lu8g_digital_write( struct _lu8g_userdata_t *lu8g, uint8_t pin_index, uint8_t value ) +{ + uint8_t pin; + + pin = lu8g->u8g.pin_list[pin_index]; + if ( pin != U8G_PIN_NONE ) + platform_gpio_write( pin, value ); +} + +void u8g_Delay(u8g_t *u8g, uint16_t msec) +{ + struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g; + const uint16_t chunk = 50; + + if (lu8g->use_delay == 0) + return; + + while (msec > chunk) + { + os_delay_us( chunk*1000 ); + msec -= chunk; + } + if (msec > 0) + os_delay_us( msec*1000 ); +} +void u8g_MicroDelay(void) +{ + os_delay_us( 1 ); +} +void u8g_10MicroDelay(void) +{ + os_delay_us( 10 ); +} + + +uint8_t u8g_com_esp8266_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) +{ + struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g; + + switch(msg) + { + case U8G_COM_MSG_STOP: + break; + + case U8G_COM_MSG_INIT: + // we assume that the SPI interface was already initialized + // just care for the /CS and D/C pins + lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_HIGH ); + platform_gpio_mode( lu8g->u8g.pin_list[U8G_PI_CS], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT ); + platform_gpio_mode( lu8g->u8g.pin_list[U8G_PI_A0], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT ); + break; + + case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */ + lu8g_digital_write( lu8g, U8G_PI_A0, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH ); + break; + + case U8G_COM_MSG_CHIP_SELECT: + if (arg_val == 0) + { + /* disable */ + lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_HIGH ); + } + else + { + /* enable */ + lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_LOW ); + } + break; + + case U8G_COM_MSG_RESET: + if ( lu8g->u8g.pin_list[U8G_PI_RESET] != U8G_PIN_NONE ) + lu8g_digital_write( lu8g, U8G_PI_RESET, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH ); + break; + + case U8G_COM_MSG_WRITE_BYTE: + platform_spi_send( 1, 8, arg_val ); + break; + + case U8G_COM_MSG_WRITE_SEQ: + case U8G_COM_MSG_WRITE_SEQ_P: + { + register uint8_t *ptr = arg_ptr; + while( arg_val > 0 ) + { + platform_spi_send( 1, 8, *ptr++ ); + arg_val--; + } + } + break; + } + return 1; +} + + +uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) +{ + struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g; + + switch(msg) + { + case U8G_COM_MSG_INIT: + // we assume that the i2c bus was already initialized + //u8g_i2c_init(u8g->pin_list[U8G_PI_I2C_OPTION]); + + break; + + case U8G_COM_MSG_STOP: + break; + + case U8G_COM_MSG_RESET: + /* Currently disabled, but it could be enable. Previous restrictions have been removed */ + /* u8g_com_arduino_digital_write(u8g, U8G_PI_RESET, arg_val); */ + break; + + case U8G_COM_MSG_CHIP_SELECT: + lu8g->u8g.pin_list[U8G_PI_A0_STATE] = 0; + lu8g->u8g.pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again, also forces start condition */ + if ( arg_val == 0 ) + { + /* disable chip, send stop condition */ + platform_i2c_send_stop( ESP_I2C_ID ); + } + else + { + /* enable, do nothing: any byte writing will trigger the i2c start */ + } + break; + + case U8G_COM_MSG_WRITE_BYTE: + //u8g->pin_list[U8G_PI_SET_A0] = 1; + if ( u8g_com_esp8266_ssd_start_sequence(lu8g) == 0 ) + return platform_i2c_send_stop( ESP_I2C_ID ), 0; + // ignore return value -> tolerate missing ACK + if ( platform_i2c_send_byte( ESP_I2C_ID, arg_val) == 0 ) + ; //return platform_i2c_send_stop( ESP_I2C_ID ), 0; + // platform_i2c_send_stop( ESP_I2C_ID ); + break; + + case U8G_COM_MSG_WRITE_SEQ: + case U8G_COM_MSG_WRITE_SEQ_P: + //u8g->pin_list[U8G_PI_SET_A0] = 1; + if ( u8g_com_esp8266_ssd_start_sequence(lu8g) == 0 ) + return platform_i2c_send_stop( ESP_I2C_ID ), 0; + { + register uint8_t *ptr = arg_ptr; + while( arg_val > 0 ) + { + // ignore return value -> tolerate missing ACK + if ( platform_i2c_send_byte( ESP_I2C_ID, *ptr++) == 0 ) + ; //return platform_i2c_send_stop( ESP_I2C_ID ), 0; + arg_val--; + } + } + // platform_i2c_send_stop( ESP_I2C_ID ); + break; + + case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */ + lu8g->u8g.pin_list[U8G_PI_A0_STATE] = arg_val; + lu8g->u8g.pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again */ + + break; + } + return 1; +} + + +// *************************************************************************** +// Generic framebuffer device and RLE comm driver +// +uint8_t u8g_dev_gen_fb_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg) +{ + switch(msg) + { + case U8G_DEV_MSG_PAGE_FIRST: + // tell comm driver to start new framebuffer + u8g_SetChipSelect(u8g, dev, 1); + break; + case U8G_DEV_MSG_PAGE_NEXT: + { + u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem); + if ( u8g_pb_WriteBuffer(pb, u8g, dev) == 0 ) + return 0; + } + break; + } + + return u8g_dev_pb8v1_base_fn(u8g, dev, msg, arg); +} + +static int bit_at( uint8_t *buf, int line, int x ) +{ + uint8_t byte = buf[x]; + + return buf[x] & (1 << line) ? 1 : 0; +} + +struct _lu8g_fbrle_item +{ + uint8_t start_x; + uint8_t len; +}; + +struct _lu8g_fbrle_line +{ + uint8_t num_valid; + struct _lu8g_fbrle_item items[0]; +}; + +uint8_t u8g_com_esp8266_fbrle_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) +{ + struct _lu8g_userdata_t *lud = (struct _lu8g_userdata_t *)u8g; + + switch(msg) + { + case U8G_COM_MSG_INIT: + // allocate memory -> done + // init buffer + break; + + case U8G_COM_MSG_CHIP_SELECT: + if (arg_val == 1) { + // new frame starts + if (lud->cb_ref != LUA_NOREF) { + // fire callback with nil argument + lua_State *L = lua_getstate(); + lua_rawgeti( L, LUA_REGISTRYINDEX, lud->cb_ref ); + lua_pushnil( L ); + lua_call( L, 1, 0 ); + } + } + break; + + case U8G_COM_MSG_WRITE_SEQ: + case U8G_COM_MSG_WRITE_SEQ_P: + { + uint8_t xwidth = u8g->pin_list[U8G_PI_EN]; + size_t fbrle_line_size = sizeof( struct _lu8g_fbrle_line ) + sizeof( struct _lu8g_fbrle_item ) * (xwidth/2); + int num_lines = arg_val / (xwidth/8); + uint8_t *buf = (uint8_t *)arg_ptr; + + struct _lu8g_fbrle_line *fbrle_line; + if (!(fbrle_line = (struct _lu8g_fbrle_line *)c_malloc( fbrle_line_size ))) { + break; + } + + for (int line = 0; line < num_lines; line++) { + int start_run = -1; + fbrle_line->num_valid = 0; + + for (int x = 0; x < xwidth; x++) { + if (bit_at( buf, line, x ) == 0) { + if (start_run >= 0) { + // inside run, end it and enter result + fbrle_line->items[fbrle_line->num_valid].start_x = start_run; + fbrle_line->items[fbrle_line->num_valid++].len = x - start_run; + //NODE_ERR( " line: %d x: %d len: %d\n", line, start_run, x - start_run ); + start_run = -1; + } + } else { + if (start_run < 0) { + // outside run, start it + start_run = x; + } + } + + if (fbrle_line->num_valid >= xwidth/2) break; + } + + // active run? + if (start_run >= 0 && fbrle_line->num_valid < xwidth/2) { + fbrle_line->items[fbrle_line->num_valid].start_x = start_run; + fbrle_line->items[fbrle_line->num_valid++].len = xwidth - start_run; + } + + // line done, trigger callback + if (lud->cb_ref != LUA_NOREF) { + lua_State *L = lua_getstate(); + + lua_rawgeti( L, LUA_REGISTRYINDEX, lud->cb_ref ); + lua_pushlstring( L, (const char *)fbrle_line, fbrle_line_size ); + lua_call( L, 1, 0 ); + } + } + + c_free( fbrle_line ); + } + break; + } + return 1; +} diff --git a/app/modules/u8g_glue.h b/app/modules/u8g_glue.h new file mode 100644 index 00000000..e8057651 --- /dev/null +++ b/app/modules/u8g_glue.h @@ -0,0 +1,22 @@ + +#ifndef _U8G_GLUE_H_ +#define _U8G_GLUE_H_ + +#include "u8g.h" + +struct _lu8g_userdata_t +{ + u8g_t u8g; + uint8_t i2c_addr; + uint8_t use_delay; + int cb_ref; +}; +typedef struct _lu8g_userdata_t lu8g_userdata_t; + +// shorthand macro for the u8g structure inside the userdata +#define LU8G (&(lud->u8g)) + +uint8_t u8g_com_esp8266_fbrle_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); +uint8_t u8g_dev_gen_fb_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg); + +#endif diff --git a/app/modules/websocket.c b/app/modules/websocket.c index 899d04dc..6feb6e82 100644 --- a/app/modules/websocket.c +++ b/app/modules/websocket.c @@ -15,6 +15,7 @@ #include "c_types.h" #include "c_string.h" +#include "c_stdlib.h" #include "websocketclient.h" @@ -45,7 +46,7 @@ static void websocketclient_onConnectionCallback(ws_info *ws) { } } -static void websocketclient_onReceiveCallback(ws_info *ws, char *message, int opCode) { +static void websocketclient_onReceiveCallback(ws_info *ws, int len, char *message, int opCode) { NODE_DBG("websocketclient_onReceiveCallback\n"); lua_State *L = lua_getstate(); @@ -59,7 +60,7 @@ static void websocketclient_onReceiveCallback(ws_info *ws, char *message, int op if (data->onReceive != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, data->onReceive); // load the callback function lua_rawgeti(L, LUA_REGISTRYINDEX, data->self_ref); // pass itself, #1 callback argument - lua_pushstring(L, message); // #2 callback argument + lua_pushlstring(L, message, len); // #2 callback argument lua_pushnumber(L, opCode); // #3 callback argument lua_call(L, 3, 0); } @@ -102,6 +103,7 @@ static int websocket_createClient(lua_State *L) { ws_info *ws = (ws_info *) lua_newuserdata(L, sizeof(ws_info)); ws->connectionState = 0; + ws->extraHeaders = NULL; ws->onConnection = &websocketclient_onConnectionCallback; ws->onReceive = &websocketclient_onReceiveCallback; ws->onFailure = &websocketclient_onCloseCallback; @@ -118,7 +120,6 @@ static int websocketclient_on(lua_State *L) { NODE_DBG("websocketclient_on\n"); ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); - luaL_argcheck(L, ws, 1, "Client websocket expected"); ws_data *data = (ws_data *) ws->reservedData; @@ -170,7 +171,6 @@ static int websocketclient_connect(lua_State *L) { NODE_DBG("websocketclient_connect is called.\n"); ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); - luaL_argcheck(L, ws, 1, "Client websocket expected"); ws_data *data = (ws_data *) ws->reservedData; @@ -188,11 +188,61 @@ static int websocketclient_connect(lua_State *L) { return 0; } +static header_t *realloc_headers(header_t *headers, int new_size) { + if(headers) { + for(header_t *header = headers; header->key; header++) { + c_free(header->value); + c_free(header->key); + } + c_free(headers); + } + if(!new_size) + return NULL; + return (header_t *)c_malloc(sizeof(header_t) * (new_size + 1)); +} + +static int websocketclient_config(lua_State *L) { + NODE_DBG("websocketclient_config is called.\n"); + + ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); + + ws_data *data = (ws_data *) ws->reservedData; + + luaL_checktype(L, 2, LUA_TTABLE); + lua_getfield(L, 2, "headers"); + if(lua_istable(L, -1)) { + + lua_pushnil(L); + int size = 0; + while(lua_next(L, -2)) { + size++; + lua_pop(L, 1); + } + + ws->extraHeaders = realloc_headers(ws->extraHeaders, size); + if(ws->extraHeaders) { + header_t *header = ws->extraHeaders; + + lua_pushnil(L); + while(lua_next(L, -2)) { + header->key = c_strdup(lua_tostring(L, -2)); + header->value = c_strdup(lua_tostring(L, -1)); + header++; + lua_pop(L, 1); + } + + header->key = header->value = NULL; + } + } + lua_pop(L, 1); // pop headers + + return 0; +} + static int websocketclient_send(lua_State *L) { NODE_DBG("websocketclient_send is called.\n"); ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); - luaL_argcheck(L, ws, 1, "Client websocket expected"); ws_data *data = (ws_data *) ws->reservedData; @@ -225,7 +275,8 @@ static int websocketclient_gc(lua_State *L) { NODE_DBG("websocketclient_gc\n"); ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); - luaL_argcheck(L, ws, 1, "Client websocket expected"); + + ws->extraHeaders = realloc_headers(ws->extraHeaders, 0); ws_data *data = (ws_data *) ws->reservedData; @@ -265,6 +316,7 @@ static const LUA_REG_TYPE websocket_map[] = static const LUA_REG_TYPE websocketclient_map[] = { { LSTRKEY("on"), LFUNCVAL(websocketclient_on) }, + { LSTRKEY("config"), LFUNCVAL(websocketclient_config) }, { LSTRKEY("connect"), LFUNCVAL(websocketclient_connect) }, { LSTRKEY("send"), LFUNCVAL(websocketclient_send) }, { LSTRKEY("close"), LFUNCVAL(websocketclient_close) }, diff --git a/app/modules/wifi.c b/app/modules/wifi.c index c130dfda..ab761898 100644 --- a/app/modules/wifi.c +++ b/app/modules/wifi.c @@ -1,5 +1,7 @@ // Module for interfacing with WIFI +// FIXME: sprintf->snprintf everywhere. + #include "module.h" #include "lauxlib.h" #include "platform.h" @@ -76,12 +78,8 @@ static void wifi_smart_succeed_cb(sc_status status, void *pdata){ #endif // WIFI_SMART_ENABLE static int wifi_scan_succeed = LUA_NOREF; -/** - * @brief Wifi ap scan over callback to display. - * @param arg: contain the aps information - * @param status: scan over status - * @retval None - */ + +// callback for wifi_station_listap static void wifi_scan_done(void *arg, STATUS status) { lua_State* L = lua_getstate(); @@ -215,23 +213,26 @@ static int wifi_exit_smart( lua_State* L ) } #endif // WIFI_SMART_ENABLE -// Lua: realmode = setmode(mode) +// Lua: wifi.setmode(mode, save_to_flash) static int wifi_setmode( lua_State* L ) { unsigned mode; - + bool save_to_flash=true; mode = luaL_checkinteger( L, 1 ); - - if ( mode != STATION_MODE && mode != SOFTAP_MODE && mode != STATIONAP_MODE && mode != NULL_MODE ) - return luaL_error( L, "wrong arg type" ); - wifi_set_opmode( (uint8_t)mode); + luaL_argcheck(L, mode == STATION_MODE || mode == SOFTAP_MODE || mode == STATIONAP_MODE || mode == NULL_MODE, 1, "Invalid mode"); + if(!lua_isnoneornil(L, 2)) + { + if(!lua_isboolean(L, 2)) luaL_typerror(L, 2, lua_typename(L, LUA_TBOOLEAN)); + save_to_flash=lua_toboolean(L, 2); + } + if(save_to_flash) wifi_set_opmode( (uint8_t)mode); + else wifi_set_opmode_current( (uint8_t)mode); mode = (unsigned)wifi_get_opmode(); lua_pushinteger( L, mode ); return 1; } -// Lua: realmode = getmode() - +// Lua: wifi.getmode() static int wifi_getmode( lua_State* L ) { unsigned mode; @@ -239,20 +240,17 @@ static int wifi_getmode( lua_State* L ) lua_pushinteger( L, mode ); return 1; } -/** - * wifi.getchannel() - * Description: - * Get current wifi Channel - * - * Syntax: - * wifi.getchannel() - * Parameters: - * nil - * - * Returns: - * Current wifi channel - */ +// Lua: wifi.getdefaultmode() +static int wifi_getdefaultmode( lua_State* L ) +{ + unsigned mode; + mode = (unsigned)wifi_get_opmode_default(); + lua_pushinteger( L, mode ); + return 1; +} + +// Lua: wifi.getchannel() static int wifi_getchannel( lua_State* L ) { unsigned channel; @@ -261,22 +259,7 @@ static int wifi_getchannel( lua_State* L ) return 1; } -/** - * wifi.setphymode() - * Description: - * Set wifi physical mode(802.11 b/g/n) - * Note: SoftAP only supports 802.11 b/g. - * Syntax: - * wifi.setphymode(mode) - * Parameters: - * mode: - * wifi.PHYMODE_B - * wifi.PHYMODE_G - * wifi.PHYMODE_N - * Returns: - * Current physical mode after setup - */ - +// Lua: wifi.setphymode() static int wifi_setphymode( lua_State* L ) { unsigned mode; @@ -291,18 +274,7 @@ static int wifi_setphymode( lua_State* L ) return 1; } -/** - * wifi.getphymode() - * Description: - * Get wifi physical mode(802.11 b/g/n) - * Syntax: - * wifi.getphymode() - * Parameters: - * nil - * Returns: - * Current physical mode. - * - */ +// Lua: wifi.getphymode() static int wifi_getphymode( lua_State* L ) { unsigned mode; @@ -311,7 +283,7 @@ static int wifi_getphymode( lua_State* L ) return 1; } -//wifi.sleep() +// Lua: wifi.sleep() static int wifi_sleep(lua_State* L) { uint8 desired_sleep_state = 2; @@ -372,7 +344,7 @@ static int wifi_sleep(lua_State* L) return 2; } -//wifi.nullmodesleep() +// Lua: wifi.nullmodesleep() static int wifi_null_mode_auto_sleep(lua_State* L) { if (!lua_isnone(L, 1)) @@ -459,7 +431,7 @@ static int wifi_getbroadcast( lua_State* L, uint8_t mode ) } } - +// Used by wifi_setip static uint32_t parse_key(lua_State* L, const char * key){ lua_getfield(L, 1, key); if( lua_isstring(L, -1) ) // deal with the ip/netmask/gw string @@ -506,24 +478,86 @@ static int wifi_setip( lua_State* L, uint8_t mode ) return 1; } -// Lua: realtype = sleeptype(type) -static int wifi_sleeptype( lua_State* L ) +// Lua: wifi.sta.getaplist +static int wifi_station_get_ap_info4lua( lua_State* L ) { - unsigned type; - - if ( lua_isnumber(L, 1) ){ - type = lua_tointeger(L, 1); - if ( type != NONE_SLEEP_T && type != LIGHT_SLEEP_T && type != MODEM_SLEEP_T ) - return luaL_error( L, "wrong arg type" ); - if(!wifi_set_sleep_type(type)){ - lua_pushnil(L); - return 1; - } - } + struct station_config config[5]; + char temp[sizeof(config[0].password)+1]; //max password length + '\0' + uint8 number_of_aps = wifi_station_get_ap_info(config); - type = wifi_get_sleep_type(); - lua_pushinteger( L, type ); - return 1; +#if defined(WIFI_DEBUG) + char debug_temp[128]; +#endif + lua_newtable(L); + lua_pushnumber(L, number_of_aps); + lua_setfield(L, -2, "qty"); + WIFI_DBG("\n\t# of APs stored in flash:%d\n", number_of_aps); + WIFI_DBG(" %-6s %-32s %-64s %-17s\n", "index:", "ssid:", "password:", "bssid:"); + + for(int i=0;i= 8) + { + memcpy(temp, config[i].password, sizeof(config[i].password)); + lua_pushstring(L, temp); + lua_setfield(L, -2, "pwd"); + } +#if defined(WIFI_DEBUG) + c_sprintf(debug_temp + strlen(debug_temp), "%-64s ", temp); +#endif + + memset(temp, 0, sizeof(temp)); + if (config[i].bssid_set) + { + c_sprintf(temp, MACSTR, MAC2STR(config[i].bssid)); + lua_pushstring(L, temp); + lua_setfield(L, -2, "bssid"); + } + +#if defined(WIFI_DEBUG) + WIFI_DBG("%s%-17s \n", debug_temp, temp); +#endif + lua_pushnumber(L, i+1); //Add one, so that AP index follows Lua Conventions + lua_insert(L, -2); + lua_settable(L, -3); + } + return 1; +} + +// Lua: wifi.setapnumber(number_of_aps_to_save) +static int wifi_station_ap_number_set4lua( lua_State* L ) +{ + unsigned limit=luaL_checkinteger(L, 1); + luaL_argcheck(L, (limit >= 1 && limit <= 5), 1, "Valid range: 1-5"); + lua_pushboolean(L, wifi_station_ap_number_set((uint8)limit)); + return 1; +} + +// Lua: wifi.setapnumber(number_of_aps_to_save) +static int wifi_station_change_ap( lua_State* L ) +{ + uint8 ap_index=luaL_checkinteger(L, 1); + luaL_argcheck(L, (ap_index >= 1 && ap_index <= 5), 1, "Valid range: 1-5"); + lua_pushboolean(L, wifi_station_ap_change(ap_index-1)); + return 1; +} + +// Lua: wifi.setapnumber(number_of_aps_to_save) +static int wifi_station_get_ap_index( lua_State* L ) +{ + lua_pushnumber(L, wifi_station_get_current_ap_id()+1); + return 1; } // Lua: wifi.sta.getmac() @@ -551,161 +585,230 @@ static int wifi_station_getbroadcast( lua_State* L ){ return wifi_getbroadcast(L, STATION_IF); } -/** - * wifi.sta.getconfig() - * Description: - * Get current Station configuration. - * Note: if bssid_set==1 STATION is configured to connect to specified BSSID - * if bssid_set==0 specified BSSID address is irrelevant. - * Syntax: - * ssid, pwd, bssid_set, bssid=wifi.sta.getconfig() - * Parameters: - * none - * Returns: - * SSID, Password, BSSID_set, BSSID - */ -static int wifi_station_getconfig( lua_State* L ) +// Used by wifi_station_getconfig_xxx +static int wifi_station_getconfig( lua_State* L, bool get_flash_cfg) { - struct station_config sta_conf; - char bssid[17]; - wifi_station_get_config(&sta_conf); - if(sta_conf.ssid==0) - { - lua_pushnil(L); - return 1; - } - else - { - lua_pushstring( L, sta_conf.ssid ); - lua_pushstring( L, sta_conf.password ); - lua_pushinteger( L, sta_conf.bssid_set); - c_sprintf(bssid, MACSTR, MAC2STR(sta_conf.bssid)); - lua_pushstring( L, bssid); - return 4; - } + struct station_config sta_conf; + char temp[sizeof(sta_conf.password)+1]; //max password length + '\0' + if(get_flash_cfg) wifi_station_get_config_default(&sta_conf); + else wifi_station_get_config(&sta_conf); + if(sta_conf.ssid==0) + { + lua_pushnil(L); + return 1; + } + else + { + if(lua_isboolean(L, 1) && lua_toboolean(L, 1)==true) + { + lua_newtable(L); + memset(temp, 0, sizeof(temp)); + memcpy(temp, sta_conf.ssid, sizeof(sta_conf.ssid)); + lua_pushstring(L, temp); + lua_setfield(L, -2, "ssid"); + + if(strlen(sta_conf.password) >= 8) + { + memset(temp, 0, sizeof(temp)); + memcpy(temp, sta_conf.password, sizeof(sta_conf.password)); + lua_pushstring(L, temp); + lua_setfield(L, -2, "pwd"); + } + + if(sta_conf.bssid_set==1) + { + memset(temp, 0, sizeof(temp)); + c_sprintf(temp, MACSTR, MAC2STR(sta_conf.bssid)); + lua_pushstring( L, temp); + lua_setfield(L, -2, "bssid"); + } + return 1; + } + else + { + memset(temp, 0, sizeof(temp)); + memcpy(temp, sta_conf.ssid, sizeof(sta_conf.ssid)); + lua_pushstring(L, temp); + memset(temp, 0, sizeof(temp)); + memcpy(temp, sta_conf.password, sizeof(sta_conf.password)); + lua_pushstring(L, temp); + lua_pushinteger( L, sta_conf.bssid_set); + c_sprintf(temp, MACSTR, MAC2STR(sta_conf.bssid)); + lua_pushstring( L, temp); + return 4; + } + } } -/** - * wifi.sta.config() - * Description: - * Set current Station configuration. - * Note: If there are multiple APs with the same ssid, you can connect to a specific one by entering it's MAC address into the "bssid" field. - * Syntax: - * wifi.sta.getconfig(ssid, password) --Set STATION configuration, Auto-connect by default, Connects to any BSSID - * wifi.sta.getconfig(ssid, password, Auto_connect) --Set STATION configuration, Auto-connect(0 or 1), Connects to any BSSID - * wifi.sta.getconfig(ssid, password, bssid) --Set STATION configuration, Auto-connect by default, Connects to specific BSSID - * wifi.sta.getconfig(ssid, password, Auto_connect, bssid) --Set STATION configuration, Auto-connect(0 or 1), Connects to specific BSSID - * Parameters: - * ssid: string which is less than 32 bytes. - * Password: string which is less than 64 bytes. - * Auto_connect: 0 (disable Auto-connect) or 1 (to enable Auto-connect). - * bssid: MAC address of Access Point you would like to connect to. - * Returns: - * Nothing. - * - * Example: - --Connect to Access Point automatically when in range - wifi.sta.getconfig("myssid", "password") +// Lua: wifi.sta.getconfig() +static int wifi_station_getconfig_current(lua_State *L) +{ + return wifi_station_getconfig(L, false); +} - --Connect to Access Point, User decides when to connect/disconnect to/from AP - wifi.sta.getconfig("myssid", "mypassword", 0) - wifi.sta.connect() - --do some wifi stuff - wifi.sta.disconnect() +// Lua: wifi.sta.getdefaultconfig() +static int wifi_station_getconfig_default(lua_State *L) +{ + return wifi_station_getconfig(L, true); +} - --Connect to specific Access Point automatically when in range - wifi.sta.getconfig("myssid", "mypassword", "12:34:56:78:90:12") - - --Connect to specific Access Point, User decides when to connect/disconnect to/from AP - wifi.sta.getconfig("myssid", "mypassword", 0) - wifi.sta.connect() - --do some wifi stuff - wifi.sta.disconnect() - * - */ +// Lua: wifi.sta.config() static int wifi_station_config( lua_State* L ) { - size_t sl, pl, ml; struct station_config sta_conf; - int auto_connect=0; - const char *ssid = luaL_checklstring( L, 1, &sl ); - if (sl>32 || ssid == NULL) - return luaL_error( L, "ssid:<32" ); - const char *password = luaL_checklstring( L, 2, &pl ); - if ((pl!=0 && (pl<8 || pl>64)) || password == NULL) - return luaL_error( L, "pwd:0,8~64" ); + bool auto_connect=true; + bool save_to_flash=true; + size_t sl, pl, ml; - if(lua_isnumber(L, 3)) + memset(sta_conf.ssid, 0, sizeof(sta_conf.ssid)); + memset(sta_conf.password, 0, sizeof(sta_conf.password)); + memset(sta_conf.bssid, 0, sizeof(sta_conf.bssid)); + sta_conf.bssid_set=0; + + if(lua_istable(L, 1)) { - auto_connect=luaL_checkinteger( L, 3 );; - if ( auto_connect != 0 && auto_connect != 1) - return luaL_error( L, "wrong arg type" ); - } - else if (lua_isstring(L, 3)&& !(lua_isnumber(L, 3))) - { - lua_pushnil(L); - lua_insert(L, 3); - auto_connect=1; + lua_getfield(L, 1, "ssid"); + if (!lua_isnil(L, -1)) + { + if( lua_isstring(L, -1) ) + { + const char *ssid = luaL_checklstring( L, -1, &sl ); + luaL_argcheck(L, ((sl>=1 && sl<=sizeof(sta_conf.ssid)) ), 1, "ssid: length:1-32"); + memcpy(sta_conf.ssid, ssid, sl); + } + else return luaL_argerror( L, 1, "ssid:not string" ); + } + else return luaL_argerror( L, 1, "ssid required" ); + lua_pop(L, 1); + + lua_getfield(L, 1, "pwd"); + if (!lua_isnil(L, -1)) + { + if( lua_isstring(L, -1) ) + { + const char *pwd = luaL_checklstring( L, -1, &pl ); + luaL_argcheck(L, ((pl>=8 && pl<=sizeof(sta_conf.password)) ), 1, "pwd: length:8-64"); + memcpy(sta_conf.password, pwd, pl); + } + else return luaL_argerror( L, 1, "pwd:not string" ); + } + lua_pop(L, 1); + + lua_getfield(L, 1, "bssid"); + if (!lua_isnil(L, -1)) + { + if (lua_isstring(L, -1)) + { + const char *macaddr = luaL_checklstring( L, -1, &ml ); + luaL_argcheck(L, ((ml==sizeof("AA:BB:CC:DD:EE:FF")-1) ), 1, "bssid: FF:FF:FF:FF:FF:FF"); + ets_str2macaddr(sta_conf.bssid, macaddr); + sta_conf.bssid_set = 1; + } + else return luaL_argerror(L, 1, "bssid:not string"); + } + lua_pop(L, 1); + + lua_getfield(L, 1, "auto"); + if (!lua_isnil(L, -1)) + { + if (lua_isboolean(L, -1)) + { + auto_connect=lua_toboolean(L, -1); + } + else return luaL_argerror(L, 1, "auto:not boolean"); + } + lua_pop(L, 1); + + lua_getfield(L, 1, "save"); + if (!lua_isnil(L, -1)) + { + if (lua_isboolean(L, -1)) save_to_flash=lua_toboolean(L, -1); + else return luaL_argerror(L, 1, "save:not boolean"); + } + else save_to_flash=false; + lua_pop(L, 1); } - else + else //to be depreciated { - if(lua_isnil(L, 3)) - return luaL_error( L, "wrong arg type" ); - auto_connect=1; + const char *ssid = luaL_checklstring( L, 1, &sl ); + luaL_argcheck(L, ((sl>=1 && sl=8 && pl32 || ssid == NULL) - return luaL_error( L, "ssid:1~32" ); - c_memset(config.ssid, 0, 32); - c_memcpy(config.ssid, ssid, len); - NODE_DBG(config.ssid); - NODE_DBG("\n"); - config.ssid_len = len; + const char *ssid = luaL_checklstring( L, -1, &sl ); + luaL_argcheck(L, ((sl>=1 && sl<=sizeof(config.ssid)) ), 1, "ssid: length:1-32"); + memcpy(config.ssid, ssid, sl); + config.ssid_len = sl; config.ssid_hidden = 0; } - else - return luaL_error( L, "wrong arg type" ); + else return luaL_argerror( L, 1, "ssid: not string" ); } - else - return luaL_error( L, "ssid required" ); + else return luaL_argerror( L, 1, "ssid: required" ); + lua_pop(L, 1); + lua_getfield(L, 1, "pwd"); if (!lua_isnil(L, -1)){ /* found? */ if( lua_isstring(L, -1) ) // deal with the password string { - const char *pwd = luaL_checklstring( L, -1, &len ); - if(len<8 || len>64 || pwd == NULL) - return luaL_error( L, "pwd:8~64" ); - c_memset(config.password, 0, 64); - c_memcpy(config.password, pwd, len); - NODE_DBG(config.password); - NODE_DBG("\n"); + const char *pwd = luaL_checklstring( L, -1, &pl ); + luaL_argcheck(L, (pl>=8 && pl<=sizeof(config.password)), 1, "pwd: length:0 or 8-64"); + memcpy(config.password, pwd, pl); config.authmode = AUTH_WPA_WPA2_PSK; } else - return luaL_error( L, "wrong arg type" ); + return luaL_argerror( L, 1, "pwd: not string" ); } else{ config.authmode = AUTH_OPEN; } + lua_pop(L, 1); lua_getfield(L, 1, "auth"); if (!lua_isnil(L, -1)) { - config.authmode = (uint8_t)luaL_checkinteger(L, -1); - NODE_DBG("%d\n", config.authmode); - } - else - { - // keep whatever value resulted from "pwd" logic above + if(lua_isnumber(L, -1)) + { + lint=luaL_checkinteger(L, -1); + luaL_argcheck(L, (lint >= 0 && lint < AUTH_MAX), 1, "auth: Range:0-4"); + config.authmode = (uint8_t)luaL_checkinteger(L, -1); + } + else return luaL_argerror(L, 1, "auth: not number"); + } + lua_pop(L, 1); + lua_getfield(L, 1, "channel"); if (!lua_isnil(L, -1)) { - unsigned channel = luaL_checkinteger(L, -1); - if (channel < 1 || channel > 13) - return luaL_error( L, "channel:1~13" ); + if(lua_isnumber(L, -1)) + { + lint=luaL_checkinteger(L, -1); + luaL_argcheck(L, (lint >= 1 && lint <= 13), 1, "channel: Range:1-13"); + config.channel = (uint8_t)lint; + } + else luaL_argerror(L, 1, "channel: not number"); - config.channel = (uint8_t)channel; - NODE_DBG("%d\n", config.channel); } else { config.channel = 6; } + lua_pop(L, 1); + lua_getfield(L, 1, "hidden"); if (!lua_isnil(L, -1)) { - config.ssid_hidden = (uint8_t)luaL_checkinteger(L, -1); - NODE_DBG("%d\n", config.ssid_hidden); - NODE_DBG("\n"); + Ltype_tmp=lua_type(L, -1); + if(Ltype_tmp==LUA_TNUMBER||Ltype_tmp==LUA_TBOOLEAN) + { + if(Ltype_tmp==LUA_TNUMBER)lint=luaL_checkinteger(L, -1); + if(Ltype_tmp==LUA_TBOOLEAN)lint=(lua_Number)lua_toboolean(L, -1); + + luaL_argcheck(L, (lint == 0 || lint==1), 1, "hidden: 0 or 1"); + config.ssid_hidden = (uint8_t)lint; + } + else return luaL_argerror(L, 1, "hidden: not boolean"); } else { config.ssid_hidden = 0; } + lua_pop(L, 1); + lua_getfield(L, 1, "max"); if (!lua_isnil(L, -1)) { - unsigned max = luaL_checkinteger(L, -1); - if (max < 1 || max > 4) - return luaL_error( L, "max:1~4" ); + if(lua_isnumber(L, -1)) + { + lint=luaL_checkinteger(L, -1); + luaL_argcheck(L, (lint >= 1 && lint <= 4), 1, "max: 1-4"); - config.max_connection = (uint8_t)max; - NODE_DBG("%d\n", config.max_connection); + config.max_connection = (uint8_t)lint; + } + else return luaL_argerror(L, 1, "max: not number"); } else { config.max_connection = 4; } + lua_pop(L, 1); + lua_getfield(L, 1, "beacon"); if (!lua_isnil(L, -1)) { - unsigned beacon = luaL_checkinteger(L, -1); - if (beacon < 100 || beacon > 60000) - return luaL_error( L, "beacon:100~60000" ); - - config.beacon_interval = (uint16_t)beacon; - NODE_DBG("%d\n", config.beacon_interval); + if(lua_isnumber(L, -1)) + { + lint=luaL_checkinteger(L, -1); + luaL_argcheck(L, (lint >= 100 && lint <= 60000), 1, "beacon: 100-60000"); + config.beacon_interval = (uint16_t)lint; + } + else return luaL_argerror(L, 1, "beacon: not number"); } else { config.beacon_interval = 100; } + lua_pop(L, 1); - wifi_softap_set_config(&config); - // system_restart(); - return 0; + + lua_getfield(L, 1, "save"); + if (!lua_isnil(L, -1)) + { + if (lua_isboolean(L, -1)) + { + save_to_flash=lua_toboolean(L, -1); + } + else return luaL_argerror(L, 1, "save: not boolean"); + } + lua_pop(L, 1); + + +#if defined(WIFI_DEBUG) + char debug_temp[sizeof(config.password)+1]; + memset(debug_temp, 0, sizeof(debug_temp)); + memcpy(debug_temp, config.ssid, sizeof(config.ssid)); + WIFI_DBG("\n\tconfig.ssid=\"%s\" len=%d\n", debug_temp, sl); + memset(debug_temp, 0, sizeof(debug_temp)); + memcpy(debug_temp, config.password, sizeof(config.password)); + WIFI_DBG("\tconfig.password=\"%s\" len=%d\n", debug_temp, pl); + WIFI_DBG("\tconfig.authmode=%d\n", config.authmode); + WIFI_DBG("\tconfig.channel=%d\n", config.channel); + WIFI_DBG("\tconfig.ssid_hidden=%d\n", config.ssid_hidden); + WIFI_DBG("\tconfig.max_connection=%d\n", config.max_connection); + WIFI_DBG("\tconfig.beacon_interval=%d\n", config.beacon_interval); + WIFI_DBG("\tsave_to_flash=%s\n", save_to_flash ? "true":"false"); +#endif + + bool config_success; + if(save_to_flash) config_success = wifi_softap_set_config(&config); + else config_success = wifi_softap_set_config_current(&config); + lua_pushboolean(L, config_success); + return 1; } // Lua: table = wifi.ap.getclient() @@ -1183,6 +1374,7 @@ static int wifi_ap_listclient( lua_State* L ) return 1; } + // Lua: ip = wifi.ap.dhcp.config() static int wifi_ap_dhcp_config( lua_State* L ) { @@ -1220,6 +1412,7 @@ static int wifi_ap_dhcp_config( lua_State* L ) return 2; } + // Lua: wifi.ap.dhcp.start() static int wifi_ap_dhcp_start( lua_State* L ) { @@ -1227,6 +1420,7 @@ static int wifi_ap_dhcp_start( lua_State* L ) return 1; } + // Lua: wifi.ap.dhcp.stop() static int wifi_ap_dhcp_stop( lua_State* L ) { @@ -1234,28 +1428,35 @@ static int wifi_ap_dhcp_stop( lua_State* L ) return 1; } + // Module function map static const LUA_REG_TYPE wifi_station_map[] = { - { LSTRKEY( "getconfig" ), LFUNCVAL( wifi_station_getconfig ) }, - { LSTRKEY( "config" ), LFUNCVAL( wifi_station_config ) }, - { LSTRKEY( "connect" ), LFUNCVAL( wifi_station_connect4lua ) }, - { LSTRKEY( "disconnect" ), LFUNCVAL( wifi_station_disconnect4lua ) }, - { LSTRKEY( "autoconnect" ), LFUNCVAL( wifi_station_setauto ) }, - { LSTRKEY( "getip" ), LFUNCVAL( wifi_station_getip ) }, - { LSTRKEY( "setip" ), LFUNCVAL( wifi_station_setip ) }, - { LSTRKEY( "getbroadcast" ), LFUNCVAL( wifi_station_getbroadcast) }, - { LSTRKEY( "getmac" ), LFUNCVAL( wifi_station_getmac ) }, - { LSTRKEY( "setmac" ), LFUNCVAL( wifi_station_setmac ) }, - { LSTRKEY( "getap" ), LFUNCVAL( wifi_station_listap ) }, - { LSTRKEY( "sethostname" ), LFUNCVAL( wifi_sta_sethostname_lua ) }, - { LSTRKEY( "gethostname" ), LFUNCVAL( wifi_sta_gethostname ) }, - { LSTRKEY( "getrssi" ), LFUNCVAL( wifi_station_getrssi ) }, - { LSTRKEY( "status" ), LFUNCVAL( wifi_station_status ) }, + { LSTRKEY( "autoconnect" ), LFUNCVAL( wifi_station_setauto ) }, + { LSTRKEY( "changeap" ), LFUNCVAL( wifi_station_change_ap ) }, + { LSTRKEY( "config" ), LFUNCVAL( wifi_station_config ) }, + { LSTRKEY( "connect" ), LFUNCVAL( wifi_station_connect4lua ) }, + { LSTRKEY( "disconnect" ), LFUNCVAL( wifi_station_disconnect4lua ) }, #if defined(WIFI_STATION_STATUS_MONITOR_ENABLE) - { LSTRKEY( "eventMonReg" ), LFUNCVAL( wifi_station_event_mon_reg ) }, //declared in wifi_eventmon.c - { LSTRKEY( "eventMonStart" ), LFUNCVAL( wifi_station_event_mon_start ) }, //declared in wifi_eventmon.c - { LSTRKEY( "eventMonStop" ), LFUNCVAL( wifi_station_event_mon_stop ) }, //declared in wifi_eventmon.c + { LSTRKEY( "eventMonReg" ), LFUNCVAL( wifi_station_event_mon_reg ) }, //defined in wifi_eventmon.c + { LSTRKEY( "eventMonStart" ), LFUNCVAL( wifi_station_event_mon_start ) }, //defined in wifi_eventmon.c + { LSTRKEY( "eventMonStop" ), LFUNCVAL( wifi_station_event_mon_stop ) }, //defined in wifi_eventmon.c #endif + { LSTRKEY( "getap" ), LFUNCVAL( wifi_station_listap ) }, + { LSTRKEY( "getapindex" ), LFUNCVAL( wifi_station_get_ap_index ) }, + { LSTRKEY( "getapinfo" ), LFUNCVAL( wifi_station_get_ap_info4lua ) }, + { LSTRKEY( "getbroadcast" ), LFUNCVAL( wifi_station_getbroadcast) }, + { LSTRKEY( "getconfig" ), LFUNCVAL( wifi_station_getconfig_current ) }, + { LSTRKEY( "getdefaultconfig" ), LFUNCVAL( wifi_station_getconfig_default ) }, + { LSTRKEY( "gethostname" ), LFUNCVAL( wifi_sta_gethostname ) }, + { LSTRKEY( "getip" ), LFUNCVAL( wifi_station_getip ) }, + { LSTRKEY( "getmac" ), LFUNCVAL( wifi_station_getmac ) }, + { LSTRKEY( "getrssi" ), LFUNCVAL( wifi_station_getrssi ) }, + { LSTRKEY( "setaplimit" ), LFUNCVAL( wifi_station_ap_number_set4lua ) }, + { LSTRKEY( "sethostname" ), LFUNCVAL( wifi_sta_sethostname_lua ) }, + { LSTRKEY( "setip" ), LFUNCVAL( wifi_station_setip ) }, + { LSTRKEY( "setmac" ), LFUNCVAL( wifi_station_setmac ) }, + { LSTRKEY( "sleeptype" ), LFUNCVAL( wifi_station_sleeptype ) }, + { LSTRKEY( "status" ), LFUNCVAL( wifi_station_status ) }, { LNILKEY, LNILVAL } }; @@ -1267,23 +1468,25 @@ static const LUA_REG_TYPE wifi_ap_dhcp_map[] = { }; static const LUA_REG_TYPE wifi_ap_map[] = { - { LSTRKEY( "config" ), LFUNCVAL( wifi_ap_config ) }, - { LSTRKEY( "deauth" ), LFUNCVAL( wifi_ap_deauth ) }, - { LSTRKEY( "getip" ), LFUNCVAL( wifi_ap_getip ) }, - { LSTRKEY( "setip" ), LFUNCVAL( wifi_ap_setip ) }, - { LSTRKEY( "getbroadcast" ), LFUNCVAL( wifi_ap_getbroadcast) }, - { LSTRKEY( "getmac" ), LFUNCVAL( wifi_ap_getmac ) }, - { LSTRKEY( "setmac" ), LFUNCVAL( wifi_ap_setmac ) }, - { LSTRKEY( "getclient" ), LFUNCVAL( wifi_ap_listclient ) }, - { LSTRKEY( "getconfig" ), LFUNCVAL( wifi_ap_getconfig ) }, - { LSTRKEY( "dhcp" ), LROVAL( wifi_ap_dhcp_map ) }, -//{ LSTRKEY( "__metatable" ), LROVAL( wifi_ap_map ) }, + { LSTRKEY( "config" ), LFUNCVAL( wifi_ap_config ) }, + { LSTRKEY( "deauth" ), LFUNCVAL( wifi_ap_deauth ) }, + { LSTRKEY( "getip" ), LFUNCVAL( wifi_ap_getip ) }, + { LSTRKEY( "setip" ), LFUNCVAL( wifi_ap_setip ) }, + { LSTRKEY( "getbroadcast" ), LFUNCVAL( wifi_ap_getbroadcast) }, + { LSTRKEY( "getmac" ), LFUNCVAL( wifi_ap_getmac ) }, + { LSTRKEY( "setmac" ), LFUNCVAL( wifi_ap_setmac ) }, + { LSTRKEY( "getclient" ), LFUNCVAL( wifi_ap_listclient ) }, + { LSTRKEY( "getconfig" ), LFUNCVAL( wifi_ap_getconfig_current ) }, + { LSTRKEY( "getdefaultconfig" ), LFUNCVAL( wifi_ap_getconfig_default ) }, + { LSTRKEY( "dhcp" ), LROVAL( wifi_ap_dhcp_map ) }, +//{ LSTRKEY( "__metatable" ), LROVAL( wifi_ap_map ) }, { LNILKEY, LNILVAL } }; static const LUA_REG_TYPE wifi_map[] = { { LSTRKEY( "setmode" ), LFUNCVAL( wifi_setmode ) }, { LSTRKEY( "getmode" ), LFUNCVAL( wifi_getmode ) }, + { LSTRKEY( "getdefaultmode" ), LFUNCVAL( wifi_getdefaultmode ) }, { LSTRKEY( "getchannel" ), LFUNCVAL( wifi_getchannel ) }, { LSTRKEY( "setphymode" ), LFUNCVAL( wifi_setphymode ) }, { LSTRKEY( "getphymode" ), LFUNCVAL( wifi_getphymode ) }, @@ -1293,7 +1496,7 @@ static const LUA_REG_TYPE wifi_map[] = { { LSTRKEY( "startsmart" ), LFUNCVAL( wifi_start_smart ) }, { LSTRKEY( "stopsmart" ), LFUNCVAL( wifi_exit_smart ) }, #endif - { LSTRKEY( "sleeptype" ), LFUNCVAL( wifi_sleeptype ) }, + { LSTRKEY( "sleeptype" ), LFUNCVAL( wifi_station_sleeptype ) }, { LSTRKEY( "sta" ), LROVAL( wifi_station_map ) }, { LSTRKEY( "ap" ), LROVAL( wifi_ap_map ) }, @@ -1330,6 +1533,7 @@ static const LUA_REG_TYPE wifi_map[] = { { LNILKEY, LNILVAL } }; +// Used by user_rf_pre_init(user_main.c) void wifi_change_default_host_name(void) { uint8 opmode_temp=wifi_get_opmode(); diff --git a/app/modules/wifi_common.h b/app/modules/wifi_common.h index 633fd3a4..65b7a7bd 100644 --- a/app/modules/wifi_common.h +++ b/app/modules/wifi_common.h @@ -13,6 +13,9 @@ #include "c_stdio.h" #include "task/task.h" +//#define WIFI_DEBUG +//#define EVENT_DEBUG + void wifi_add_sprintf_field(lua_State* L, char* name, char* string, ...); void wifi_add_int_field(lua_State* L, char* name, lua_Integer integer); @@ -37,7 +40,13 @@ static inline void unregister_lua_cb(lua_State* L, int* cb_ref) void wifi_change_default_host_name(void); -#ifdef NODE_DEBUG +#if defined(WIFI_DEBUG) || defined(NODE_DEBUG) +#define WIFI_DBG(...) c_printf(__VA_ARGS__) +#else +#define WIFI_DBG(...) //c_printf(__VA_ARGS__) +#endif + +#if defined(EVENT_DEBUG) || defined(NODE_DEBUG) #define EVENT_DBG(...) c_printf(__VA_ARGS__) #else #define EVENT_DBG(...) //c_printf(__VA_ARGS__) diff --git a/app/modules/ws2812.c b/app/modules/ws2812.c index 383070d2..c84c7456 100644 --- a/app/modules/ws2812.c +++ b/app/modules/ws2812.c @@ -19,7 +19,6 @@ typedef struct { - int canary; int size; uint8_t colorsPerLed; uint8_t values[0]; @@ -132,8 +131,7 @@ static int ws2812_write(lua_State* L) { } else if (type == LUA_TUSERDATA) { - ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); - luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); buffer1 = buffer->values; length1 = buffer->colorsPerLed*buffer->size; @@ -156,8 +154,7 @@ static int ws2812_write(lua_State* L) { } else if (type == LUA_TUSERDATA) { - ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 2); - luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 2, "ws2812.buffer expected"); + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer"); buffer2 = buffer->values; length2 = buffer->colorsPerLed*buffer->size; @@ -173,14 +170,13 @@ static int ws2812_write(lua_State* L) { return 0; } -// Handle a buffer where we can store led values -static int ws2812_new_buffer(lua_State *L) { - const int leds = luaL_checkint(L, 1); - const int colorsPerLed = luaL_checkint(L, 2); - - luaL_argcheck(L, leds > 0, 1, "should be a positive integer"); - luaL_argcheck(L, colorsPerLed > 0, 2, "should be a positive integer"); +static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) { + /* relative string position: negative means back from end */ + if (pos < 0) pos += (ptrdiff_t)len + 1; + return (pos >= 0) ? pos : 0; +} +static ws2812_buffer *allocate_buffer(lua_State *L, int leds, int colorsPerLed) { // Allocate memory size_t size = sizeof(ws2812_buffer) + colorsPerLed*leds*sizeof(uint8_t); ws2812_buffer * buffer = (ws2812_buffer*)lua_newuserdata(L, size); @@ -193,16 +189,26 @@ static int ws2812_new_buffer(lua_State *L) { buffer->size = leds; buffer->colorsPerLed = colorsPerLed; - // Store canary for future type checks - buffer->canary = CANARY_VALUE; + return buffer; +} + +// Handle a buffer where we can store led values +static int ws2812_new_buffer(lua_State *L) { + const int leds = luaL_checkint(L, 1); + const int colorsPerLed = luaL_checkint(L, 2); + + luaL_argcheck(L, leds > 0, 1, "should be a positive integer"); + luaL_argcheck(L, colorsPerLed > 0, 2, "should be a positive integer"); + + ws2812_buffer * buffer = allocate_buffer(L, leds, colorsPerLed); + + c_memset(buffer->values, 0, colorsPerLed * leds); return 1; } static int ws2812_buffer_fill(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); - - luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); // Grab colors int i, j; @@ -230,11 +236,10 @@ static int ws2812_buffer_fill(lua_State* L) { } static int ws2812_buffer_fade(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); const int fade = luaL_checkinteger(L, 2); unsigned direction = luaL_optinteger( L, 3, FADE_OUT ); - luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); luaL_argcheck(L, fade > 0, 2, "fade value should be a strict positive number"); uint8_t * p = &buffer->values[0]; @@ -260,17 +265,25 @@ static int ws2812_buffer_fade(lua_State* L) { static int ws2812_buffer_shift(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); const int shiftValue = luaL_checkinteger(L, 2); const unsigned shift_type = luaL_optinteger( L, 3, SHIFT_LOGICAL ); - luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); - luaL_argcheck(L, shiftValue > 0-buffer->size && shiftValue < buffer->size, 2, "shifting more elements than buffer size"); + ptrdiff_t start = posrelat(luaL_optinteger(L, 4, 1), buffer->size); + ptrdiff_t end = posrelat(luaL_optinteger(L, 5, -1), buffer->size); + if (start < 1) start = 1; + if (end > (ptrdiff_t)buffer->size) end = (ptrdiff_t)buffer->size; + + start--; + int size = end - start; + size_t offset = start * buffer->colorsPerLed; + + luaL_argcheck(L, shiftValue > 0-size && shiftValue < size, 2, "shifting more elements than buffer size"); int shift = shiftValue >= 0 ? shiftValue : -shiftValue; // check if we want to shift at all - if (shift == 0) + if (shift == 0 || size <= 0) { return 0; } @@ -280,38 +293,38 @@ static int ws2812_buffer_shift(lua_State* L) { size_t shift_len, remaining_len; // calculate length of shift section and remaining section shift_len = shift*buffer->colorsPerLed; - remaining_len = (buffer->size-shift)*buffer->colorsPerLed; + remaining_len = (size-shift)*buffer->colorsPerLed; if (shiftValue > 0) { // Store the values which are moved out of the array (last n pixels) - c_memcpy(tmp_pixels, &buffer->values[(buffer->size-shift)*buffer->colorsPerLed], shift_len); + c_memcpy(tmp_pixels, &buffer->values[offset + (size-shift)*buffer->colorsPerLed], shift_len); // Move pixels to end - os_memmove(&buffer->values[shift*buffer->colorsPerLed], &buffer->values[0], remaining_len); + os_memmove(&buffer->values[offset + shift*buffer->colorsPerLed], &buffer->values[offset], remaining_len); // Fill beginning with temp data if (shift_type == SHIFT_LOGICAL) { - c_memset(&buffer->values[0], 0, shift_len); + c_memset(&buffer->values[offset], 0, shift_len); } else { - c_memcpy(&buffer->values[0], tmp_pixels, shift_len); + c_memcpy(&buffer->values[offset], tmp_pixels, shift_len); } } else { // Store the values which are moved out of the array (last n pixels) - c_memcpy(tmp_pixels, &buffer->values[0], shift_len); + c_memcpy(tmp_pixels, &buffer->values[offset], shift_len); // Move pixels to end - os_memmove(&buffer->values[0], &buffer->values[shift*buffer->colorsPerLed], remaining_len); + os_memmove(&buffer->values[offset], &buffer->values[offset + shift*buffer->colorsPerLed], remaining_len); // Fill beginning with temp data if (shift_type == SHIFT_LOGICAL) { - c_memset(&buffer->values[(buffer->size-shift)*buffer->colorsPerLed], 0, shift_len); + c_memset(&buffer->values[offset + (size-shift)*buffer->colorsPerLed], 0, shift_len); } else { - c_memcpy(&buffer->values[(buffer->size-shift)*buffer->colorsPerLed], tmp_pixels, shift_len); + c_memcpy(&buffer->values[offset + (size-shift)*buffer->colorsPerLed], tmp_pixels, shift_len); } } // Free memory @@ -320,13 +333,107 @@ static int ws2812_buffer_shift(lua_State* L) { return 0; } +static int ws2812_buffer_dump(lua_State* L) { + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + lua_pushlstring(L, buffer->values, buffer->size * buffer->colorsPerLed); + + return 1; +} + +static int ws2812_buffer_replace(lua_State* L) { + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + size_t l = buffer->size; + ptrdiff_t start = posrelat(luaL_optinteger(L, 3, 1), l); + + uint8_t *src; + size_t srcLen; + + if (lua_type(L, 2) == LUA_TSTRING) { + size_t length; + + src = (uint8_t *) lua_tolstring(L, 2, &length); + srcLen = length / buffer->colorsPerLed; + } else { + ws2812_buffer * rhs = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer"); + src = rhs->values; + srcLen = rhs->size; + luaL_argcheck(L, rhs->colorsPerLed == buffer->colorsPerLed, 2, "Buffers have different colors"); + } + + luaL_argcheck(L, srcLen + start - 1 <= buffer->size, 2, "Does not fit into destination"); + + c_memcpy(buffer->values + (start - 1) * buffer->colorsPerLed, src, srcLen * buffer->colorsPerLed); + + return 0; +} + +// buffer:mix(factor1, buffer1, ..) +// factor is 256 for 100% +// uses saturating arithmetic (one buffer at a time) +static int ws2812_buffer_mix(lua_State* L) { + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + + int pos = 2; + size_t cells = buffer->size * buffer->colorsPerLed; + + int n_sources = (lua_gettop(L) - 1) / 2; + + struct { + int factor; + const uint8_t *values; + } source[n_sources]; + + int src; + for (src = 0; src < n_sources; src++, pos += 2) { + int factor = luaL_checkinteger(L, pos); + ws2812_buffer *src_buffer = (ws2812_buffer*) luaL_checkudata(L, pos + 1, "ws2812.buffer"); + + luaL_argcheck(L, src_buffer->size == buffer->size && src_buffer->colorsPerLed == buffer->colorsPerLed, pos + 1, "Buffer not same shape"); + + source[src].factor = factor; + source[src].values = src_buffer->values; + } + + size_t i; + for (i = 0; i < cells; i++) { + int val = 0; + for (src = 0; src < n_sources; src++) { + val += ((int)(source[src].values[i] * source[src].factor) >> 8); + } + + if (val < 0) { + val = 0; + } else if (val > 255) { + val = 255; + } + buffer->values[i] = val; + } + + return 0; +} + +// Returns the total of all channels +static int ws2812_buffer_power(lua_State* L) { + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + + size_t cells = buffer->size * buffer->colorsPerLed; + + size_t i; + int total = 0; + for (i = 0; i < cells; i++) { + total += buffer->values[i]; + } + + lua_pushnumber(L, total); + + return 1; +} static int ws2812_buffer_get(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); const int led = luaL_checkinteger(L, 2) - 1; - luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range"); int i; @@ -339,10 +446,9 @@ static int ws2812_buffer_get(lua_State* L) { } static int ws2812_buffer_set(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); const int led = luaL_checkinteger(L, 2) - 1; - luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range"); int type = lua_type(L, 3); @@ -387,24 +493,94 @@ static int ws2812_buffer_set(lua_State* L) { } static int ws2812_buffer_size(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); - - luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); lua_pushnumber(L, buffer->size); return 1; } +static int ws2812_buffer_sub(lua_State* L) { + ws2812_buffer * lhs = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + size_t l = lhs->size; + ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l); + ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l); + if (start < 1) start = 1; + if (end > (ptrdiff_t)l) end = (ptrdiff_t)l; + if (start <= end) { + ws2812_buffer *result = allocate_buffer(L, end - start + 1, lhs->colorsPerLed); + c_memcpy(result->values, lhs->values + lhs->colorsPerLed * (start - 1), lhs->colorsPerLed * (end - start + 1)); + } else { + ws2812_buffer *result = allocate_buffer(L, 0, lhs->colorsPerLed); + } + return 1; +} + +static int ws2812_buffer_concat(lua_State* L) { + ws2812_buffer * lhs = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + ws2812_buffer * rhs = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer"); + + luaL_argcheck(L, lhs->colorsPerLed == rhs->colorsPerLed, 1, "Can only concatenate buffers with same colors"); + + int colorsPerLed = lhs->colorsPerLed; + int leds = lhs->size + rhs->size; + + ws2812_buffer * buffer = allocate_buffer(L, leds, colorsPerLed); + + c_memcpy(buffer->values, lhs->values, lhs->colorsPerLed * lhs->size); + c_memcpy(buffer->values + lhs->colorsPerLed * lhs->size, rhs->values, rhs->colorsPerLed * rhs->size); + + return 1; +} + +static int ws2812_buffer_tostring(lua_State* L) { + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + + luaL_Buffer result; + luaL_buffinit(L, &result); + + luaL_addchar(&result, '['); + int i; + int p = 0; + for (i = 0; i < buffer->size; i++) { + int j; + if (i > 0) { + luaL_addchar(&result, ','); + } + luaL_addchar(&result, '('); + for (j = 0; j < buffer->colorsPerLed; j++, p++) { + if (j > 0) { + luaL_addchar(&result, ','); + } + char numbuf[5]; + c_sprintf(numbuf, "%d", buffer->values[p]); + luaL_addstring(&result, numbuf); + } + luaL_addchar(&result, ')'); + } + + luaL_addchar(&result, ']'); + luaL_pushresult(&result); + + return 1; +} + static const LUA_REG_TYPE ws2812_buffer_map[] = { + { LSTRKEY( "dump" ), LFUNCVAL( ws2812_buffer_dump )}, { LSTRKEY( "fade" ), LFUNCVAL( ws2812_buffer_fade )}, { LSTRKEY( "fill" ), LFUNCVAL( ws2812_buffer_fill )}, { LSTRKEY( "get" ), LFUNCVAL( ws2812_buffer_get )}, + { LSTRKEY( "replace" ), LFUNCVAL( ws2812_buffer_replace )}, + { LSTRKEY( "mix" ), LFUNCVAL( ws2812_buffer_mix )}, + { LSTRKEY( "power" ), LFUNCVAL( ws2812_buffer_power )}, { LSTRKEY( "set" ), LFUNCVAL( ws2812_buffer_set )}, - { LSTRKEY( "size" ), LFUNCVAL( ws2812_buffer_size )}, { LSTRKEY( "shift" ), LFUNCVAL( ws2812_buffer_shift )}, + { LSTRKEY( "size" ), LFUNCVAL( ws2812_buffer_size )}, + { LSTRKEY( "sub" ), LFUNCVAL( ws2812_buffer_sub )}, + { LSTRKEY( "__concat" ),LFUNCVAL( ws2812_buffer_concat )}, { LSTRKEY( "__index" ), LROVAL( ws2812_buffer_map )}, + { LSTRKEY( "__tostring" ), LFUNCVAL( ws2812_buffer_tostring )}, { LNILKEY, LNILVAL} }; diff --git a/app/net/nodemcu_mdns.c b/app/net/nodemcu_mdns.c index 3e434e16..ea743888 100644 --- a/app/net/nodemcu_mdns.c +++ b/app/net/nodemcu_mdns.c @@ -52,7 +52,7 @@ #include "nodemcu_mdns.h" #if 0 -#define MDNS_DBG(...) os_printf(...) +#define MDNS_DBG(...) dbg_printf(...) #else #define MDNS_DBG(...) do {} while (0) #endif diff --git a/app/platform/vfs.c b/app/platform/vfs.c index 9baad28d..0446a11b 100644 --- a/app/platform/vfs.c +++ b/app/platform/vfs.c @@ -355,11 +355,12 @@ sint32_t vfs_chdir( const char *path ) sint32_t vfs_errno( const char *name ) { vfs_fs_fns *fs_fns; - const char *normname = normalize_path( name ); char *outname; if (!name) name = ""; // current drive + const char *normname = normalize_path( name ); + #ifdef BUILD_SPIFFS if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) { return fs_fns->ferrno( ); diff --git a/app/platform/vfs.h b/app/platform/vfs.h index 5a468255..572d0372 100644 --- a/app/platform/vfs.h +++ b/app/platform/vfs.h @@ -85,7 +85,7 @@ inline sint32_t vfs_flush( int fd ) { // Returns: File size inline uint32_t vfs_size( int fd ) { vfs_file *f = (vfs_file *)fd; - return f && f->fns->size ? f->fns->size( f ) : 0; + return f ? f->fns->size( f ) : 0; } // vfs_ferrno - get file system specific errno diff --git a/app/spiffs/spiffs.c b/app/spiffs/spiffs.c index 1a6a465f..3b71cc03 100644 --- a/app/spiffs/spiffs.c +++ b/app/spiffs/spiffs.c @@ -2,6 +2,8 @@ #include "platform.h" #include "spiffs.h" +#include "spiffs_nucleus.h" + spiffs fs; #define LOG_PAGE_SIZE 256 @@ -10,9 +12,9 @@ spiffs fs; #define MIN_BLOCKS_FS 4 static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2]; -static u8_t spiffs_fds[32*4]; +static u8_t spiffs_fds[sizeof(spiffs_fd) * SPIFFS_MAX_OPEN_FILES]; #if SPIFFS_CACHE -static u8_t spiffs_cache[(LOG_PAGE_SIZE+32)*2]; +static u8_t myspiffs_cache[(LOG_PAGE_SIZE+32)*2]; #endif static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) { @@ -168,8 +170,8 @@ static bool myspiffs_mount_internal(bool force_mount) { spiffs_fds, sizeof(spiffs_fds), #if SPIFFS_CACHE - spiffs_cache, - sizeof(spiffs_cache), + myspiffs_cache, + sizeof(myspiffs_cache), #else 0, 0, #endif @@ -243,6 +245,7 @@ static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int static sint32_t myspiffs_vfs_eof( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_tell( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_flush( const struct vfs_file *fd ); +static uint32_t myspiffs_vfs_size( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_closedir( const struct vfs_dir *dd ); @@ -295,7 +298,7 @@ static vfs_file_fns myspiffs_file_fns = { .eof = myspiffs_vfs_eof, .tell = myspiffs_vfs_tell, .flush = myspiffs_vfs_flush, - .size = NULL, + .size = myspiffs_vfs_size, .ferrno = myspiffs_vfs_ferrno }; @@ -430,13 +433,17 @@ static sint32_t myspiffs_vfs_close( const struct vfs_file *fd ) { static sint32_t myspiffs_vfs_read( const struct vfs_file *fd, void *ptr, size_t len ) { GET_FILE_FH(fd); - return SPIFFS_read( &fs, fh, ptr, len ); + sint32_t n = SPIFFS_read( &fs, fh, ptr, len ); + + return n >= 0 ? n : VFS_RES_ERR; } static sint32_t myspiffs_vfs_write( const struct vfs_file *fd, const void *ptr, size_t len ) { GET_FILE_FH(fd); - return SPIFFS_write( &fs, fh, (void *)ptr, len ); + sint32_t n = SPIFFS_write( &fs, fh, (void *)ptr, len ); + + return n >= 0 ? n : VFS_RES_ERR; } static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int whence ) { @@ -456,7 +463,8 @@ static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int break; } - return SPIFFS_lseek( &fs, fh, off, spiffs_whence ); + sint32_t res = SPIFFS_lseek( &fs, fh, off, spiffs_whence ); + return res >= 0 ? res : VFS_RES_ERR; } static sint32_t myspiffs_vfs_eof( const struct vfs_file *fd ) { @@ -477,6 +485,16 @@ static sint32_t myspiffs_vfs_flush( const struct vfs_file *fd ) { return SPIFFS_fflush( &fs, fh ) >= 0 ? VFS_RES_OK : VFS_RES_ERR; } +static uint32_t myspiffs_vfs_size( const struct vfs_file *fd ) { + GET_FILE_FH(fd); + + int32_t curpos = SPIFFS_tell( &fs, fh ); + int32_t size = SPIFFS_lseek( &fs, fh, 0, SPIFFS_SEEK_END ); + (void) SPIFFS_lseek( &fs, fh, curpos, SPIFFS_SEEK_SET ); + + return size; +} + static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd ) { return SPIFFS_errno( &fs ); } diff --git a/app/spiffs/spiffs_config.h b/app/spiffs/spiffs_config.h index d4e161c1..40394a3c 100644 --- a/app/spiffs/spiffs_config.h +++ b/app/spiffs/spiffs_config.h @@ -20,19 +20,19 @@ // Set generic spiffs debug output call. #ifndef SPIFFS_DBG -#define SPIFFS_DBG(...) //printf(__VA_ARGS__) +#define SPIFFS_DBG(...) //dbg_printf(__VA_ARGS__) #endif // Set spiffs debug output call for garbage collecting. #ifndef SPIFFS_GC_DBG -#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__) +#define SPIFFS_GC_DBG(...) //dbg_printf(__VA_ARGS__) #endif // Set spiffs debug output call for caching. #ifndef SPIFFS_CACHE_DBG -#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__) +#define SPIFFS_CACHE_DBG(...) //dbg_printf(__VA_ARGS__) #endif // Set spiffs debug output call for system consistency checks. #ifndef SPIFFS_CHECK_DBG -#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__) +#define SPIFFS_CHECK_DBG(...) //dbg_printf(__VA_ARGS__) #endif // Enable/disable API functions to determine exact number of bytes @@ -211,7 +211,7 @@ #endif #if SPIFFS_TEST_VISUALISATION #ifndef spiffs_printf -#define spiffs_printf(...) printf(__VA_ARGS__) +#define spiffs_printf(...) dbg_printf(__VA_ARGS__) #endif // spiffs_printf argument for a free page #ifndef SPIFFS_TEST_VIS_FREE_STR diff --git a/app/spiffs/spiffs_nucleus.c b/app/spiffs/spiffs_nucleus.c index b4d32e95..6c6b61d7 100644 --- a/app/spiffs/spiffs_nucleus.c +++ b/app/spiffs/spiffs_nucleus.c @@ -292,27 +292,60 @@ s32_t spiffs_probe( SPIFFS_CHECK_RES(res); } - // check that we have sane number of blocks - if (bix_count[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS; // check that the order is correct, take aborted erases in calculation + // Note that bix_count[0] should be blockcnt, [1] should be blockcnt - 1 + // and [2] should be blockcnt - 3 // first block aborted erase - if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) { - return (bix_count[1]+1) * cfg->log_block_size; - } + int fs_size; + if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 2) { + fs_size = bix_count[1]+1; + } else // second block aborted erase - if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) { - return bix_count[0] * cfg->log_block_size; - } + if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 3) { + fs_size = bix_count[0]; + } else // third block aborted erase if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) { - return bix_count[0] * cfg->log_block_size; - } + fs_size = bix_count[0]; + } else // no block has aborted erase - if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) { - return bix_count[0] * cfg->log_block_size; + if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 2) { + fs_size = bix_count[0]; + } else { + return SPIFFS_ERR_PROBE_NOT_A_FS; } - return SPIFFS_ERR_PROBE_NOT_A_FS; + // check that we have sane number of blocks + if (fs_size < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS; + + dummy_fs.block_count = fs_size; + + // Now verify that there is at least one good block at the end + for (bix = fs_size - 1; bix >= 3; bix--) { + spiffs_obj_id end_magic; + paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix); +#if SPIFFS_HAL_CALLBACK_EXTRA + // not any proper fs to report here, so callback with null + // (cross fingers that no-one gets angry) + res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&end_magic); +#else + res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&end_magic); +#endif + if (res < 0) { + return SPIFFS_ERR_PROBE_NOT_A_FS; + } + if (end_magic == (spiffs_obj_id)(-1)) { + if (bix < fs_size - 1) { + return SPIFFS_ERR_PROBE_NOT_A_FS; + } + } else if (end_magic != SPIFFS_MAGIC(&dummy_fs, bix)) { + return SPIFFS_ERR_PROBE_NOT_A_FS; + } else { + break; + } + } + + return fs_size * cfg->log_block_size; } #endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 diff --git a/app/spiffs/spiffs_nucleus.h b/app/spiffs/spiffs_nucleus.h index df7a9d8e..f6acaacf 100644 --- a/app/spiffs/spiffs_nucleus.h +++ b/app/spiffs/spiffs_nucleus.h @@ -137,7 +137,7 @@ ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) #else // SPIFFS_USE_MAGIC_LENGTH #define SPIFFS_MAGIC(fs, bix) \ - ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix)))) + ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - ((bix) < 3 ? (1<<(bix)) - 1 : (bix)<<2)))) #endif // SPIFFS_USE_MAGIC_LENGTH #endif // SPIFFS_USE_MAGIC diff --git a/app/u8glib/u8g.h b/app/u8glib/u8g.h index 6103ffdc..c1d6e02b 100644 --- a/app/u8glib/u8g.h +++ b/app/u8glib/u8g.h @@ -1178,9 +1178,6 @@ struct _u8g_t u8g_state_cb state_cb; u8g_box_t current_page; /* current box of the visible page */ - - uint8_t i2c_addr; - uint8_t use_delay; }; #define u8g_GetFontAscent(u8g) ((u8g)->font_ref_ascent) diff --git a/app/user/Makefile b/app/user/Makefile index 50da583a..87f4be55 100644 --- a/app/user/Makefile +++ b/app/user/Makefile @@ -24,7 +24,7 @@ STD_CFLAGS=-std=gnu11 -Wimplicit # makefile at its root level - these are then overridden # for a subtree within the makefile rooted therein # -#DEFINES += +DEFINES += -DESP_INIT_DATA_DEFAULT="\"$(SDK_DIR)/bin/esp_init_data_default.bin\"" ############################################################# # Recursion Magic - Don't touch this!! diff --git a/app/user/user_main.c b/app/user/user_main.c index 3c0189b4..320969b5 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -29,11 +29,21 @@ #include "rtc/rtctime.h" #endif -#define SIG_LUA 0 -#define SIG_UARTINPUT 1 -#define TASK_QUEUE_LEN 4 +static task_handle_t input_sig; +static uint8 input_sig_flag = 0; -static os_event_t *taskQueue; +/* Contents of esp_init_data_default.bin */ +extern const uint32_t init_data[]; +extern const uint32_t init_data_end[]; +__asm__( + /* Place in .text for same reason as user_start_trampoline */ + ".section \".text\"\n" + ".align 4\n" + "init_data:\n" + ".incbin \"" ESP_INIT_DATA_DEFAULT "\"\n" + "init_data_end:\n" + ".previous\n" +); /* Note: the trampoline *must* be explicitly put into the .text segment, since * by the time it is invoked the irom has not yet been mapped. This naturally @@ -50,6 +60,31 @@ void TEXT_SECTION_ATTR user_start_trampoline (void) rtctime_early_startup (); #endif + /* Re-implementation of default init data deployment. The SDK does not + * appear to be laying down its own version of init data anymore, so + * we have to do it again. To see whether we need to, we read out + * the flash size and do a test for esp_init_data based on that size. + * If it's missing, we need to initialize it *right now* before the SDK + * starts up and gets stuck at "rf_cal[0] !=0x05,is 0xFF". + * If the size byte is wrong, then we'll end up fixing up the init data + * again on the next boot, after we've corrected the size byte. + * Only remaining issue is lack of spare code bytes in iram, so this + * is deliberately quite terse and not as readable as one might like. + */ + SPIFlashInfo sfi; + SPIRead (0, (uint32_t *)(&sfi), sizeof (sfi)); // Cache read not enabled yet, safe to use + if (sfi.size < 2) // Compensate for out-of-order 4mbit vs 2mbit values + sfi.size ^= 1; + uint32_t flash_end_addr = (256 * 1024) << sfi.size; + uint32_t init_data_hdr = 0xffffffff; + uint32_t init_data_addr = flash_end_addr - 4 * SPI_FLASH_SEC_SIZE; + SPIRead (init_data_addr, &init_data_hdr, sizeof (init_data_hdr)); + if (init_data_hdr == 0xffffffff) + { + SPIEraseSector (init_data_addr); + SPIWrite (init_data_addr, init_data, 4 * (init_data_end - init_data)); + } + call_user_start (); } @@ -58,17 +93,18 @@ static void start_lua(task_param_t param, uint8 priority) { char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL }; NODE_DBG("Task task_lua started.\n"); lua_main( 2, lua_argv ); + // Only enable UART interrupts once we've successfully started up, + // otherwise the task queue might fill up with input events and prevent + // the start_lua task from being posted. + ETS_UART_INTR_ENABLE(); } static void handle_input(task_param_t flag, uint8 priority) { -// c_printf("HANDLE_INPUT: %u %u\n", flag, priority); REMOVE - lua_handle_input (flag); -} - -static task_handle_t input_sig; - -task_handle_t user_get_input_sig(void) { - return input_sig; + (void)priority; + if (flag & 0x8000) { + input_sig_flag = flag & 0x4000 ? 1 : 0; + } + lua_handle_input (flag & 0x01); } bool user_process_input(bool force) { @@ -92,9 +128,7 @@ void nodemcu_init(void) // Fit hardware real flash size. flash_rom_set_size_byte(flash_safe_get_size_byte()); - // Reboot to get SDK to use (or write) init data at new location system_restart (); - // Don't post the start_lua task, we're about to reboot... return; } @@ -107,7 +141,7 @@ void nodemcu_init(void) #ifdef BUILD_SPIFFS if (!vfs_mount("/FLASH", 0)) { // Failed to mount -- try reformat - c_printf("Formatting file system. Please wait...\n"); + dbg_printf("Formatting file system. Please wait...\n"); if (!vfs_format()) { NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" ); NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" ); @@ -118,7 +152,8 @@ void nodemcu_init(void) #endif // endpoint_setup(); - task_post_low(task_get_id(start_lua),'s'); + if (!task_post_low(task_get_id(start_lua),'s')) + NODE_ERR("Failed to post the start_lua task!\n"); } #ifdef LUA_USE_MODULES_WIFI @@ -146,7 +181,7 @@ void user_rf_pre_init(void) uint32 user_rf_cal_sector_set(void) { - enum flash_size_map size_map = system_get_flash_size_map(); + enum ext_flash_size_map size_map = system_get_flash_size_map(); uint32 rf_cal_sec = 0; switch (size_map) { @@ -165,9 +200,18 @@ user_rf_cal_sector_set(void) case FLASH_SIZE_32M_MAP_512_512: case FLASH_SIZE_32M_MAP_1024_1024: + case FLASH_SIZE_32M_MAP_2048_2048: rf_cal_sec = 1024 - 5; break; + case FLASH_SIZE_64M_MAP: + rf_cal_sec = 2048 - 5; + break; + + case FLASH_SIZE_128M_MAP: + rf_cal_sec = 4096 - 5; + break; + default: rf_cal_sec = 0; break; @@ -191,7 +235,7 @@ void user_init(void) UartBautRate br = BIT_RATE_DEFAULT; input_sig = task_get_id(handle_input); - uart_init (br, br, input_sig); + uart_init (br, br, input_sig, &input_sig_flag); #ifndef NODE_DEBUG system_set_os_print(0); diff --git a/app/websocket/websocketclient.c b/app/websocket/websocketclient.c index d9fa29ce..834996e7 100644 --- a/app/websocket/websocketclient.c +++ b/app/websocket/websocketclient.c @@ -47,18 +47,10 @@ #define PORT_INSECURE 80 #define PORT_MAX_VALUE 65535 -// TODO: user agent configurable -#define WS_INIT_HEADERS "GET %s HTTP/1.1\r\n"\ - "Host: %s:%d\r\n"\ - "Upgrade: websocket\r\n"\ - "Connection: Upgrade\r\n"\ - "User-Agent: ESP8266\r\n"\ - "Sec-Websocket-Key: %s\r\n"\ - "Sec-WebSocket-Protocol: chat\r\n"\ - "Sec-WebSocket-Version: 13\r\n"\ - "\r\n" +#define WS_INIT_REQUEST "GET %s HTTP/1.1\r\n"\ + "Host: %s:%d\r\n" -#define WS_INIT_HEADERS_LENGTH 169 +#define WS_INIT_REQUEST_LENGTH 30 #define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" #define WS_GUID_LENGTH 36 @@ -77,6 +69,13 @@ #define WS_OPCODE_PING 0x9 #define WS_OPCODE_PONG 0xA +header_t DEFAULT_HEADERS[] = { + {"User-Agent", "ESP8266"}, + {"Sec-WebSocket-Protocol", "chat"}, + {0} +}; +header_t *EMPTY_HEADERS = DEFAULT_HEADERS + sizeof(DEFAULT_HEADERS) / sizeof(header_t) - 1; + static char *cryptoSha1(char *data, unsigned int len) { SHA1_CTX ctx; SHA1Init(&ctx); @@ -128,6 +127,44 @@ static void generateSecKeys(char **key, char **expectedKey) { os_free(keyEncrypted); } +static char *_strcpy(char *dst, char *src) { + while(*dst++ = *src++); + return dst - 1; +} + +static int headers_length(header_t *headers) { + int length = 0; + for(; headers->key; headers++) + length += strlen(headers->key) + strlen(headers->value) + 4; + return length; +} + +static char *sprintf_headers(char *buf, ...) { + char *dst = buf; + va_list args; + va_start(args, buf); + for(header_t *header_set = va_arg(args, header_t *); header_set; header_set = va_arg(args, header_t *)) + for(header_t *header = header_set; header->key; header++) { + va_list args2; + va_start(args2, buf); + for(header_t *header_set2 = va_arg(args2, header_t *); header_set2; header_set2 = va_arg(args2, header_t *)) + for(header_t *header2 = header_set2; header2->key; header2++) { + if(header == header2) + goto ok; + if(!strcasecmp(header->key, header2->key)) + goto skip; + } +ok: + dst = _strcpy(dst, header->key); + dst = _strcpy(dst, ": "); + dst = _strcpy(dst, header->value); + dst = _strcpy(dst, "\r\n"); +skip:; + } + dst = _strcpy(dst, "\r\n"); + return dst; +} + static void ws_closeSentCallback(void *arg) { NODE_DBG("ws_closeSentCallback \n"); struct espconn *conn = (struct espconn *) arg; @@ -452,7 +489,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) { } else if (opCode == WS_OPCODE_PONG) { // ping alarm was already reset... } else { - if (ws->onReceive) ws->onReceive(ws, payload, opCode); + if (ws->onReceive) ws->onReceive(ws, payloadLength, payload, opCode); } os_free(payload); } @@ -509,7 +546,7 @@ static void ws_initReceiveCallback(void *arg, char *buf, unsigned short len) { } // Check server has valid sec key - if (strstr(buf, WS_HTTP_SEC_WEBSOCKET_ACCEPT) == NULL || strstr(buf, ws->expectedSecKey) == NULL) { + if (strstr(buf, ws->expectedSecKey) == NULL) { NODE_DBG("Server has invalid response\n"); ws->knownFailureCode = -7; if (ws->isSecure) @@ -550,12 +587,31 @@ static void connect_callback(void *arg) { char *key; generateSecKeys(&key, &ws->expectedSecKey); - char buf[WS_INIT_HEADERS_LENGTH + strlen(ws->path) + strlen(ws->hostname) + strlen(key)]; - int len = os_sprintf(buf, WS_INIT_HEADERS, ws->path, ws->hostname, ws->port, key); + header_t headers[] = { + {"Upgrade", "websocket"}, + {"Connection", "Upgrade"}, + {"Sec-WebSocket-Key", key}, + {"Sec-WebSocket-Version", "13"}, + {0} + }; + + header_t *extraHeaders = ws->extraHeaders ? ws->extraHeaders : EMPTY_HEADERS; + + char buf[WS_INIT_REQUEST_LENGTH + strlen(ws->path) + strlen(ws->hostname) + + headers_length(DEFAULT_HEADERS) + headers_length(headers) + headers_length(extraHeaders) + 2]; + + int len = os_sprintf( + buf, + WS_INIT_REQUEST, + ws->path, + ws->hostname, + ws->port + ); + + len = sprintf_headers(buf + len, headers, extraHeaders, DEFAULT_HEADERS, 0) - buf; os_free(key); - - NODE_DBG("connecting\n"); + NODE_DBG("request: %s", buf); if (ws->isSecure) espconn_secure_send(conn, (uint8_t *) buf, len); else @@ -630,7 +686,7 @@ static void dns_callback(const char *hostname, ip_addr_t *addr, void *arg) { ws_info *ws = (ws_info *) conn->reverse; if (ws->conn == NULL || ws->connectionState == 4) { - return; + return; } if (addr == NULL) { diff --git a/app/websocket/websocketclient.h b/app/websocket/websocketclient.h index 35746b48..0c029418 100644 --- a/app/websocket/websocketclient.h +++ b/app/websocket/websocketclient.h @@ -40,9 +40,14 @@ struct ws_info; typedef void (*ws_onConnectionCallback)(struct ws_info *wsInfo); -typedef void (*ws_onReceiveCallback)(struct ws_info *wsInfo, char *message, int opCode); +typedef void (*ws_onReceiveCallback)(struct ws_info *wsInfo, int len, char *message, int opCode); typedef void (*ws_onFailureCallback)(struct ws_info *wsInfo, int errorCode); +typedef struct { + char *key; + char *value; +} header_t; + typedef struct ws_info { int connectionState; @@ -51,6 +56,7 @@ typedef struct ws_info { int port; char *path; char *expectedSecKey; + header_t *extraHeaders; struct espconn *conn; void *reservedData; diff --git a/docs/en/flash.md b/docs/en/flash.md index 21771f63..3a5f6560 100644 --- a/docs/en/flash.md +++ b/docs/en/flash.md @@ -1,13 +1,20 @@ -Adafruit provides a really nice [firmware flashing tutorial](https://learn.adafruit.com/building-and-running-micropython-on-the-esp8266/flash-firmware). Below you'll find just the basics for the two popular tools esptool and NodeMCU Flasher. +Below you'll find all necessary information to flash a NodeMCU firmware binary to ESP8266 or ESP8285. Note that this is a reference documentation and not a tutorial with fancy screen shots. Turn to your favorite search engine for those. Make sure you follow a recent tutorial rather than one that is several months old! !!! attention Keep in mind that the ESP8266 needs to be [put into flash mode](#putting-device-into-flash-mode) before you can flash a new firmware! -## esptool.py -> A cute Python utility to communicate with the ROM bootloader in Espressif ESP8266. It is intended to be a simple, platform independent, open source replacement for XTCOM. +!!! important -Source: [https://github.com/themadinventor/esptool](https://github.com/themadinventor/esptool) + When switching between NodeMCU versions, see the notes about + [Upgrading Firmware](#upgrading-firmware). + +## Tool overview + +### esptool.py +> A Python-based, open source, platform independent, utility to communicate with the ROM bootloader in Espressif ESP8266. + +Source: [https://github.com/espressif/esptool](https://github.com/espressif/esptool) Supported platforms: OS X, Linux, Windows, anything that runs Python @@ -15,27 +22,33 @@ Supported platforms: OS X, Linux, Windows, anything that runs Python Run the following command to flash an *aggregated* binary as is produced for example by the [cloud build service](build.md#cloud-build-service) or the [Docker image](build.md#docker-image). -`esptool.py --port write_flash -fm -fs 0x00000 .bin` +`esptool.py --port write_flash -fm 0x00000 .bin` -- `mode` is `qio` for 512 kByte modules and `dio` for 4 MByte modules (`qio` might work as well, YMMV). -- `size` is given in bits. Specify `4m` for 512 kByte and `32m` for 4 MByte. +`mode` is `qio` for 512 kByte modules and `dio` for >=4 MByte modules (`qio` might work as well, YMMV). -Check the [esptool flash modes documentation](https://github.com/themadinventor/esptool#flash-modes) for details and other options. +**Gotchas** -## NodeMCU Flasher +- See [below](#determine-flash-size) if you don't know or are uncertain about the capacity of the flash chip on your device. It might help to double check as e.g. some ESP-01 modules come with 512kB while others are equipped with 1MB. +- esptool.py is under heavy development. It's advised you run the latest version (check with `esptool.py version`). Since this documentation may not have been able to keep up refer to the [esptool flash modes documentation](https://github.com/themadinventor/esptool#flash-modes) for current options and parameters. +- In some uncommon cases, the [SDK init data](#sdk-init-data) may be invalid and NodeMCU may fail to boot. The easiest solution is to fully erase the chip before flashing: +`esptool.py --port erase_flash` + +### NodeMCU Flasher > A firmware Flash tool for NodeMCU...We are working on next version and will use QT framework. It will be cross platform and open-source. Source: [https://github.com/nodemcu/nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher) Supported platforms: Windows +Note that this tool was created by the initial developers of the NodeMCU firmware. It hasn't seen updates since September 2015 and is not maintained by the current NodeMCU *firmware* team. Be careful to not accidentally flash the very old default firmware the tool is shipped with. + ## Putting Device Into Flash Mode To enable ESP8266 firmware flashing GPIO0 pin must be pulled low before the device is reset. Conversely, for a normal boot, GPIO0 must be pulled high or floating. If you have a [NodeMCU dev kit](https://github.com/nodemcu/nodemcu-devkit-v1.0) then you don't need to do anything, as the USB connection can pull GPIO0 low by asserting DTR and reset your board by asserting RTS. -If you have an ESP-01 or other device without built-in USB, you will need to enable flashing yourself by pulling GPIO0 low or pressing a "flash" switch. +If you have an ESP-01 or other device without built-in USB, you will need to enable flashing yourself by pulling GPIO0 low or pressing a "flash" switch, while powering up or resetting the module. ## Which Files To Flash @@ -46,55 +59,59 @@ Otherwise, if you built your own firmware from source code: - `bin/0x00000.bin` to 0x00000 - `bin/0x10000.bin` to 0x10000 -Also, in some special circumstances, you may need to flash `blank.bin` or `esp_init_data_default.bin` to various addresses on the flash (depending on flash size and type), see [below](#upgrading-from-sdk-09x-firmware). - ## Upgrading Firmware -!!! important +There are three potential issues that arise from upgrading (or downgrading!) firmware from one NodeMCU version to another: - It goes without saying that you shouldn't expect your NodeMCU 0.9.x Lua scripts to work error-free on a more recent firmware. Most notably Espressif changed the `socket:send` operation to be asynchronous i.e. non-blocking. See [API documentation](modules/net.md#netsocketsend) for details. +* Lua scripts written for one NodeMCU version (like 0.9.x) may not work error-free on a more recent firmware. For example, Espressif changed the `socket:send` operation to be asynchronous i.e. non-blocking. See [API documentation](modules/net.md#netsocketsend) for details. -Espressif changes the init data block (`esp_init_data_default.bin`) for their devices along the way with the SDK. So things break when a NodeMCU firmware with a certain SDK is flashed to a module which contains init data from a different SDK. Hence, this section applies to upgrading NodeMCU firmware just as well as *downgrading* firmware. +* The NodeMCU flash file system may need to be reformatted, particularly if its address has changed because the new firmware is different in size from the old firmware. If it is not automatically formatted then it should be valid and have the same contents as before the flash operation. You can still run [`file.format()`](modules/file.md#fileformat) manually to re-format your flash file system. You will know if you need to do this if your flash files exist but seem empty, or if data cannot be written to new files. However, this should be an exceptional case. +Formatting a file system on a large flash device (e.g. the 16MB parts) can take some time. So, on the first boot, you shouldn't get worried if nothing appears to happen for a minute. There's a message printed to console to make you aware of this. -A typical case that often fails is when a module is upgraded from a 0.9.x firmware to a recent version. It might look like the new firmware is broken, but the reason for the missing Lua prompt is related to the big jump in SDK versions. +* The Espressif SDK Init Data may change between each NodeMCU firmware version, and may need to be erased or reflashed. See [SDK Init Data](#sdk-init-data) for details. Fully erasing the module before upgrading firmware will avoid this issue. -If there is no init data block found during SDK startup, the SDK will install one itself. If there is a previous (potentially too old) init block, the SDK *probably* doesn't do anything with it but there is no documentation from Espressif on this topic. +## SDK Init Data -Hence, there are two strategies to update the SDK init data: +!!! note -- Erase flash completely. This will also erase the (Lua) files you uploaded to the device! The SDK will install the init data block during startup. -- Don't erase the flash but replace just the init data with a new file during the flashing procedure. For this you would download [SDK patch 1.5.4.1](http://bbs.espressif.com/download/file.php?id=1572) and extract `esp_init_data_default.bin` from there. + Normally, NodeMCU will take care of writing the SDK init data when needed. Most users can ignore this section. -When flashing a new firmware (particularly with a much different size), the flash filesystem may be reformatted as the firmware starts. If it is not automatically reformatted, then it should be valid and have the same contents as before the flash operation. You can still run [`file.format()`](modules/file.md#fileformat) to re-format your flash filesystem. You will know if you need to do this if your flash files exist but seem empty, or if data cannot be written to new files. However, this should be an exceptional case. +NodeMCU versions are compiled against specific versions of the Espressif SDK. The SDK reserves space in flash that is used to store calibration and other data. This data changes between SDK versions, and if it is invalid or not present, the firmware may not boot correctly. Symptoms include messages like `rf_cal[0] !=0x05,is 0xFF`, or endless reboot loops and/or fast blinking module LEDs. -**esptool.py** +!!! tip -For [esptool.py](https://github.com/themadinventor/esptool) you specify the init data file as an additional file for the `write_flash` command. + If you are seeing one or several of the above symptoms, ensure that your chip is fully erased before flashing, for example: + + `esptool.py --port erase_flash` + + Also verify that you are using an up-to-date NodeMCU release, as some early releases of NodeMCU 1.5.4.1 did not write the SDK init data to a freshly erased chip. + +Espressif refers to this area as "System Param" and it resides in the last four 4 kB sectors of flash. Since SDK 1.5.4.1 a fifth sector is reserved for RF calibration (and its placement is controlled by NodeMCU) as described by this [patch notice](http://bbs.espressif.com/viewtopic.php?f=46&t=2407). At minimum, Espressif states that the 4th sector from the end needs to be flashed with "init data", and the 2nd sector from the end should be blank. + +The default init data is provided as part of the SDK in the file `esp_init_data_default.bin`. NodeMCU will automatically flash this file to the right place on first boot if the sector appears to be empty. + +If you need to customize init data then first download the [Espressif SDK patch 1.5.4.1](http://bbs.espressif.com/download/file.php?id=1572) and extract `esp_init_data_default.bin`. Then flash that file just like you'd flash the firmware. The correct address for the init data depends on the capacity of the flash chip. + +- `0x7c000` for 512 kB, modules like most ESP-01, -03, -07 etc. +- `0xfc000` for 1 MB, modules like ESP8285, PSF-A85, some ESP-01, -03 etc. +- `0x1fc000` for 2 MB +- `0x3fc000` for 4 MB, modules like ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini + +See "4.1 Non-FOTA Flash Map" and "6.3 RF Initialization Configuration" of the [ESP8266 Getting Started Guide](https://espressif.com/en/support/explore/get-started/esp8266/getting-started-guide) for details on init data addresses and customization. + +## Determine flash size + +To determine the capacity of the flash chip *before* a firmware is installed you can run + +`esptool.py --port flash_id` + +It will return a manufacturer ID and a chip ID like so: ``` -esptool.py --port erase_flash -esptool.py --port write_flash 0x00000 .bin esp_init_data_default.bin +Connecting... +Manufacturer: e0 +Device: 4016 ``` +The chip ID can then be looked up in [https://code.coreboot.org/p/flashrom/source/tree/HEAD/trunk/flashchips.h](https://code.coreboot.org/p/flashrom/source/tree/HEAD/trunk/flashchips.h). This leads to a manufacturer name and a chip model name/number e.g. `AMIC_A25LQ032`. That information can then be fed into your favorite search engine to find chip descriptions and data sheets. -!!! note "Note:" - - The address for `esp_init_data_default.bin` depends on the size of your module's flash. - - - `0x7c000` for 512 kB, modules like ESP-01, -03, -07 etc. - - `0xfc000` for 1 MB, modules like ESP8285, PSF-A85 - - `0x1fc000` for 2 MB - - `0x3fc000` for 4 MB, modules like ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini - -**NodeMCU Flasher** - -The [NodeMCU Flasher](https://github.com/nodemcu/nodemcu-flasher) will download init data using a special path: -``` -INTERNAL://DEFAULT -``` - -Replace the provided (old) `esp_init_data_default.bin` with the one extracted above and use the flasher like you're used to. - -**References** - -* [2A-ESP8266__IOT_SDK_User_Manual__EN_v1.5.pdf, Chapter 6](http://bbs.espressif.com/viewtopic.php?f=51&t=1024) -* [SPI Flash ROM Layout (without OTA upgrades)](https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map#spi-flash-rom-layout-without-ota-upgrades) +By convention the last two or three digits in the module name denote the capacity in megabits. So, `A25LQ032` in the example above is a 32Mb(=4MB) module. diff --git a/docs/en/index.md b/docs/en/index.md index c8731c50..8000df91 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -44,7 +44,7 @@ print(gpio.read(pin)) ``` ## Getting Started -1. [Build the firmeware](build.md) with the modules you need. +1. [Build the firmware](build.md) with the modules you need. 1. [Flash the firmware](flash.md) to the chip. 1. [Upload code](upload.md) to the firmware. diff --git a/docs/en/modules/enduser-setup.md b/docs/en/modules/enduser-setup.md index 704d1dfc..ade57594 100644 --- a/docs/en/modules/enduser-setup.md +++ b/docs/en/modules/enduser-setup.md @@ -7,10 +7,29 @@ This module provides a simple way of configuring ESP8266 chips without using a s ![enduser setup config dialog](../../img/enduser-setup.jpg "enduser setup config dialog") -After running [`enduser_setup.start()`](#enduser_setupstart) a portal like the above can be accessed through a wireless network called SetupGadget_XXXXXX. The portal is used to submit the credentials for the WiFi of the enduser. -After an IP address has been successfully obtained this module will stop as if [`enduser_setup.stop()`](#enduser_setupstop) had been called. +After running [`enduser_setup.start()`](#enduser_setupstart), a wireless network named "SetupGadget_XXXXXX" will start. Connect to that SSID and then navigate to the root +of any website (e.g., `http://example.com/` will work, but do not use `.local` domains because it will fail on iOS). A web page similar to the picture above will load, allowing the +end user to provide their Wi-Fi information. + +After an IP address has been successfully obtained, then this module will stop as if [`enduser_setup.stop()`](#enduser_setupstop) had been called. There is a 10-second delay before +teardown to allow connected clients to obtain a last status message while the SoftAP is still active. + +Alternative HTML can be served by placing a file called `enduser_setup.html` on the filesystem. Everything needed by the web page must be included in this one file. This file will be kept +in RAM, so keep it as small as possible. The file can be gzip'd ahead of time to reduce the size (i.e., using `gzip -n` or `zopfli`), and when served, the End User Setup module will add +the appropriate `Content-Encoding` header to the response. *Note: Even if gzipped, the file still needs to be named `enduser_setup.html`.* + +The following HTTP endpoints exist: + +|Endpoint|Description| +|--------|-----------| +|/|Returns HTML for the web page. Will return the contents of `enduser_setup.html` if it exists on the filesystem, otherwise will return a page embedded into the firmware image.| +|/aplist|Forces the ESP8266 to perform a site survey across all channels, reporting access points that it can find. Return payload is a JSON array: `[{"ssid":"foobar","rssi":-36,"chan":3}]`| +|/generate_204|Returns a HTTP 204 status (expected by certain Android clients during Wi-Fi connectivity checks)| +|/status|Returns plaintext status description, used by the web page| +|/status.json|Returns a JSON payload containing the ESP8266's chip id in hexadecimal format and the status code: 0=Idle, 1=Connecting, 2=Wrong Password, 3=Network not Found, 4=Failed, 5=Success| +|/setwifi|Endpoint intended for services to use for setting the wifi credentials. Identical to `/update` except returns the same payload as `/status.json` instead of redirecting to `/`.| +|/update|Form submission target. Example: `http://example.com/update?wifi_ssid=foobar&wifi_password=CorrectHorseBatteryStaple`. Must be a GET request. Will redirect to `/` when complete. | -Alternative HTML can be served by placing a file called `enduser_setup.html` in the filesystem. This file will be kept in RAM, so keep it as small as possible. ## enduser_setup.manual() @@ -53,7 +72,7 @@ Starts the captive portal. #### Parameters - `onConnected()` callback will be fired when an IP-address has been obtained, just before the enduser_setup module will terminate itself - `onError()` callback will be fired if an error is encountered. `err_num` is a number describing the error, and `string` contains a description of the error. - - `onDebug()` callback is disabled by default. It is intended to be used to find internal issues in the module. `string` contains a description of what is going on. + - `onDebug()` callback is disabled by default (controlled by `#define ENDUSER_SETUP_DEBUG_ENABLE` in `enduser_setup.c`). It is intended to be used to find internal issues in the module. `string` contains a description of what is going on. #### Returns `nil` diff --git a/docs/en/modules/file.md b/docs/en/modules/file.md index 8db14f1f..9cbfa93e 100644 --- a/docs/en/modules/file.md +++ b/docs/en/modules/file.md @@ -7,8 +7,6 @@ The file module provides access to the file system and its individual files. The file system is a flat file system, with no notion of subdirectories/folders. -Only one file can be open at any given time. - Besides the SPIFFS file system on internal flash, this module can also access FAT partitions on an external SD card is [FatFS is enabled](../sdcard.md). ```lua @@ -43,30 +41,6 @@ Current directory defaults to the root of internal SPIFFS (`/FLASH`) after syste #### Returns `true` on success, `false` otherwise -## file.close() - -Closes the open file, if any. - -#### Syntax -`file.close()` - -#### Parameters -none - -#### Returns -`nil` - -#### Example -```lua --- open 'init.lua', print the first line. -if file.open("init.lua", "r") then - print(file.readline()) - file.close() -end -``` -#### See also -[`file.open()`](#fileopen) - ## file.exists() Determines whether the specified file exists. @@ -95,34 +69,6 @@ end #### See also [`file.list()`](#filelist) -## file.flush() - -Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()`](#fileclose) performs an implicit flush as well. - -#### Syntax -`file.flush()` - -#### Parameters -none - -#### Returns -`nil` - -#### Example -```lua --- open 'init.lua' in 'a+' mode -if file.open("init.lua", "a+") then - -- write 'foo bar' to the end of the file - file.write('foo bar') - file.flush() - -- write 'baz' too - file.write('baz') - file.close() -end -``` -#### See also -[`file.close()`](#fileclose) - ## file.format() Format the file system. Completely erases any existing file system and writes a new one. Depending on the size of the flash chip in the ESP, this may take several seconds. @@ -280,9 +226,9 @@ When done with the file, it must be closed using `file.close()`. - "a+": append update mode, previous data is preserved, writing is only allowed at the end of file #### Returns -`nil` if file not opened, or not exists (read modes). `true` if file opened ok. +file object if file opened ok. `nil` if file not opened, or not exists (read modes). -#### Example +#### Example (basic model) ```lua -- open 'init.lua', print the first line. if file.open("init.lua", "r") then @@ -290,70 +236,19 @@ if file.open("init.lua", "r") then file.close() end ``` -#### See also -- [`file.close()`](#fileclose) -- [`file.readline()`](#filereadline) - -## file.read() - -Read content from the open file. - -#### Syntax -`file.read([n_or_str])` - -#### Parameters -- `n_or_str`: - - if nothing passed in, read up to `LUAL_BUFFERSIZE` bytes (default 1024) or the entire file (whichever is smaller) - - if passed a number n, then read the file until the lesser of `n` bytes, `LUAL_BUFFERSIZE` bytes, or EOF is reached. Specifying a number larger than the buffer size will read the buffer size. - - if passed a string `str`, then read until `str` appears next in the file, `LUAL_BUFFERSIZE` bytes have been read, or EOF is reached - -#### Returns -File content as a string, or nil when EOF - -#### Example +#### Example (object model) ```lua --- print the first line of 'init.lua' -if file.open("init.lua", "r") then - print(file.read('\n')) - file.close() -end - --- print the first 5 bytes of 'init.lua' -if file.open("init.lua", "r") then - print(file.read(5)) - file.close() +-- open 'init.lua', print the first line. +fd = file.open("init.lua", "r") +if fd then + print(fd:readline()) + fd:close(); fd = nil end ``` #### See also -- [`file.open()`](#fileopen) -- [`file.readline()`](#filereadline) - -## file.readline() - -Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than `LUAL_BUFFERSIZE`, this function only returns the first `LUAL_BUFFERSIZE` bytes (this is 1024 bytes by default). - -#### Syntax -`file.readline()` - -#### Parameters -none - -#### Returns -File content in string, line by line, including EOL('\n'). Return `nil` when EOF. - -#### Example -```lua --- print the first line of 'init.lua' -if file.open("init.lua", "r") then - print(file.readline()) - file.close() -end -``` -#### See also -- [`file.open()`](#fileopen) - [`file.close()`](#fileclose) -- [`file.read()`](#filereade) +- [`file.readline()`](#filereadline) ## file.remove() @@ -398,12 +293,188 @@ Renames a file. If a file is currently open, it will be closed first. file.rename("temp.lua","init.lua") ``` +# File access functions + +The `file` module provides several functions to access the content of a file after it has been opened with [`file.open()`](#fileopen). They can be used as part of a basic model or an object model: + +## Basic model +In the basic model there is max one file opened at a time. The file access functions operate on this file per default. If another file is opened, the previous default file needs to be closed beforehand. + +```lua +-- open 'init.lua', print the first line. +if file.open("init.lua", "r") then + print(file.readline()) + file.close() +end +``` + +## Object model +Files are represented by file objects which are created by `file.open()`. File access functions are available as methods of this object, and multiple file objects can coexist. + +```lua +src = file.open("init.lua", "r") +if src then + dest = file.open("copy.lua", "w") + if dest then + local line + repeat + line = src:read() + if line then + dest:write(line) + end + until line == nil + dest:close(); dest = nil + end + src:close(); dest = nil +end +``` + +!!! Attention + + It is recommended to use only one single model within the application. Concurrent use of both models can yield unpredictable behavior: Closing the default file from basic model will also close the correspoding file object. Closing a file from object model will also close the default file if they are the same file. + +!!! Note + + The maximum number of open files on SPIFFS is determined at compile time by `SPIFFS_MAX_OPEN_FILES` in `user_config.h`. + +## file.close() +## file.obj:close() + +Closes the open file, if any. + +#### Syntax +`file.close()` + +`fd:close()` + +#### Parameters +none + +#### Returns +`nil` + +#### See also +[`file.open()`](#fileopen) + +## file.flush() +## file.obj:flush() + +Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()` / `fd:close()`](#fileclose) performs an implicit flush as well. + +#### Syntax +`file.flush()` + +`fd:flush()` + +#### Parameters +none + +#### Returns +`nil` + +#### Example (basic model) +```lua +-- open 'init.lua' in 'a+' mode +if file.open("init.lua", "a+") then + -- write 'foo bar' to the end of the file + file.write('foo bar') + file.flush() + -- write 'baz' too + file.write('baz') + file.close() +end +``` + +#### See also +[`file.close()` / `file.obj:close()`](#fileclose) + +## file.read() +## file.obj:read() + +Read content from the open file. + +!!! note + + The function temporarily allocates 2 * (number of requested bytes) on the heap for buffering and processing the read data. Default chunk size (`FILE_READ_CHUNK`) is 1024 bytes and is regarded to be safe. Pushing this by 4x or more can cause heap overflows depending on the application. Consider this when selecting a value for parameter `n_or_char`. + +#### Syntax +`file.read([n_or_char])` + +`fd:read([n_or_char])` + +#### Parameters +- `n_or_char`: + - if nothing passed in, then read up to `FILE_READ_CHUNK` bytes or the entire file (whichever is smaller). + - if passed a number `n`, then read up to `n` bytes or the entire file (whichever is smaller). + - if passed a string containing the single character `char`, then read until `char` appears next in the file, `FILE_READ_CHUNK` bytes have been read, or EOF is reached. + +#### Returns +File content as a string, or nil when EOF + +#### Example (basic model) +```lua +-- print the first line of 'init.lua' +if file.open("init.lua", "r") then + print(file.read('\n')) + file.close() +end +``` + +#### Example (object model) +```lua +-- print the first 5 bytes of 'init.lua' +fd = file.open("init.lua", "r") +if fd then + print(fd:read(5)) + fd:close(); fd = nil +end +``` + +#### See also +- [`file.open()`](#fileopen) +- [`file.readline()` / `file.obj:readline()`](#filereadline) + +## file.readline() +## file.obj:readline() + +Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than 1024, this function only returns the first 1024 bytes. + +#### Syntax +`file.readline()` + +`fd:readline()` + +#### Parameters +none + +#### Returns +File content in string, line by line, including EOL('\n'). Return `nil` when EOF. + +#### Example (basic model) +```lua +-- print the first line of 'init.lua' +if file.open("init.lua", "r") then + print(file.readline()) + file.close() +end +``` + +#### See also +- [`file.open()`](#fileopen) +- [`file.close()` / `file.obj:close()`](#fileclose) +- [`file.read()` / `file.obj:read()`](#fileread) + + ## file.seek() +## file.obj:seek() + Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence. #### Syntax `file.seek([whence [, offset]])` +`fd:seek([whence [, offset]])` + #### Parameters - `whence` - "set": base is position 0 (beginning of the file) @@ -416,7 +487,7 @@ If no parameters are given, the function simply returns the current file offset. #### Returns the resulting file position, or `nil` on error -#### Example +#### Example (basic model) ```lua if file.open("init.lua", "r") then -- skip the first 5 bytes of the file @@ -429,19 +500,22 @@ end [`file.open()`](#fileopen) ## file.write() +## file.obj:write() Write a string to the open file. #### Syntax `file.write(string)` +`fd:write(string)` + #### Parameters `string` content to be write to file #### Returns `true` if the write is ok, `nil` on error -#### Example +#### Example (basic model) ```lua -- open 'init.lua' in 'a+' mode if file.open("init.lua", "a+") then @@ -451,24 +525,38 @@ if file.open("init.lua", "a+") then end ``` +#### Example (object model) +```lua +-- open 'init.lua' in 'a+' mode +fd = file.open("init.lua", "a+") +if fd then + -- write 'foo bar' to the end of the file + fd:write('foo bar') + fd:close() +end +``` + #### See also - [`file.open()`](#fileopen) -- [`file.writeline()`](#filewriteline) +- [`file.writeline()` / `file.obj:writeline()`](#filewriteline) ## file.writeline() +## file.obj:writeline() Write a string to the open file and append '\n' at the end. #### Syntax `file.writeline(string)` +`fd:writeline(string)` + #### Parameters `string` content to be write to file #### Returns `true` if write ok, `nil` on error -#### Example +#### Example (basic model) ```lua -- open 'init.lua' in 'a+' mode if file.open("init.lua", "a+") then @@ -480,4 +568,4 @@ end #### See also - [`file.open()`](#fileopen) -- [`file.readline()`](#filereadline) +- [`file.readline()` / `file.obj:readline()`](#filereadline) diff --git a/docs/en/modules/gpio.md b/docs/en/modules/gpio.md index 2d7cbc1b..4eb7c6b8 100644 --- a/docs/en/modules/gpio.md +++ b/docs/en/modules/gpio.md @@ -69,28 +69,29 @@ gpio.read(0) ## gpio.serout() -Serialize output based on a sequence of delay-times in µs. After each delay, the pin is toggled. After the last repeat and last delay the pin is not toggled. +Serialize output based on a sequence of delay-times in µs. After each delay, the pin is toggled. After the last cycle and last delay the pin is not toggled. The function works in two modes: * synchronous - for sub-50 µs resolution, restricted to max. overall duration, * asynchrounous - synchronous operation with less granularity but virtually unrestricted duration. -Whether the asynchronous mode is chosen is defined by presence of the `callback` parameter. If present and is of function type the function goes asynchronous the callback function is invoked when sequence finishes. If the parameter is numeric the function still goes asynchronous but no callback is invoked when done. +Whether the asynchronous mode is chosen is defined by presence of the `callback` parameter. If present and is of function type the function goes asynchronous and the callback function is invoked when sequence finishes. If the parameter is numeric the function still goes asynchronous but no callback is invoked when done. -For asynchronous version minimum delay time should not be shorter than 50 μs and maximum delay time is 0x7fffff μs (~8.3 seconds). -In this mode the function does not block the stack and returns immediately before the output sequence is finalized. HW timer inf `FRC1_SOURCE` mode is used to change the states. +For the asynchronous version, the minimum delay time should not be shorter than 50 μs and maximum delay time is 0x7fffff μs (~8.3 seconds). +In this mode the function does not block the stack and returns immediately before the output sequence is finalized. HW timer `FRC1_SOURCE` mode is used to change the states. As there is only a single hardware timer, there +are restrictions on which modules can be used at the same time. An error will be raised if the timer is already in use. -Note that the synchronous variant (no or nil `callback` parameter) function blocks the stach and as such any use of it must adhere to the SDK guidelines (also explained [here](https://nodemcu.readthedocs.io/en/dev/en/extn-developer-faq/#extension-developer-faq)). Failure to do so may lead to WiFi issues or outright to crashes/reboots. Shortly it means that sum of all delay times multiplied by the number of repeats should not exceed 15 ms. +Note that the synchronous variant (no or nil `callback` parameter) function blocks the stack and as such any use of it must adhere to the SDK guidelines (also explained [here](../extn-developer-faq/#extension-developer-faq)). Failure to do so may lead to WiFi issues or outright to crashes/reboots. In short it means that the sum of all delay times multiplied by the number of cycles should not exceed 15 ms. #### Syntax -`gpio.serout(pin, start_level, delay_times [, repeat_num[, callback]])` +`gpio.serout(pin, start_level, delay_times [, cycle_num[, callback]])` #### Parameters - `pin` pin to use, IO index - `start_level` level to start on, either `gpio.HIGH` or `gpio.LOW` - `delay_times` an array of delay times in µs between each toggle of the gpio pin. -- `repeat_num` an optional number of times to run through the sequence. -- `callback` an optional callback function or number, if present the function ruturns immediately and goes asynchronous. +- `cycle_num` an optional number of times to run through the sequence. (default is 1) +- `callback` an optional callback function or number, if present the function returns immediately and goes asynchronous. #### Returns diff --git a/docs/en/modules/net.md b/docs/en/modules/net.md index 5580cba8..0fc78cb7 100644 --- a/docs/en/modules/net.md +++ b/docs/en/modules/net.md @@ -135,15 +135,21 @@ Listen on port from IP address. #### Example ```lua - -- 30s time out for a inactive client -sv = net.createServer(net.TCP, 30) -- server listens on 80, if data received, print data to console and send "hello world" back to caller -sv:listen(80, function(c) - c:on("receive", function(c, pl) - print(pl) +-- 30s time out for a inactive client +sv = net.createServer(net.TCP, 30) + +function receiver(sck, data) + print(data) + sck:close() +end + +if sv then + sv:listen(80, function(conn) + conn:on("receive", receiver) + conn:send("hello world") end) - c:send("hello world") -end) +end ``` #### See also @@ -303,32 +309,35 @@ Multiple consecutive `send()` calls aren't guaranteed to work (and often don't) #### Example ```lua srv = net.createServer(net.TCP) -srv:listen(80, function(conn) - conn:on("receive", function(sck, req) - local response = {} - -- if you're sending back HTML over HTTP you'll want something like this instead - -- local response = {"HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n"} +function receiver(sck, data) + local response = {} - response[#response + 1] = "lots of data" - response[#response + 1] = "even more data" - response[#response + 1] = "e.g. content read from a file" - - -- sends and removes the first element from the 'response' table - local function send(sk) - if #response > 0 - then sk:send(table.remove(response, 1)) - else - sk:close() - response = nil - end + -- if you're sending back HTML over HTTP you'll want something like this instead + -- local response = {"HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n"} + + response[#response + 1] = "lots of data" + response[#response + 1] = "even more data" + response[#response + 1] = "e.g. content read from a file" + + -- sends and removes the first element from the 'response' table + local function send(localSocket) + if #response > 0 + then localSocket:send(table.remove(response, 1)) + else + localSocket:close() + response = nil end + end - -- triggers the send() function again once the first chunk of data was sent - sck:on("sent", send) - - send(sck) - end) + -- triggers the send() function again once the first chunk of data was sent + sck:on("sent", send) + + send(sck) +end + +srv:listen(80, function(conn) + conn:on("receive", receiver) end) ``` If you do not or can not keep all the data you send back in memory at one time (remember that `response` is an aggregation) you may use explicit callbacks instead of building up a table like so: diff --git a/docs/en/modules/ow.md b/docs/en/modules/ow.md index 4cc23462..459ba831 100644 --- a/docs/en/modules/ow.md +++ b/docs/en/modules/ow.md @@ -84,7 +84,7 @@ Reads multi bytes. #### Parameters - `pin` 1~12, I/O index -- `size` number of bytes to be read from slave device +- `size` number of bytes to be read from slave device (up to 256) #### Returns `string` bytes read from slave device diff --git a/docs/en/modules/rtctime.md b/docs/en/modules/rtctime.md index 4fc01d7a..860e1138 100644 --- a/docs/en/modules/rtctime.md +++ b/docs/en/modules/rtctime.md @@ -55,7 +55,7 @@ rtctime.dsleep(5000000, 4) For applications where it is necessary to take samples with high regularity, this function is useful. It provides an easy way to implement a "wake up on the next 5-minute boundary" scheme, without having to explicitly take into account how long the module has been active for etc before going back to sleep. #### Syntax -`rtctime.dsleep(aligned_us, minsleep_us [, option])` +`rtctime.dsleep_aligned(aligned_us, minsleep_us [, option])` #### Parameters - `aligned_us` boundary interval in microseconds diff --git a/docs/en/modules/sntp.md b/docs/en/modules/sntp.md index 987d7950..9729aa49 100644 --- a/docs/en/modules/sntp.md +++ b/docs/en/modules/sntp.md @@ -12,7 +12,7 @@ When compiled together with the [rtctime](rtctime.md) module it also offers seam Attempts to obtain time synchronization. -For best results you may want to to call this periodically in order to compensate for internal clock drift. As stated in the [rtctime](rtctime.md) module documentation it's advisable to sync time after deep sleep and it's necessary to sync after module reset (add it to [`init.lua`](upload.md#initlua) after WiFi initialization). +For best results you may want to to call this periodically in order to compensate for internal clock drift. As stated in the [rtctime](rtctime.md) module documentation it's advisable to sync time after deep sleep and it's necessary to sync after module reset (add it to [`init.lua`](../upload.md#initlua) after WiFi initialization). #### Syntax `sntp.sync([server_ip], [callback], [errcallback])` diff --git a/docs/en/modules/somfy.md b/docs/en/modules/somfy.md new file mode 100644 index 00000000..07fe0a41 --- /dev/null +++ b/docs/en/modules/somfy.md @@ -0,0 +1,44 @@ +# Somfy module +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2016-09-27 | [vsky279](https://github.com/vsky279) | [vsky279](https://github.com/vsky279) | [somfy.c](../../../app/modules/somfy.c)| + +This module provides a simple interface to control Somfy blinds via an RF transmitter (433.42 MHz). It is based on [Nickduino Somfy Remote Arduino skecth](https://github.com/Nickduino/Somfy_Remote). + +The hardware used is the standard 433 MHz RF transmitter. Unfortunately these chips are usually transmitting at he frequency of 433.92MHz so the crystal resonator should be replaced with the 433.42 MHz resonator though some reporting that it is working even with the original crystal. + +To understand details of the Somfy protocol please refer to [Somfy RTS protocol](https://pushstack.wordpress.com/somfy-rts-protocol/) and also discussion [here](https://forum.arduino.cc/index.php?topic=208346.0). + +The module is using hardware timer so it cannot be used at the same time with other NodeMCU modules using the hardware timer, i.e. `sigma delta`, `pcm`, `perf`, or `pwm` modules. + +## somfy.sendcommand() + +Builds an frame defined by Somfy protocol and sends it to the RF transmitter. + +#### Syntax +`somfy.sendcommand(pin, remote_address, command, rolling_code, repeat_count, call_back)` + +#### Parameters +- `pin` GPIO pin the RF transmitter is connected to. +- `remote_address` address of the remote control. The device to be controlled is programmed with the addresses of the remote controls it should listen to. +- `command` command to be transmitted. Can be one of `somfy.SOMFY_UP`, `somfy.SOMFY_DOWN`, `somfy.SOMFY_PROG`, `somfy.SOMFY_STOP` +- `rolling_code` The rolling code is increased every time a button is pressed. The receiver only accepts command if the rolling code is above the last received code and is not to far ahead of the last received code. This window is in the order of a 100 big. The rolling code needs to be stored in the EEPROM (i.e. filesystem) to survive the ESP8266 reset. +- `repeat_count` how many times the command is repeated +- `call_back` a function to be called after the command is transmitted. Allows chaining commands to set the blinds to a defined position. + +My original remote is [TELIS 4 MODULIS RTS](https://www.somfy.co.uk/products/1810765/telis-4-modulis-rts). This remote is working with the additional info - additional 56 bits that follow data (shortening the Inter-frame gap). It seems that the scrumbling alhorithm has not been revealed yet. + +When I send the `somfy.DOWN` command, repeating the frame twice (which seems to be the standard for a short button press), i.e. `repeat_count` equal to 2, the blinds go only 1 step down. This corresponds to the movement of the wheel on the original remote. The down button on the original remote sends also `somfy.DOWN` command but the additional info is different and this makes the blinds go full down. Fortunately it seems that repeating the frame 16 times makes the blinds go fully down. + +#### Returns +nil + +#### Example +To start with controlling your Somfy blinds you need to +- Choose an arbitrary remote address (different from your existing remote) - `123` in this example +- Choose a starting point for the rolling code. Any unsigned int works, 1 is a good start +- Long-press the program button of your existing remote control until your blind goes up and down slightly +- execute `somfy.sendcommand(4, 123, somfy.PROG, 1, 2)` - the blinds will react and your ESP8266 remote control is now registered +- running `somfy.sendcommand(4, 123, somfy.DOWN, 2, 16)` - fully closes the blinds + +For more elaborated example please refer to [`somfy.lua`](../../../lua_examples/somfy.lua). diff --git a/docs/en/modules/spi.md b/docs/en/modules/spi.md index 280a7e49..abe3a98f 100644 --- a/docs/en/modules/spi.md +++ b/docs/en/modules/spi.md @@ -6,6 +6,10 @@ All transactions for sending and receiving are most-significant-bit first and least-significant last. For technical details of the underlying hardware refer to [metalphreak's ESP8266 HSPI articles](http://d.av.id.au/blog/tag/hspi/). +!!! note + + The ESP hardware provides two SPI busses, with IDs 0, and 1, which map to pins generally labelled SPI and HSPI. If you are using any kind of development board which provides flash, then bus ID 0 (SPI) is almost certainly used for communicating with the flash chip. You probably want to choose bus ID 1 (HSPI) for your communication, as you will have uncontended use of it. + ## High Level Functions The high level functions provide a send & receive API for half- and full-duplex mode. Sent and received data items are restricted to 1 - 32 bit diff --git a/docs/en/modules/u8g.md b/docs/en/modules/u8g.md index 3bbb46da..8d022f31 100644 --- a/docs/en/modules/u8g.md +++ b/docs/en/modules/u8g.md @@ -8,7 +8,7 @@ U8glib is a graphics library developed at [olikraus/u8glib](https://github.com/o I²C and SPI mode: - sh1106_128x64 -- ssd1306 - 128x64 and 64x48 variants +- ssd1306 - 128x32, 128x64, and 64x48 variants - ssd1309_128x64 - ssd1327_96x96_gr - uc1611 - dogm240 and dogxl240 variants @@ -107,6 +107,7 @@ Initialize a display via I²C. The init sequence would insert delays to match the display specs. These can destabilize the overall system if wifi service is blocked for too long. It is therefore advisable to disable such delays unless the specific use case can exclude wifi traffic while initializing the display driver. - `u8g.sh1106_128x64_i2c()` +- `u8g.ssd1306_128x32_i2c()` - `u8g.ssd1306_128x64_i2c()` - `u8g.ssd1306_64x48_i2c()` - `u8g.ssd1309_128x64_i2c()` @@ -146,6 +147,7 @@ The init sequence would insert delays to match the display specs. These can dest - `u8g.pcd8544_84x48_hw_spi()` - `u8g.pcf8812_96x65_hw_spi()` - `u8g.sh1106_128x64_hw_spi()` +- `u8g.ssd1306_128x32_hw_spi()` - `u8g.ssd1306_128x64_hw_spi()` - `u8g.ssd1306_64x48_hw_spi()` - `u8g.ssd1309_128x64_hw_spi()` @@ -202,6 +204,30 @@ disp = u8g.ssd1306_128x64_hw_spi(cs, dc, res) #### See also [I²C Display Drivers](#i2c-display-drivers) +## u8g.fb_rle +Initialize a virtual display that provides run-length encoded framebuffer contents to a Lua callback. + +The callback function can be used to process the framebuffer line by line. It's called with either `nil` as parameter to indicate the start of a new frame or with a string containing a line of the framebuffer with run-length encoding. First byte in the string specifies how many pairs of (x, len) follow, while each pair defines the start (leftmost x-coordinate) and length of a sequence of lit pixels. All other pixels in the line are dark. + +```lua +n = struct.unpack("B", rle_line) +print(n.." pairs") +for i = 0,n-1 do + print(string.format(" x: %d len: %d", struct.unpack("BB", rle_line, 1+1 + i*2))) +end +``` + +#### Syntax +`u8g.fb_rle(cb_fn, width, height)` + +#### Parameters +- `cb_fn([rle_line])` callback function. `rle_line` is a string containing a run-length encoded framebuffer line, or `nil` to indicate start of frame. +- `width` of display. Must be a multiple of 8, less than or equal to 248. +- `height` of display. Must be a multiple of 8, less than or equal to 248. + +#### Returns +u8g display object + ___ ## Constants diff --git a/docs/en/modules/websocket.md b/docs/en/modules/websocket.md index 177c9abe..5f319c7c 100644 --- a/docs/en/modules/websocket.md +++ b/docs/en/modules/websocket.md @@ -7,10 +7,6 @@ A websocket *client* module that implements [RFC6455](https://tools.ietf.org/htm The implementation supports fragmented messages, automatically respondes to ping requests and periodically pings if the server isn't communicating. -!!! note - - Currently, it is **not** possible to change the request headers, most notably the user agent. - **SSL/TLS support** Take note of constraints documented in the [net module](net.md). @@ -70,6 +66,27 @@ ws = nil -- fully dispose the client as lua will now gc it ``` +## websocket.client:config(params) + +Configures websocket client instance. + +#### Syntax +`websocket:config(params)` + +#### Parameters +- `params` table with configuration parameters. Following keys are recognized: + - `headers` table of extra request headers affecting every request + +#### Returns +`nil` + +#### Example +```lua +ws = websocket.createClient() +ws:config({headers={['User-Agent']='NodeMCU'}}) +``` + + ## websocket.client:connect() Attempts to estabilish a websocket connection to the given URL. diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index dd2b9874..b34d8f00 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -24,6 +24,23 @@ Gets the current WiFi channel. #### Returns current WiFi channel +## wifi.getdefaultmode() + +Gets default WiFi operation mode. + +#### Syntax +`wifi.getdefaultmode()` + +#### Parameters +`nil` + +#### Returns +The WiFi mode, as one of the `wifi.STATION`, `wifi.SOFTAP`, `wifi.STATIONAP` or `wifi.NULLMODE` constants. + +#### See also +[`wifi.getmode()`](#wifigetmode) +[`wifi.setmode()`](#wifisetmode) + ## wifi.getmode() Gets WiFi operation mode. @@ -38,6 +55,7 @@ Gets WiFi operation mode. The WiFi mode, as one of the `wifi.STATION`, `wifi.SOFTAP`, `wifi.STATIONAP` or `wifi.NULLMODE` constants. #### See also +[`wifi.getdefaultmode()`](#wifigetdefaultmode) [`wifi.setmode()`](#wifisetmode) ## wifi.getphymode() @@ -67,18 +85,20 @@ Configures the WiFi mode to use. NodeMCU can run in one of four WiFi modes: When using the combined Station + AP mode, the same channel will be used for both networks as the radio can only listen on a single channel. -NOTE: WiFi Mode configuration will be retained until changed even if device is turned off. +NOTE: WiFi Mode configuration will be retained until changed even if device is turned off. #### Syntax -`wifi.setmode(mode)` +`wifi.setmode(mode[, save])` #### Parameters -`mode` value should be one of - -- `wifi.STATION` for when the device is connected to a WiFi router. This is often done to give the device access to the Internet. -- `wifi.SOFTAP` for when the device is acting *only* as an access point. This will allow you to see the device in the list of WiFi networks (unless you hide the SSID, of course). In this mode your computer can connect to the device, creating a local area network. Unless you change the value, the NodeMCU device will be given a local IP address of 192.168.4.1 and assign your computer the next available IP address, such as 192.168.4.2. -- `wifi.STATIONAP` is the combination of `wifi.STATION` and `wifi.SOFTAP`. It allows you to create a local WiFi connection *and* connect to another WiFi router. -- `wifi.NULLMODE` to switch off WiFi +- `mode` value should be one of + - `wifi.STATION` for when the device is connected to a WiFi router. This is often done to give the device access to the Internet. + - `wifi.SOFTAP` for when the device is acting *only* as an access point. This will allow you to see the device in the list of WiFi networks (unless you hide the SSID, of course). In this mode your computer can connect to the device, creating a local area network. Unless you change the value, the NodeMCU device will be given a local IP address of 192.168.4.1 and assign your computer the next available IP address, such as 192.168.4.2. + - `wifi.STATIONAP` is the combination of `wifi.STATION` and `wifi.SOFTAP`. It allows you to create a local WiFi connection *and* connect to another WiFi router. + - `wifi.NULLMODE` changing WiFi mode to NULL_MODE will put wifi into a low power state similar to MODEM_SLEEP, provided `wifi.nullmodesleep(false)` has not been called. +- `save` choose whether or not to save wifi mode to flash + - `true` WiFi mode configuration **will** be retained through power cycle. (Default) + - `false` WiFi mode configuration **will not** be retained through power cycle. #### Returns current mode after setup @@ -90,11 +110,12 @@ wifi.setmode(wifi.STATION) #### See also [`wifi.getmode()`](#wifigetmode) +[`wifi.getdefaultmode()`](#wifigetdefaultmode) ## wifi.setphymode() Sets WiFi physical mode. - + - `wifi.PHYMODE_B` 802.11b, more range, low Transfer rate, more current draw - `wifi.PHYMODE_G` @@ -116,7 +137,7 @@ Information from the Espressif datasheet v4.3 `wifi.setphymode(mode)` #### Parameters -`mode` one of the following +`mode` one of the following - `wifi.PHYMODE_B` - `wifi.PHYMODE_G` @@ -130,39 +151,23 @@ physical mode after setup ## wifi.nullmodesleep() -Configures whether or not WiFi automatically goes to sleep in NULL_MODE. Enabled by default. +Configures whether or not WiFi automatically goes to sleep in NULL_MODE. Enabled by default. + + !!! note + This function **does not** store it's setting in flash, if auto sleep in NULL_MODE is not desired, `wifi.nullmodesleep(false)` must be called after powerup, restart, or wake from deep sleep. #### Syntax -`wifi.nullmodesleep(enable)` +`wifi.nullmodesleep([enable])` #### Parameters -- `enable` - - true: Enable WiFi auto sleep in NULL_MODE. (Default setting) - - false: Disable WiFi auto sleep in NULL_MODE. +- `enable` + - `true` Enable WiFi auto sleep in NULL_MODE. (Default setting) + - `false` Disable WiFi auto sleep in NULL_MODE. #### Returns -Current/new NULL_MODE sleep setting. - -## wifi.sleeptype() - -Configures the WiFi modem sleep type. - -#### Syntax -`wifi.sleeptype(type_wanted)` - -#### Parameters -`type_wanted` one of the following: - -- `wifi.NONE_SLEEP` to keep the modem on at all times -- `wifi.LIGHT_SLEEP` to allow the modem to power down under some circumstances -- `wifi.MODEM_SLEEP` to power down the modem as much as possible - -#### Returns -The actual sleep mode set, as one of `wifi.NONE_SLEEP`, `wifi.LIGHT_SLEEP` or `wifi.MODEM_SLEEP`. - -#### See also -- [`node.dsleep()`](node.md#nodedsleep) -- [`rtctime.dsleep()`](rtctime.md#rtctimedsleep) +- `sleep_enabled` Current/New NULL_MODE sleep setting + - If `wifi.nullmodesleep()` is called with no arguments, current setting is returned. + - If `wifi.nullmodesleep()` is called with `enable` argument, confirmation of new setting is returned. ## wifi.startsmart() @@ -241,61 +246,95 @@ wifi.sta.autoconnect(1) - [`wifi.sta.connect()`](#wifistaconnect) - [`wifi.sta.disconnect()`](#wifistadisconnect) +## wifi.sta.changeap() + +Select Access Point from list returned by `wifi.sta.getapinfo()` + +#### Syntax +`wifi.sta.changeap(ap_index)` + +#### Parameters +`ap_index` Index of Access Point you would like to change to. (Range:1-5) + - Corresponds to index used by [`wifi.sta.getapinfo()`](#wifistagetapinfo) and [`wifi.sta.getapindex()`](#wifistagetapindex) + +#### Returns +- `true` Success +- `false` Failure + +#### Example +```lua +wifi.sta.changeap(4) +``` + +#### See also +- [`wifi.sta.getapinfo()`](#wifistagetapinfo) +- [`wifi.sta.getapindex()`](#wifistagetapindex) + ## wifi.sta.config() Sets the WiFi station configuration. -NOTE: Station configuration will be retained until changed even if device is turned off. - #### Syntax -`wifi.sta.config(ssid, password[, auto[, bssid]])` +`wifi.sta.config(station_config)` #### Parameters - -- `ssid` string which is less than 32 bytes. -- `password` string which is 8-64 or 0 bytes. Empty string indicates an open WiFi access point. -- `auto` defaults to 1 - - 0 to disable auto connect and remain disconnected from access point - - 1 to enable auto connect and connect to access point, hence with `auto=1` there's no need to call [`wifi.sta.connect()`](#wifistaconnect) later -- `bssid` string that contains the MAC address of the access point (optional) +- `station_config` table containing configuration data for station + - `ssid` string which is less than 32 bytes. + - `pwd` string which is 8-64 or 0 bytes. Empty string indicates an open WiFi access point. + - `auto` defaults to true + - `true` to enable auto connect and connect to access point, hence with `auto=true` there's no need to call [`wifi.sta.connect()`](#wifistaconnect) + - `false` to disable auto connect and remain disconnected from access point + - `bssid` string that contains the MAC address of the access point (optional) - You can set BSSID if you have multiple access points with the same SSID. - Note: if you set BSSID for a specific SSID and would like to configure station to connect to the same SSID only without the BSSID requirement, you MUST first configure to station to a different SSID first, then connect to the desired SSID - The following formats are valid: - - "DE-C1-A5-51-F1-ED" + - "DE:C1:A5:51:F1:ED" - "AC-1D-1C-B1-0B-22" - "DE AD BE EF 7A C0" + - `save` Save station configuration to flash. + - `true` configuration **will** be retained through power cycle. + - `false` configuration **will not** be retained through power cycle. (Default) #### Returns -`nil` +- `true` Success +- `false` Failure #### Example ```lua --- Connect to access point automatically when in range, `auto` defaults to 1 -wifi.sta.config("myssid", "password") +--connect to Access Point (DO NOT save config to flash) +station_cfg={} +station_cfg.ssid="NODE-AABBCC" +station_cfg.pwd="password" +wifi.sta.config(station_cfg) --- Connect to Unsecured access point automatically when in range, `auto` defaults to 1 -wifi.sta.config("myssid", "") - --- Connect to access point, User decides when to connect/disconnect to/from AP due to `auto=0` -wifi.sta.config("myssid", "mypassword", 0) -wifi.sta.connect() --- ... do some WiFi stuff -wifi.sta.disconnect() - --- Connect to specific access point automatically when in range, `auto` defaults to 1 -wifi.sta.config("myssid", "mypassword", "12:34:56:78:90:12") +--connect to Access Point (DO save config to flash) +station_cfg={} +station_cfg.ssid="NODE-AABBCC" +station_cfg.pwd="password" +station_cfg.save=true +wifi.sta.config(station_cfg) + +--connect to Access Point with specific MAC address +station_cfg={} +station_cfg.ssid="NODE-AABBCC" +station_cfg.pwd="password" +station_cfg.bssid="AA:BB:CC:DD:EE:FF" +wifi.sta.config(station_cfg) + +--configure station but don't connect to Access point +station_cfg={} +station_cfg.ssid="NODE-AABBCC" +station_cfg.pwd="password" +station_cfg.auto=false +wifi.sta.config(station_cfg) --- Connect to specific access point, User decides when to connect/disconnect to/from AP due to `auto=0` -wifi.sta.config("myssid", "mypassword", 0, "12:34:56:78:90:12") -wifi.sta.connect() --- ... do some WiFi stuff -wifi.sta.disconnect() ``` #### See also - [`wifi.sta.connect()`](#wifistaconnect) - [`wifi.sta.disconnect()`](#wifistadisconnect) +- [`wifi.sta.apinfo()`](#wifistaapinfo) ## wifi.sta.connect() @@ -318,6 +357,10 @@ none Disconnects from AP in station mode. + !!! note + Please note that disconnecting from Access Point does not reduce power consumption. + If power saving is your goal, please refer to the description for `wifi.NULLMODE` in the function [`wifi.setmode()`](#wifisetmode) for more details. + #### Syntax `wifi.sta.disconnect()` @@ -339,14 +382,14 @@ Registers callbacks for WiFi station status events. - `wifi.sta.eventMonReg(wifi_status[, function([previous_state])])` #### Parameters -- `wifi_status` WiFi status you would like to set a callback for: +- `wifi_status` WiFi status you would like to set a callback for: - `wifi.STA_IDLE` - `wifi.STA_CONNECTING` - `wifi.STA_WRONGPWD` - `wifi.STA_APNOTFOUND` - `wifi.STA_FAIL` - `wifi.STA_GOTIP` -- `function` callback function to perform when event occurs +- `function` callback function to perform when event occurs - Note: leaving field blank unregisters callback. - `previous_state` previous wifi_state(0 - 5) @@ -354,7 +397,7 @@ Registers callbacks for WiFi station status events. `nil` #### Example -```lua +```lua --register callback wifi.sta.eventMonReg(wifi.STA_IDLE, function() print("STATION_IDLE") end) wifi.sta.eventMonReg(wifi.STA_CONNECTING, function() print("STATION_CONNECTING") end) @@ -362,16 +405,16 @@ wifi.sta.eventMonReg(wifi.STA_WRONGPWD, function() print("STATION_WRONG_PASSWORD wifi.sta.eventMonReg(wifi.STA_APNOTFOUND, function() print("STATION_NO_AP_FOUND") end) wifi.sta.eventMonReg(wifi.STA_FAIL, function() print("STATION_CONNECT_FAIL") end) wifi.sta.eventMonReg(wifi.STA_GOTIP, function() print("STATION_GOT_IP") end) - + --register callback: use previous state wifi.sta.eventMonReg(wifi.STA_CONNECTING, function(previous_State) - if(previous_State==wifi.STA_GOTIP) then + if(previous_State==wifi.STA_GOTIP) then print("Station lost connection with access point\n\tAttempting to reconnect...") else print("STATION_CONNECTING") end end) - + --unregister callback wifi.sta.eventMonReg(wifi.STA_IDLE) ``` @@ -447,7 +490,7 @@ Scans AP list as a Lua table into callback function. #### Parameters - `cfg` table that contains scan configuration - - `ssid` SSID == nil, don't filter SSID + - `ssid` SSID == nil, don't filter SSID - `bssid` BSSID == nil, don't filter BSSID - `channel` channel == 0, scan all channels, otherwise scan set channel (default is 0) - `show_hidden` show_hidden == 1, get info for router with hidden SSID (default is 0) @@ -479,7 +522,7 @@ function listap(t) -- (SSID : Authmode, RSSI, BSSID, Channel) end end wifi.sta.getap(listap) - + -- print AP list in new format function listap(t) for k,v in pairs(t) do @@ -507,8 +550,8 @@ function listap(t) end end scan_cfg = {} -scan_cfg.ssid = "myssid" -scan_cfg.bssid = "AA:AA:AA:AA:AA:AA" +scan_cfg.ssid = "myssid" +scan_cfg.bssid = "AA:AA:AA:AA:AA:AA" scan_cfg.channel = 0 scan_cfg.show_hidden = 1 wifi.sta.getap(scan_cfg, 1, listap) @@ -520,10 +563,10 @@ function listap(t) print("CURRENT RSSI IS: "..rssi) end end -ssid, tmp, bssid_set, bssid=wifi.sta.getconfig() +ssid, tmp, bssid_set, bssid=wifi.sta.getconfig() scan_cfg = {} -scan_cfg.ssid = ssid +scan_cfg.ssid = ssid if bssid_set == 1 then scan_cfg.bssid = bssid else scan_cfg.bssid = nil end scan_cfg.channel = wifi.getchannel() scan_cfg.show_hidden = 0 @@ -535,6 +578,88 @@ wifi.sta.getap(scan_cfg, 1, listap) #### See also [`wifi.sta.getip()`](#wifistagetip) +## wifi.sta.getapindex() + +Get index of current Access Point stored in AP cache. + + +#### Syntax +`wifi.sta.getapindex()` + +#### Parameters +none + +#### Returns +`current_index` index of currently selected Access Point. (Range:1-5) + +#### Example +```lua +print("the index of the currently selected AP is: "..wifi.sta.getapindex()) +``` + +#### See also +- [`wifi.sta.getapindex()`](#wifistagetapindex) +- [`wifi.sta.apinfo()`](#wifistaapinfo) +- [`wifi.sta.apchange()`](#wifistaapchange) + +## wifi.sta.getapinfo() + +Get information of APs cached by ESP8266 station. + +!!! Note + Any Access Points configured with save disabled `wifi.sta.config({save=false})` will populate this list (appearing to overwrite APs stored in flash) until restart. + +#### Syntax +`wifi.sta.getapinfo()` + +#### Parameters +`nil` + +#### Returns +- `ap_info` + - `qty` quantity of APs returned + - `1-5` index of AP. (the index corresponds to index used by [`wifi.sta.changeap()`](#wifistachangeap) and [`wifi.sta.getapindex()`](#wifistagetapindex)) + - `ssid` ssid of Access Point + - `pwd` Password for Access Point + - If no password was configured, the `pwd` field will be `nil` + - `bssid` MAC address of Access Point + - If no MAC address was configured, the `bssid` field will be `nil` + + +#### Example +```lua +--print stored access point info +do + for k,v in pairs(wifi.sta.getapinfo()) do + if (type(v)=="table") then + print(" "..k.." : "..type(v)) + for k,v in pairs(v) do + print("\t\t"..k.." : "..v) + end + else + print(" "..k.." : "..v) + end + end +end + +--print stored access point info(formatted) +do + local x=wifi.sta.getapinfo() + local y=wifi.sta.getapindex() + print("\n Number of APs stored in flash:", x.qty) + print(string.format(" %-6s %-32s %-64s %-18s", "index:", "SSID:", "Password:", "BSSID:")) + for i=1, (x.qty), 1 do + print(string.format(" %s%-6d %-32s %-64s %-18s",(i==y and ">" or " "), i, x[i].ssid, x[i].pwd and x[i].pwd or type(nil), x[i].bssid and x[i].bssid or type(nil))) + end +end +``` + +#### See also +- [`wifi.sta.getapindex()`](#wifistagetapindex) +- [`wifi.sta.setaplimit()`](#wifistasetaplimit) +- [`wifi.sta.changeap()`](#wifistachangeap) +- [`wifi.sta.config()`](#wifistaconfig) + ## wifi.sta.getbroadcast() Gets the broadcast address in station mode. @@ -546,7 +671,7 @@ Gets the broadcast address in station mode. `nil` #### Returns -broadcast address as string, for example "192.168.0.255", +broadcast address as string, for example "192.168.0.255", returns `nil` if IP address = "0.0.0.0". #### See also @@ -554,23 +679,39 @@ returns `nil` if IP address = "0.0.0.0". ## wifi.sta.getconfig() -Gets the WiFi station configuration. +Gets the WiFi station configuration. #### Syntax `wifi.sta.getconfig()` #### Parameters -none +- `return_table` + - `true` returns data in a table + - `false` returns data in the old format (default) #### Returns -ssid, password, bssid_set, bssid +If `return_table` is `true`: +- `config_table` + - `ssid` ssid of Access Point. + - `pwd` password to Access Point. + - If no password was configured, the `pwd` field will be `nil` + - `bssid` MAC address of Access Point + - If no MAC address was configured, the `bssid` field will be `nil` -Note: If bssid_set is equal to 0 then bssid is irrelevant +If `return_table` is `false`: +- ssid, password, bssid_set, bssid + - Note: If `bssid_set` is equal to `0` then `bssid` is irrelevant, #### Example ```lua ---Get current Station configuration +--Get current Station configuration (NEW FORMAT) +do +local def_sta_config=wifi.sta.getconfig(true) +print(string.format("\tDefault station config\n\tssid:\"%s\"\tpassword:\"%s\"%s", def_sta_config.ssid, def_sta_config.pwd, (type(def_sta_config.bssid)=="string" and "\tbssid:\""..def_sta_config.bssid.."\"" or ""))) +end + +--Get current Station configuration (OLD FORMAT) ssid, password, bssid_set, bssid=wifi.sta.getconfig() print("\nCurrent Station configuration:\nSSID : "..ssid .."\nPassword : "..password @@ -580,6 +721,55 @@ ssid, password, bssid_set, bssid=nil, nil, nil, nil ``` #### See also +- [`wifi.sta.getdefaultconfig()`](#wifistagetdefaultconfig) +- [`wifi.sta.connect()`](#wifistaconnect) +- [`wifi.sta.disconnect()`](#wifistadisconnect) + +## wifi.sta.getdefaultconfig() + +Gets the default WiFi station configuration stored in flash. + +#### Syntax +`wifi.sta.getdefaultconfig(return_table)` + +#### Parameters +- `return_table` + - `true` returns data in a table + - `false` returns data in the old format (default) + +#### Returns +If `return_table` is `true`: +- `config_table` + - `ssid` ssid of Access Point. + - `pwd` password to Access Point. + - If no password was configured, the `pwd` field will be `nil` + - `bssid` MAC address of Access Point + - If no MAC address was configured, the `bssid` field will be `nil` + +If `return_table` is `false`: +- ssid, password, bssid_set, bssid + - Note: If `bssid_set` is equal to `0` then `bssid` is irrelevant, + +#### Example + +```lua +--Get default Station configuration (NEW FORMAT) +do + local def_sta_config=wifi.sta.getdefaultconfig(true) + print(string.format("\tDefault station config\n\tssid:\"%s\"\tpassword:\"%s\"%s", def_sta_config.ssid, def_sta_config.pwd, (type(def_sta_config.bssid)=="string" and "\tbssid:\""..def_sta_config.bssid.."\"" or ""))) +end + +--Get default Station configuration (OLD FORMAT) +ssid, password, bssid_set, bssid=wifi.sta.getdefaultconfig() +print("\nCurrent Station configuration:\nSSID : "..ssid +.."\nPassword : "..password +.."\nBSSID_set : "..bssid_set +.."\nBSSID: "..bssid.."\n") +ssid, password, bssid_set, bssid=nil, nil, nil, nil +``` + +#### See also +- [`wifi.sta.getconfig()`](#wifistagetconfig) - [`wifi.sta.connect()`](#wifistaconnect) - [`wifi.sta.disconnect()`](#wifistadisconnect) @@ -667,6 +857,32 @@ RSSI=wifi.sta.getrssi() print("RSSI is", RSSI) ``` +## wifi.sta.setaplimit() + +Set Maximum number of Access Points to store in flash. + - This value is written to flash + +!!! Attention + If 5 Access Points are stored and AP limit is set to 4, the AP at index 5 will remain until [`node.restore()`](node.md#noderestore) is called or AP limit is set to 5 and AP is overwritten. + +#### Syntax +`wifi.sta.setaplimit(qty)` + +#### Parameters +`qty` Quantity of Access Points to store in flash. Range: 1-5 (Default: 5) + +#### Returns +- `true` Success +- `false` Failure + +#### Example +```lua +wifi.sta.setaplimit(true) +``` + +#### See also +- [`wifi.sta.getapinfo()`](#wifistagetapinfo) + ## wifi.sta.sethostname() Sets station hostname. @@ -732,6 +948,26 @@ print(wifi.sta.setmac("DE:AD:BE:EF:7A:C0")) #### See also [`wifi.sta.setip()`](#wifistasetip) +## wifi.sta.sleeptype() + +Configures the WiFi modem sleep type to be used while station is connected to an Access Point. + + !!! note + Does not apply to `wifi.SOFTAP`, `wifi.STATIONAP` or `wifi.NULLMODE`. + +#### Syntax +`wifi.sta.sleeptype(type_wanted)` + +#### Parameters +`type_wanted` one of the following: + +- `wifi.NONE_SLEEP` to keep the modem on at all times +- `wifi.LIGHT_SLEEP` to allow the CPU to power down under some circumstances +- `wifi.MODEM_SLEEP` to power down the modem as much as possible + +#### Returns +The actual sleep mode set, as one of `wifi.NONE_SLEEP`, `wifi.LIGHT_SLEEP` or `wifi.MODEM_SLEEP`. + ## wifi.sta.status() Gets the current status in station mode. @@ -758,22 +994,26 @@ number: 0~5 Sets SSID and password in AP mode. Be sure to make the password at least 8 characters long! If you don't it will default to *no* password and not set the SSID! It will still work as an access point but use a default SSID like e.g. NODE-9997C3. -NOTE: SoftAP Configuration will be retained until changed even if device is turned off. - #### Syntax `wifi.ap.config(cfg)` #### Parameters -- `ssid` SSID chars 1-32 -- `pwd` password chars 8-64 -- `auth` authentication method, one of `wifi.OPEN` (default), `wifi.WPA_PSK`, `wifi.WPA2_PSK`, `wifi.WPA_WPA2_PSK` -- `channel` channel number 1-14 default = 6 -- `hidden` 0 = not hidden, 1 = hidden, default 0 -- `max` maximal number of connections 1-4 default=4 -- `beacon` beacon interval time in range 100-60000, default = 100 +- `cfg` table to hold configuration + - `ssid` SSID chars 1-32 + - `pwd` password chars 8-64 + - `auth` authentication method, one of `wifi.OPEN` (default), `wifi.WPA_PSK`, `wifi.WPA2_PSK`, `wifi.WPA_WPA2_PSK` + - `channel` channel number 1-14 default = 6 + - `hidden` false = not hidden, true = hidden, default = false + - `max` maximum number of connections 1-4 default=4 + - `beacon` beacon interval time in range 100-60000, default = 100 + - `save` save configuration to flash. + - `true` configuration **will** be retained through power cycle. (Default) + - `false` configuration **will not** be retained through power cycle. + #### Returns -`nil` +- `true` Success +- `false` Failure #### Example: ```lua @@ -785,7 +1025,7 @@ NOTE: SoftAP Configuration will be retained until changed even if device is turn ## wifi.ap.deauth() -Deauths (forcibly removes) a client from the ESP access point by sending a corresponding IEEE802.11 management packet (first) and removing the client from it's data structures (afterwards). +Deauths (forcibly removes) a client from the ESP access point by sending a corresponding IEEE802.11 management packet (first) and removing the client from it's data structures (afterwards). The IEEE802.11 reason code used is 2 for "Previous authentication no longer valid"(AUTH_EXPIRE). @@ -803,11 +1043,11 @@ Returns true unless called while the ESP is in the STATION opmode ```lua allowed_mac_list={"18:fe:34:00:00:00", "18:fe:34:00:00:01"} -wifi.eventmon.register(wifi.eventmon.AP_STACONNECTED, function(T) +wifi.eventmon.register(wifi.eventmon.AP_STACONNECTED, function(T) print("\n\tAP - STATION CONNECTED".."\n\tMAC: "..T.MAC.."\n\tAID: "..T.AID) if(allowed_mac_list~=nil) then - for _, v in pairs(allowed_mac_list) do - if(v == T.MAC) then return end + for _, v in pairs(allowed_mac_list) do + if(v == T.MAC) then return end end end wifi.ap.deauth(T.MAC) @@ -831,7 +1071,7 @@ Gets broadcast address in AP mode. none #### Returns -broadcast address in string, for example "192.168.0.255", +broadcast address in string, for example "192.168.0.255", returns `nil` if IP address = "0.0.0.0". #### Example @@ -871,6 +1111,102 @@ for mac,ip in pairs(wifi.ap.getclient()) do end ``` +## wifi.ap.getconfig() + +Gets the current SoftAP configuration. + +#### Syntax +`wifi.ap.getconfig(return_table)` + +#### Parameters +- `return_table` + - `true` returns data in a table + - `false` returns data in the old format (default) + +#### Returns +If `return_table` is true: +- `config_table` + - `ssid` Network name + - `pwd` Password + - If no password was configured, the `pwd` field will be `nil` + - `auth` Authentication Method (`wifi.OPEN`, `wifi.WPA_PSK`, `wifi.WPA2_PSK` or `wifi.WPA_WPA2_PSK`) + - `channel` Channel number + - `hidden` `false` = not hidden, `true` = hidden + - `max` Maximum number of client connections + - `beacon` Beacon interval + +If `return_table` is false: +ssid, password + Note: If bssid_set is equal to 0 then bssid is irrelevant + +#### Example + +```lua +--Get SoftAP configuration table (NEW FORMAT) +do + print("\n Current SoftAP configuration:") + for k,v in pairs(wifi.ap.getconfig(true)) do + print(" "..k.." :",v) + end +end + +--Get current SoftAP configuration (OLD FORMAT) +do + local ssid, password=wifi.ap.getconfig() + print("\n Current SoftAP configuration:\n SSID : "..ssid.. + "\n Password :",password) + ssid, password=nil, nil +end +``` + +## wifi.ap.getdefaultconfig() + +Gets the default SoftAP configuration stored in flash. + +#### Syntax +`wifi.ap.getdefaultconfig(return_table)` + +#### Parameters +- `return_table` + - `true` returns data in a table + - `false` returns data in the old format (default) + +#### Returns +If `return_table` is true: +- `config_table` + - `ssid` Network name + - `pwd` Password + - If no password was configured, the `pwd` field will be `nil` + - `auth` Authentication Method (`wifi.OPEN`, `wifi.WPA_PSK`, `wifi.WPA2_PSK` or `wifi.WPA_WPA2_PSK`) + - `channel` Channel number + - `hidden` `false` = not hidden, `true` = hidden + - `max` Maximum number of client connections + - `beacon` Beacon interval + +If `return_table` is false: +ssid, password + Note: If bssid_set is equal to 0 then bssid is irrelevant + +#### Example + +```lua +--Get default SoftAP configuration table (NEW FORMAT) +do + print("\n Default SoftAP configuration:") + for k,v in pairs(wifi.ap.getdefaultconfig(true)) do + print(" "..k.." :",v) + end +end + +--Get default SoftAP configuration (OLD FORMAT) +do + local ssid, password=wifi.ap.getdefaultconfig() + print("\n Default SoftAP configuration:\n SSID : "..ssid.. + "\n Password :",password) + ssid, password=nil, nil +end +``` + ## wifi.ap.getip() Gets IP address, netmask and gateway in AP mode. @@ -1077,39 +1413,39 @@ T: Table returned by event. #### Example ```lua - wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T) + wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T) print("\n\tSTA - CONNECTED".."\n\tSSID: "..T.SSID.."\n\tBSSID: ".. T.BSSID.."\n\tChannel: "..T.channel) end) - - wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T) + + wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T) print("\n\tSTA - DISCONNECTED".."\n\tSSID: "..T.SSID.."\n\tBSSID: ".. T.BSSID.."\n\treason: "..T.reason) end) - wifi.eventmon.register(wifi.eventmon.STA_AUTHMODE_CHANGE, Function(T) + wifi.eventmon.register(wifi.eventmon.STA_AUTHMODE_CHANGE, Function(T) print("\n\tSTA - AUTHMODE CHANGE".."\n\told_auth_mode: ".. - T.old_auth_mode.."\n\tnew_auth_mode: "..T.new_auth_mode) + T.old_auth_mode.."\n\tnew_auth_mode: "..T.new_auth_mode) end) - wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T) + wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T) print("\n\tSTA - GOT IP".."\n\tStation IP: "..T.IP.."\n\tSubnet mask: ".. T.netmask.."\n\tGateway IP: "..T.gateway) end) - wifi.eventmon.register(wifi.eventmon.STA_DHCP_TIMEOUT, function() + wifi.eventmon.register(wifi.eventmon.STA_DHCP_TIMEOUT, function() print("\n\tSTA - DHCP TIMEOUT") end) - wifi.eventmon.register(wifi.eventmon.AP_STACONNECTED, function(T) + wifi.eventmon.register(wifi.eventmon.AP_STACONNECTED, function(T) print("\n\tAP - STATION CONNECTED".."\n\tMAC: "..T.MAC.."\n\tAID: "..T.AID) end) - wifi.eventmon.register(wifi.eventmon.AP_STADISCONNECTED, function(T) + wifi.eventmon.register(wifi.eventmon.AP_STADISCONNECTED, function(T) print("\n\tAP - STATION DISCONNECTED".."\n\tMAC: "..T.MAC.."\n\tAID: "..T.AID) end) - wifi.eventmon.register(wifi.eventmon.AP_PROBEREQRECVED, function(T) + wifi.eventmon.register(wifi.eventmon.AP_PROBEREQRECVED, function(T) print("\n\tAP - STATION DISCONNECTED".."\n\tMAC: ".. T.MAC.."\n\tRSSI: "..T.RSSI) end) ``` diff --git a/docs/en/modules/ws2812.md b/docs/en/modules/ws2812.md index ed8a8a2f..21f32ae5 100644 --- a/docs/en/modules/ws2812.md +++ b/docs/en/modules/ws2812.md @@ -71,7 +71,7 @@ ws2812.write(nil, string.char(0, 255, 0, 0, 255, 0)) -- turn the two first RGB l For more advanced animations, it is useful to keep a "framebuffer" of the strip, interact with it and flush it to the strip. -For this purpose, the ws2812 library offers a read/write buffer. +For this purpose, the ws2812 library offers a read/write buffer. This buffer has a `__tostring` method so that it can be printed. This is useful for debugging. #### Example Led chaser with a RGBW strip @@ -181,6 +181,92 @@ The number of given bytes must match the number of bytesPerLed of the buffer buffer:fill(0, 0, 0) -- fill the buffer with black for a RGB strip ``` +## ws2812.buffer:dump() +Returns the contents of the buffer (the pixel values) as a string. This can then be saved to a file or sent over a network. + +#### Syntax +`buffer:dump()` + +#### Returns +A string containing the pixel values. + +#### Example +```lua +local s = buffer:dump() +``` + +## ws2812.buffer:replace() +Inserts a string (or a buffer) into another buffer with an offset. +The buffer must have the same number of colors per led or an error will be thrown. + +#### Syntax +`buffer:replace(source[, offset])` + +#### Parameters + - `source` the pixel values to be set into the buffer. This is either a string or a buffer. + - `offset` the offset where the source is to be placed in the buffer. Default is 1. Negative values can be used. + +#### Returns +`nil` + +#### Example +```lua +buffer:replace(anotherbuffer:dump()) -- copy one buffer into another via a string +buffer:replace(anotherbuffer) -- copy one buffer into another +newbuffer = buffer.sub(1) -- make a copy of a buffer into a new buffer +``` + +## ws2812.buffer:mix() +This is a general method that loads data into a buffer that is a linear combination of data from other buffers. It can be used to copy a buffer or, +more usefully, do a cross fade. The pixel values are computed as integers and then range limited to [0, 255]. This means that negative +factors work as expected, and that the order of combining buffers does not matter. + +#### Syntax +`buffer:mix(factor1, buffer1, ...)` + +#### Parameters + - `factor1` This is the factor that the contents of `buffer1` are multiplied by. This factor is scaled by a factor of 256. Thus `factor1` value of 256 is a factor of 1.0. + - `buffer1` This is the source buffer. It must be of the same shape as the destination buffer. + +There can be any number of factor/buffer pairs. + +#### Returns +`nil` + +#### Example +```lua +-- loads buffer with a crossfade between buffer1 and buffer2 +buffer:mix(256 - crossmix, buffer1, crossmix, buffer2) + +-- multiplies all values in buffer by 0.75 +-- This can be used in place of buffer:fade +buffer:mix(192, buffer) +``` + +## ws2812.buffer:power() +Computes the total energy requirement for the buffer. This is merely the total sum of all the pixel values (which assumes that each color in each +pixel consumes the same amount of power). A real WS2812 (or WS2811) has three constant current drivers of 20mA -- one for each of R, G and B. The +pulse width modulation will cause the *average* current to scale linearly with pixel value. + +#### Syntax +`buffer:power()` + +#### Returns +An integer which is the sum of all the pixel values. + +#### Example +```lua +-- Dim the buffer to no more than the PSU can provide +local psu_current_ma = 1000 +local led_current_ma = 20 +local led_sum = psu_current_ma * 255 / led_current_ma + +local p = buffer:power() +if p > led_sum then + buffer:mix(256 * led_sum / p, buffer) -- power is now limited +end +``` + ## ws2812.buffer:fade() Fade in or out. Defaults to out. Multiply or divide each byte of each led with/by the given value. Useful for a fading effect. @@ -200,14 +286,17 @@ buffer:fade(2) buffer:fade(2, ws2812.FADE_IN) ``` ## ws2812.buffer:shift() -Shift the content of the buffer in positive or negative direction. This allows simple animation effects. +Shift the content of (a piece of) the buffer in positive or negative direction. This allows simple animation effects. A slice of the buffer can be specified by using the +standard start and end offset Lua notation. Negative values count backwards from the end of the buffer. #### Syntax -`buffer:shift(value [, mode])` +`buffer:shift(value [, mode[, i[, j]]])` #### Parameters - `value` number of pixels by which to rotate the buffer. Positive values rotate forwards, negative values backwards. - `mode` is the shift mode to use. Can be one of `ws2812.SHIFT_LOGICAL` or `ws2812.SHIFT_CIRCULAR`. In case of SHIFT\_LOGICAL, the freed pixels are set to 0 (off). In case of SHIFT\_CIRCULAR, the buffer is treated like a ring buffer, inserting the pixels falling out on one end again on the other end. Defaults to SHIFT\_LOGICAL. + - `i` is the first offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is 1. + - `j` is the last offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is -1. #### Returns `nil` @@ -216,3 +305,42 @@ Shift the content of the buffer in positive or negative direction. This allows s ```lua buffer:shift(3) ``` + +## ws2812.buffer:sub() +This implements the extraction function like `string.sub`. The indexes are in leds and all the same rules apply. + +#### Syntax +`buffer1:sub(i[, j])` + +#### Parameters + - `i` This is the start of the extracted data. Negative values can be used. + - `j` this is the end of the extracted data. Negative values can be used. The default is -1. + +#### Returns +A buffer containing the extracted piece. + +#### Example +``` +b = buffer:sub(1,10) +``` + +## ws2812.buffer:__concat() +This implements the `..` operator to concatenate two buffers. They must have the same number of colors per led. + +#### Syntax +`buffer1 .. buffer2` + +#### Parameters + - `buffer1` this is the start of the resulting buffer + - `buffer2` this is the end of the resulting buffer + +#### Returns +The concatenated buffer. + +#### Example +``` +ws2812.write(buffer1 .. buffer2) +``` + + + diff --git a/lua_examples/somfy.lua b/lua_examples/somfy.lua new file mode 100644 index 00000000..f3baa3ed --- /dev/null +++ b/lua_examples/somfy.lua @@ -0,0 +1,145 @@ +-- Somfy module example (beside somfy module requires also CJSON module) +-- The rolling code number is stored in the file somfy.cfg. A cached write of the somfy.cfg file is implemented in order to reduce the number of write to the EEPROM memory. Together with the logic of the file module it should allow long lasting operation. + +config_file = "somfy." +-- somfy.cfg looks like +-- {"window1":{"rc":1,"address":123},"window2":{"rc":1,"address":124}} + +local tmr_cache = tmr.create() +local tmr_delay = tmr.create() + +pin = 4 +gpio.mode(pin, gpio.OUTPUT, gpio.PULLUP) + +function deepcopy(orig) + local orig_type = type(orig) + local copy + if orig_type == 'table' then + copy = {} + for orig_key, orig_value in next, orig, nil do + copy[deepcopy(orig_key)] = deepcopy(orig_value) + end + setmetatable(copy, deepcopy(getmetatable(orig))) + else -- number, string, boolean, etc + copy = orig + end + return copy +end + +function readconfig() + local cfg, ok, ln + if file.exists(config_file.."cfg") then + print("Reading config from "..config_file.."cfg") + file.open(config_file.."cfg", "r+") + ln = file.readline() + file.close() + else + if file.exists(config_file.."bak") then + print("Reading config from "..config_file.."bak") + file.open(config_file.."bak", "r+") + ln = file.readline() + file.close() + end + end + if not ln then ln = "{}" end + print("Configuration: "..ln) + config = cjson.decode(ln) + config_saved = deepcopy(config) +end + +function writeconfighard() + print("Saving config") + file.remove(config_file.."bak") + file.rename(config_file.."cfg", config_file.."bak") + file.open(config_file.."cfg", "w+") + local ok, cfg = pcall(cjson.encode, config) + if ok then + file.writeline(cfg) + else + print("Config not saved!") + end + file.close() + + config_saved = deepcopy(config) +end + +function writeconfig() + tmr.stop(tmr_cache) + local savenow = false + local savelater = false + +--print("Config: "..cjson.encode(config)) +--print("Config saved: "..cjson.encode(config)) + + local count = 0 + for _ in pairs(config_saved) do count = count + 1 end + if count == 0 then + config_saved = readconfig() + end + for remote,cfg in pairs(config_saved) do + savelater = savelater or not config[remote] or config[remote].rc > cfg.rc + savenow = savenow or not config[remote] or config[remote].rc > cfg.rc + 10 + end + savelater = savelater and not savenow + if savenow then + print("Saving config now!") + writeconfighard() + end + if savelater then + print("Saving config later") + tmr.alarm(tmr_cache, 65000, tmr.ALARM_SINGLE, writeconfighard) + end +end + +--======================================================================================================-- +function down(remote, cb, par) + par = par or {} + print("down: ".. remote) + config[remote].rc=config[remote].rc+1 + somfy.sendcommand(pin, config[remote].address, somfy.DOWN, config[remote].rc, 16, function() wait(100, cb, par) end) + writeconfig() +end + +function up(remote, cb, par) + par = par or {} + print("up: ".. remote) + config[remote].rc=config[remote].rc+1 + somfy.sendcommand(pin, config[remote].address, somfy.UP, config[remote].rc, 16, function() wait(100, cb, par) end) + writeconfig() +end + +function downStep(remote, cb, par) + par = par or {} + print("downStep: ".. remote) + config[remote].rc=config[remote].rc+1 + somfy.sendcommand(pin, config[remote].address, somfy.DOWN, config[remote].rc, 2, function() wait(300, cb, par) end) + writeconfig() +end + +function upStep(remote, cb, par) + par = par or {} + print("upStep: ".. remote) + config[remote].rc=config[remote].rc+1 + somfy.sendcommand(pin, config[remote].address, somfy.UP, config[remote].rc, 2, function() wait(300, cb, par) end) + writeconfig() +end + +function wait(ms, cb, par) + par = par or {} + print("wait: ".. ms) + if cb then tmr.alarm(tmr_delay, ms, tmr.ALARM_SINGLE, function () cb(unpack(par)) end) end +end + + +--======================================================================================================-- +if not config then readconfig() end +if #config == 0 then -- somfy.cfg does not exist + config = cjson.decode([[{"window1":{"rc":1,"address":123},"window2":{"rc":1,"address":124}}]]) + config_saved = deepcopy(config) +end +down('window1', + wait, {60000, + up, {'window1', + wait, {9000, + downStep, {'window1', downStep, {'window1', downStep, {'window1', downStep, {'window1', downStep, {'window1', downStep, {'window1', downStep, {'window1' +}}}}}}}}}}) diff --git a/sdk-overrides/include/user_interface.h b/sdk-overrides/include/user_interface.h index b288f995..61ce8356 100644 --- a/sdk-overrides/include/user_interface.h +++ b/sdk-overrides/include/user_interface.h @@ -6,5 +6,11 @@ bool wifi_softap_deauth(uint8 mac[6]); uint8 get_fpm_auto_sleep_flag(void); +enum ext_flash_size_map { + FLASH_SIZE_32M_MAP_2048_2048 = 7, + FLASH_SIZE_64M_MAP = 8, + FLASH_SIZE_128M_MAP = 9 +}; + #endif /* SDK_OVERRIDES_INCLUDE_USER_INTERFACE_H_ */ diff --git a/tools/pr-build.sh b/tools/pr-build.sh index 0bc34e94..7f715479 100644 --- a/tools/pr-build.sh +++ b/tools/pr-build.sh @@ -21,7 +21,7 @@ cat user_config.h cd "$TRAVIS_BUILD_DIR"/ld || exit # increase irom0_0_seg size for all modules build -sed -E -i.bak 's@(.*irom0_0_seg *:.*len *=) *[^,]*(.*)@\1 0xC0000\2@' nodemcu.ld +sed -E -i.bak 's@(.*irom0_0_seg *:.*len *=) *[^,]*(.*)@\1 0xD0000\2@' nodemcu.ld cat nodemcu.ld # change to "root" directory no matter where the script was started from diff --git a/tools/spiffsimg/Makefile b/tools/spiffsimg/Makefile index 82dfb797..8656d114 100644 --- a/tools/spiffsimg/Makefile +++ b/tools/spiffsimg/Makefile @@ -2,7 +2,7 @@ SRCS=\ main.c \ ../../app/spiffs/spiffs_cache.c ../../app/spiffs/spiffs_check.c ../../app/spiffs/spiffs_gc.c ../../app/spiffs/spiffs_hydrogen.c ../../app/spiffs/spiffs_nucleus.c -CFLAGS=-g -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -I. -I../../app/spiffs -I../../app/include -DNODEMCU_SPIFFS_NO_INCLUDE --include spiffs_typedefs.h +CFLAGS=-g -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -I. -I../../app/spiffs -I../../app/include -DNODEMCU_SPIFFS_NO_INCLUDE --include spiffs_typedefs.h -Ddbg_printf=printf spiffsimg: $(SRCS) $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ From ec265a6c21db22640795f190bdcb8a4f014cdced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Thu, 1 Dec 2016 21:57:24 +0100 Subject: [PATCH 09/11] Fix Somfy docs --- docs/en/modules/somfy.md | 3 ++- mkdocs.yml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/en/modules/somfy.md b/docs/en/modules/somfy.md index 07fe0a41..f5dd8eba 100644 --- a/docs/en/modules/somfy.md +++ b/docs/en/modules/somfy.md @@ -34,7 +34,8 @@ When I send the `somfy.DOWN` command, repeating the frame twice (which seems to nil #### Example -To start with controlling your Somfy blinds you need to +To start with controlling your Somfy blinds you need to: + - Choose an arbitrary remote address (different from your existing remote) - `123` in this example - Choose a starting point for the rolling code. Any unsigned int works, 1 is a good start - Long-press the program button of your existing remote control until your blind goes up and down slightly diff --git a/mkdocs.yml b/mkdocs.yml index e3e76560..c6b81d46 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -70,6 +70,7 @@ pages: - 'rtctime': 'en/modules/rtctime.md' - 'sigma delta': 'en/modules/sigma-delta.md' - 'sntp': 'en/modules/sntp.md' + - 'somfy': 'en/modules/somfy.md' - 'spi': 'en/modules/spi.md' - 'struct': 'en/modules/struct.md' - 'switec': 'en/modules/switec.md' From ecf9c6440453802f7eaf3583b8d990d65eec1cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Wed, 7 Dec 2016 22:18:35 +0100 Subject: [PATCH 10/11] Revert "Next 1.5.4.1 master drop (#1627)" This reverts commit 04ce0adf6fa7ccda242f1235b7916252cd1ad543. --- CONTRIBUTING.md | 24 +- Makefile | 4 +- app/driver/i2c_master.c | 7 - app/driver/uart.c | 16 +- app/http/httpclient.c | 74 +-- app/http/httpclient.h | 19 +- app/include/driver/uart.h | 2 +- app/include/rom.h | 2 - app/include/sections.h | 1 - app/include/u8g_config.h | 6 - app/include/user_config.h | 7 +- app/include/user_modules.h | 1 - app/libc/c_stdio.h | 2 - app/libc/dbg_printf.c | 164 ----- app/lua/lauxlib.c | 2 +- app/lua/liolib.c | 25 +- app/lua/luaconf.h | 4 +- app/modules/enduser_setup.c | 602 +++++++---------- app/modules/eus/enduser_setup.html | 315 --------- app/modules/eus/enduser_setup.html.gz | Bin 2393 -> 0 bytes app/modules/eus/http_html_backup.def | 203 ------ app/modules/eus/prepare.sh | 7 - app/modules/file.c | 232 ++----- app/modules/gpio.c | 20 +- app/modules/http.c | 6 +- app/modules/mqtt.c | 94 +-- app/modules/ow.c | 2 - app/modules/sntp.c | 2 +- app/modules/somfy.c | 248 ------- app/modules/u8g.c | 293 +++++++-- app/modules/u8g_glue.c | 347 ---------- app/modules/u8g_glue.h | 22 - app/modules/websocket.c | 64 +- app/modules/wifi.c | 868 ++++++++++--------------- app/modules/wifi_common.h | 11 +- app/modules/ws2812.c | 256 ++------ app/net/nodemcu_mdns.c | 2 +- app/platform/vfs.c | 3 +- app/platform/vfs.h | 2 +- app/spiffs/spiffs.c | 34 +- app/spiffs/spiffs_config.h | 10 +- app/spiffs/spiffs_nucleus.c | 59 +- app/spiffs/spiffs_nucleus.h | 2 +- app/u8glib/u8g.h | 3 + app/user/Makefile | 2 +- app/user/user_main.c | 80 +-- app/websocket/websocketclient.c | 92 +-- app/websocket/websocketclient.h | 8 +- docs/en/flash.md | 111 ++-- docs/en/index.md | 2 +- docs/en/modules/enduser-setup.md | 27 +- docs/en/modules/file.md | 326 ++++------ docs/en/modules/gpio.md | 17 +- docs/en/modules/net.md | 73 +-- docs/en/modules/ow.md | 2 +- docs/en/modules/rtctime.md | 2 +- docs/en/modules/sntp.md | 2 +- docs/en/modules/somfy.md | 45 -- docs/en/modules/spi.md | 4 - docs/en/modules/u8g.md | 28 +- docs/en/modules/websocket.md | 25 +- docs/en/modules/wifi.md | 556 ++++------------ docs/en/modules/ws2812.md | 134 +--- lua_examples/somfy.lua | 145 ----- sdk-overrides/include/user_interface.h | 6 - tools/pr-build.sh | 2 +- tools/spiffsimg/Makefile | 2 +- 67 files changed, 1420 insertions(+), 4338 deletions(-) delete mode 100644 app/libc/dbg_printf.c delete mode 100644 app/modules/eus/enduser_setup.html delete mode 100644 app/modules/eus/enduser_setup.html.gz delete mode 100644 app/modules/eus/http_html_backup.def delete mode 100644 app/modules/eus/prepare.sh delete mode 100644 app/modules/somfy.c delete mode 100644 app/modules/u8g_glue.c delete mode 100644 app/modules/u8g_glue.h delete mode 100644 docs/en/modules/somfy.md delete mode 100644 lua_examples/somfy.lua diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 42ee59bc..996162da 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,22 +4,16 @@ The following is a set of guidelines for contributing to NodeMCU on GitHub. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request. -It is appreciated if you raise an issue _before_ you start changing NodeMCU, discussing the proposed change; emphasing that the you are proposing to develop the patch yourself, and outlining the strategy for implementation. This type of discussion is what we should be doing on the issues list and it is better to do this before or in parallel to developing the patch rather than having "you should have done it this way" type of feedback on the PR itself. +It is appreciated but optional if you raise an issue _before_ you start changing NodeMCU, discussing the proposed change; emphasing that the you are proposing to develop the patch yourself, and outlining the strategy for implementation. This type of discussion is what we should be doing on the issues list and it is better to do this before or in parallel to developing the patch rather than having "you should have done it this way" type of feedback on the PR itself. ### Table Of Contents -* [General remarks](#general-remarks) + * [Development environment setup](#development-environment-setup) * [Writing Documentation](#writing-documentation) * [Working with Git and GitHub](#working-with-git-and-github) * [General flow](#general-flow) * [Keeping your fork in sync](#keeping-your-fork-in-sync) * [Commit messages](#commit-messages) -* [For collaborators](#for-collaborators) - * [Handling releases](#handling-release) - -## General remarks -We are a friendly and welcoming community and look forward to your contributions. Once your contribution is integrated into this repository we feel responsible for it. Therefore, be prepared for constructive feedback. Before we merge anything we need to ensure that it fits in and is consistent with the rest of NodeMCU. -If you made something really cool but won't spend time to integrate it into this upstream project please still share it in your fork on GitHub. If you mention it in an issues we'll take a look at it anyway. ## Development environment setup Use the platform and tools you feel most comfortable with. There are no constraints imposed by this project. You have (at least) two options to set up the toolchain to build the NodeMCU firmware: @@ -58,7 +52,7 @@ Avoid intermediate merge commits. [Rebase](https://www.atlassian.com/git/tutoria 1. `git checkout ` 1. Make changes to the code base and commit them using e.g. `git commit -a -m 'Look ma, I did it'` 1. When you're done: - 1. Think about [squashing (some of) your commits](http://www.andrewconnell.com/blog/squash-multiple-git-commits-into-one). There are [several ways](http://stackoverflow.com/a/5201642/131929) to do this. There's no need to squash everything into a single commit as GitHub offers to do this when we merge your changes. However, you might want to trim your commit history to relevant chunks. + 1. [Squash your commits](http://www.andrewconnell.com/blog/squash-multiple-git-commits-into-one). There are [several ways](http://stackoverflow.com/a/5201642/131929) of doing this. 1. Bring your fork up-to-date with the NodeMCU upstream repo ([see below](#keeping-your-fork-in-sync)). Then rebase your branch on `dev` running `git rebase dev`. 1. `git push` 1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/) (PR) on GitHub. @@ -96,15 +90,3 @@ Further paragraphs come after blank lines. Don't forget to [reference affected issues](https://help.github.com/articles/closing-issues-via-commit-messages/) in the commit message to have them closed automatically on GitHub. [Amend](https://help.github.com/articles/changing-a-commit-message/) your commit messages if necessary to make sure what the world sees on GitHub is as expressive and meaningful as possible. - -## For collaborators - -### Handling releases -- Create a [milestone](https://github.com/nodemcu/nodemcu-firmware/milestones) right after you cut a new release. Give it a meaningful name if you already have an idea what the scope of the upcoming release is going to be. Also set the due date to ~2 months in the future. -- Add this milestone to every PR before you merge it. Also add the milestone to PRs you want to see land in this milestone. -- Add notes to the description of the milestone in the course of the ~2 months it lives. -- Be careful and reluctant to merge PRs once we're past the 6-weeks mark of a milestone. Ideally we don't merge anything in the last 2 weeks. -- Cutting a release - - Create an annotated tag like so: `git tag -a -master_ -m ""`, `git push --tags` - - Create a new [release](https://github.com/nodemcu/nodemcu-firmware/releases) based on the tag you just pushed. The version name is the same as the tag name. - - Write release notes. Mention breaking changes explicitly. Since every PR that went into this release is linked to from the milestone it should be fairly easy to include important changes in the release notes. diff --git a/Makefile b/Makefile index 2e3f3900..f3e8bc45 100644 --- a/Makefile +++ b/Makefile @@ -207,14 +207,14 @@ sdk_patched: sdk_extracted $(TOP_DIR)/sdk/.patched-$(SDK_VER) $(TOP_DIR)/sdk/.extracted-$(SDK_BASE_VER): $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip mkdir -p "$(dir $@)" - (cd "$(dir $@)" && rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip ESP8266_NONOS_SDK/lib/* ESP8266_NONOS_SDK/ld/eagle.rom.addr.v6.ld ESP8266_NONOS_SDK/include/* ESP8266_NONOS_SDK/bin/esp_init_data_default.bin) + (cd "$(dir $@)" && rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip ESP8266_NONOS_SDK/lib/* ESP8266_NONOS_SDK/ld/eagle.rom.addr.v6.ld ESP8266_NONOS_SDK/include/* ) mv $(dir $@)/ESP8266_NONOS_SDK $(dir $@)/esp_iot_sdk_v$(SDK_VER) rm -f $(SDK_DIR)/lib/liblwip.a touch $@ $(TOP_DIR)/sdk/.patched-$(SDK_VER): $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VER).zip mkdir -p "$(dir $@)/patch" - (cd "$(dir $@)/patch" && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VER)*.zip *.a esp_init_data_default.bin && mv *.a $(SDK_DIR)/lib/ && mv esp_init_data_default.bin $(SDK_DIR)/bin/) + (cd "$(dir $@)/patch" && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VER)*.zip *.a && mv *.a $(SDK_DIR)/lib/) rmdir $(dir $@)/patch rm -f $(SDK_DIR)/lib/liblwip.a touch $@ diff --git a/app/driver/i2c_master.c b/app/driver/i2c_master.c index f8de4bc4..772be691 100644 --- a/app/driver/i2c_master.c +++ b/app/driver/i2c_master.c @@ -33,8 +33,6 @@ LOCAL uint8 pinSCL = 15; LOCAL void ICACHE_FLASH_ATTR i2c_master_setDC(uint8 SDA, uint8 SCL) { - uint8 sclLevel; - SDA &= 0x01; SCL &= 0x01; m_nLastSDA = SDA; @@ -49,11 +47,6 @@ i2c_master_setDC(uint8 SDA, uint8 SCL) } else { I2C_MASTER_SDA_HIGH_SCL_HIGH(); } - if(1 == SCL) { - do { - sclLevel = GPIO_INPUT_GET(GPIO_ID_PIN(I2C_MASTER_SCL_GPIO)); - } while(sclLevel == 0); - } } /****************************************************************************** diff --git a/app/driver/uart.c b/app/driver/uart.c index 39969b45..688dcd2d 100755 --- a/app/driver/uart.c +++ b/app/driver/uart.c @@ -30,8 +30,6 @@ // For event signalling static task_handle_t sig = 0; -static uint8 *sig_flag; -static uint8 isr_flag = 0; // UartDev is defined and initialized in rom code. extern UartDevice UartDev; @@ -279,12 +277,8 @@ uart0_rx_intr_handler(void *para) got_input = true; } - if (got_input && sig) { - if (isr_flag == *sig_flag) { - isr_flag ^= 0x01; - task_post_low (sig, 0x8000 | isr_flag << 14 | false); - } - } + if (got_input && sig) + task_post_low (sig, false); } static void @@ -322,21 +316,21 @@ uart_stop_autobaud() * Description : user interface for init uart * Parameters : UartBautRate uart0_br - uart0 bautrate * UartBautRate uart1_br - uart1 bautrate + * uint8 task_prio - task priority to signal on input * os_signal_t sig_input - signal to post - * uint8 *flag_input - flag of consumer task * Returns : NONE *******************************************************************************/ void ICACHE_FLASH_ATTR -uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input, uint8 *flag_input) +uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input) { sig = sig_input; - sig_flag = flag_input; // rom use 74880 baut_rate, here reinitialize UartDev.baut_rate = uart0_br; uart_config(UART0); UartDev.baut_rate = uart1_br; uart_config(UART1); + ETS_UART_INTR_ENABLE(); #ifdef BIT_RATE_AUTOBAUD uart_init_autobaud(0); #endif diff --git a/app/http/httpclient.c b/app/http/httpclient.c index 42e5b8f1..5e0fe555 100644 --- a/app/http/httpclient.c +++ b/app/http/httpclient.c @@ -13,7 +13,6 @@ */ #include "osapi.h" -#include "../libc/c_stdio.h" #include "user_interface.h" #include "espconn.h" #include "mem.h" @@ -95,7 +94,7 @@ static int ICACHE_FLASH_ATTR http_chunked_decode( const char * chunked, char * d char * endstr; /* [chunk-size] */ i = strtoul( str + j, NULL, 16 ); - HTTPCLIENT_DEBUG( "Chunk Size:%d", i ); + HTTPCLIENT_DEBUG( "Chunk Size:%d\r\n", i ); if ( i <= 0 ) break; /* [chunk-size-end-ptr] */ @@ -138,7 +137,7 @@ static void ICACHE_FLASH_ATTR http_receive_callback( void * arg, char * buf, uns char * new_buffer; if ( new_size > BUFFER_SIZE_MAX || NULL == (new_buffer = (char *) os_malloc( new_size ) ) ) { - HTTPCLIENT_ERR( "Response too long (%d)", new_size ); + HTTPCLIENT_DEBUG( "Response too long (%d)\n", new_size ); req->buffer[0] = '\0'; /* Discard the buffer to avoid using an incomplete response. */ if ( req->secure ) espconn_secure_disconnect( conn ); @@ -164,12 +163,12 @@ static void ICACHE_FLASH_ATTR http_send_callback( void * arg ) if ( req->post_data == NULL ) { - HTTPCLIENT_DEBUG( "All sent" ); + HTTPCLIENT_DEBUG( "All sent\n" ); } else { /* The headers were sent, now send the contents. */ - HTTPCLIENT_DEBUG( "Sending request body" ); + HTTPCLIENT_DEBUG( "Sending request body\n" ); if ( req->secure ) espconn_secure_send( conn, (uint8_t *) req->post_data, strlen( req->post_data ) ); else @@ -182,7 +181,7 @@ static void ICACHE_FLASH_ATTR http_send_callback( void * arg ) static void ICACHE_FLASH_ATTR http_connect_callback( void * arg ) { - HTTPCLIENT_DEBUG( "Connected" ); + HTTPCLIENT_DEBUG( "Connected\n" ); struct espconn * conn = (struct espconn *) arg; request_args_t * req = (request_args_t *) conn->reverse; espconn_regist_recvcb( conn, http_receive_callback ); @@ -251,7 +250,7 @@ static void ICACHE_FLASH_ATTR http_connect_callback( void * arg ) } req->headers = NULL; - HTTPCLIENT_DEBUG( "Sending request header" ); + HTTPCLIENT_DEBUG( "Sending request header\n" ); } static void http_free_req( request_args_t * req) @@ -273,7 +272,7 @@ static void http_free_req( request_args_t * req) static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) { - HTTPCLIENT_DEBUG( "Disconnected" ); + HTTPCLIENT_DEBUG( "Disconnected\n" ); struct espconn *conn = (struct espconn *) arg; if ( conn == NULL ) @@ -296,7 +295,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) if ( req->buffer == NULL ) { - HTTPCLIENT_DEBUG( "Buffer probably shouldn't be NULL" ); + HTTPCLIENT_DEBUG( "Buffer probably shouldn't be NULL\n" ); } else if ( req->buffer[0] != '\0' ) { @@ -306,7 +305,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) if (( os_strncmp( req->buffer, version_1_0, strlen( version_1_0 ) ) != 0 ) && ( os_strncmp( req->buffer, version_1_1, strlen( version_1_1 ) ) != 0 )) { - HTTPCLIENT_ERR( "Invalid version in %s", req->buffer ); + HTTPCLIENT_DEBUG( "Invalid version in %s\n", req->buffer ); } else { @@ -327,7 +326,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) char *locationOffsetEnd = (char *) os_strstr(locationOffset, "\r\n"); if ( locationOffsetEnd == NULL ) { - HTTPCLIENT_ERR( "Found Location header but was incomplete" ); + HTTPCLIENT_DEBUG( "Found Location header but was incomplete\n" ); http_status = -1; } else { *locationOffsetEnd = '\0'; @@ -372,7 +371,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) return; } } else { - HTTPCLIENT_ERR("Too many redirections"); + HTTPCLIENT_DEBUG("Too many redirections\n"); http_status = -1; } } else { @@ -380,7 +379,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) if (NULL == body) { /* Find missing body */ - HTTPCLIENT_ERR("Body shouldn't be NULL"); + HTTPCLIENT_DEBUG("Body shouldn't be NULL\n"); /* To avoid NULL body */ body = ""; } else { @@ -412,9 +411,16 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg ) } +static void ICACHE_FLASH_ATTR http_error_callback( void *arg, sint8 errType ) +{ + HTTPCLIENT_DEBUG( "Disconnected with error\n" ); + http_disconnect_callback( arg ); +} + + static void ICACHE_FLASH_ATTR http_timeout_callback( void *arg ) { - HTTPCLIENT_ERR( "Connection timeout" ); + HTTPCLIENT_DEBUG( "Connection timeout\n" ); struct espconn * conn = (struct espconn *) arg; if ( conn == NULL ) { @@ -433,20 +439,13 @@ static void ICACHE_FLASH_ATTR http_timeout_callback( void *arg ) } -static void ICACHE_FLASH_ATTR http_error_callback( void *arg, sint8 errType ) -{ - HTTPCLIENT_ERR( "Disconnected with error: %d", errType ); - http_timeout_callback( arg ); -} - - static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_t * addr, void * arg ) { request_args_t * req = (request_args_t *) arg; if ( addr == NULL ) { - HTTPCLIENT_ERR( "DNS failed for %s", hostname ); + HTTPCLIENT_DEBUG( "DNS failed for %s\n", hostname ); if ( req->callback_handle != NULL ) { req->callback_handle( "", -1, "" ); @@ -455,7 +454,7 @@ static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_ } else { - HTTPCLIENT_DEBUG( "DNS found %s " IPSTR, hostname, IP2STR( addr ) ); + HTTPCLIENT_DEBUG( "DNS found %s " IPSTR "\n", hostname, IP2STR( addr ) ); struct espconn * conn = (struct espconn *) os_zalloc( sizeof(struct espconn) ); conn->type = ESPCONN_TCP; @@ -490,7 +489,7 @@ static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_ void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool secure, const char * method, const char * path, const char * headers, const char * post_data, http_callback_t callback_handle, int redirect_follow_count ) { - HTTPCLIENT_DEBUG( "DNS request" ); + HTTPCLIENT_DEBUG( "DNS request\n" ); request_args_t * req = (request_args_t *) os_zalloc( sizeof(request_args_t) ); req->hostname = esp_strdup( hostname ); @@ -513,7 +512,7 @@ void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool s if ( error == ESPCONN_INPROGRESS ) { - HTTPCLIENT_DEBUG( "DNS pending" ); + HTTPCLIENT_DEBUG( "DNS pending\n" ); } else if ( error == ESPCONN_OK ) { @@ -524,9 +523,9 @@ void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool s { if ( error == ESPCONN_ARG ) { - HTTPCLIENT_ERR( "DNS arg error %s", hostname ); + HTTPCLIENT_DEBUG( "DNS arg error %s\n", hostname ); }else { - HTTPCLIENT_ERR( "DNS error code %d", error ); + HTTPCLIENT_DEBUG( "DNS error code %d\n", error ); } http_dns_callback( hostname, NULL, req ); /* Handle all DNS errors the same way. */ } @@ -562,7 +561,7 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons } else { - HTTPCLIENT_ERR( "URL is not HTTP or HTTPS %s", url ); + HTTPCLIENT_DEBUG( "URL is not HTTP or HTTPS %s\n", url ); return; } @@ -579,7 +578,7 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons } if (path - url >= sizeof(hostname)) { - HTTPCLIENT_ERR( "hostname is too long %s", url ); + HTTPCLIENT_DEBUG( "hostname is too long %s\n", url ); return; } @@ -593,7 +592,7 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons port = atoi( colon + 1 ); if ( port == 0 ) { - HTTPCLIENT_ERR( "Port error %s", url ); + HTTPCLIENT_DEBUG( "Port error %s\n", url ); return; } @@ -607,10 +606,10 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons path = "/"; } - HTTPCLIENT_DEBUG( "hostname=%s", hostname ); - HTTPCLIENT_DEBUG( "port=%d", port ); - HTTPCLIENT_DEBUG( "method=%s", method ); - HTTPCLIENT_DEBUG( "path=%s", path ); + HTTPCLIENT_DEBUG( "hostname=%s\n", hostname ); + HTTPCLIENT_DEBUG( "port=%d\n", port ); + HTTPCLIENT_DEBUG( "method=%s\n", method ); + HTTPCLIENT_DEBUG( "path=%s\n", path ); http_raw_request( hostname, port, secure, method, path, headers, post_data, callback_handle, redirect_follow_count); } @@ -646,11 +645,10 @@ void ICACHE_FLASH_ATTR http_put( const char * url, const char * headers, const c void ICACHE_FLASH_ATTR http_callback_example( char * response, int http_status, char * full_response ) { - dbg_printf( "http_status=%d\n", http_status ); + os_printf( "http_status=%d\n", http_status ); if ( http_status != HTTP_STATUS_GENERIC_ERROR ) { - dbg_printf( "strlen(full_response)=%d\n", strlen( full_response ) ); - dbg_printf( "response=%s\n", response ); + os_printf( "strlen(full_response)=%d\n", strlen( full_response ) ); + os_printf( "response=%s\n", response ); } } - diff --git a/app/http/httpclient.h b/app/http/httpclient.h index 98c214d9..bb718f03 100644 --- a/app/http/httpclient.h +++ b/app/http/httpclient.h @@ -10,25 +10,18 @@ #ifndef __HTTPCLIENT_H__ #define __HTTPCLIENT_H__ -static const char log_prefix[] = "HTTP client: "; - -#if defined(DEVELOP_VERSION) - #define HTTPCLIENT_DEBUG_ON +#if defined(GLOBAL_DEBUG_ON) +#define HTTPCLIENT_DEBUG_ON #endif #if defined(HTTPCLIENT_DEBUG_ON) - #define HTTPCLIENT_DEBUG(format, ...) dbg_printf("%s"format"\n", log_prefix, ##__VA_ARGS__) +#define HTTPCLIENT_DEBUG(format, ...) os_printf(format, ##__VA_ARGS__) #else - #define HTTPCLIENT_DEBUG(...) -#endif -#if defined(NODE_ERROR) - #define HTTPCLIENT_ERR(format, ...) NODE_ERR("%s"format"\n", log_prefix, ##__VA_ARGS__) -#else - #define HTTPCLIENT_ERR(...) +#define HTTPCLIENT_DEBUG(format, ...) #endif #if defined(USES_SDK_BEFORE_V140) - #define espconn_send espconn_sent - #define espconn_secure_send espconn_secure_sent +#define espconn_send espconn_sent +#define espconn_secure_send espconn_secure_sent #endif /* diff --git a/app/include/driver/uart.h b/app/include/driver/uart.h index e4007e1c..f9eada98 100755 --- a/app/include/driver/uart.h +++ b/app/include/driver/uart.h @@ -101,7 +101,7 @@ typedef struct { int buff_uart_no; //indicate which uart use tx/rx buffer } UartDevice; -void uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input, uint8 *flag_input); +void uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input); void uart0_alt(uint8 on); void uart0_sendStr(const char *str); void uart0_putc(const char c); diff --git a/app/include/rom.h b/app/include/rom.h index c2ad7d3d..7de1c5e0 100644 --- a/app/include/rom.h +++ b/app/include/rom.h @@ -149,7 +149,5 @@ void uart_div_modify(int no, unsigned int freq); /* Returns 0 on success, 1 on failure */ uint8_t SPIRead(uint32_t src_addr, uint32_t *des_addr, uint32_t size); -uint8_t SPIWrite(uint32_t dst_addr, const uint32_t *src, uint32_t size); -uint8_t SPIEraseSector(uint32_t sector); #endif diff --git a/app/include/sections.h b/app/include/sections.h index 58613c90..d09c576e 100644 --- a/app/include/sections.h +++ b/app/include/sections.h @@ -2,6 +2,5 @@ #define _SECTIONS_H_ #define TEXT_SECTION_ATTR __attribute__((section(".text"))) -#define RAM_CONST_SECTION_ATTR __attribute((section(".data"))) #endif diff --git a/app/include/u8g_config.h b/app/include/u8g_config.h index 99a137e4..09866f1b 100644 --- a/app/include/u8g_config.h +++ b/app/include/u8g_config.h @@ -24,7 +24,6 @@ // // I2C based displays go into here: // U8G_DISPLAY_TABLE_ENTRY(sh1106_128x64_i2c) \ -// U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x32_i2c) \ // U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_i2c) \ // U8G_DISPLAY_TABLE_ENTRY(ssd1306_64x48_i2c) \ // U8G_DISPLAY_TABLE_ENTRY(ssd1309_128x64_i2c) \ @@ -41,7 +40,6 @@ // U8G_DISPLAY_TABLE_ENTRY(pcd8544_84x48_hw_spi) \ // U8G_DISPLAY_TABLE_ENTRY(pcf8812_96x65_hw_spi) \ // U8G_DISPLAY_TABLE_ENTRY(sh1106_128x64_hw_spi) \ -// U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x32_hw_spi) \ // U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_hw_spi) \ // U8G_DISPLAY_TABLE_ENTRY(ssd1306_64x48_hw_spi) \ // U8G_DISPLAY_TABLE_ENTRY(ssd1309_128x64_hw_spi) \ @@ -77,10 +75,6 @@ U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_hw_spi) \ #undef U8G_DISPLAY_TABLE_ENTRY - -// Special display device to provide run-length encoded framebuffer contents -// to a Lua callback: -//#define U8G_DISPLAY_FB_RLE // // *************************************************************************** diff --git a/app/include/user_config.h b/app/include/user_config.h index c786e909..2a375e08 100644 --- a/app/include/user_config.h +++ b/app/include/user_config.h @@ -36,13 +36,13 @@ extern void luaL_assertfail(const char *file, int line, const char *message); #define NODE_ERROR #ifdef NODE_DEBUG -#define NODE_DBG dbg_printf +#define NODE_DBG c_printf #else #define NODE_DBG #endif /* NODE_DEBUG */ #ifdef NODE_ERROR -#define NODE_ERR dbg_printf +#define NODE_ERR c_printf #else #define NODE_ERR #endif /* NODE_ERROR */ @@ -76,9 +76,6 @@ extern void luaL_assertfail(const char *file, int line, const char *message); // maximum length of a filename #define FS_OBJ_NAME_LEN 31 -// maximum number of open files for SPIFFS -#define SPIFFS_MAX_OPEN_FILES 4 - // Uncomment this next line for fastest startup // It reduces the format time dramatically // #define SPIFFS_MAX_FILESYSTEM_SIZE 32768 diff --git a/app/include/user_modules.h b/app/include/user_modules.h index f34285f7..40b6dbb6 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -53,7 +53,6 @@ //#define LUA_USE_MODULES_RTCTIME //#define LUA_USE_MODULES_SIGMA_DELTA //#define LUA_USE_MODULES_SNTP -//#define LUA_USE_MODULES_SOMFY #define LUA_USE_MODULES_SPI //#define LUA_USE_MODULES_STRUCT //#define LUA_USE_MODULES_SWITEC diff --git a/app/libc/c_stdio.h b/app/libc/c_stdio.h index 4c9a9bf3..41391bd7 100644 --- a/app/libc/c_stdio.h +++ b/app/libc/c_stdio.h @@ -63,8 +63,6 @@ extern void output_redirect(const char *str); int c_sprintf(char* s,const char *fmt, ...); #endif -extern void dbg_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); - #define c_vsprintf ets_vsprintf #define c_printf(...) do { \ unsigned char __print_buf[BUFSIZ]; \ diff --git a/app/libc/dbg_printf.c b/app/libc/dbg_printf.c deleted file mode 100644 index 92f3668a..00000000 --- a/app/libc/dbg_printf.c +++ /dev/null @@ -1,164 +0,0 @@ -/* $NetBSD: printf.c,v 1.12 1997/06/26 19:11:48 drochner Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)printf.c 8.1 (Berkeley) 6/11/93 - */ - -// This version uses almost no stack, and does not suffer from buffer -// overflows. The downside is that it does not implement a wide range -// of formatting characters. - -#include -#include -#include -#include "driver/uart.h" - -static void kprintn (void (*)(const char), uint32_t, int, int, char); -static void kdoprnt (void (*)(const char), const char *, va_list); - -void -dbg_printf(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - kdoprnt(uart0_putc, fmt, ap); - va_end(ap); -} - -void -dbg_vprintf(const char *fmt, va_list ap) -{ - kdoprnt(uart0_putc, fmt, ap); -} - -void -kdoprnt(void (*put)(const char), const char *fmt, va_list ap) -{ - register char *p; - register int ch, n; - unsigned long ul; - int lflag, set; - char zwidth; - char width; - - for (;;) { - while ((ch = *fmt++) != '%') { - if (ch == '\0') - return; - put(ch); - } - lflag = 0; - width = 0; - zwidth = ' '; -reswitch: switch (ch = *fmt++) { - case '\0': - /* XXX print the last format character? */ - return; - case 'l': - lflag = 1; - goto reswitch; - case 'c': - ch = va_arg(ap, int); - put(ch & 0x7f); - break; - case 's': - p = va_arg(ap, char *); - if (p == 0) { - p = ""; - } - while ((ch = *p++)) - put(ch); - break; - case 'd': - ul = lflag ? - va_arg(ap, long) : va_arg(ap, int); - if ((long)ul < 0) { - put('-'); - ul = -(long)ul; - } - kprintn(put, ul, 10, width, zwidth); - break; - case 'o': - ul = lflag ? - va_arg(ap, uint32_t) : va_arg(ap, uint32_t); - kprintn(put, ul, 8, width, zwidth); - break; - case 'u': - ul = lflag ? - va_arg(ap, uint32_t) : va_arg(ap, uint32_t); - kprintn(put, ul, 10, width, zwidth); - break; - case 'x': - ul = lflag ? - va_arg(ap, uint32_t) : va_arg(ap, uint32_t); - kprintn(put, ul, 16, width, zwidth); - break; - default: - if (ch >= '0' && ch <= '9') { - if (ch == '0' && width == 0 && zwidth == ' ') { - zwidth = '0'; - } else { - width = width * 10 + ch - '0'; - } - goto reswitch; - } - put('%'); - if (lflag) - put('l'); - put(ch); - } - } - va_end(ap); -} - -static void -kprintn(void (*put)(const char), unsigned long ul, int base, int width, char padchar) -{ - /* hold a long in base 8 */ - char *p, buf[(sizeof(long) * 8 / 3) + 2]; - - p = buf; - do { - *p++ = "0123456789abcdef"[ul % base]; - } while (ul /= base); - - while (p - buf < width--) { - put(padchar); - } - - do { - put(*--p); - } while (p > buf); -} diff --git a/app/lua/lauxlib.c b/app/lua/lauxlib.c index bc9010fa..2161c27e 100644 --- a/app/lua/lauxlib.c +++ b/app/lua/lauxlib.c @@ -807,7 +807,7 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { } LUALIB_API void luaL_assertfail(const char *file, int line, const char *message) { - dbg_printf("ASSERT@%s(%d): %s\n", file, line, message); + c_printf("ASSERT@%s(%d): %s\n", file, line, message); } static int panic (lua_State *L) { diff --git a/app/lua/liolib.c b/app/lua/liolib.c index 4ac31fe4..03e67beb 100644 --- a/app/lua/liolib.c +++ b/app/lua/liolib.c @@ -343,19 +343,28 @@ static int read_line (lua_State *L, int f) { static int read_line (lua_State *L, int f) { luaL_Buffer b; luaL_buffinit(L, &b); - signed char c; - do { + char *p = luaL_prepbuffer(&b); + signed char c = EOF; + int i = 0; + do{ c = (signed char)vfs_getc(f); - if (c==EOF) { + if(c==EOF){ break; } - if (c != '\n') { - luaL_addchar(&b, c); - } - } while (c != '\n'); + p[i++] = c; + }while((c!=EOF) && (c!='\n') && (i0 && p[i-1] == '\n') + i--; /* do not include `eol' */ + + if(i==0){ + luaL_pushresult(&b); /* close buffer */ + return (lua_objlen(L, -1) > 0); /* check whether read something */ + } + + luaL_addsize(&b, i); luaL_pushresult(&b); /* close buffer */ - return (lua_objlen(L, -1) > 0); /* check whether read something */ + return 1; /* read at least an `eol' */ } #endif diff --git a/app/lua/luaconf.h b/app/lua/luaconf.h index d039b18f..01c052a2 100644 --- a/app/lua/luaconf.h +++ b/app/lua/luaconf.h @@ -346,7 +346,7 @@ extern int readline4lua(const char *prompt, char *buffer, int length); ** (A format string with one argument is enough for Lua...) */ #if !defined(LUA_USE_STDIO) -#define luai_writestringerror(s,p) dbg_printf((s), (p)) +#define luai_writestringerror(s,p) c_printf((s), (p)) #endif // defined(LUA_USE_STDIO) @@ -556,7 +556,7 @@ extern int readline4lua(const char *prompt, char *buffer, int length); ** For example: If set to 4K a call to string.gsub will need more than ** 5k C stack space. */ -#define LUAL_BUFFERSIZE 256 +#define LUAL_BUFFERSIZE BUFSIZ /* }================================================================== */ diff --git a/app/modules/enduser_setup.c b/app/modules/enduser_setup.c index 19af1ac5..39a9975b 100644 --- a/app/modules/enduser_setup.c +++ b/app/modules/enduser_setup.c @@ -31,7 +31,6 @@ * @author Robert Foss * * Additions & fixes: Johny Mattsson - * Jason Follas */ #include "module.h" @@ -51,8 +50,6 @@ #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #define LITLEN(strliteral) (sizeof (strliteral) -1) -#define STRINGIFY(x) #x -#define NUMLEN(x) (sizeof(STRINGIFY(x)) - 1) #define ENDUSER_SETUP_ERR_FATAL (1 << 0) #define ENDUSER_SETUP_ERR_NONFATAL (1 << 1) @@ -62,7 +59,7 @@ #define ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND 2 #define ENDUSER_SETUP_ERR_UNKOWN_ERROR 3 #define ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN 4 -#define ENDUSER_SETUP_ERR_MAX_NUMBER 5 + /** * DNS Response Packet: @@ -81,19 +78,159 @@ static const char dns_body[] = { 0x00, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x04 }; static const char http_html_filename[] = "enduser_setup.html"; -static const char http_header_200[] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nConnection:close\r\nContent-Type:text/html\r\n"; /* Note single \r\n here! */ -static const char http_header_204[] = "HTTP/1.1 204 No Content\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; -static const char http_header_302[] = "HTTP/1.1 302 Moved\r\nLocation: /\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; -static const char http_header_400[] = "HTTP/1.1 400 Bad request\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; -static const char http_header_404[] = "HTTP/1.1 404 Not found\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; -static const char http_header_405[] = "HTTP/1.1 405 Method Not Allowed\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; -static const char http_header_500[] = "HTTP/1.1 500 Internal Error\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; +static const char http_header_200[] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nContent-Type: text/html\r\n"; /* Note single \r\n here! */ +static const char http_header_204[] = "HTTP/1.1 204 No Content\r\n\r\n"; +static const char http_header_302[] = "HTTP/1.1 302 Moved\r\nLocation: /\r\n\r\n"; +static const char http_header_401[] = "HTTP/1.1 401 Bad request\r\n\r\n"; +static const char http_header_404[] = "HTTP/1.1 404 Not found\r\n\r\n"; +static const char http_header_500[] = "HTTP/1.1 500 Internal Error\r\n\r\n"; -static const char http_header_content_len_fmt[] = "Content-length:%5d\r\n\r\n"; -static const char http_html_gzip_contentencoding[] = "Content-Encoding: gzip\r\n"; +/* The below is the un-minified version of the http_html_backup[] string. + * Minified using https://kangax.github.io/html-minifier/ + * Note: using method="get" due to iOS not always sending body in same + * packet as the HTTP header, and us thus missing it in that case + */ +#if 0 + + + + + + + WiFi Login + + + +
+
+
+ +

WiFi Login

+

Connect gadget to your WiFi network

+ + + + +

Status: Updating...

+
+

+
+
+ + + +#endif +static const char http_html_backup[] = +"WiFi Login

WiFi Login

Connect gadget to your WiFi network

Status: Updating...

"; -// Externally defined: static const char http_html_backup[] = ... -#include "eus/http_html_backup.def" typedef struct scan_listener { @@ -113,15 +250,9 @@ typedef struct int lua_err_cb_ref; int lua_dbg_cb_ref; scan_listener_t *scan_listeners; - uint8_t softAPchannel; - uint8_t success; - uint8_t callbackDone; - uint8_t lastStationStatus; - uint8_t connecting; } enduser_setup_state_t; static enduser_setup_state_t *state; - static bool manual = false; static task_handle_t do_station_cfg_handle; @@ -209,7 +340,7 @@ static void enduser_setup_check_station_start(void) ENDUSER_SETUP_DEBUG("enduser_setup_check_station_start"); os_timer_setfn(&(state->check_station_timer), enduser_setup_check_station, NULL); - os_timer_arm(&(state->check_station_timer), 3*1000, TRUE); + os_timer_arm(&(state->check_station_timer), 1*1000, TRUE); } @@ -246,60 +377,13 @@ static void enduser_setup_check_station(void *p) has_ip |= ((char *) &ip)[i]; } - uint8_t currChan = wifi_get_channel(); - if (has_ip == 0) { - // No IP Address yet, so check the reported status - uint8_t curr_status = wifi_station_get_connect_status(); - char buf[20]; - c_sprintf(buf, "status=%d,chan=%d", curr_status, currChan); - ENDUSER_SETUP_DEBUG(buf); - - if (curr_status == 2 || curr_status == 3 || curr_status == 4) - { - state->connecting = 0; - - // If the status is an error status and the channel changed, then cache the - // status to state since the Station won't be able to report the same status - // after switching the channel back to the SoftAP's - if (currChan != state->softAPchannel) { - state->lastStationStatus = curr_status; - - ENDUSER_SETUP_DEBUG("Turning off Station due to different channel than AP"); - - wifi_station_disconnect(); - wifi_set_opmode(SOFTAP_MODE); - enduser_setup_ap_start(); - } - } - return; + return; } - state->success = 1; - state->lastStationStatus = 5; // We have an IP Address, so the status is 5 (as of SDK 1.5.1) - state->connecting = 0; - -#if ENDUSER_SETUP_DEBUG_ENABLE - char debuginfo[100]; - c_sprintf(debuginfo, "AP_CHAN: %d, STA_CHAN: %d", state->softAPchannel, currChan); - ENDUSER_SETUP_DEBUG(debuginfo); -#endif - - if (currChan == state->softAPchannel) - { - enduser_setup_connected_callback(); - state->callbackDone = 1; - } - else - { - ENDUSER_SETUP_DEBUG("Turning off Station due to different channel than AP"); - wifi_station_disconnect(); - wifi_set_opmode(SOFTAP_MODE); - enduser_setup_ap_start(); - } - enduser_setup_check_station_stop(); + enduser_setup_connected_callback(); /* Trigger shutdown, but allow time for HTTP client to fetch last status. */ if (!manual) @@ -327,8 +411,6 @@ static void enduser_setup_check_station(void *p) /* Callback on timeout to hard-close a connection */ static err_t force_abort (void *arg, struct tcp_pcb *pcb) { - ENDUSER_SETUP_DEBUG("force_abort"); - (void)arg; tcp_poll (pcb, 0, 0); tcp_abort (pcb); @@ -338,8 +420,6 @@ static err_t force_abort (void *arg, struct tcp_pcb *pcb) /* Callback to detect a remote-close of a connection */ static err_t handle_remote_close (void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { - ENDUSER_SETUP_DEBUG("handle_remote_close"); - (void)arg; (void)err; if (p) /* server sent us data, just ACK and move on */ { @@ -358,8 +438,6 @@ static err_t handle_remote_close (void *arg, struct tcp_pcb *pcb, struct pbuf *p /* Set up a deferred close of a connection, as discussed above. */ static inline void deferred_close (struct tcp_pcb *pcb) { - ENDUSER_SETUP_DEBUG("deferred_close"); - tcp_poll (pcb, force_abort, 15); /* ~3sec from now */ tcp_recv (pcb, handle_remote_close); tcp_sent (pcb, 0); @@ -368,8 +446,6 @@ static inline void deferred_close (struct tcp_pcb *pcb) /* Convenience function to queue up a close-after-send. */ static err_t close_once_sent (void *arg, struct tcp_pcb *pcb, u16_t len) { - ENDUSER_SETUP_DEBUG("close_once_sent"); - (void)arg; (void)len; deferred_close (pcb); return ERR_OK; @@ -397,6 +473,7 @@ static int enduser_setup_srch_str(const char *str, const char *srch_str) } } + /** * Load HTTP Payload * @@ -418,26 +495,14 @@ static int enduser_setup_http_load_payload(void) err2 = vfs_lseek(f, 0, VFS_SEEK_SET); } - char cl_hdr[30]; - size_t ce_len = 0; - - c_sprintf(cl_hdr, http_header_content_len_fmt, file_len); - size_t cl_len = c_strlen(cl_hdr); + const char cl_hdr[] = "Content-length:%5d\r\n\r\n"; + const size_t cl_len = LITLEN(cl_hdr) + 3; /* room to expand %4d */ - if (!f || err == VFS_RES_ERR || err2 == VFS_RES_ERR) + if (!f || err != VFS_RES_OK || err2 != VFS_RES_OK) { ENDUSER_SETUP_DEBUG("enduser_setup_http_load_payload unable to load file enduser_setup.html, loading backup HTML."); - c_sprintf(cl_hdr, http_header_content_len_fmt, sizeof(http_html_backup)); - cl_len = c_strlen(cl_hdr); - - if (http_html_backup[0] == 0x1f && http_html_backup[1] == 0x8b) - { - ce_len = c_strlen(http_html_gzip_contentencoding); - ENDUSER_SETUP_DEBUG("Content is gzipped"); - } - - int payload_len = LITLEN(http_header_200) + cl_len + ce_len + LITLEN(http_html_backup); + int payload_len = LITLEN(http_header_200) + cl_len + LITLEN(http_html_backup); state->http_payload_len = payload_len; state->http_payload_data = (char *) c_malloc(payload_len); if (state->http_payload_data == NULL) @@ -448,25 +513,13 @@ static int enduser_setup_http_load_payload(void) int offset = 0; c_memcpy(&(state->http_payload_data[offset]), &(http_header_200), LITLEN(http_header_200)); offset += LITLEN(http_header_200); - if (ce_len > 0) - offset += c_sprintf(state->http_payload_data + offset, http_html_gzip_contentencoding, ce_len); - c_memcpy(&(state->http_payload_data[offset]), &(cl_hdr), cl_len); - offset += cl_len; - c_memcpy(&(state->http_payload_data[offset]), &(http_html_backup), sizeof(http_html_backup)); - - return 1; + offset += c_sprintf(state->http_payload_data + offset, cl_hdr, LITLEN(http_html_backup)); + c_memcpy(&(state->http_payload_data[offset]), &(http_html_backup), LITLEN(http_html_backup)); + + return 1; } - char magic[2]; - vfs_read(f, magic, 2); - - if (magic[0] == 0x1f && magic[1] == 0x8b) - { - ce_len = c_strlen(http_html_gzip_contentencoding); - ENDUSER_SETUP_DEBUG("Content is gzipped"); - } - - int payload_len = LITLEN(http_header_200) + cl_len + ce_len + file_len; + int payload_len = LITLEN(http_header_200) + cl_len + file_len; state->http_payload_len = payload_len; state->http_payload_data = (char *) c_malloc(payload_len); if (state->http_payload_data == NULL) @@ -474,19 +527,13 @@ static int enduser_setup_http_load_payload(void) return 2; } - vfs_lseek(f, 0, VFS_SEEK_SET); - int offset = 0; - c_memcpy(&(state->http_payload_data[offset]), &(http_header_200), LITLEN(http_header_200)); offset += LITLEN(http_header_200); - if (ce_len > 0) - offset += c_sprintf(state->http_payload_data + offset, http_html_gzip_contentencoding, ce_len); - c_memcpy(&(state->http_payload_data[offset]), &(cl_hdr), cl_len); - offset += cl_len; + offset += c_sprintf(state->http_payload_data + offset, cl_hdr, file_len); vfs_read(f, &(state->http_payload_data[offset]), file_len); - vfs_close(f); - + vfs_close(f); + return 0; } @@ -558,9 +605,6 @@ static int enduser_setup_http_urldecode(char *dst, const char *src, int src_len, */ static void do_station_cfg (task_param_t param, uint8_t prio) { - ENDUSER_SETUP_DEBUG("do_station_cfg"); - - state->connecting = 1; struct station_config *cnf = (struct station_config *)param; (void)prio; @@ -591,9 +635,6 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data { ENDUSER_SETUP_DEBUG("enduser_setup_http_handle_credentials"); - state->success = 0; - state->lastStationStatus = 0; - char *name_str = (char *) ((uint32_t)strstr(&(data[6]), "wifi_ssid=")); char *pwd_str = (char *) ((uint32_t)strstr(&(data[6]), "wifi_password=")); if (name_str == NULL || pwd_str == NULL) @@ -622,7 +663,7 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data int err; err = enduser_setup_http_urldecode(cnf->ssid, name_str_start, name_str_len, sizeof(cnf->ssid)); err |= enduser_setup_http_urldecode(cnf->password, pwd_str_start, pwd_str_len, sizeof(cnf->password)); - if (err != 0 || c_strlen(cnf->ssid) == 0) + if (err != 0) { ENDUSER_SETUP_DEBUG("Unable to decode HTTP parameter to valid password or SSID"); return 1; @@ -653,7 +694,7 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data static int enduser_setup_http_serve_header(struct tcp_pcb *http_client, const char *header, uint32_t header_len) { ENDUSER_SETUP_DEBUG("enduser_setup_http_serve_header"); - + err_t err = tcp_write (http_client, header, header_len, TCP_WRITE_FLAG_COPY); if (err != ERR_OK) { @@ -667,9 +708,8 @@ static int enduser_setup_http_serve_header(struct tcp_pcb *http_client, const ch static err_t streamout_sent (void *arg, struct tcp_pcb *pcb, u16_t len) { - ENDUSER_SETUP_DEBUG("streamout_sent"); - (void)len; + unsigned offs = (unsigned)arg; if (!state || !state->http_payload_data) @@ -714,7 +754,6 @@ static err_t streamout_sent (void *arg, struct tcp_pcb *pcb, u16_t len) static int enduser_setup_http_serve_html(struct tcp_pcb *http_client) { ENDUSER_SETUP_DEBUG("enduser_setup_http_serve_html"); - if (state->http_payload_data == NULL) { enduser_setup_http_load_payload(); @@ -735,7 +774,7 @@ static int enduser_setup_http_serve_html(struct tcp_pcb *http_client) } -static void enduser_setup_serve_status(struct tcp_pcb *conn) +static void serve_status(struct tcp_pcb *conn) { ENDUSER_SETUP_DEBUG("enduser_setup_serve_status"); @@ -743,12 +782,11 @@ static void enduser_setup_serve_status(struct tcp_pcb *conn) "HTTP/1.1 200 OK\r\n" "Cache-control:no-cache\r\n" "Connection:close\r\n" - "Access-Control-Allow-Origin: *\r\n" "Content-type:text/plain\r\n" "Content-length: %d\r\n" "\r\n" - "%s"; - const char *states[] = + "%s%s"; + const char *state[] = { "Idle.", "Connecting to \"%s\".", @@ -758,8 +796,8 @@ static void enduser_setup_serve_status(struct tcp_pcb *conn) "Connected to \"%s\" (%s)." }; - const size_t num_states = sizeof(states)/sizeof(states[0]); - uint8_t curr_state = state->lastStationStatus > 0 ? state->lastStationStatus : wifi_station_get_connect_status (); + const size_t num_states = sizeof(state)/sizeof(state[0]); + uint8_t curr_state = wifi_station_get_connect_status (); if (curr_state < num_states) { switch (curr_state) @@ -769,21 +807,22 @@ static void enduser_setup_serve_status(struct tcp_pcb *conn) case STATION_NO_AP_FOUND: case STATION_GOT_IP: { - const char *s = states[curr_state]; + const char *s = state[curr_state]; struct station_config config; wifi_station_get_config(&config); config.ssid[31] = '\0'; - struct ip_info ip_info; + struct ip_info ip_info; - wifi_get_ip_info(STATION_IF , &ip_info); + wifi_get_ip_info(STATION_IF , &ip_info); + + char ip_addr[16]; + ip_addr[0] = '\0'; + if (curr_state == STATION_GOT_IP) + { + c_sprintf (ip_addr, "%d.%d.%d.%d", IP2STR(&ip_info.ip.addr)); + } - char ip_addr[16]; - ip_addr[0] = '\0'; - if (curr_state == STATION_GOT_IP) - { - c_sprintf (ip_addr, "%d.%d.%d.%d", IP2STR(&ip_info.ip.addr)); - } int state_len = c_strlen(s); int ip_len = c_strlen(ip_addr); @@ -805,7 +844,7 @@ static void enduser_setup_serve_status(struct tcp_pcb *conn) /* Handle non-formatted strings */ default: { - const char *s = states[curr_state]; + const char *s = state[curr_state]; int status_len = c_strlen(s); int buf_len = sizeof(fmt) + status_len + 10; //10 = (9+1), 1 byte is '\0' and 9 are reserved for length field char buf[buf_len]; @@ -823,76 +862,12 @@ static void enduser_setup_serve_status(struct tcp_pcb *conn) } } -static void enduser_setup_serve_status_as_json (struct tcp_pcb *http_client) -{ - ENDUSER_SETUP_DEBUG("enduser_setup_serve_status_as_json"); - - // If the station is currently shut down because of wi-fi channel issue, use the cached status - uint8_t curr_status = state->lastStationStatus > 0 ? state->lastStationStatus : wifi_station_get_connect_status (); - - char json_payload[64]; - c_sprintf(json_payload, "{\"deviceid\":\"%06X\", \"status\":%d}", system_get_chip_id(), curr_status); - - const char fmt[] = - "HTTP/1.1 200 OK\r\n" - "Cache-Control: no-cache\r\n" - "Connection: close\r\n" - "Access-Control-Allow-Origin: *\r\n" - "Content-Type: application/json\r\n" - "Content-Length: %d\r\n" - "\r\n" - "%s"; - - int len = c_strlen(json_payload); - char buf[c_strlen(fmt) + NUMLEN(len) + len - 4]; - len = c_sprintf (buf, fmt, len, json_payload); - enduser_setup_http_serve_header (http_client, buf, len); -} - - -static void enduser_setup_handle_OPTIONS (struct tcp_pcb *http_client, char *data, unsigned short data_len) -{ - ENDUSER_SETUP_DEBUG("enduser_setup_handle_OPTIONS"); - - const char json[] = - "HTTP/1.1 200 OK\r\n" - "Cache-Control: no-cache\r\n" - "Connection: close\r\n" - "Content-Type: application/json\r\n" - "Content-Length: 0\r\n" - "Access-Control-Allow-Origin: *\r\n" - "Access-Control-Allow-Methods: GET\r\n" - "Access-Control-Allow-Age: 300\r\n" - "\r\n"; - - const char others[] = - "HTTP/1.1 200 OK\r\n" - "Cache-Control: no-cache\r\n" - "Connection: close\r\n" - "Content-Length: 0\r\n" - "\r\n"; - - int type = 0; - - if (c_strncmp(data, "GET ", 4) == 0) - { - if (c_strncmp(data + 4, "/aplist", 7) == 0 || c_strncmp(data + 4, "/setwifi?", 9) == 0 || c_strncmp(data + 4, "/status.json", 12) == 0) - { - enduser_setup_http_serve_header (http_client, json, c_strlen(json)); - return; - } - } - enduser_setup_http_serve_header (http_client, others, c_strlen(others)); - return; -} /* --- WiFi AP scanning support -------------------------------------------- */ static void free_scan_listeners (void) { - ENDUSER_SETUP_DEBUG("free_scan_listeners"); - if (!state || !state->scan_listeners) { return; @@ -911,8 +886,6 @@ static void free_scan_listeners (void) static void remove_scan_listener (scan_listener_t *l) { - ENDUSER_SETUP_DEBUG("remove_scan_listener"); - if (state) { scan_listener_t **sl = &state->scan_listeners; @@ -948,8 +921,6 @@ static char *escape_ssid (char *dst, const char *src) static void notify_scan_listeners (const char *payload, size_t sz) { - ENDUSER_SETUP_DEBUG("notify_scan_listeners"); - if (!state) { return; @@ -973,8 +944,6 @@ static void notify_scan_listeners (const char *payload, size_t sz) static void on_scan_done (void *arg, STATUS status) { - ENDUSER_SETUP_DEBUG("on_scan_done"); - if (!state || !state->scan_listeners) { return; @@ -992,14 +961,13 @@ static void on_scan_done (void *arg, STATUS status) "HTTP/1.1 200 OK\r\n" "Connection:close\r\n" "Cache-control:no-cache\r\n" - "Access-Control-Allow-Origin: *\r\n" "Content-type:application/json\r\n" "Content-length:%4d\r\n" "\r\n"; const size_t hdr_sz = sizeof (header_fmt) +1 -1; /* +expand %4d, -\0 */ /* To be able to safely escape a pathological SSID, we need 2*32 bytes */ - const size_t max_entry_sz = 27 + 2*32 + 6; // {"ssid":"","rssi":,"chan":} + const size_t max_entry_sz = sizeof("{\"ssid\":\"\",\"rssi\":},") + 2*32 + 6; const size_t alloc_sz = hdr_sz + num_nets * max_entry_sz + 3; char *http = os_zalloc (alloc_sz); if (!http) @@ -1028,12 +996,6 @@ static void on_scan_done (void *arg, STATUS status) p += sizeof (entry_mid) -1; p += c_sprintf (p, "%d", wn->rssi); - - const char entry_chan[] = ",\"chan\":"; - strcpy (p, entry_chan); - p += sizeof (entry_chan) -1; - - p += c_sprintf (p, "%d", wn->channel); *p++ = '}'; } @@ -1044,8 +1006,6 @@ static void on_scan_done (void *arg, STATUS status) http[hdr_sz] = '['; /* Rewrite the \0 with the correct start of body */ notify_scan_listeners (http, hdr_sz + body_sz); - ENDUSER_SETUP_DEBUG(http + hdr_sz); - c_free (http); return; } @@ -1088,9 +1048,6 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s pbuf_free (p); err_t ret = ERR_OK; - - ENDUSER_SETUP_DEBUG(data); - if (c_strncmp(data, "GET ", 4) == 0) { if (c_strncmp(data + 4, "/ ", 2) == 0) @@ -1104,52 +1061,38 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s goto free_out; /* streaming now in progress */ } } - else if (c_strncmp(data + 4, "/aplist", 7) == 0) + else if (c_strncmp(data + 4, "/aplist ", 8) == 0) { - // Don't do an AP Scan while station is trying to connect to Wi-Fi - if (state->connecting == 0) + scan_listener_t *l = os_malloc (sizeof (scan_listener_t)); + if (!l) { - scan_listener_t *l = os_malloc (sizeof (scan_listener_t)); - if (!l) - { - ENDUSER_SETUP_ERROR("out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); - } - - bool already = (state->scan_listeners != NULL); - - tcp_arg (http_client, l); - /* TODO: check if also need a tcp_err() cb, or if recv() is enough */ - l->conn = http_client; - l->next = state->scan_listeners; - state->scan_listeners = l; - - if (!already) - { - if (!wifi_station_scan(NULL, on_scan_done)) - { - enduser_setup_http_serve_header(http_client, http_header_500, LITLEN(http_header_500)); - deferred_close (l->conn); - l->conn = 0; - free_scan_listeners(); - } - } - goto free_out; /* request queued */ + ENDUSER_SETUP_ERROR("out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); } - else + + bool already = (state->scan_listeners != NULL); + + tcp_arg (http_client, l); + /* TODO: check if also need a tcp_err() cb, or if recv() is enough */ + l->conn = http_client; + l->next = state->scan_listeners; + state->scan_listeners = l; + + if (!already) { - // Return No Content status to the caller - enduser_setup_http_serve_header(http_client, http_header_204, LITLEN(http_header_204)); + if (!wifi_station_scan(NULL, on_scan_done)) + { + enduser_setup_http_serve_header(http_client, http_header_500, LITLEN(http_header_500)); + deferred_close (l->conn); + l->conn = 0; + free_scan_listeners(); + } } + goto free_out; /* request queued */ } - else if (c_strncmp(data + 4, "/status.json", 12) == 0) + else if (c_strncmp(data + 4, "/status ", 8) == 0) { - enduser_setup_serve_status_as_json(http_client); - } - else if (c_strncmp(data + 4, "/status", 7) == 0) - { - enduser_setup_serve_status(http_client); + serve_status(http_client); } - else if (c_strncmp(data + 4, "/update?", 8) == 0) { switch (enduser_setup_http_handle_credentials(data, data_len)) @@ -1158,29 +1101,14 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s enduser_setup_http_serve_header(http_client, http_header_302, LITLEN(http_header_302)); break; case 1: - enduser_setup_http_serve_header(http_client, http_header_400, LITLEN(http_header_400)); + enduser_setup_http_serve_header(http_client, http_header_401, LITLEN(http_header_401)); break; default: ENDUSER_SETUP_ERROR("http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); break; } } - else if (c_strncmp(data + 4, "/setwifi?", 9) == 0) - { - switch (enduser_setup_http_handle_credentials(data, data_len)) - { - case 0: - enduser_setup_serve_status_as_json(http_client); - break; - case 1: - enduser_setup_http_serve_header(http_client, http_header_400, LITLEN(http_header_400)); - break; - default: - ENDUSER_SETUP_ERROR("http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); - break; - } - } - else if (c_strncmp(data + 4, "/generate_204", 13) == 0) + else if (c_strncmp(data + 4, "/generate_204 ", 14) == 0) { /* Convince Android devices that they have internet access to avoid pesky dialogues. */ enduser_setup_http_serve_header(http_client, http_header_204, LITLEN(http_header_204)); @@ -1188,16 +1116,13 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s else { ENDUSER_SETUP_DEBUG("serving 404"); + ENDUSER_SETUP_DEBUG(data + 4); enduser_setup_http_serve_header(http_client, http_header_404, LITLEN(http_header_404)); } } - else if (c_strncmp(data, "OPTIONS ", 8) == 0) + else /* not GET */ { - enduser_setup_handle_OPTIONS(http_client, data, data_len); - } - else /* not GET or OPTIONS */ - { - enduser_setup_http_serve_header(http_client, http_header_405, LITLEN(http_header_405)); + enduser_setup_http_serve_header(http_client, http_header_401, LITLEN(http_header_401)); } deferred_close (http_client); @@ -1210,7 +1135,6 @@ free_out: static err_t enduser_setup_http_connectcb(void *arg, struct tcp_pcb *pcb, err_t err) { ENDUSER_SETUP_DEBUG("enduser_setup_http_connectcb"); - if (!state) { ENDUSER_SETUP_DEBUG("connect callback but no state?!"); @@ -1284,7 +1208,7 @@ static void enduser_setup_http_stop(void) static void enduser_setup_ap_stop(void) { - ENDUSER_SETUP_DEBUG("enduser_setup_ap_stop"); + ENDUSER_SETUP_DEBUG("enduser_setup_station_stop"); wifi_set_opmode(~SOFTAP_MODE & wifi_get_opmode()); } @@ -1310,47 +1234,15 @@ static void enduser_setup_ap_start(void) cnf.ssid[ssid_name_len] = '_'; c_sprintf(cnf.ssid + ssid_name_len + 1, "%02X%02X%02X", mac[3], mac[4], mac[5]); cnf.ssid_len = ssid_name_len + 7; - cnf.channel = state == NULL? 1 : state->softAPchannel; + cnf.channel = 1; cnf.authmode = AUTH_OPEN; cnf.ssid_hidden = 0; cnf.max_connection = 5; cnf.beacon_interval = 100; wifi_set_opmode(STATIONAP_MODE); wifi_softap_set_config(&cnf); - -#if ENDUSER_SETUP_DEBUG_ENABLE - char debuginfo[100]; - c_sprintf(debuginfo, "SSID: %s, CHAN: %d", cnf.ssid, cnf.channel); - ENDUSER_SETUP_DEBUG(debuginfo); -#endif } -static void on_initial_scan_done (void *arg, STATUS status) -{ - ENDUSER_SETUP_DEBUG("on_initial_scan_done"); - - if (!state) - { - return; - } - - int8_t rssi = -100; - - if (status == OK) - { - for (struct bss_info *wn = arg; wn; wn = wn->next.stqe_next) - { - if (wn->rssi > rssi) - { - state->softAPchannel = wn->channel; - rssi = wn->rssi; - } - } - } - - enduser_setup_ap_start(); - enduser_setup_check_station_start(); -} static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned short recv_len) { @@ -1363,31 +1255,6 @@ static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned uint32_t dns_reply_static_len = (uint32_t) sizeof(dns_header) + (uint32_t) sizeof(dns_body) + 2 + 4; // dns_id=2bytes, ip=4bytes uint32_t dns_reply_len = dns_reply_static_len + qname_len; -#if ENDUSER_SETUP_DEBUG_ENABLE - char *qname = c_malloc(qname_len + 12); - if (qname != NULL) - { - c_sprintf(qname, "DNS QUERY = %s", &(recv_data[12])); - - uint32_t p; - int i, j; - - for(i=12;i<(int)strlen(qname);i++) - { - p=qname[i]; - for(j=0;j<(int)p;j++) - { - qname[i]=qname[i+1]; - i=i+1; - } - qname[i]='.'; - } - qname[i-1]='\0'; - ENDUSER_SETUP_DEBUG(qname); - c_free(qname); - } -#endif - uint8_t if_mode = wifi_get_opmode(); if ((if_mode & SOFTAP_MODE) == 0) { @@ -1438,14 +1305,6 @@ static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned { ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Can't execute transmission.", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_FATAL); } - else if (err == ESPCONN_MAXNUM) - { - ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Buffer full. Discarding...", ENDUSER_SETUP_ERR_MAX_NUMBER, ENDUSER_SETUP_ERR_NONFATAL); - } - else if (err == ESPCONN_IF) - { - ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Send UDP data failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); - } else if (err != 0) { ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. espconn_send failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); @@ -1549,7 +1408,6 @@ static void enduser_setup_dns_stop(void) static int enduser_setup_init(lua_State *L) { - // Note: Normal to not see this debug message on first invocation because debug callback is set below ENDUSER_SETUP_DEBUG("enduser_setup_init"); if (state != NULL) @@ -1594,19 +1452,12 @@ static int enduser_setup_init(lua_State *L) { lua_pushvalue (L, 3); state->lua_dbg_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); - ENDUSER_SETUP_DEBUG("enduser_setup_init: Debug callback has been defined"); } else { state->lua_dbg_cb_ref = LUA_NOREF; } - state->softAPchannel = 1; - state->success = 0; - state->callbackDone = 0; - state->lastStationStatus = 0; - state->connecting = 0; - return 0; } @@ -1624,7 +1475,6 @@ static int enduser_setup_manual(lua_State *L) static int enduser_setup_start(lua_State *L) { - // Note: The debug callback is set in enduser_setup_init. It's normal to not see this debug message on first invocation. ENDUSER_SETUP_DEBUG("enduser_setup_start"); if (!do_station_cfg_handle) @@ -1637,14 +1487,10 @@ static int enduser_setup_start(lua_State *L) goto failed; } + enduser_setup_check_station_start(); if (!manual) { - ENDUSER_SETUP_DEBUG("Performing AP Scan to identify likely AP's channel"); - wifi_station_scan(NULL, on_initial_scan_done); - } - else - { - enduser_setup_check_station_start(); + enduser_setup_ap_start(); } if(enduser_setup_dns_start()) @@ -1683,12 +1529,6 @@ static int enduser_setup_stop(lua_State* L) { enduser_setup_ap_stop(); } - if (state->success && !state->callbackDone) - { - wifi_set_opmode(STATION_MODE | wifi_get_opmode()); - wifi_station_connect(); - enduser_setup_connected_callback(); - } enduser_setup_dns_stop(); enduser_setup_http_stop(); enduser_setup_free(); diff --git a/app/modules/eus/enduser_setup.html b/app/modules/eus/enduser_setup.html deleted file mode 100644 index b1e0de09..00000000 --- a/app/modules/eus/enduser_setup.html +++ /dev/null @@ -1,315 +0,0 @@ - - - - - - WiFi Login - - - - -
-
-
-
-

Connect device to your Wi-Fi

- - - - - -
-
-

Success!

-
-

Your device has successfully connected to the Wi-Fi network.

-
-
-
-

Trying...

- -
-
-

Updating Status...

-
- - - - \ No newline at end of file diff --git a/app/modules/eus/enduser_setup.html.gz b/app/modules/eus/enduser_setup.html.gz deleted file mode 100644 index 3abbaa13a7d8a321c7fa1825d827d2576d28d278..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2393 zcmV-f38wZRiwFP!000021C?3*a@#f%|GPJH@30>CLb;)2eb{Nn64lA&k|sAv+Ns^? zbuyU_M1m$_3g7{tWnJobKP&-ShCqk4T2}!+6&zyJIS3cW6ufB)OU zed7})nR)tVv8I{;Cu^ES*Nko|A$0_);F@xsL>rdrBFX4AOX;v##K1VyjFdx_l9DEq z(Ku=ZX{JlM{EfY4@J?JZzBsl!c&7BWq@cGIO*FmH$Ei}d|IK&s1;4&o6KOckU_5V@ z70EKjFWdDk{s;fi{1_irBHKFFg0jm(pTT52{)gqxjq$^rtXa7|14XzRDk@oS4^jP) zl+Wi$tuP&vid3FYeCnqP_$%(J~(8a{5t%PEl2_uE9lqhRWoqSQB7rsL{n?zwgH9Jjn6ky;DiHpZ)3+rZRD32z=1Eos`CJKWIK z6*Hb!6(y4Jl%4?>oVKRPjLM-T8LL$g^_<Q)hFVIqx zKbFB^nx^+94BLcFoM@LO2en>jQc((N?43@hJ6sy(XGY^%aG@%aQr~Y3o^P|C#cm0H zgoaM9Hd!_Vl5*Xr#G%G1vHUL1LBa#D%&OMkS}J6$1lwJ!jH#+5TN9m9q*p;4I&Wu2 zQz41HTn?k2`G}3vPH4;~tCQ33!(4SZdzOo|R`I=!LR?b`UphfOhN)4CA&Ko8-QMZ* zYSiprvJf29`s#z?*|TSzShFzp$a(0xINW`oz zT03rrE@a=I+K71`%&Kl!w;ot4f&$(YgYJ9m{0NP9sqwZRpPW$sVJ1aoxOm*G@~Qit z+3(fW)G2VFb?EDs5wrnGOQP8|eV|gH{U!|?SH8U(f9fh#cjfW*ld5E@rwN7H!zQUk z6||zDP4(1CuhDu1DY&koA5x0V9q9i(!=7ZO7ayb}+TuxNlO^ zh-~7%d~$d*Jzb@96NHNFh-|Y#G6yr93*EEPWRD%S^VUAw&T9CSV%cuaYp|XQDGgg9 z^4zXT#WX2RSxNAIK~<8J7NX3kOrjOn)-q7%Kh{%_mky$ou*W&1C!^;UaFW~Ta5gUh;RTYhyrE# z^13XykXiuI46&mN3N~5}B_qU8n4B)l?#PPi@;b=4gleDdzlqBubvk5qtq@OpzQik=AR<_2|DfmD_Viu%n#A?c5hhRz~KxgXplmfYDJ5 zq;i|~K14@*OiGLDMOmUXq*OEpNz9)zRCjbcJ5?bzgFJ?5`*7x^Ra$Z}Ji7K7+mSxkLag8#si2%f@w zqKlCvJQM4INpAGxtBWWGPB-T|hH0NMX)VK;*`!8?`NT-dorvIY`nAIu9`-1=Mh;XA zSztjk*0s@!hi zXxsLlFWJZ*uq+5pd(qQ|tRf7pdyoo4{juBb=xtWgD1PA3jS03WKS=eOu##plgpCv? z@NHA$UN+-Fy|wXA7C)OD{&hb#wd-?Sx6`V5;y51_Vt*;x?mgc24lZ*r1ysH%^Nz)7A$&-c|4%}SU)4-6{>tU^cQAzowE?^1c z2P*>_<99aX?5R0{VzK&6QyukbL57~T>9LHIkb1BK8i`@$>j)>l)krGEUt2rDEO#;4Yx>T+uNIq_wOJ<6&-wJg)=ur;7Sgg0bnzNnlW5bm-V) zjDAmeO-gvhwcJLJ%8;`@>@`7LD3NJl${+^k4n^kfI(^;159hJq`m|RQTCOFgfE!I= zVR-aAqkYZ)+UufiEcgohKnL0|!?1Bv!M*Vc5=BLz|xgkRFW@nC=M^MCAc zo1U86K&c%>tx{*6-3lmIS5uFy)3k6@_SEB|`^|PdIt? http_html_backup.def - diff --git a/app/modules/file.c b/app/modules/file.c index 3e9de55d..049f2a83 100644 --- a/app/modules/file.c +++ b/app/modules/file.c @@ -2,24 +2,15 @@ #include "module.h" #include "lauxlib.h" -#include "lmem.h" #include "platform.h" #include "c_types.h" #include "vfs.h" #include "c_string.h" -#include - -#define FILE_READ_CHUNK 1024 - static int file_fd = 0; -static int file_fd_ref = LUA_NOREF; static int rtc_cb_ref = LUA_NOREF; -typedef struct _file_fd_ud { - int fd; -} file_fd_ud; static void table2tm( lua_State *L, vfs_time *tm ) { @@ -100,48 +91,13 @@ static int file_on(lua_State *L) // Lua: close() static int file_close( lua_State* L ) { - int need_pop = FALSE; - file_fd_ud *ud; - - if (lua_type( L, 1 ) != LUA_TUSERDATA) { - // fall back to last opened file - if (file_fd_ref != LUA_NOREF) { - lua_rawgeti( L, LUA_REGISTRYINDEX, file_fd_ref ); - // top of stack is now default file descriptor - ud = (file_fd_ud *)luaL_checkudata(L, -1, "file.obj"); - lua_pop( L, 1 ); - } else { - // no default file currently opened - return 0; - } - } else { - ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj"); - } - - // unref default file descriptor - luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref ); - file_fd_ref = LUA_NOREF; - - if(ud->fd){ - vfs_close(ud->fd); - // mark as closed - ud->fd = 0; + if(file_fd){ + vfs_close(file_fd); + file_fd = 0; } return 0; } -static int file_obj_free( lua_State *L ) -{ - file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj"); - if (ud->fd) { - // close file if it's still open - vfs_close(ud->fd); - ud->fd = 0; - } - - return 0; -} - // Lua: format() static int file_format( lua_State* L ) { @@ -174,10 +130,10 @@ static int file_fscfg (lua_State *L) static int file_open( lua_State* L ) { size_t len; - - // unref last file descriptor to allow gc'ing if not kept by user script - luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref ); - file_fd_ref = LUA_NOREF; + if(file_fd){ + vfs_close(file_fd); + file_fd = 0; + } const char *fname = luaL_checklstring( L, 1, &len ); const char *basename = vfs_basename( fname ); @@ -190,14 +146,7 @@ static int file_open( lua_State* L ) if(!file_fd){ lua_pushnil(L); } else { - file_fd_ud *ud = (file_fd_ud *) lua_newuserdata( L, sizeof( file_fd_ud ) ); - ud->fd = file_fd; - luaL_getmetatable( L, "file.obj" ); - lua_setmetatable( L, -2 ); - - // store reference to opened file - lua_pushvalue( L, -1 ); - file_fd_ref = luaL_ref( L, LUA_REGISTRYINDEX ); + lua_pushboolean(L, 1); } return 1; } @@ -221,36 +170,19 @@ static int file_list( lua_State* L ) return 0; } -static int get_file_obj( lua_State *L, int *argpos ) +static int file_seek (lua_State *L) { - if (lua_type( L, 1 ) == LUA_TUSERDATA) { - file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj"); - *argpos = 2; - return ud->fd; - } else { - *argpos = 1; - return file_fd; - } -} - -#define GET_FILE_OBJ int argpos; \ - int fd = get_file_obj( L, &argpos ); - -static int file_seek (lua_State *L) -{ - GET_FILE_OBJ; - static const int mode[] = {VFS_SEEK_SET, VFS_SEEK_CUR, VFS_SEEK_END}; static const char *const modenames[] = {"set", "cur", "end", NULL}; - if(!fd) + if(!file_fd) return luaL_error(L, "open a file first"); - int op = luaL_checkoption(L, argpos, "cur", modenames); - long offset = luaL_optlong(L, ++argpos, 0); - op = vfs_lseek(fd, offset, mode[op]); + int op = luaL_checkoption(L, 1, "cur", modenames); + long offset = luaL_optlong(L, 2, 0); + op = vfs_lseek(file_fd, offset, mode[op]); if (op < 0) lua_pushnil(L); /* error */ else - lua_pushinteger(L, vfs_tell(fd)); + lua_pushinteger(L, vfs_tell(file_fd)); return 1; } @@ -278,6 +210,7 @@ static int file_remove( lua_State* L ) const char *fname = luaL_checklstring( L, 1, &len ); const char *basename = vfs_basename( fname ); luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid"); + file_close(L); vfs_remove((char *)fname); return 0; } @@ -285,11 +218,9 @@ static int file_remove( lua_State* L ) // Lua: flush() static int file_flush( lua_State* L ) { - GET_FILE_OBJ; - - if(!fd) + if(!file_fd) return luaL_error(L, "open a file first"); - if(vfs_flush(fd) == 0) + if(vfs_flush(file_fd) == 0) lua_pushboolean(L, 1); else lua_pushnil(L); @@ -300,6 +231,10 @@ static int file_flush( lua_State* L ) static int file_rename( lua_State* L ) { size_t len; + if(file_fd){ + vfs_close(file_fd); + file_fd = 0; + } const char *oldname = luaL_checklstring( L, 1, &len ); const char *basename = vfs_basename( oldname ); @@ -318,64 +253,38 @@ static int file_rename( lua_State* L ) } // g_read() -static int file_g_read( lua_State* L, int n, int16_t end_char, int fd ) +static int file_g_read( lua_State* L, int n, int16_t end_char ) { - static char *heap_mem = NULL; - // free leftover memory - if (heap_mem) { - luaM_free(L, heap_mem); - heap_mem = NULL; - } - - if(n <= 0) - n = FILE_READ_CHUNK; - + if(n <= 0 || n > LUAL_BUFFERSIZE) + n = LUAL_BUFFERSIZE; if(end_char < 0 || end_char >255) end_char = EOF; - - - if(!fd) + + luaL_Buffer b; + if(!file_fd) return luaL_error(L, "open a file first"); - char *p; + luaL_buffinit(L, &b); + char *p = luaL_prepbuffer(&b); int i; - if (n > LUAL_BUFFERSIZE) { - // get buffer from heap - p = heap_mem = luaM_malloc(L, n); - } else { - // small chunks go onto the stack - p = alloca(n); - } - - n = vfs_read(fd, p, n); - // bypass search if no end character provided - if (n > 0 && end_char != EOF) { - for (i = 0; i < n; ++i) - if (p[i] == end_char) - { - ++i; - break; - } - } else { - i = n; - } - - if (i == 0 || n == VFS_RES_ERR) { - if (heap_mem) { - luaM_free(L, heap_mem); - heap_mem = NULL; + n = vfs_read(file_fd, p, n); + for (i = 0; i < n; ++i) + if (p[i] == end_char) + { + ++i; + break; } - return 0; + + if(i==0){ + luaL_pushresult(&b); /* close buffer */ + return (lua_objlen(L, -1) > 0); /* check whether read something */ } - vfs_lseek(fd, -(n - i), VFS_SEEK_CUR); - lua_pushlstring(L, p, i); - if (heap_mem) { - luaM_free(L, heap_mem); - heap_mem = NULL; - } - return 1; + vfs_lseek(file_fd, -(n - i), VFS_SEEK_CUR); + luaL_addsize(&b, i); + luaL_pushresult(&b); /* close buffer */ + return 1; /* read at least an `eol' */ } // Lua: read() @@ -384,46 +293,42 @@ static int file_g_read( lua_State* L, int n, int16_t end_char, int fd ) // file.read('q') will read until 'q' or EOF is reached. static int file_read( lua_State* L ) { - unsigned need_len = FILE_READ_CHUNK; + unsigned need_len = LUAL_BUFFERSIZE; int16_t end_char = EOF; size_t el; - - GET_FILE_OBJ; - - if( lua_type( L, argpos ) == LUA_TNUMBER ) + if( lua_type( L, 1 ) == LUA_TNUMBER ) { - need_len = ( unsigned )luaL_checkinteger( L, argpos ); + need_len = ( unsigned )luaL_checkinteger( L, 1 ); + if( need_len > LUAL_BUFFERSIZE ){ + need_len = LUAL_BUFFERSIZE; + } } - else if(lua_isstring(L, argpos)) + else if(lua_isstring(L, 1)) { - const char *end = luaL_checklstring( L, argpos, &el ); + const char *end = luaL_checklstring( L, 1, &el ); if(el!=1){ return luaL_error( L, "wrong arg range" ); } end_char = (int16_t)end[0]; } - return file_g_read(L, need_len, end_char, fd); + return file_g_read(L, need_len, end_char); } // Lua: readline() static int file_readline( lua_State* L ) { - GET_FILE_OBJ; - - return file_g_read(L, LUAL_BUFFERSIZE, '\n', fd); + return file_g_read(L, LUAL_BUFFERSIZE, '\n'); } // Lua: write("string") static int file_write( lua_State* L ) { - GET_FILE_OBJ; - - if(!fd) + if(!file_fd) return luaL_error(L, "open a file first"); size_t l, rl; - const char *s = luaL_checklstring(L, argpos, &l); - rl = vfs_write(fd, s, l); + const char *s = luaL_checklstring(L, 1, &l); + rl = vfs_write(file_fd, s, l); if(rl==l) lua_pushboolean(L, 1); else @@ -434,15 +339,13 @@ static int file_write( lua_State* L ) // Lua: writeline("string") static int file_writeline( lua_State* L ) { - GET_FILE_OBJ; - - if(!fd) + if(!file_fd) return luaL_error(L, "open a file first"); size_t l, rl; - const char *s = luaL_checklstring(L, argpos, &l); - rl = vfs_write(fd, s, l); + const char *s = luaL_checklstring(L, 1, &l); + rl = vfs_write(file_fd, s, l); if(rl==l){ - rl = vfs_write(fd, "\n", 1); + rl = vfs_write(file_fd, "\n", 1); if(rl==1) lua_pushboolean(L, 1); else @@ -517,20 +420,6 @@ static int file_vol_umount( lua_State *L ) } -static const LUA_REG_TYPE file_obj_map[] = -{ - { LSTRKEY( "close" ), LFUNCVAL( file_close ) }, - { LSTRKEY( "read" ), LFUNCVAL( file_read ) }, - { LSTRKEY( "readline" ), LFUNCVAL( file_readline ) }, - { LSTRKEY( "write" ), LFUNCVAL( file_write ) }, - { LSTRKEY( "writeline" ), LFUNCVAL( file_writeline ) }, - { LSTRKEY( "seek" ), LFUNCVAL( file_seek ) }, - { LSTRKEY( "flush" ), LFUNCVAL( file_flush ) }, - { LSTRKEY( "__gc" ), LFUNCVAL( file_obj_free ) }, - { LSTRKEY( "__index" ), LROVAL( file_obj_map ) }, - { LNILKEY, LNILVAL } -}; - static const LUA_REG_TYPE file_vol_map[] = { { LSTRKEY( "umount" ), LFUNCVAL( file_vol_umount )}, @@ -570,7 +459,6 @@ static const LUA_REG_TYPE file_map[] = { int luaopen_file( lua_State *L ) { luaL_rometatable( L, "file.vol", (void *)file_vol_map ); - luaL_rometatable( L, "file.obj", (void *)file_obj_map ); return 0; } diff --git a/app/modules/gpio.c b/app/modules/gpio.c index de2fbd47..75634429 100644 --- a/app/modules/gpio.c +++ b/app/modules/gpio.c @@ -190,20 +190,19 @@ static void seroutasync_done (task_param_t arg) { lua_State *L = lua_getstate(); luaM_freearray(L, serout.delay_table, serout.tablelen, uint32); - serout.delay_table = NULL; - if (serout.lua_done_ref != LUA_NOREF) { + if (serout.lua_done_ref != LUA_REFNIL) { // we're here so serout.lua_done_ref != LUA_NOREF lua_rawgeti (L, LUA_REGISTRYINDEX, serout.lua_done_ref); - luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref); - serout.lua_done_ref = LUA_NOREF; if (lua_pcall(L, 0, 0, 0)) { // Uncaught Error. Print instead of sudden reset luaL_error(L, "error: %s", lua_tostring(L, -1)); } + luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref); } } static void ICACHE_RAM_ATTR seroutasync_cb(os_param_t p) { (void) p; + NODE_DBG("%d\t%d\t%d\t%d\t%d\t%d\t%d\n", serout.repeats, serout.index, serout.level, serout.pin, serout.tablelen, serout.delay_table[serout.index], system_get_time()); // timing is delayed for short timings when debug output is enabled if (serout.index < serout.tablelen) { GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[serout.pin]), serout.level); serout.level = serout.level==LOW ? HIGH : LOW; @@ -221,25 +220,17 @@ static int lgpio_serout( lua_State* L ) serout.pin = luaL_checkinteger( L, 1 ); serout.level = luaL_checkinteger( L, 2 ); serout.repeats = luaL_optint( L, 4, 1 )-1; - luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref); - uint8_t is_async = FALSE; if (!lua_isnoneornil(L, 5)) { if (lua_isnumber(L, 5)) { - serout.lua_done_ref = LUA_NOREF; + serout.lua_done_ref = LUA_REFNIL; } else { lua_pushvalue(L, 5); serout.lua_done_ref = luaL_ref(L, LUA_REGISTRYINDEX); } - is_async = TRUE; } else { serout.lua_done_ref = LUA_NOREF; } - if (serout.delay_table) { - luaM_freearray(L, serout.delay_table, serout.tablelen, uint32); - serout.delay_table = NULL; - } - luaL_argcheck(L, platform_gpio_exists(serout.pin), 1, "Invalid pin"); luaL_argcheck(L, serout.level==HIGH || serout.level==LOW, 2, "Wrong level type" ); luaL_argcheck(L, lua_istable( L, 3 ) && @@ -253,7 +244,7 @@ static int lgpio_serout( lua_State* L ) lua_pop( L, 1 ); } - if (is_async) { // async version for duration above 15 mSec + if (serout.lua_done_ref != LUA_NOREF) { // async version for duration above 15 mSec if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, TRUE)) { // Failed to init the timer luaL_error(L, "Unable to initialize timer"); @@ -305,7 +296,6 @@ int luaopen_gpio( lua_State *L ) { platform_gpio_init(task_get_id(gpio_intr_callback_task)); #endif serout.done_taskid = task_get_id((task_callback_t) seroutasync_done); - serout.lua_done_ref = LUA_NOREF; return 0; } diff --git a/app/modules/http.c b/app/modules/http.c index dc4c9b4e..2b7ee5e6 100644 --- a/app/modules/http.c +++ b/app/modules/http.c @@ -14,13 +14,13 @@ static int http_callback_registry = LUA_NOREF; static void http_callback( char * response, int http_status, char * full_response ) { #if defined(HTTPCLIENT_DEBUG_ON) - dbg_printf( "http_status=%d\n", http_status ); + c_printf( "http_status=%d\n", http_status ); if ( http_status != HTTP_STATUS_GENERIC_ERROR ) { if (full_response != NULL) { - dbg_printf( "strlen(full_response)=%d\n", strlen( full_response ) ); + c_printf( "strlen(full_response)=%d\n", strlen( full_response ) ); } - dbg_printf( "response=%s\n", response ); + c_printf( "response=%s\n", response ); } #endif if (http_callback_registry != LUA_NOREF) diff --git a/app/modules/mqtt.c b/app/modules/mqtt.c index c9813af2..ef604ac7 100644 --- a/app/modules/mqtt.c +++ b/app/modules/mqtt.c @@ -72,7 +72,6 @@ typedef struct lmqtt_userdata uint8_t secure; #endif bool connected; // indicate socket connected, not mqtt prot connected. - bool keepalive_sent; ETSTimer mqttTimer; tConnState connState; }lmqtt_userdata; @@ -213,9 +212,9 @@ static void mqtt_connack_fail(lmqtt_userdata * mud, int reason_code) { return; } - - lua_State *L = lua_getstate(); - + + lua_State *L = lua_getstate(); + lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_connect_fail_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata(client) to callback func in lua lua_pushinteger(L, reason_code); @@ -242,12 +241,12 @@ static sint8 mqtt_send_if_possible(struct espconn *pesp_conn) #ifdef CLIENT_SSL_ENABLE if( mud->secure ) { - espconn_status = espconn_secure_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length ); + espconn_status = espconn_secure_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length ); } else #endif { - espconn_status = espconn_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length ); + espconn_status = espconn_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length ); } mud->keep_alive_tick = 0; } @@ -276,7 +275,7 @@ static void mqtt_socket_received(void *arg, char *pdata, unsigned short len) READPACKET: if(length > MQTT_BUF_SIZE || length <= 0) - return; + return; // c_memcpy(in_buffer, pdata, length); uint8_t temp_buffer[MQTT_BUF_SIZE]; @@ -288,7 +287,7 @@ READPACKET: case MQTT_CONNECT_SENDING: case MQTT_CONNECT_SENT: mud->event_timeout = 0; - + if(mqtt_get_type(in_buffer) != MQTT_MSG_TYPE_CONNACK){ NODE_DBG("MQTT: Invalid packet\r\n"); mud->connState = MQTT_INIT; @@ -302,16 +301,16 @@ READPACKET: { espconn_disconnect(pesp_conn); } - + mqtt_connack_fail(mud, MQTT_CONN_FAIL_NOT_A_CONNACK_MSG); - + break; - + } else if (mqtt_get_connect_ret_code(in_buffer) != MQTT_CONNACK_ACCEPTED) { NODE_DBG("MQTT: CONNACK REFUSED (CODE: %d)\n", mqtt_get_connect_ret_code(in_buffer)); - + mud->connState = MQTT_INIT; - + #ifdef CLIENT_SSL_ENABLE if(mud->secure) { @@ -321,12 +320,12 @@ READPACKET: #endif { espconn_disconnect(pesp_conn); - } - + } + mqtt_connack_fail(mud, mqtt_get_connect_ret_code(in_buffer)); - + break; - + } else { mud->connState = MQTT_DATA; NODE_DBG("MQTT: Connected\r\n"); @@ -455,7 +454,6 @@ READPACKET: break; case MQTT_MSG_TYPE_PINGRESP: // Ignore - mud->keepalive_sent = 0; NODE_DBG("MQTT: PINGRESP received\r\n"); break; } @@ -587,7 +585,7 @@ void mqtt_socket_timer(void *arg) NODE_DBG("timer, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q))); if(mud->event_timeout > 0){ NODE_DBG("event_timeout: %d.\n", mud->event_timeout); - mud->event_timeout --; + mud->event_timeout --; if(mud->event_timeout > 0){ return; } else { @@ -607,7 +605,7 @@ void mqtt_socket_timer(void *arg) NODE_DBG("sSend MQTT_CONNECT failed.\n"); mud->connState = MQTT_INIT; mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_SENDING); - + #ifdef CLIENT_SSL_ENABLE if(mud->secure) { @@ -622,7 +620,7 @@ void mqtt_socket_timer(void *arg) } else if(mud->connState == MQTT_CONNECT_SENT) { // wait for CONACK time out. NODE_DBG("MQTT_CONNECT timeout.\n"); mud->connState = MQTT_INIT; - + #ifdef CLIENT_SSL_ENABLE if(mud->secure) { @@ -632,7 +630,7 @@ void mqtt_socket_timer(void *arg) #endif { espconn_disconnect(mud->pesp_conn); - } + } mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_RECEIVING); } else if(mud->connState == MQTT_DATA){ msg_queue_t *pending_msg = msg_peek(&(mud->mqtt_state.pending_msg_q)); @@ -642,20 +640,13 @@ void mqtt_socket_timer(void *arg) // no queued event. mud->keep_alive_tick ++; if(mud->keep_alive_tick > mud->mqtt_state.connect_info->keepalive){ - if (mud->keepalive_sent) { - // Oh dear -- keepalive timer expired and still no ack of previous message - mqtt_socket_reconnected(mud->pesp_conn, 0); - } else { - uint8_t temp_buffer[MQTT_BUF_SIZE]; - mqtt_msg_init(&mud->mqtt_state.mqtt_connection, temp_buffer, MQTT_BUF_SIZE); - NODE_DBG("\r\nMQTT: Send keepalive packet\r\n"); - mqtt_message_t* temp_msg = mqtt_msg_pingreq(&mud->mqtt_state.mqtt_connection); - msg_queue_t *node = msg_enqueue( &(mud->mqtt_state.pending_msg_q), temp_msg, - 0, MQTT_MSG_TYPE_PINGREQ, (int)mqtt_get_qos(temp_msg->data) ); - mud->keepalive_sent = 1; - mud->keep_alive_tick = 0; // Need to reset to zero in case flow control stopped. - mqtt_send_if_possible(mud->pesp_conn); - } + uint8_t temp_buffer[MQTT_BUF_SIZE]; + mqtt_msg_init(&mud->mqtt_state.mqtt_connection, temp_buffer, MQTT_BUF_SIZE); + NODE_DBG("\r\nMQTT: Send keepalive packet\r\n"); + mqtt_message_t* temp_msg = mqtt_msg_pingreq(&mud->mqtt_state.mqtt_connection); + msg_queue_t *node = msg_enqueue( &(mud->mqtt_state.pending_msg_q), temp_msg, + 0, MQTT_MSG_TYPE_PINGREQ, (int)mqtt_get_qos(temp_msg->data) ); + mqtt_send_if_possible(mud->pesp_conn); } } } @@ -684,7 +675,6 @@ static int mqtt_socket_client( lua_State* L ) // create a object mud = (lmqtt_userdata *)lua_newuserdata(L, sizeof(lmqtt_userdata)); - c_memset(mud, 0, sizeof(*mud)); // pre-initialize it, in case of errors mud->self_ref = LUA_NOREF; mud->cb_connect_ref = LUA_NOREF; @@ -695,8 +685,18 @@ static int mqtt_socket_client( lua_State* L ) mud->cb_suback_ref = LUA_NOREF; mud->cb_unsuback_ref = LUA_NOREF; mud->cb_puback_ref = LUA_NOREF; + mud->pesp_conn = NULL; +#ifdef CLIENT_SSL_ENABLE + mud->secure = 0; +#endif + mud->keep_alive_tick = 0; + mud->event_timeout = 0; mud->connState = MQTT_INIT; + mud->connected = false; + c_memset(&mud->mqttTimer, 0, sizeof(ETSTimer)); + c_memset(&mud->mqtt_state, 0, sizeof(mqtt_state_t)); + c_memset(&mud->connect_info, 0, sizeof(mqtt_connect_info_t)); // set its metatable luaL_getmetatable(L, "mqtt.socket"); @@ -761,7 +761,7 @@ static int mqtt_socket_client( lua_State* L ) c_free(mud->connect_info.password); mud->connect_info.password = NULL; } - return luaL_error(L, "not enough memory"); + return luaL_error(L, "not enough memory"); } c_memcpy(mud->connect_info.client_id, clientId, idl); @@ -819,8 +819,8 @@ static int mqtt_delete( lua_State* L ) // ---- alloc-ed in mqtt_socket_lwt() if(mud->connect_info.will_topic){ - c_free(mud->connect_info.will_topic); - mud->connect_info.will_topic = NULL; + c_free(mud->connect_info.will_topic); + mud->connect_info.will_topic = NULL; } if(mud->connect_info.will_message){ @@ -926,7 +926,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) if( dns_reconn_count >= 5 ){ NODE_ERR( "DNS Fail!\n" ); // Note: should delete the pesp_conn or unref self_ref here. - + struct espconn *pesp_conn = arg; if(pesp_conn != NULL) { lmqtt_userdata *mud = (lmqtt_userdata *)pesp_conn->reverse; @@ -934,7 +934,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) mqtt_connack_fail(mud, MQTT_CONN_FAIL_DNS); } } - + mqtt_socket_disconnected(arg); // although not connected, but fire disconnect callback to release every thing. return -1; } @@ -1070,7 +1070,7 @@ static int mqtt_socket_connect( lua_State* L ) } stack++; - + // call back function when a connection fails if ((stack<=top) && (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION)){ lua_pushvalue(L, stack); // copy argument (func) to the top of stack @@ -1135,7 +1135,7 @@ static int mqtt_socket_close( lua_State* L ) // Send disconnect message mqtt_message_t* temp_msg = mqtt_msg_disconnect(&mud->mqtt_state.mqtt_connection); NODE_DBG("Send MQTT disconnect infomation, data len: %d, d[0]=%d \r\n", temp_msg->length, temp_msg->data[0]); - + #ifdef CLIENT_SSL_ENABLE if(mud->secure) { espconn_status = espconn_secure_send(mud->pesp_conn, temp_msg->data, temp_msg->length); @@ -1150,7 +1150,7 @@ static int mqtt_socket_close( lua_State* L ) } } mud->connected = 0; - + while (mud->mqtt_state.pending_msg_q) { msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); } @@ -1595,7 +1595,7 @@ static const LUA_REG_TYPE mqtt_socket_map[] = { { LNILKEY, LNILVAL } }; - + static const LUA_REG_TYPE mqtt_map[] = { { LSTRKEY( "Client" ), LFUNCVAL( mqtt_socket_client ) }, @@ -1609,7 +1609,7 @@ static const LUA_REG_TYPE mqtt_map[] = { { LSTRKEY( "CONNACK_REFUSED_ID_REJECTED" ), LNUMVAL( MQTT_CONNACK_REFUSED_ID_REJECTED ) }, { LSTRKEY( "CONNACK_REFUSED_SERVER_UNAVAILABLE" ), LNUMVAL( MQTT_CONNACK_REFUSED_SERVER_UNAVAILABLE ) }, { LSTRKEY( "CONNACK_REFUSED_BAD_USER_OR_PASS" ), LNUMVAL( MQTT_CONNACK_REFUSED_BAD_USER_OR_PASS ) }, - { LSTRKEY( "CONNACK_REFUSED_NOT_AUTHORIZED" ), LNUMVAL( MQTT_CONNACK_REFUSED_NOT_AUTHORIZED ) }, + { LSTRKEY( "CONNACK_REFUSED_NOT_AUTHORIZED" ), LNUMVAL( MQTT_CONNACK_REFUSED_NOT_AUTHORIZED ) }, { LSTRKEY( "__metatable" ), LROVAL( mqtt_map ) }, { LNILKEY, LNILVAL } diff --git a/app/modules/ow.c b/app/modules/ow.c index 9fec9bc9..6ce773bf 100644 --- a/app/modules/ow.c +++ b/app/modules/ow.c @@ -134,8 +134,6 @@ static int ow_read_bytes( lua_State *L ) if( size == 0 ) return 0; - luaL_argcheck(L, size <= LUAL_BUFFERSIZE, 2, "Attempt to read too many characters"); - luaL_Buffer b; luaL_buffinit( L, &b ); char *p = luaL_prepbuffer(&b); diff --git a/app/modules/sntp.c b/app/modules/sntp.c index af65d4ab..14695395 100644 --- a/app/modules/sntp.c +++ b/app/modules/sntp.c @@ -53,7 +53,7 @@ #define MAX_ATTEMPTS 5 #if 0 -# define sntp_dbg(...) dbg_printf(__VA_ARGS__) +# define sntp_dbg(...) c_printf(__VA_ARGS__) #else # define sntp_dbg(...) #endif diff --git a/app/modules/somfy.c b/app/modules/somfy.c deleted file mode 100644 index 4266aa6e..00000000 --- a/app/modules/somfy.c +++ /dev/null @@ -1,248 +0,0 @@ -// *************************************************************************** -// Somfy module for ESP8266 with NodeMCU -// -// Written by Lukas Voborsky, @voborsky -// based on https://github.com/Nickduino/Somfy_Remote -// Somfy protocol description: https://pushstack.wordpress.com/somfy-rts-protocol/ -// and discussion: https://forum.arduino.cc/index.php?topic=208346.0 -// -// MIT license, http://opensource.org/licenses/MIT -// *************************************************************************** - -//#define NODE_DEBUG - -#include "os_type.h" -#include "osapi.h" -#include "sections.h" - -#include "module.h" -#include "lauxlib.h" -#include "lmem.h" -#include "platform.h" -#include "hw_timer.h" -#include "user_interface.h" - -#define SYMBOL 640 // symbol width in microseconds -#define SOMFY_UP 0x2 -#define SOMFY_STOP 0x1 -#define SOMFY_DOWN 0x4 -#define SOMFY_PROG 0x8 - -#define DIRECT_WRITE_LOW(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 0)) -#define DIRECT_WRITE_HIGH(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 1)) - -static const os_param_t TIMER_OWNER = 0x736f6d66; // "somf" -static task_handle_t done_taskid; - -static uint8_t pin; -static uint8_t frame[7]; -static uint8_t sync; -static uint8_t repeat; - -//static uint32_t delay[10] = {9415, 89565, 4*SYMBOL, 4*SYMBOL, 4*SYMBOL, 4550, SYMBOL, SYMBOL, SYMBOL, 30415}; // in us -// the `delay` array of constants must be in RAM as it is accessed from the timer interrupt -static const RAM_CONST_SECTION_ATTR uint32_t delay[10] = {US_TO_RTC_TIMER_TICKS(9415), US_TO_RTC_TIMER_TICKS(89565), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4550), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(30415)}; // in ticks (no need to recalculate) - -static uint8_t repeatindex; -static uint8_t signalindex; -static uint8_t subindex; -static uint8_t bitcondition; - -int lua_done_ref; // callback when transmission is done - -void buildFrame(uint8_t *frame, uint64_t remote, uint8_t button, uint16_t code) { - // NODE_DBG("remote: %x\n", remote); - // NODE_DBG("button: %x\n", button); - // NODE_DBG("rolling code: %x\n", code); - frame[0] = 0xA7; // Encryption key. Doesn't matter much - frame[1] = button << 4; // Which button did you press? The 4 LSB will be the checksum - frame[2] = code >> 8; // Rolling code (big endian) - frame[3] = code; // Rolling code - frame[4] = remote >> 16; // Remote address - frame[5] = remote >> 8; // Remote address - frame[6] = remote; // Remote address - // frame[7] = 0x80; - // frame[8] = 0x0; - // frame[9] = 0x0; - - // NODE_DBG("Frame:\t\t\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]); - // Checksum calculation: a XOR of all the nibbles - uint8_t checksum = 0; - for(uint8_t i = 0; i < 7; i++) { - checksum = checksum ^ frame[i] ^ (frame[i] >> 4); - } - checksum &= 0b1111; // We keep the last 4 bits only - - //Checksum integration - frame[1] |= checksum; // If a XOR of all the nibbles is equal to 0, the blinds will consider the checksum ok. - // NODE_DBG("With checksum:\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]); - - // Obfuscation: a XOR of all the uint8_ts - for(uint8_t i = 1; i < 7; i++) { - frame[i] ^= frame[i-1]; - } - // NODE_DBG("Obfuscated:\t\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]); -} - -static void somfy_transmissionDone (task_param_t arg) -{ - lua_State *L = lua_getstate(); - lua_rawgeti (L, LUA_REGISTRYINDEX, lua_done_ref); - luaL_unref (L, LUA_REGISTRYINDEX, lua_done_ref); - lua_done_ref = LUA_NOREF; - lua_call (L, 0, 0); -} - -static void ICACHE_RAM_ATTR sendCommand(os_param_t p) { - (void) p; - // NODE_DBG("%d\t%d\n", signalindex, subindex); - switch (signalindex) { - case 0: - subindex = 0; - if(sync == 2) { // Only with the first frame. - //Wake-up pulse & Silence - DIRECT_WRITE_HIGH(pin); - signalindex++; - // delayMicroseconds(9415); - break; - } else { - signalindex++; signalindex++; //no break means: go directly to step 3 - } - case 1: - //Wake-up pulse & Silence - DIRECT_WRITE_LOW(pin); - signalindex++; - // delayMicroseconds(89565); - break; - case 2: - signalindex++; - // no break means go directly to step 3 - // a "useless" step to allow repeating the hardware sync w/o the silence after wake-up pulse - case 3: - // Hardware sync: two sync for the first frame, seven for the following ones. - DIRECT_WRITE_HIGH(pin); - signalindex++; - // delayMicroseconds(4*SYMBOL); - break; - case 4: - DIRECT_WRITE_LOW(pin); - subindex++; - if (subindex < sync) {signalindex--;} else {signalindex++;} - // delayMicroseconds(4*SYMBOL); - break; - case 5: - // Software sync - DIRECT_WRITE_HIGH(pin); - signalindex++; - // delayMicroseconds(4550); - break; - case 6: - DIRECT_WRITE_LOW(pin); - signalindex++; - subindex=0; - // delayMicroseconds(SYMBOL); - break; - case 7: - //Data: bits are sent one by one, starting with the MSB. - bitcondition = ((frame[subindex/8] >> (7 - (subindex%8))) & 1) == 1; - if(bitcondition) { - DIRECT_WRITE_LOW(pin); - } - else { - DIRECT_WRITE_HIGH(pin); - } - signalindex++; - // delayMicroseconds(SYMBOL); - break; - case 8: - //Data: bits are sent one by one, starting with the MSB. - if(bitcondition) { - DIRECT_WRITE_HIGH(pin); - } - else { - DIRECT_WRITE_LOW(pin); - } - - if (subindex<56) { - subindex++; - signalindex--; - } - else { - signalindex++; - } - // delayMicroseconds(SYMBOL); - break; - case 9: - DIRECT_WRITE_LOW(pin); - signalindex++; - // delayMicroseconds(30415); // Inter-frame silence - break; - case 10: - repeatindex++; - if (repeatindexu8g)) + // helper function: retrieve and check userdata argument static lu8g_userdata_t *get_lud( lua_State *L ) @@ -769,6 +780,214 @@ static int lu8g_setFontLineSpacingFactor( lua_State *L ) } +// ------------------------------------------------------------ +// comm functions +// +#define I2C_CMD_MODE 0x000 +#define I2C_DATA_MODE 0x040 + +#define ESP_I2C_ID 0 + + +static uint8_t do_i2c_start(uint8_t id, uint8_t sla) +{ + platform_i2c_send_start( id ); + + // ignore return value -> tolerate missing ACK + platform_i2c_send_address( id, sla, PLATFORM_I2C_DIRECTION_TRANSMITTER ); + + return 1; +} + +static uint8_t u8g_com_esp8266_ssd_start_sequence(u8g_t *u8g) +{ + /* are we requested to set the a0 state? */ + if ( u8g->pin_list[U8G_PI_SET_A0] == 0 ) + return 1; + + /* setup bus, might be a repeated start */ + if ( do_i2c_start( ESP_I2C_ID, u8g->i2c_addr ) == 0 ) + return 0; + if ( u8g->pin_list[U8G_PI_A0_STATE] == 0 ) + { + // ignore return value -> tolerate missing ACK + if ( platform_i2c_send_byte( ESP_I2C_ID, I2C_CMD_MODE ) == 0 ) + ; //return 0; + } + else + { + platform_i2c_send_byte( ESP_I2C_ID, I2C_DATA_MODE ); + } + + u8g->pin_list[U8G_PI_SET_A0] = 0; + return 1; +} + + +static void lu8g_digital_write( u8g_t *u8g, uint8_t pin_index, uint8_t value ) +{ + uint8_t pin; + + pin = u8g->pin_list[pin_index]; + if ( pin != U8G_PIN_NONE ) + platform_gpio_write( pin, value ); +} + +void u8g_Delay(u8g_t *u8g, uint16_t msec) +{ + const uint16_t chunk = 50; + + if (u8g->use_delay == 0) + return; + + while (msec > chunk) + { + os_delay_us( chunk*1000 ); + msec -= chunk; + } + if (msec > 0) + os_delay_us( msec*1000 ); +} +void u8g_MicroDelay(void) +{ + os_delay_us( 1 ); +} +void u8g_10MicroDelay(void) +{ + os_delay_us( 10 ); +} + + +uint8_t u8g_com_esp8266_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) +{ + switch(msg) + { + case U8G_COM_MSG_STOP: + break; + + case U8G_COM_MSG_INIT: + // we assume that the SPI interface was already initialized + // just care for the /CS and D/C pins + lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_HIGH ); + platform_gpio_mode( u8g->pin_list[U8G_PI_CS], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT ); + platform_gpio_mode( u8g->pin_list[U8G_PI_A0], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT ); + break; + + case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */ + lu8g_digital_write( u8g, U8G_PI_A0, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH ); + break; + + case U8G_COM_MSG_CHIP_SELECT: + if (arg_val == 0) + { + /* disable */ + lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_HIGH ); + } + else + { + /* enable */ + //u8g_com_arduino_digital_write(u8g, U8G_PI_SCK, LOW); + lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_LOW ); + } + break; + + case U8G_COM_MSG_RESET: + if ( u8g->pin_list[U8G_PI_RESET] != U8G_PIN_NONE ) + lu8g_digital_write( u8g, U8G_PI_RESET, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH ); + break; + + case U8G_COM_MSG_WRITE_BYTE: + platform_spi_send( 1, 8, arg_val ); + break; + + case U8G_COM_MSG_WRITE_SEQ: + case U8G_COM_MSG_WRITE_SEQ_P: + { + register uint8_t *ptr = arg_ptr; + while( arg_val > 0 ) + { + platform_spi_send( 1, 8, *ptr++ ); + arg_val--; + } + } + break; + } + return 1; +} + + +uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) +{ + switch(msg) + { + case U8G_COM_MSG_INIT: + // we assume that the i2c bus was already initialized + //u8g_i2c_init(u8g->pin_list[U8G_PI_I2C_OPTION]); + + break; + + case U8G_COM_MSG_STOP: + break; + + case U8G_COM_MSG_RESET: + /* Currently disabled, but it could be enable. Previous restrictions have been removed */ + /* u8g_com_arduino_digital_write(u8g, U8G_PI_RESET, arg_val); */ + break; + + case U8G_COM_MSG_CHIP_SELECT: + u8g->pin_list[U8G_PI_A0_STATE] = 0; + u8g->pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again, also forces start condition */ + if ( arg_val == 0 ) + { + /* disable chip, send stop condition */ + platform_i2c_send_stop( ESP_I2C_ID ); + } + else + { + /* enable, do nothing: any byte writing will trigger the i2c start */ + } + break; + + case U8G_COM_MSG_WRITE_BYTE: + //u8g->pin_list[U8G_PI_SET_A0] = 1; + if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 ) + return platform_i2c_send_stop( ESP_I2C_ID ), 0; + // ignore return value -> tolerate missing ACK + if ( platform_i2c_send_byte( ESP_I2C_ID, arg_val) == 0 ) + ; //return platform_i2c_send_stop( ESP_I2C_ID ), 0; + // platform_i2c_send_stop( ESP_I2C_ID ); + break; + + case U8G_COM_MSG_WRITE_SEQ: + case U8G_COM_MSG_WRITE_SEQ_P: + //u8g->pin_list[U8G_PI_SET_A0] = 1; + if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 ) + return platform_i2c_send_stop( ESP_I2C_ID ), 0; + { + register uint8_t *ptr = arg_ptr; + while( arg_val > 0 ) + { + // ignore return value -> tolerate missing ACK + if ( platform_i2c_send_byte( ESP_I2C_ID, *ptr++) == 0 ) + ; //return platform_i2c_send_stop( ESP_I2C_ID ), 0; + arg_val--; + } + } + // platform_i2c_send_stop( ESP_I2C_ID ); + break; + + case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */ + u8g->pin_list[U8G_PI_A0_STATE] = arg_val; + u8g->pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again */ + + break; + } + return 1; +} + + + + // device destructor static int lu8g_close_display( lua_State *L ) { @@ -777,19 +996,6 @@ static int lu8g_close_display( lua_State *L ) if ((lud = get_lud( L )) == NULL) return 0; - if (lud->cb_ref != LUA_NOREF) { - // this is the fb_rle device - u8g_dev_t *fb_dev = LU8G->dev; - u8g_pb_t *fb_dev_pb = (u8g_pb_t *)(fb_dev->dev_mem); - uint8_t *fb_dev_buf = fb_dev_pb->buf; - - luaM_free( L, fb_dev_buf ); - luaM_free( L, fb_dev_pb ); - luaM_free( L, fb_dev ); - - luaL_unref( L, lud->cb_ref, LUA_REGISTRYINDEX ); - } - return 0; } @@ -810,10 +1016,9 @@ static int lu8g_close_display( lua_State *L ) return luaL_error( L, "i2c address required" ); \ \ lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \ - lud->cb_ref = LUA_NOREF; \ \ - lud->i2c_addr = (uint8_t)addr; \ - lud->use_delay = del > 0 ? 1 : 0; \ + lud->u8g.i2c_addr = (uint8_t)addr; \ + lud->u8g.use_delay = del > 0 ? 1 : 0; \ \ u8g_InitI2C( LU8G, &u8g_dev_ ## device, U8G_I2C_OPT_NONE); \ \ @@ -844,9 +1049,8 @@ U8G_DISPLAY_TABLE_I2C unsigned del = luaL_optinteger( L, 4, 0 ); \ \ lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \ - lud->cb_ref = LUA_NOREF; \ \ - lud->use_delay = del > 0 ? 1 : 0; \ + lud->u8g.use_delay = del > 0 ? 1 : 0; \ \ u8g_InitHWSPI( LU8G, &u8g_dev_ ## device, cs, dc, res ); \ \ @@ -860,53 +1064,6 @@ U8G_DISPLAY_TABLE_I2C // Unroll the display table and insert binding functions for SPI based displays. U8G_DISPLAY_TABLE_SPI // -// -// -// This display forwards the framebuffer contents as run-length encoded chunks to a Lua callback -static int lu8g_fb_rle( lua_State *L ) { - lu8g_userdata_t *lud; - - if ((lua_type( L, 1 ) != LUA_TFUNCTION) && - (lua_type( L, 1 ) != LUA_TLIGHTFUNCTION)) { - luaL_typerror( L, 1, "function" ); - } - - int width = luaL_checkint( L, 2 ); - int height = luaL_checkint( L, 3 ); - - luaL_argcheck( L, (width > 0) && (width < 256) && ((width % 8) == 0), 2, "invalid width" ); - luaL_argcheck( L, (height > 0) && (height < 256) && ((height % 8) == 0), 3, "invalid height" ); - - // construct display device structures manually because width and height are configurable - uint8_t *fb_dev_buf = (uint8_t *)luaM_malloc( L, width ); - - u8g_pb_t *fb_dev_pb = (u8g_pb_t *)luaM_malloc( L, sizeof( u8g_pb_t ) ); - fb_dev_pb->p.page_height = 8; - fb_dev_pb->p.total_height = height; - fb_dev_pb->p.page_y0 = 0; - fb_dev_pb->p.page_y1 = 0; - fb_dev_pb->p.page = 0; - fb_dev_pb->width = width; - fb_dev_pb->buf = fb_dev_buf; - - u8g_dev_t *fb_dev = (u8g_dev_t *)luaM_malloc( L, sizeof( u8g_dev_t ) ); - fb_dev->dev_fn = u8g_dev_gen_fb_fn; - fb_dev->dev_mem = fb_dev_pb; - fb_dev->com_fn = u8g_com_esp8266_fbrle_fn; - - lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); - lua_pushvalue( L, 1 ); // copy argument (func) to the top of stack - lud->cb_ref = luaL_ref( L, LUA_REGISTRYINDEX ); - - /* set metatable for userdata */ - luaL_getmetatable(L, "u8g.display"); - lua_setmetatable(L, -2); - - u8g_Init8BitFixedPort( LU8G, fb_dev, width, height, 0, 0, 0); - - return 1; -} -// // *************************************************************************** @@ -980,8 +1137,6 @@ static const LUA_REG_TYPE lu8g_map[] = { #define U8G_FONT_TABLE_ENTRY(font) \ { LSTRKEY( #font ), LUDATA( (void *)(u8g_ ## font) ) }, U8G_FONT_TABLE - // - { LSTRKEY( "fb_rle" ), LFUNCVAL( lu8g_fb_rle ) }, // Options for circle/ ellipse drawing { LSTRKEY( "DRAW_UPPER_RIGHT" ), LNUMVAL( U8G_DRAW_UPPER_RIGHT ) }, { LSTRKEY( "DRAW_UPPER_LEFT" ), LNUMVAL( U8G_DRAW_UPPER_LEFT ) }, diff --git a/app/modules/u8g_glue.c b/app/modules/u8g_glue.c deleted file mode 100644 index 41fe2b72..00000000 --- a/app/modules/u8g_glue.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - Functions for integrating U8glib into the nodemcu platform. -*/ - -#include "lauxlib.h" -#include "platform.h" - -#include "c_stdlib.h" - -#include "u8g.h" -#include "u8g_glue.h" - - -// ------------------------------------------------------------ -// comm functions -// -#define I2C_CMD_MODE 0x000 -#define I2C_DATA_MODE 0x040 - -#define ESP_I2C_ID 0 - - -static uint8_t do_i2c_start(uint8_t id, uint8_t sla) -{ - platform_i2c_send_start( id ); - - // ignore return value -> tolerate missing ACK - platform_i2c_send_address( id, sla, PLATFORM_I2C_DIRECTION_TRANSMITTER ); - - return 1; -} - -static uint8_t u8g_com_esp8266_ssd_start_sequence(struct _lu8g_userdata_t *lu8g) -{ - /* are we requested to set the a0 state? */ - if ( lu8g->u8g.pin_list[U8G_PI_SET_A0] == 0 ) - return 1; - - /* setup bus, might be a repeated start */ - if ( do_i2c_start( ESP_I2C_ID, lu8g->i2c_addr ) == 0 ) - return 0; - if ( lu8g->u8g.pin_list[U8G_PI_A0_STATE] == 0 ) - { - // ignore return value -> tolerate missing ACK - if ( platform_i2c_send_byte( ESP_I2C_ID, I2C_CMD_MODE ) == 0 ) - ; //return 0; - } - else - { - platform_i2c_send_byte( ESP_I2C_ID, I2C_DATA_MODE ); - } - - lu8g->u8g.pin_list[U8G_PI_SET_A0] = 0; - return 1; -} - - -static void lu8g_digital_write( struct _lu8g_userdata_t *lu8g, uint8_t pin_index, uint8_t value ) -{ - uint8_t pin; - - pin = lu8g->u8g.pin_list[pin_index]; - if ( pin != U8G_PIN_NONE ) - platform_gpio_write( pin, value ); -} - -void u8g_Delay(u8g_t *u8g, uint16_t msec) -{ - struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g; - const uint16_t chunk = 50; - - if (lu8g->use_delay == 0) - return; - - while (msec > chunk) - { - os_delay_us( chunk*1000 ); - msec -= chunk; - } - if (msec > 0) - os_delay_us( msec*1000 ); -} -void u8g_MicroDelay(void) -{ - os_delay_us( 1 ); -} -void u8g_10MicroDelay(void) -{ - os_delay_us( 10 ); -} - - -uint8_t u8g_com_esp8266_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) -{ - struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g; - - switch(msg) - { - case U8G_COM_MSG_STOP: - break; - - case U8G_COM_MSG_INIT: - // we assume that the SPI interface was already initialized - // just care for the /CS and D/C pins - lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_HIGH ); - platform_gpio_mode( lu8g->u8g.pin_list[U8G_PI_CS], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT ); - platform_gpio_mode( lu8g->u8g.pin_list[U8G_PI_A0], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT ); - break; - - case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */ - lu8g_digital_write( lu8g, U8G_PI_A0, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH ); - break; - - case U8G_COM_MSG_CHIP_SELECT: - if (arg_val == 0) - { - /* disable */ - lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_HIGH ); - } - else - { - /* enable */ - lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_LOW ); - } - break; - - case U8G_COM_MSG_RESET: - if ( lu8g->u8g.pin_list[U8G_PI_RESET] != U8G_PIN_NONE ) - lu8g_digital_write( lu8g, U8G_PI_RESET, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH ); - break; - - case U8G_COM_MSG_WRITE_BYTE: - platform_spi_send( 1, 8, arg_val ); - break; - - case U8G_COM_MSG_WRITE_SEQ: - case U8G_COM_MSG_WRITE_SEQ_P: - { - register uint8_t *ptr = arg_ptr; - while( arg_val > 0 ) - { - platform_spi_send( 1, 8, *ptr++ ); - arg_val--; - } - } - break; - } - return 1; -} - - -uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) -{ - struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g; - - switch(msg) - { - case U8G_COM_MSG_INIT: - // we assume that the i2c bus was already initialized - //u8g_i2c_init(u8g->pin_list[U8G_PI_I2C_OPTION]); - - break; - - case U8G_COM_MSG_STOP: - break; - - case U8G_COM_MSG_RESET: - /* Currently disabled, but it could be enable. Previous restrictions have been removed */ - /* u8g_com_arduino_digital_write(u8g, U8G_PI_RESET, arg_val); */ - break; - - case U8G_COM_MSG_CHIP_SELECT: - lu8g->u8g.pin_list[U8G_PI_A0_STATE] = 0; - lu8g->u8g.pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again, also forces start condition */ - if ( arg_val == 0 ) - { - /* disable chip, send stop condition */ - platform_i2c_send_stop( ESP_I2C_ID ); - } - else - { - /* enable, do nothing: any byte writing will trigger the i2c start */ - } - break; - - case U8G_COM_MSG_WRITE_BYTE: - //u8g->pin_list[U8G_PI_SET_A0] = 1; - if ( u8g_com_esp8266_ssd_start_sequence(lu8g) == 0 ) - return platform_i2c_send_stop( ESP_I2C_ID ), 0; - // ignore return value -> tolerate missing ACK - if ( platform_i2c_send_byte( ESP_I2C_ID, arg_val) == 0 ) - ; //return platform_i2c_send_stop( ESP_I2C_ID ), 0; - // platform_i2c_send_stop( ESP_I2C_ID ); - break; - - case U8G_COM_MSG_WRITE_SEQ: - case U8G_COM_MSG_WRITE_SEQ_P: - //u8g->pin_list[U8G_PI_SET_A0] = 1; - if ( u8g_com_esp8266_ssd_start_sequence(lu8g) == 0 ) - return platform_i2c_send_stop( ESP_I2C_ID ), 0; - { - register uint8_t *ptr = arg_ptr; - while( arg_val > 0 ) - { - // ignore return value -> tolerate missing ACK - if ( platform_i2c_send_byte( ESP_I2C_ID, *ptr++) == 0 ) - ; //return platform_i2c_send_stop( ESP_I2C_ID ), 0; - arg_val--; - } - } - // platform_i2c_send_stop( ESP_I2C_ID ); - break; - - case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */ - lu8g->u8g.pin_list[U8G_PI_A0_STATE] = arg_val; - lu8g->u8g.pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again */ - - break; - } - return 1; -} - - -// *************************************************************************** -// Generic framebuffer device and RLE comm driver -// -uint8_t u8g_dev_gen_fb_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg) -{ - switch(msg) - { - case U8G_DEV_MSG_PAGE_FIRST: - // tell comm driver to start new framebuffer - u8g_SetChipSelect(u8g, dev, 1); - break; - case U8G_DEV_MSG_PAGE_NEXT: - { - u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem); - if ( u8g_pb_WriteBuffer(pb, u8g, dev) == 0 ) - return 0; - } - break; - } - - return u8g_dev_pb8v1_base_fn(u8g, dev, msg, arg); -} - -static int bit_at( uint8_t *buf, int line, int x ) -{ - uint8_t byte = buf[x]; - - return buf[x] & (1 << line) ? 1 : 0; -} - -struct _lu8g_fbrle_item -{ - uint8_t start_x; - uint8_t len; -}; - -struct _lu8g_fbrle_line -{ - uint8_t num_valid; - struct _lu8g_fbrle_item items[0]; -}; - -uint8_t u8g_com_esp8266_fbrle_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) -{ - struct _lu8g_userdata_t *lud = (struct _lu8g_userdata_t *)u8g; - - switch(msg) - { - case U8G_COM_MSG_INIT: - // allocate memory -> done - // init buffer - break; - - case U8G_COM_MSG_CHIP_SELECT: - if (arg_val == 1) { - // new frame starts - if (lud->cb_ref != LUA_NOREF) { - // fire callback with nil argument - lua_State *L = lua_getstate(); - lua_rawgeti( L, LUA_REGISTRYINDEX, lud->cb_ref ); - lua_pushnil( L ); - lua_call( L, 1, 0 ); - } - } - break; - - case U8G_COM_MSG_WRITE_SEQ: - case U8G_COM_MSG_WRITE_SEQ_P: - { - uint8_t xwidth = u8g->pin_list[U8G_PI_EN]; - size_t fbrle_line_size = sizeof( struct _lu8g_fbrle_line ) + sizeof( struct _lu8g_fbrle_item ) * (xwidth/2); - int num_lines = arg_val / (xwidth/8); - uint8_t *buf = (uint8_t *)arg_ptr; - - struct _lu8g_fbrle_line *fbrle_line; - if (!(fbrle_line = (struct _lu8g_fbrle_line *)c_malloc( fbrle_line_size ))) { - break; - } - - for (int line = 0; line < num_lines; line++) { - int start_run = -1; - fbrle_line->num_valid = 0; - - for (int x = 0; x < xwidth; x++) { - if (bit_at( buf, line, x ) == 0) { - if (start_run >= 0) { - // inside run, end it and enter result - fbrle_line->items[fbrle_line->num_valid].start_x = start_run; - fbrle_line->items[fbrle_line->num_valid++].len = x - start_run; - //NODE_ERR( " line: %d x: %d len: %d\n", line, start_run, x - start_run ); - start_run = -1; - } - } else { - if (start_run < 0) { - // outside run, start it - start_run = x; - } - } - - if (fbrle_line->num_valid >= xwidth/2) break; - } - - // active run? - if (start_run >= 0 && fbrle_line->num_valid < xwidth/2) { - fbrle_line->items[fbrle_line->num_valid].start_x = start_run; - fbrle_line->items[fbrle_line->num_valid++].len = xwidth - start_run; - } - - // line done, trigger callback - if (lud->cb_ref != LUA_NOREF) { - lua_State *L = lua_getstate(); - - lua_rawgeti( L, LUA_REGISTRYINDEX, lud->cb_ref ); - lua_pushlstring( L, (const char *)fbrle_line, fbrle_line_size ); - lua_call( L, 1, 0 ); - } - } - - c_free( fbrle_line ); - } - break; - } - return 1; -} diff --git a/app/modules/u8g_glue.h b/app/modules/u8g_glue.h deleted file mode 100644 index e8057651..00000000 --- a/app/modules/u8g_glue.h +++ /dev/null @@ -1,22 +0,0 @@ - -#ifndef _U8G_GLUE_H_ -#define _U8G_GLUE_H_ - -#include "u8g.h" - -struct _lu8g_userdata_t -{ - u8g_t u8g; - uint8_t i2c_addr; - uint8_t use_delay; - int cb_ref; -}; -typedef struct _lu8g_userdata_t lu8g_userdata_t; - -// shorthand macro for the u8g structure inside the userdata -#define LU8G (&(lud->u8g)) - -uint8_t u8g_com_esp8266_fbrle_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); -uint8_t u8g_dev_gen_fb_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg); - -#endif diff --git a/app/modules/websocket.c b/app/modules/websocket.c index 6feb6e82..899d04dc 100644 --- a/app/modules/websocket.c +++ b/app/modules/websocket.c @@ -15,7 +15,6 @@ #include "c_types.h" #include "c_string.h" -#include "c_stdlib.h" #include "websocketclient.h" @@ -46,7 +45,7 @@ static void websocketclient_onConnectionCallback(ws_info *ws) { } } -static void websocketclient_onReceiveCallback(ws_info *ws, int len, char *message, int opCode) { +static void websocketclient_onReceiveCallback(ws_info *ws, char *message, int opCode) { NODE_DBG("websocketclient_onReceiveCallback\n"); lua_State *L = lua_getstate(); @@ -60,7 +59,7 @@ static void websocketclient_onReceiveCallback(ws_info *ws, int len, char *messag if (data->onReceive != LUA_NOREF) { lua_rawgeti(L, LUA_REGISTRYINDEX, data->onReceive); // load the callback function lua_rawgeti(L, LUA_REGISTRYINDEX, data->self_ref); // pass itself, #1 callback argument - lua_pushlstring(L, message, len); // #2 callback argument + lua_pushstring(L, message); // #2 callback argument lua_pushnumber(L, opCode); // #3 callback argument lua_call(L, 3, 0); } @@ -103,7 +102,6 @@ static int websocket_createClient(lua_State *L) { ws_info *ws = (ws_info *) lua_newuserdata(L, sizeof(ws_info)); ws->connectionState = 0; - ws->extraHeaders = NULL; ws->onConnection = &websocketclient_onConnectionCallback; ws->onReceive = &websocketclient_onReceiveCallback; ws->onFailure = &websocketclient_onCloseCallback; @@ -120,6 +118,7 @@ static int websocketclient_on(lua_State *L) { NODE_DBG("websocketclient_on\n"); ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); + luaL_argcheck(L, ws, 1, "Client websocket expected"); ws_data *data = (ws_data *) ws->reservedData; @@ -171,6 +170,7 @@ static int websocketclient_connect(lua_State *L) { NODE_DBG("websocketclient_connect is called.\n"); ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); + luaL_argcheck(L, ws, 1, "Client websocket expected"); ws_data *data = (ws_data *) ws->reservedData; @@ -188,61 +188,11 @@ static int websocketclient_connect(lua_State *L) { return 0; } -static header_t *realloc_headers(header_t *headers, int new_size) { - if(headers) { - for(header_t *header = headers; header->key; header++) { - c_free(header->value); - c_free(header->key); - } - c_free(headers); - } - if(!new_size) - return NULL; - return (header_t *)c_malloc(sizeof(header_t) * (new_size + 1)); -} - -static int websocketclient_config(lua_State *L) { - NODE_DBG("websocketclient_config is called.\n"); - - ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); - - ws_data *data = (ws_data *) ws->reservedData; - - luaL_checktype(L, 2, LUA_TTABLE); - lua_getfield(L, 2, "headers"); - if(lua_istable(L, -1)) { - - lua_pushnil(L); - int size = 0; - while(lua_next(L, -2)) { - size++; - lua_pop(L, 1); - } - - ws->extraHeaders = realloc_headers(ws->extraHeaders, size); - if(ws->extraHeaders) { - header_t *header = ws->extraHeaders; - - lua_pushnil(L); - while(lua_next(L, -2)) { - header->key = c_strdup(lua_tostring(L, -2)); - header->value = c_strdup(lua_tostring(L, -1)); - header++; - lua_pop(L, 1); - } - - header->key = header->value = NULL; - } - } - lua_pop(L, 1); // pop headers - - return 0; -} - static int websocketclient_send(lua_State *L) { NODE_DBG("websocketclient_send is called.\n"); ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); + luaL_argcheck(L, ws, 1, "Client websocket expected"); ws_data *data = (ws_data *) ws->reservedData; @@ -275,8 +225,7 @@ static int websocketclient_gc(lua_State *L) { NODE_DBG("websocketclient_gc\n"); ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT); - - ws->extraHeaders = realloc_headers(ws->extraHeaders, 0); + luaL_argcheck(L, ws, 1, "Client websocket expected"); ws_data *data = (ws_data *) ws->reservedData; @@ -316,7 +265,6 @@ static const LUA_REG_TYPE websocket_map[] = static const LUA_REG_TYPE websocketclient_map[] = { { LSTRKEY("on"), LFUNCVAL(websocketclient_on) }, - { LSTRKEY("config"), LFUNCVAL(websocketclient_config) }, { LSTRKEY("connect"), LFUNCVAL(websocketclient_connect) }, { LSTRKEY("send"), LFUNCVAL(websocketclient_send) }, { LSTRKEY("close"), LFUNCVAL(websocketclient_close) }, diff --git a/app/modules/wifi.c b/app/modules/wifi.c index ab761898..c130dfda 100644 --- a/app/modules/wifi.c +++ b/app/modules/wifi.c @@ -1,7 +1,5 @@ // Module for interfacing with WIFI -// FIXME: sprintf->snprintf everywhere. - #include "module.h" #include "lauxlib.h" #include "platform.h" @@ -78,8 +76,12 @@ static void wifi_smart_succeed_cb(sc_status status, void *pdata){ #endif // WIFI_SMART_ENABLE static int wifi_scan_succeed = LUA_NOREF; - -// callback for wifi_station_listap +/** + * @brief Wifi ap scan over callback to display. + * @param arg: contain the aps information + * @param status: scan over status + * @retval None + */ static void wifi_scan_done(void *arg, STATUS status) { lua_State* L = lua_getstate(); @@ -213,26 +215,23 @@ static int wifi_exit_smart( lua_State* L ) } #endif // WIFI_SMART_ENABLE -// Lua: wifi.setmode(mode, save_to_flash) +// Lua: realmode = setmode(mode) static int wifi_setmode( lua_State* L ) { unsigned mode; - bool save_to_flash=true; + mode = luaL_checkinteger( L, 1 ); - luaL_argcheck(L, mode == STATION_MODE || mode == SOFTAP_MODE || mode == STATIONAP_MODE || mode == NULL_MODE, 1, "Invalid mode"); - if(!lua_isnoneornil(L, 2)) - { - if(!lua_isboolean(L, 2)) luaL_typerror(L, 2, lua_typename(L, LUA_TBOOLEAN)); - save_to_flash=lua_toboolean(L, 2); - } - if(save_to_flash) wifi_set_opmode( (uint8_t)mode); - else wifi_set_opmode_current( (uint8_t)mode); + + if ( mode != STATION_MODE && mode != SOFTAP_MODE && mode != STATIONAP_MODE && mode != NULL_MODE ) + return luaL_error( L, "wrong arg type" ); + wifi_set_opmode( (uint8_t)mode); mode = (unsigned)wifi_get_opmode(); lua_pushinteger( L, mode ); return 1; } -// Lua: wifi.getmode() +// Lua: realmode = getmode() + static int wifi_getmode( lua_State* L ) { unsigned mode; @@ -240,17 +239,20 @@ static int wifi_getmode( lua_State* L ) lua_pushinteger( L, mode ); return 1; } +/** + * wifi.getchannel() + * Description: + * Get current wifi Channel + * + * Syntax: + * wifi.getchannel() + * Parameters: + * nil + * + * Returns: + * Current wifi channel + */ -// Lua: wifi.getdefaultmode() -static int wifi_getdefaultmode( lua_State* L ) -{ - unsigned mode; - mode = (unsigned)wifi_get_opmode_default(); - lua_pushinteger( L, mode ); - return 1; -} - -// Lua: wifi.getchannel() static int wifi_getchannel( lua_State* L ) { unsigned channel; @@ -259,7 +261,22 @@ static int wifi_getchannel( lua_State* L ) return 1; } -// Lua: wifi.setphymode() +/** + * wifi.setphymode() + * Description: + * Set wifi physical mode(802.11 b/g/n) + * Note: SoftAP only supports 802.11 b/g. + * Syntax: + * wifi.setphymode(mode) + * Parameters: + * mode: + * wifi.PHYMODE_B + * wifi.PHYMODE_G + * wifi.PHYMODE_N + * Returns: + * Current physical mode after setup + */ + static int wifi_setphymode( lua_State* L ) { unsigned mode; @@ -274,7 +291,18 @@ static int wifi_setphymode( lua_State* L ) return 1; } -// Lua: wifi.getphymode() +/** + * wifi.getphymode() + * Description: + * Get wifi physical mode(802.11 b/g/n) + * Syntax: + * wifi.getphymode() + * Parameters: + * nil + * Returns: + * Current physical mode. + * + */ static int wifi_getphymode( lua_State* L ) { unsigned mode; @@ -283,7 +311,7 @@ static int wifi_getphymode( lua_State* L ) return 1; } -// Lua: wifi.sleep() +//wifi.sleep() static int wifi_sleep(lua_State* L) { uint8 desired_sleep_state = 2; @@ -344,7 +372,7 @@ static int wifi_sleep(lua_State* L) return 2; } -// Lua: wifi.nullmodesleep() +//wifi.nullmodesleep() static int wifi_null_mode_auto_sleep(lua_State* L) { if (!lua_isnone(L, 1)) @@ -431,7 +459,7 @@ static int wifi_getbroadcast( lua_State* L, uint8_t mode ) } } -// Used by wifi_setip + static uint32_t parse_key(lua_State* L, const char * key){ lua_getfield(L, 1, key); if( lua_isstring(L, -1) ) // deal with the ip/netmask/gw string @@ -478,86 +506,24 @@ static int wifi_setip( lua_State* L, uint8_t mode ) return 1; } -// Lua: wifi.sta.getaplist -static int wifi_station_get_ap_info4lua( lua_State* L ) +// Lua: realtype = sleeptype(type) +static int wifi_sleeptype( lua_State* L ) { - struct station_config config[5]; - char temp[sizeof(config[0].password)+1]; //max password length + '\0' - uint8 number_of_aps = wifi_station_get_ap_info(config); - -#if defined(WIFI_DEBUG) - char debug_temp[128]; -#endif - lua_newtable(L); - lua_pushnumber(L, number_of_aps); - lua_setfield(L, -2, "qty"); - WIFI_DBG("\n\t# of APs stored in flash:%d\n", number_of_aps); - WIFI_DBG(" %-6s %-32s %-64s %-17s\n", "index:", "ssid:", "password:", "bssid:"); - - for(int i=0;i= 8) - { - memcpy(temp, config[i].password, sizeof(config[i].password)); - lua_pushstring(L, temp); - lua_setfield(L, -2, "pwd"); + unsigned type; + + if ( lua_isnumber(L, 1) ){ + type = lua_tointeger(L, 1); + if ( type != NONE_SLEEP_T && type != LIGHT_SLEEP_T && type != MODEM_SLEEP_T ) + return luaL_error( L, "wrong arg type" ); + if(!wifi_set_sleep_type(type)){ + lua_pushnil(L); + return 1; } -#if defined(WIFI_DEBUG) - c_sprintf(debug_temp + strlen(debug_temp), "%-64s ", temp); -#endif - - memset(temp, 0, sizeof(temp)); - if (config[i].bssid_set) - { - c_sprintf(temp, MACSTR, MAC2STR(config[i].bssid)); - lua_pushstring(L, temp); - lua_setfield(L, -2, "bssid"); - } - -#if defined(WIFI_DEBUG) - WIFI_DBG("%s%-17s \n", debug_temp, temp); -#endif - lua_pushnumber(L, i+1); //Add one, so that AP index follows Lua Conventions - lua_insert(L, -2); - lua_settable(L, -3); } - return 1; -} -// Lua: wifi.setapnumber(number_of_aps_to_save) -static int wifi_station_ap_number_set4lua( lua_State* L ) -{ - unsigned limit=luaL_checkinteger(L, 1); - luaL_argcheck(L, (limit >= 1 && limit <= 5), 1, "Valid range: 1-5"); - lua_pushboolean(L, wifi_station_ap_number_set((uint8)limit)); - return 1; -} - -// Lua: wifi.setapnumber(number_of_aps_to_save) -static int wifi_station_change_ap( lua_State* L ) -{ - uint8 ap_index=luaL_checkinteger(L, 1); - luaL_argcheck(L, (ap_index >= 1 && ap_index <= 5), 1, "Valid range: 1-5"); - lua_pushboolean(L, wifi_station_ap_change(ap_index-1)); - return 1; -} - -// Lua: wifi.setapnumber(number_of_aps_to_save) -static int wifi_station_get_ap_index( lua_State* L ) -{ - lua_pushnumber(L, wifi_station_get_current_ap_id()+1); - return 1; + type = wifi_get_sleep_type(); + lua_pushinteger( L, type ); + return 1; } // Lua: wifi.sta.getmac() @@ -585,230 +551,161 @@ static int wifi_station_getbroadcast( lua_State* L ){ return wifi_getbroadcast(L, STATION_IF); } -// Used by wifi_station_getconfig_xxx -static int wifi_station_getconfig( lua_State* L, bool get_flash_cfg) +/** + * wifi.sta.getconfig() + * Description: + * Get current Station configuration. + * Note: if bssid_set==1 STATION is configured to connect to specified BSSID + * if bssid_set==0 specified BSSID address is irrelevant. + * Syntax: + * ssid, pwd, bssid_set, bssid=wifi.sta.getconfig() + * Parameters: + * none + * Returns: + * SSID, Password, BSSID_set, BSSID + */ +static int wifi_station_getconfig( lua_State* L ) { + struct station_config sta_conf; + char bssid[17]; + wifi_station_get_config(&sta_conf); + if(sta_conf.ssid==0) + { + lua_pushnil(L); + return 1; + } + else + { + lua_pushstring( L, sta_conf.ssid ); + lua_pushstring( L, sta_conf.password ); + lua_pushinteger( L, sta_conf.bssid_set); + c_sprintf(bssid, MACSTR, MAC2STR(sta_conf.bssid)); + lua_pushstring( L, bssid); + return 4; + } +} + +/** + * wifi.sta.config() + * Description: + * Set current Station configuration. + * Note: If there are multiple APs with the same ssid, you can connect to a specific one by entering it's MAC address into the "bssid" field. + * Syntax: + * wifi.sta.getconfig(ssid, password) --Set STATION configuration, Auto-connect by default, Connects to any BSSID + * wifi.sta.getconfig(ssid, password, Auto_connect) --Set STATION configuration, Auto-connect(0 or 1), Connects to any BSSID + * wifi.sta.getconfig(ssid, password, bssid) --Set STATION configuration, Auto-connect by default, Connects to specific BSSID + * wifi.sta.getconfig(ssid, password, Auto_connect, bssid) --Set STATION configuration, Auto-connect(0 or 1), Connects to specific BSSID + * Parameters: + * ssid: string which is less than 32 bytes. + * Password: string which is less than 64 bytes. + * Auto_connect: 0 (disable Auto-connect) or 1 (to enable Auto-connect). + * bssid: MAC address of Access Point you would like to connect to. + * Returns: + * Nothing. + * + * Example: + --Connect to Access Point automatically when in range + wifi.sta.getconfig("myssid", "password") + + --Connect to Access Point, User decides when to connect/disconnect to/from AP + wifi.sta.getconfig("myssid", "mypassword", 0) + wifi.sta.connect() + --do some wifi stuff + wifi.sta.disconnect() + + --Connect to specific Access Point automatically when in range + wifi.sta.getconfig("myssid", "mypassword", "12:34:56:78:90:12") + + --Connect to specific Access Point, User decides when to connect/disconnect to/from AP + wifi.sta.getconfig("myssid", "mypassword", 0) + wifi.sta.connect() + --do some wifi stuff + wifi.sta.disconnect() + * + */ +static int wifi_station_config( lua_State* L ) +{ + size_t sl, pl, ml; struct station_config sta_conf; - char temp[sizeof(sta_conf.password)+1]; //max password length + '\0' - if(get_flash_cfg) wifi_station_get_config_default(&sta_conf); - else wifi_station_get_config(&sta_conf); - if(sta_conf.ssid==0) + int auto_connect=0; + const char *ssid = luaL_checklstring( L, 1, &sl ); + if (sl>32 || ssid == NULL) + return luaL_error( L, "ssid:<32" ); + const char *password = luaL_checklstring( L, 2, &pl ); + if ((pl!=0 && (pl<8 || pl>64)) || password == NULL) + return luaL_error( L, "pwd:0,8~64" ); + + if(lua_isnumber(L, 3)) { - lua_pushnil(L); - return 1; + auto_connect=luaL_checkinteger( L, 3 );; + if ( auto_connect != 0 && auto_connect != 1) + return luaL_error( L, "wrong arg type" ); + } + else if (lua_isstring(L, 3)&& !(lua_isnumber(L, 3))) + { + lua_pushnil(L); + lua_insert(L, 3); + auto_connect=1; + } else { - if(lua_isboolean(L, 1) && lua_toboolean(L, 1)==true) - { - lua_newtable(L); - memset(temp, 0, sizeof(temp)); - memcpy(temp, sta_conf.ssid, sizeof(sta_conf.ssid)); - lua_pushstring(L, temp); - lua_setfield(L, -2, "ssid"); - - if(strlen(sta_conf.password) >= 8) - { - memset(temp, 0, sizeof(temp)); - memcpy(temp, sta_conf.password, sizeof(sta_conf.password)); - lua_pushstring(L, temp); - lua_setfield(L, -2, "pwd"); - } - - if(sta_conf.bssid_set==1) - { - memset(temp, 0, sizeof(temp)); - c_sprintf(temp, MACSTR, MAC2STR(sta_conf.bssid)); - lua_pushstring( L, temp); - lua_setfield(L, -2, "bssid"); - } - return 1; - } - else - { - memset(temp, 0, sizeof(temp)); - memcpy(temp, sta_conf.ssid, sizeof(sta_conf.ssid)); - lua_pushstring(L, temp); - memset(temp, 0, sizeof(temp)); - memcpy(temp, sta_conf.password, sizeof(sta_conf.password)); - lua_pushstring(L, temp); - lua_pushinteger( L, sta_conf.bssid_set); - c_sprintf(temp, MACSTR, MAC2STR(sta_conf.bssid)); - lua_pushstring( L, temp); - return 4; - } + if(lua_isnil(L, 3)) + return luaL_error( L, "wrong arg type" ); + auto_connect=1; } -} -// Lua: wifi.sta.getconfig() -static int wifi_station_getconfig_current(lua_State *L) -{ - return wifi_station_getconfig(L, false); -} - -// Lua: wifi.sta.getdefaultconfig() -static int wifi_station_getconfig_default(lua_State *L) -{ - return wifi_station_getconfig(L, true); -} - -// Lua: wifi.sta.config() -static int wifi_station_config( lua_State* L ) -{ - struct station_config sta_conf; - bool auto_connect=true; - bool save_to_flash=true; - size_t sl, pl, ml; - - memset(sta_conf.ssid, 0, sizeof(sta_conf.ssid)); - memset(sta_conf.password, 0, sizeof(sta_conf.password)); - memset(sta_conf.bssid, 0, sizeof(sta_conf.bssid)); - sta_conf.bssid_set=0; - - if(lua_istable(L, 1)) + if(lua_isnumber(L, 4)) { - lua_getfield(L, 1, "ssid"); - if (!lua_isnil(L, -1)) - { - if( lua_isstring(L, -1) ) - { - const char *ssid = luaL_checklstring( L, -1, &sl ); - luaL_argcheck(L, ((sl>=1 && sl<=sizeof(sta_conf.ssid)) ), 1, "ssid: length:1-32"); - memcpy(sta_conf.ssid, ssid, sl); - } - else return luaL_argerror( L, 1, "ssid:not string" ); - } - else return luaL_argerror( L, 1, "ssid required" ); - lua_pop(L, 1); - - lua_getfield(L, 1, "pwd"); - if (!lua_isnil(L, -1)) - { - if( lua_isstring(L, -1) ) - { - const char *pwd = luaL_checklstring( L, -1, &pl ); - luaL_argcheck(L, ((pl>=8 && pl<=sizeof(sta_conf.password)) ), 1, "pwd: length:8-64"); - memcpy(sta_conf.password, pwd, pl); - } - else return luaL_argerror( L, 1, "pwd:not string" ); - } - lua_pop(L, 1); - - lua_getfield(L, 1, "bssid"); - if (!lua_isnil(L, -1)) - { - if (lua_isstring(L, -1)) - { - const char *macaddr = luaL_checklstring( L, -1, &ml ); - luaL_argcheck(L, ((ml==sizeof("AA:BB:CC:DD:EE:FF")-1) ), 1, "bssid: FF:FF:FF:FF:FF:FF"); - ets_str2macaddr(sta_conf.bssid, macaddr); - sta_conf.bssid_set = 1; - } - else return luaL_argerror(L, 1, "bssid:not string"); - } - lua_pop(L, 1); - - lua_getfield(L, 1, "auto"); - if (!lua_isnil(L, -1)) - { - if (lua_isboolean(L, -1)) - { - auto_connect=lua_toboolean(L, -1); - } - else return luaL_argerror(L, 1, "auto:not boolean"); - } - lua_pop(L, 1); - - lua_getfield(L, 1, "save"); - if (!lua_isnil(L, -1)) - { - if (lua_isboolean(L, -1)) save_to_flash=lua_toboolean(L, -1); - else return luaL_argerror(L, 1, "save:not boolean"); - } - else save_to_flash=false; - lua_pop(L, 1); - + sta_conf.bssid_set = 0; + c_memset(sta_conf.bssid, 0, 6); } - else //to be depreciated + else { - const char *ssid = luaL_checklstring( L, 1, &sl ); - luaL_argcheck(L, ((sl>=1 && sl=8 && pl=1 && sl<=sizeof(config.ssid)) ), 1, "ssid: length:1-32"); - memcpy(config.ssid, ssid, sl); - config.ssid_len = sl; + const char *ssid = luaL_checklstring( L, -1, &len ); + if(len<1 || len>32 || ssid == NULL) + return luaL_error( L, "ssid:1~32" ); + c_memset(config.ssid, 0, 32); + c_memcpy(config.ssid, ssid, len); + NODE_DBG(config.ssid); + NODE_DBG("\n"); + config.ssid_len = len; config.ssid_hidden = 0; } - else return luaL_argerror( L, 1, "ssid: not string" ); + else + return luaL_error( L, "wrong arg type" ); } - else return luaL_argerror( L, 1, "ssid: required" ); - lua_pop(L, 1); - + else + return luaL_error( L, "ssid required" ); lua_getfield(L, 1, "pwd"); if (!lua_isnil(L, -1)){ /* found? */ if( lua_isstring(L, -1) ) // deal with the password string { - const char *pwd = luaL_checklstring( L, -1, &pl ); - luaL_argcheck(L, (pl>=8 && pl<=sizeof(config.password)), 1, "pwd: length:0 or 8-64"); - memcpy(config.password, pwd, pl); + const char *pwd = luaL_checklstring( L, -1, &len ); + if(len<8 || len>64 || pwd == NULL) + return luaL_error( L, "pwd:8~64" ); + c_memset(config.password, 0, 64); + c_memcpy(config.password, pwd, len); + NODE_DBG(config.password); + NODE_DBG("\n"); config.authmode = AUTH_WPA_WPA2_PSK; } else - return luaL_argerror( L, 1, "pwd: not string" ); + return luaL_error( L, "wrong arg type" ); } else{ config.authmode = AUTH_OPEN; } - lua_pop(L, 1); lua_getfield(L, 1, "auth"); if (!lua_isnil(L, -1)) { - if(lua_isnumber(L, -1)) - { - lint=luaL_checkinteger(L, -1); - luaL_argcheck(L, (lint >= 0 && lint < AUTH_MAX), 1, "auth: Range:0-4"); - config.authmode = (uint8_t)luaL_checkinteger(L, -1); - } - else return luaL_argerror(L, 1, "auth: not number"); - + config.authmode = (uint8_t)luaL_checkinteger(L, -1); + NODE_DBG("%d\n", config.authmode); + } + else + { + // keep whatever value resulted from "pwd" logic above } - lua_pop(L, 1); - lua_getfield(L, 1, "channel"); if (!lua_isnil(L, -1)) { - if(lua_isnumber(L, -1)) - { - lint=luaL_checkinteger(L, -1); - luaL_argcheck(L, (lint >= 1 && lint <= 13), 1, "channel: Range:1-13"); - config.channel = (uint8_t)lint; - } - else luaL_argerror(L, 1, "channel: not number"); + unsigned channel = luaL_checkinteger(L, -1); + if (channel < 1 || channel > 13) + return luaL_error( L, "channel:1~13" ); + config.channel = (uint8_t)channel; + NODE_DBG("%d\n", config.channel); } else { config.channel = 6; } - lua_pop(L, 1); - lua_getfield(L, 1, "hidden"); if (!lua_isnil(L, -1)) { - Ltype_tmp=lua_type(L, -1); - if(Ltype_tmp==LUA_TNUMBER||Ltype_tmp==LUA_TBOOLEAN) - { - if(Ltype_tmp==LUA_TNUMBER)lint=luaL_checkinteger(L, -1); - if(Ltype_tmp==LUA_TBOOLEAN)lint=(lua_Number)lua_toboolean(L, -1); - - luaL_argcheck(L, (lint == 0 || lint==1), 1, "hidden: 0 or 1"); - config.ssid_hidden = (uint8_t)lint; - } - else return luaL_argerror(L, 1, "hidden: not boolean"); + config.ssid_hidden = (uint8_t)luaL_checkinteger(L, -1); + NODE_DBG("%d\n", config.ssid_hidden); + NODE_DBG("\n"); } else { config.ssid_hidden = 0; } - lua_pop(L, 1); - lua_getfield(L, 1, "max"); if (!lua_isnil(L, -1)) { - if(lua_isnumber(L, -1)) - { - lint=luaL_checkinteger(L, -1); - luaL_argcheck(L, (lint >= 1 && lint <= 4), 1, "max: 1-4"); + unsigned max = luaL_checkinteger(L, -1); + if (max < 1 || max > 4) + return luaL_error( L, "max:1~4" ); - config.max_connection = (uint8_t)lint; - } - else return luaL_argerror(L, 1, "max: not number"); + config.max_connection = (uint8_t)max; + NODE_DBG("%d\n", config.max_connection); } else { config.max_connection = 4; } - lua_pop(L, 1); - lua_getfield(L, 1, "beacon"); if (!lua_isnil(L, -1)) { - if(lua_isnumber(L, -1)) - { - lint=luaL_checkinteger(L, -1); - luaL_argcheck(L, (lint >= 100 && lint <= 60000), 1, "beacon: 100-60000"); - config.beacon_interval = (uint16_t)lint; - } - else return luaL_argerror(L, 1, "beacon: not number"); + unsigned beacon = luaL_checkinteger(L, -1); + if (beacon < 100 || beacon > 60000) + return luaL_error( L, "beacon:100~60000" ); + + config.beacon_interval = (uint16_t)beacon; + NODE_DBG("%d\n", config.beacon_interval); } else { config.beacon_interval = 100; } - lua_pop(L, 1); - - lua_getfield(L, 1, "save"); - if (!lua_isnil(L, -1)) - { - if (lua_isboolean(L, -1)) - { - save_to_flash=lua_toboolean(L, -1); - } - else return luaL_argerror(L, 1, "save: not boolean"); - } - lua_pop(L, 1); - - -#if defined(WIFI_DEBUG) - char debug_temp[sizeof(config.password)+1]; - memset(debug_temp, 0, sizeof(debug_temp)); - memcpy(debug_temp, config.ssid, sizeof(config.ssid)); - WIFI_DBG("\n\tconfig.ssid=\"%s\" len=%d\n", debug_temp, sl); - memset(debug_temp, 0, sizeof(debug_temp)); - memcpy(debug_temp, config.password, sizeof(config.password)); - WIFI_DBG("\tconfig.password=\"%s\" len=%d\n", debug_temp, pl); - WIFI_DBG("\tconfig.authmode=%d\n", config.authmode); - WIFI_DBG("\tconfig.channel=%d\n", config.channel); - WIFI_DBG("\tconfig.ssid_hidden=%d\n", config.ssid_hidden); - WIFI_DBG("\tconfig.max_connection=%d\n", config.max_connection); - WIFI_DBG("\tconfig.beacon_interval=%d\n", config.beacon_interval); - WIFI_DBG("\tsave_to_flash=%s\n", save_to_flash ? "true":"false"); -#endif - - bool config_success; - if(save_to_flash) config_success = wifi_softap_set_config(&config); - else config_success = wifi_softap_set_config_current(&config); - lua_pushboolean(L, config_success); - return 1; + wifi_softap_set_config(&config); + // system_restart(); + return 0; } // Lua: table = wifi.ap.getclient() @@ -1374,7 +1183,6 @@ static int wifi_ap_listclient( lua_State* L ) return 1; } - // Lua: ip = wifi.ap.dhcp.config() static int wifi_ap_dhcp_config( lua_State* L ) { @@ -1412,7 +1220,6 @@ static int wifi_ap_dhcp_config( lua_State* L ) return 2; } - // Lua: wifi.ap.dhcp.start() static int wifi_ap_dhcp_start( lua_State* L ) { @@ -1420,7 +1227,6 @@ static int wifi_ap_dhcp_start( lua_State* L ) return 1; } - // Lua: wifi.ap.dhcp.stop() static int wifi_ap_dhcp_stop( lua_State* L ) { @@ -1428,35 +1234,28 @@ static int wifi_ap_dhcp_stop( lua_State* L ) return 1; } - // Module function map static const LUA_REG_TYPE wifi_station_map[] = { - { LSTRKEY( "autoconnect" ), LFUNCVAL( wifi_station_setauto ) }, - { LSTRKEY( "changeap" ), LFUNCVAL( wifi_station_change_ap ) }, - { LSTRKEY( "config" ), LFUNCVAL( wifi_station_config ) }, - { LSTRKEY( "connect" ), LFUNCVAL( wifi_station_connect4lua ) }, - { LSTRKEY( "disconnect" ), LFUNCVAL( wifi_station_disconnect4lua ) }, + { LSTRKEY( "getconfig" ), LFUNCVAL( wifi_station_getconfig ) }, + { LSTRKEY( "config" ), LFUNCVAL( wifi_station_config ) }, + { LSTRKEY( "connect" ), LFUNCVAL( wifi_station_connect4lua ) }, + { LSTRKEY( "disconnect" ), LFUNCVAL( wifi_station_disconnect4lua ) }, + { LSTRKEY( "autoconnect" ), LFUNCVAL( wifi_station_setauto ) }, + { LSTRKEY( "getip" ), LFUNCVAL( wifi_station_getip ) }, + { LSTRKEY( "setip" ), LFUNCVAL( wifi_station_setip ) }, + { LSTRKEY( "getbroadcast" ), LFUNCVAL( wifi_station_getbroadcast) }, + { LSTRKEY( "getmac" ), LFUNCVAL( wifi_station_getmac ) }, + { LSTRKEY( "setmac" ), LFUNCVAL( wifi_station_setmac ) }, + { LSTRKEY( "getap" ), LFUNCVAL( wifi_station_listap ) }, + { LSTRKEY( "sethostname" ), LFUNCVAL( wifi_sta_sethostname_lua ) }, + { LSTRKEY( "gethostname" ), LFUNCVAL( wifi_sta_gethostname ) }, + { LSTRKEY( "getrssi" ), LFUNCVAL( wifi_station_getrssi ) }, + { LSTRKEY( "status" ), LFUNCVAL( wifi_station_status ) }, #if defined(WIFI_STATION_STATUS_MONITOR_ENABLE) - { LSTRKEY( "eventMonReg" ), LFUNCVAL( wifi_station_event_mon_reg ) }, //defined in wifi_eventmon.c - { LSTRKEY( "eventMonStart" ), LFUNCVAL( wifi_station_event_mon_start ) }, //defined in wifi_eventmon.c - { LSTRKEY( "eventMonStop" ), LFUNCVAL( wifi_station_event_mon_stop ) }, //defined in wifi_eventmon.c + { LSTRKEY( "eventMonReg" ), LFUNCVAL( wifi_station_event_mon_reg ) }, //declared in wifi_eventmon.c + { LSTRKEY( "eventMonStart" ), LFUNCVAL( wifi_station_event_mon_start ) }, //declared in wifi_eventmon.c + { LSTRKEY( "eventMonStop" ), LFUNCVAL( wifi_station_event_mon_stop ) }, //declared in wifi_eventmon.c #endif - { LSTRKEY( "getap" ), LFUNCVAL( wifi_station_listap ) }, - { LSTRKEY( "getapindex" ), LFUNCVAL( wifi_station_get_ap_index ) }, - { LSTRKEY( "getapinfo" ), LFUNCVAL( wifi_station_get_ap_info4lua ) }, - { LSTRKEY( "getbroadcast" ), LFUNCVAL( wifi_station_getbroadcast) }, - { LSTRKEY( "getconfig" ), LFUNCVAL( wifi_station_getconfig_current ) }, - { LSTRKEY( "getdefaultconfig" ), LFUNCVAL( wifi_station_getconfig_default ) }, - { LSTRKEY( "gethostname" ), LFUNCVAL( wifi_sta_gethostname ) }, - { LSTRKEY( "getip" ), LFUNCVAL( wifi_station_getip ) }, - { LSTRKEY( "getmac" ), LFUNCVAL( wifi_station_getmac ) }, - { LSTRKEY( "getrssi" ), LFUNCVAL( wifi_station_getrssi ) }, - { LSTRKEY( "setaplimit" ), LFUNCVAL( wifi_station_ap_number_set4lua ) }, - { LSTRKEY( "sethostname" ), LFUNCVAL( wifi_sta_sethostname_lua ) }, - { LSTRKEY( "setip" ), LFUNCVAL( wifi_station_setip ) }, - { LSTRKEY( "setmac" ), LFUNCVAL( wifi_station_setmac ) }, - { LSTRKEY( "sleeptype" ), LFUNCVAL( wifi_station_sleeptype ) }, - { LSTRKEY( "status" ), LFUNCVAL( wifi_station_status ) }, { LNILKEY, LNILVAL } }; @@ -1468,25 +1267,23 @@ static const LUA_REG_TYPE wifi_ap_dhcp_map[] = { }; static const LUA_REG_TYPE wifi_ap_map[] = { - { LSTRKEY( "config" ), LFUNCVAL( wifi_ap_config ) }, - { LSTRKEY( "deauth" ), LFUNCVAL( wifi_ap_deauth ) }, - { LSTRKEY( "getip" ), LFUNCVAL( wifi_ap_getip ) }, - { LSTRKEY( "setip" ), LFUNCVAL( wifi_ap_setip ) }, - { LSTRKEY( "getbroadcast" ), LFUNCVAL( wifi_ap_getbroadcast) }, - { LSTRKEY( "getmac" ), LFUNCVAL( wifi_ap_getmac ) }, - { LSTRKEY( "setmac" ), LFUNCVAL( wifi_ap_setmac ) }, - { LSTRKEY( "getclient" ), LFUNCVAL( wifi_ap_listclient ) }, - { LSTRKEY( "getconfig" ), LFUNCVAL( wifi_ap_getconfig_current ) }, - { LSTRKEY( "getdefaultconfig" ), LFUNCVAL( wifi_ap_getconfig_default ) }, - { LSTRKEY( "dhcp" ), LROVAL( wifi_ap_dhcp_map ) }, -//{ LSTRKEY( "__metatable" ), LROVAL( wifi_ap_map ) }, + { LSTRKEY( "config" ), LFUNCVAL( wifi_ap_config ) }, + { LSTRKEY( "deauth" ), LFUNCVAL( wifi_ap_deauth ) }, + { LSTRKEY( "getip" ), LFUNCVAL( wifi_ap_getip ) }, + { LSTRKEY( "setip" ), LFUNCVAL( wifi_ap_setip ) }, + { LSTRKEY( "getbroadcast" ), LFUNCVAL( wifi_ap_getbroadcast) }, + { LSTRKEY( "getmac" ), LFUNCVAL( wifi_ap_getmac ) }, + { LSTRKEY( "setmac" ), LFUNCVAL( wifi_ap_setmac ) }, + { LSTRKEY( "getclient" ), LFUNCVAL( wifi_ap_listclient ) }, + { LSTRKEY( "getconfig" ), LFUNCVAL( wifi_ap_getconfig ) }, + { LSTRKEY( "dhcp" ), LROVAL( wifi_ap_dhcp_map ) }, +//{ LSTRKEY( "__metatable" ), LROVAL( wifi_ap_map ) }, { LNILKEY, LNILVAL } }; static const LUA_REG_TYPE wifi_map[] = { { LSTRKEY( "setmode" ), LFUNCVAL( wifi_setmode ) }, { LSTRKEY( "getmode" ), LFUNCVAL( wifi_getmode ) }, - { LSTRKEY( "getdefaultmode" ), LFUNCVAL( wifi_getdefaultmode ) }, { LSTRKEY( "getchannel" ), LFUNCVAL( wifi_getchannel ) }, { LSTRKEY( "setphymode" ), LFUNCVAL( wifi_setphymode ) }, { LSTRKEY( "getphymode" ), LFUNCVAL( wifi_getphymode ) }, @@ -1496,7 +1293,7 @@ static const LUA_REG_TYPE wifi_map[] = { { LSTRKEY( "startsmart" ), LFUNCVAL( wifi_start_smart ) }, { LSTRKEY( "stopsmart" ), LFUNCVAL( wifi_exit_smart ) }, #endif - { LSTRKEY( "sleeptype" ), LFUNCVAL( wifi_station_sleeptype ) }, + { LSTRKEY( "sleeptype" ), LFUNCVAL( wifi_sleeptype ) }, { LSTRKEY( "sta" ), LROVAL( wifi_station_map ) }, { LSTRKEY( "ap" ), LROVAL( wifi_ap_map ) }, @@ -1533,7 +1330,6 @@ static const LUA_REG_TYPE wifi_map[] = { { LNILKEY, LNILVAL } }; -// Used by user_rf_pre_init(user_main.c) void wifi_change_default_host_name(void) { uint8 opmode_temp=wifi_get_opmode(); diff --git a/app/modules/wifi_common.h b/app/modules/wifi_common.h index 65b7a7bd..633fd3a4 100644 --- a/app/modules/wifi_common.h +++ b/app/modules/wifi_common.h @@ -13,9 +13,6 @@ #include "c_stdio.h" #include "task/task.h" -//#define WIFI_DEBUG -//#define EVENT_DEBUG - void wifi_add_sprintf_field(lua_State* L, char* name, char* string, ...); void wifi_add_int_field(lua_State* L, char* name, lua_Integer integer); @@ -40,13 +37,7 @@ static inline void unregister_lua_cb(lua_State* L, int* cb_ref) void wifi_change_default_host_name(void); -#if defined(WIFI_DEBUG) || defined(NODE_DEBUG) -#define WIFI_DBG(...) c_printf(__VA_ARGS__) -#else -#define WIFI_DBG(...) //c_printf(__VA_ARGS__) -#endif - -#if defined(EVENT_DEBUG) || defined(NODE_DEBUG) +#ifdef NODE_DEBUG #define EVENT_DBG(...) c_printf(__VA_ARGS__) #else #define EVENT_DBG(...) //c_printf(__VA_ARGS__) diff --git a/app/modules/ws2812.c b/app/modules/ws2812.c index c84c7456..383070d2 100644 --- a/app/modules/ws2812.c +++ b/app/modules/ws2812.c @@ -19,6 +19,7 @@ typedef struct { + int canary; int size; uint8_t colorsPerLed; uint8_t values[0]; @@ -131,7 +132,8 @@ static int ws2812_write(lua_State* L) { } else if (type == LUA_TUSERDATA) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); + luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); buffer1 = buffer->values; length1 = buffer->colorsPerLed*buffer->size; @@ -154,7 +156,8 @@ static int ws2812_write(lua_State* L) { } else if (type == LUA_TUSERDATA) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer"); + ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 2); + luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 2, "ws2812.buffer expected"); buffer2 = buffer->values; length2 = buffer->colorsPerLed*buffer->size; @@ -170,13 +173,14 @@ static int ws2812_write(lua_State* L) { return 0; } -static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) { - /* relative string position: negative means back from end */ - if (pos < 0) pos += (ptrdiff_t)len + 1; - return (pos >= 0) ? pos : 0; -} +// Handle a buffer where we can store led values +static int ws2812_new_buffer(lua_State *L) { + const int leds = luaL_checkint(L, 1); + const int colorsPerLed = luaL_checkint(L, 2); + + luaL_argcheck(L, leds > 0, 1, "should be a positive integer"); + luaL_argcheck(L, colorsPerLed > 0, 2, "should be a positive integer"); -static ws2812_buffer *allocate_buffer(lua_State *L, int leds, int colorsPerLed) { // Allocate memory size_t size = sizeof(ws2812_buffer) + colorsPerLed*leds*sizeof(uint8_t); ws2812_buffer * buffer = (ws2812_buffer*)lua_newuserdata(L, size); @@ -189,26 +193,16 @@ static ws2812_buffer *allocate_buffer(lua_State *L, int leds, int colorsPerLed) buffer->size = leds; buffer->colorsPerLed = colorsPerLed; - return buffer; -} - -// Handle a buffer where we can store led values -static int ws2812_new_buffer(lua_State *L) { - const int leds = luaL_checkint(L, 1); - const int colorsPerLed = luaL_checkint(L, 2); - - luaL_argcheck(L, leds > 0, 1, "should be a positive integer"); - luaL_argcheck(L, colorsPerLed > 0, 2, "should be a positive integer"); - - ws2812_buffer * buffer = allocate_buffer(L, leds, colorsPerLed); - - c_memset(buffer->values, 0, colorsPerLed * leds); + // Store canary for future type checks + buffer->canary = CANARY_VALUE; return 1; } static int ws2812_buffer_fill(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); + + luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); // Grab colors int i, j; @@ -236,10 +230,11 @@ static int ws2812_buffer_fill(lua_State* L) { } static int ws2812_buffer_fade(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); const int fade = luaL_checkinteger(L, 2); unsigned direction = luaL_optinteger( L, 3, FADE_OUT ); + luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); luaL_argcheck(L, fade > 0, 2, "fade value should be a strict positive number"); uint8_t * p = &buffer->values[0]; @@ -265,25 +260,17 @@ static int ws2812_buffer_fade(lua_State* L) { static int ws2812_buffer_shift(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); const int shiftValue = luaL_checkinteger(L, 2); const unsigned shift_type = luaL_optinteger( L, 3, SHIFT_LOGICAL ); - ptrdiff_t start = posrelat(luaL_optinteger(L, 4, 1), buffer->size); - ptrdiff_t end = posrelat(luaL_optinteger(L, 5, -1), buffer->size); - if (start < 1) start = 1; - if (end > (ptrdiff_t)buffer->size) end = (ptrdiff_t)buffer->size; - - start--; - int size = end - start; - size_t offset = start * buffer->colorsPerLed; - - luaL_argcheck(L, shiftValue > 0-size && shiftValue < size, 2, "shifting more elements than buffer size"); + luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); + luaL_argcheck(L, shiftValue > 0-buffer->size && shiftValue < buffer->size, 2, "shifting more elements than buffer size"); int shift = shiftValue >= 0 ? shiftValue : -shiftValue; // check if we want to shift at all - if (shift == 0 || size <= 0) + if (shift == 0) { return 0; } @@ -293,38 +280,38 @@ static int ws2812_buffer_shift(lua_State* L) { size_t shift_len, remaining_len; // calculate length of shift section and remaining section shift_len = shift*buffer->colorsPerLed; - remaining_len = (size-shift)*buffer->colorsPerLed; + remaining_len = (buffer->size-shift)*buffer->colorsPerLed; if (shiftValue > 0) { // Store the values which are moved out of the array (last n pixels) - c_memcpy(tmp_pixels, &buffer->values[offset + (size-shift)*buffer->colorsPerLed], shift_len); + c_memcpy(tmp_pixels, &buffer->values[(buffer->size-shift)*buffer->colorsPerLed], shift_len); // Move pixels to end - os_memmove(&buffer->values[offset + shift*buffer->colorsPerLed], &buffer->values[offset], remaining_len); + os_memmove(&buffer->values[shift*buffer->colorsPerLed], &buffer->values[0], remaining_len); // Fill beginning with temp data if (shift_type == SHIFT_LOGICAL) { - c_memset(&buffer->values[offset], 0, shift_len); + c_memset(&buffer->values[0], 0, shift_len); } else { - c_memcpy(&buffer->values[offset], tmp_pixels, shift_len); + c_memcpy(&buffer->values[0], tmp_pixels, shift_len); } } else { // Store the values which are moved out of the array (last n pixels) - c_memcpy(tmp_pixels, &buffer->values[offset], shift_len); + c_memcpy(tmp_pixels, &buffer->values[0], shift_len); // Move pixels to end - os_memmove(&buffer->values[offset], &buffer->values[offset + shift*buffer->colorsPerLed], remaining_len); + os_memmove(&buffer->values[0], &buffer->values[shift*buffer->colorsPerLed], remaining_len); // Fill beginning with temp data if (shift_type == SHIFT_LOGICAL) { - c_memset(&buffer->values[offset + (size-shift)*buffer->colorsPerLed], 0, shift_len); + c_memset(&buffer->values[(buffer->size-shift)*buffer->colorsPerLed], 0, shift_len); } else { - c_memcpy(&buffer->values[offset + (size-shift)*buffer->colorsPerLed], tmp_pixels, shift_len); + c_memcpy(&buffer->values[(buffer->size-shift)*buffer->colorsPerLed], tmp_pixels, shift_len); } } // Free memory @@ -333,107 +320,13 @@ static int ws2812_buffer_shift(lua_State* L) { return 0; } -static int ws2812_buffer_dump(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); - lua_pushlstring(L, buffer->values, buffer->size * buffer->colorsPerLed); - - return 1; -} - -static int ws2812_buffer_replace(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); - size_t l = buffer->size; - ptrdiff_t start = posrelat(luaL_optinteger(L, 3, 1), l); - - uint8_t *src; - size_t srcLen; - - if (lua_type(L, 2) == LUA_TSTRING) { - size_t length; - - src = (uint8_t *) lua_tolstring(L, 2, &length); - srcLen = length / buffer->colorsPerLed; - } else { - ws2812_buffer * rhs = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer"); - src = rhs->values; - srcLen = rhs->size; - luaL_argcheck(L, rhs->colorsPerLed == buffer->colorsPerLed, 2, "Buffers have different colors"); - } - - luaL_argcheck(L, srcLen + start - 1 <= buffer->size, 2, "Does not fit into destination"); - - c_memcpy(buffer->values + (start - 1) * buffer->colorsPerLed, src, srcLen * buffer->colorsPerLed); - - return 0; -} - -// buffer:mix(factor1, buffer1, ..) -// factor is 256 for 100% -// uses saturating arithmetic (one buffer at a time) -static int ws2812_buffer_mix(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); - - int pos = 2; - size_t cells = buffer->size * buffer->colorsPerLed; - - int n_sources = (lua_gettop(L) - 1) / 2; - - struct { - int factor; - const uint8_t *values; - } source[n_sources]; - - int src; - for (src = 0; src < n_sources; src++, pos += 2) { - int factor = luaL_checkinteger(L, pos); - ws2812_buffer *src_buffer = (ws2812_buffer*) luaL_checkudata(L, pos + 1, "ws2812.buffer"); - - luaL_argcheck(L, src_buffer->size == buffer->size && src_buffer->colorsPerLed == buffer->colorsPerLed, pos + 1, "Buffer not same shape"); - - source[src].factor = factor; - source[src].values = src_buffer->values; - } - - size_t i; - for (i = 0; i < cells; i++) { - int val = 0; - for (src = 0; src < n_sources; src++) { - val += ((int)(source[src].values[i] * source[src].factor) >> 8); - } - - if (val < 0) { - val = 0; - } else if (val > 255) { - val = 255; - } - buffer->values[i] = val; - } - - return 0; -} - -// Returns the total of all channels -static int ws2812_buffer_power(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); - - size_t cells = buffer->size * buffer->colorsPerLed; - - size_t i; - int total = 0; - for (i = 0; i < cells; i++) { - total += buffer->values[i]; - } - - lua_pushnumber(L, total); - - return 1; -} static int ws2812_buffer_get(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); const int led = luaL_checkinteger(L, 2) - 1; + luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range"); int i; @@ -446,9 +339,10 @@ static int ws2812_buffer_get(lua_State* L) { } static int ws2812_buffer_set(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); const int led = luaL_checkinteger(L, 2) - 1; + luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range"); int type = lua_type(L, 3); @@ -493,94 +387,24 @@ static int ws2812_buffer_set(lua_State* L) { } static int ws2812_buffer_size(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); + + luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); lua_pushnumber(L, buffer->size); return 1; } -static int ws2812_buffer_sub(lua_State* L) { - ws2812_buffer * lhs = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); - size_t l = lhs->size; - ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l); - ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l); - if (start < 1) start = 1; - if (end > (ptrdiff_t)l) end = (ptrdiff_t)l; - if (start <= end) { - ws2812_buffer *result = allocate_buffer(L, end - start + 1, lhs->colorsPerLed); - c_memcpy(result->values, lhs->values + lhs->colorsPerLed * (start - 1), lhs->colorsPerLed * (end - start + 1)); - } else { - ws2812_buffer *result = allocate_buffer(L, 0, lhs->colorsPerLed); - } - return 1; -} - -static int ws2812_buffer_concat(lua_State* L) { - ws2812_buffer * lhs = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); - ws2812_buffer * rhs = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer"); - - luaL_argcheck(L, lhs->colorsPerLed == rhs->colorsPerLed, 1, "Can only concatenate buffers with same colors"); - - int colorsPerLed = lhs->colorsPerLed; - int leds = lhs->size + rhs->size; - - ws2812_buffer * buffer = allocate_buffer(L, leds, colorsPerLed); - - c_memcpy(buffer->values, lhs->values, lhs->colorsPerLed * lhs->size); - c_memcpy(buffer->values + lhs->colorsPerLed * lhs->size, rhs->values, rhs->colorsPerLed * rhs->size); - - return 1; -} - -static int ws2812_buffer_tostring(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); - - luaL_Buffer result; - luaL_buffinit(L, &result); - - luaL_addchar(&result, '['); - int i; - int p = 0; - for (i = 0; i < buffer->size; i++) { - int j; - if (i > 0) { - luaL_addchar(&result, ','); - } - luaL_addchar(&result, '('); - for (j = 0; j < buffer->colorsPerLed; j++, p++) { - if (j > 0) { - luaL_addchar(&result, ','); - } - char numbuf[5]; - c_sprintf(numbuf, "%d", buffer->values[p]); - luaL_addstring(&result, numbuf); - } - luaL_addchar(&result, ')'); - } - - luaL_addchar(&result, ']'); - luaL_pushresult(&result); - - return 1; -} - static const LUA_REG_TYPE ws2812_buffer_map[] = { - { LSTRKEY( "dump" ), LFUNCVAL( ws2812_buffer_dump )}, { LSTRKEY( "fade" ), LFUNCVAL( ws2812_buffer_fade )}, { LSTRKEY( "fill" ), LFUNCVAL( ws2812_buffer_fill )}, { LSTRKEY( "get" ), LFUNCVAL( ws2812_buffer_get )}, - { LSTRKEY( "replace" ), LFUNCVAL( ws2812_buffer_replace )}, - { LSTRKEY( "mix" ), LFUNCVAL( ws2812_buffer_mix )}, - { LSTRKEY( "power" ), LFUNCVAL( ws2812_buffer_power )}, { LSTRKEY( "set" ), LFUNCVAL( ws2812_buffer_set )}, - { LSTRKEY( "shift" ), LFUNCVAL( ws2812_buffer_shift )}, { LSTRKEY( "size" ), LFUNCVAL( ws2812_buffer_size )}, - { LSTRKEY( "sub" ), LFUNCVAL( ws2812_buffer_sub )}, - { LSTRKEY( "__concat" ),LFUNCVAL( ws2812_buffer_concat )}, + { LSTRKEY( "shift" ), LFUNCVAL( ws2812_buffer_shift )}, { LSTRKEY( "__index" ), LROVAL( ws2812_buffer_map )}, - { LSTRKEY( "__tostring" ), LFUNCVAL( ws2812_buffer_tostring )}, { LNILKEY, LNILVAL} }; diff --git a/app/net/nodemcu_mdns.c b/app/net/nodemcu_mdns.c index ea743888..3e434e16 100644 --- a/app/net/nodemcu_mdns.c +++ b/app/net/nodemcu_mdns.c @@ -52,7 +52,7 @@ #include "nodemcu_mdns.h" #if 0 -#define MDNS_DBG(...) dbg_printf(...) +#define MDNS_DBG(...) os_printf(...) #else #define MDNS_DBG(...) do {} while (0) #endif diff --git a/app/platform/vfs.c b/app/platform/vfs.c index 0446a11b..9baad28d 100644 --- a/app/platform/vfs.c +++ b/app/platform/vfs.c @@ -355,12 +355,11 @@ sint32_t vfs_chdir( const char *path ) sint32_t vfs_errno( const char *name ) { vfs_fs_fns *fs_fns; + const char *normname = normalize_path( name ); char *outname; if (!name) name = ""; // current drive - const char *normname = normalize_path( name ); - #ifdef BUILD_SPIFFS if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) { return fs_fns->ferrno( ); diff --git a/app/platform/vfs.h b/app/platform/vfs.h index 572d0372..5a468255 100644 --- a/app/platform/vfs.h +++ b/app/platform/vfs.h @@ -85,7 +85,7 @@ inline sint32_t vfs_flush( int fd ) { // Returns: File size inline uint32_t vfs_size( int fd ) { vfs_file *f = (vfs_file *)fd; - return f ? f->fns->size( f ) : 0; + return f && f->fns->size ? f->fns->size( f ) : 0; } // vfs_ferrno - get file system specific errno diff --git a/app/spiffs/spiffs.c b/app/spiffs/spiffs.c index 3b71cc03..1a6a465f 100644 --- a/app/spiffs/spiffs.c +++ b/app/spiffs/spiffs.c @@ -2,8 +2,6 @@ #include "platform.h" #include "spiffs.h" -#include "spiffs_nucleus.h" - spiffs fs; #define LOG_PAGE_SIZE 256 @@ -12,9 +10,9 @@ spiffs fs; #define MIN_BLOCKS_FS 4 static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2]; -static u8_t spiffs_fds[sizeof(spiffs_fd) * SPIFFS_MAX_OPEN_FILES]; +static u8_t spiffs_fds[32*4]; #if SPIFFS_CACHE -static u8_t myspiffs_cache[(LOG_PAGE_SIZE+32)*2]; +static u8_t spiffs_cache[(LOG_PAGE_SIZE+32)*2]; #endif static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) { @@ -170,8 +168,8 @@ static bool myspiffs_mount_internal(bool force_mount) { spiffs_fds, sizeof(spiffs_fds), #if SPIFFS_CACHE - myspiffs_cache, - sizeof(myspiffs_cache), + spiffs_cache, + sizeof(spiffs_cache), #else 0, 0, #endif @@ -245,7 +243,6 @@ static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int static sint32_t myspiffs_vfs_eof( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_tell( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_flush( const struct vfs_file *fd ); -static uint32_t myspiffs_vfs_size( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_closedir( const struct vfs_dir *dd ); @@ -298,7 +295,7 @@ static vfs_file_fns myspiffs_file_fns = { .eof = myspiffs_vfs_eof, .tell = myspiffs_vfs_tell, .flush = myspiffs_vfs_flush, - .size = myspiffs_vfs_size, + .size = NULL, .ferrno = myspiffs_vfs_ferrno }; @@ -433,17 +430,13 @@ static sint32_t myspiffs_vfs_close( const struct vfs_file *fd ) { static sint32_t myspiffs_vfs_read( const struct vfs_file *fd, void *ptr, size_t len ) { GET_FILE_FH(fd); - sint32_t n = SPIFFS_read( &fs, fh, ptr, len ); - - return n >= 0 ? n : VFS_RES_ERR; + return SPIFFS_read( &fs, fh, ptr, len ); } static sint32_t myspiffs_vfs_write( const struct vfs_file *fd, const void *ptr, size_t len ) { GET_FILE_FH(fd); - sint32_t n = SPIFFS_write( &fs, fh, (void *)ptr, len ); - - return n >= 0 ? n : VFS_RES_ERR; + return SPIFFS_write( &fs, fh, (void *)ptr, len ); } static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int whence ) { @@ -463,8 +456,7 @@ static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int break; } - sint32_t res = SPIFFS_lseek( &fs, fh, off, spiffs_whence ); - return res >= 0 ? res : VFS_RES_ERR; + return SPIFFS_lseek( &fs, fh, off, spiffs_whence ); } static sint32_t myspiffs_vfs_eof( const struct vfs_file *fd ) { @@ -485,16 +477,6 @@ static sint32_t myspiffs_vfs_flush( const struct vfs_file *fd ) { return SPIFFS_fflush( &fs, fh ) >= 0 ? VFS_RES_OK : VFS_RES_ERR; } -static uint32_t myspiffs_vfs_size( const struct vfs_file *fd ) { - GET_FILE_FH(fd); - - int32_t curpos = SPIFFS_tell( &fs, fh ); - int32_t size = SPIFFS_lseek( &fs, fh, 0, SPIFFS_SEEK_END ); - (void) SPIFFS_lseek( &fs, fh, curpos, SPIFFS_SEEK_SET ); - - return size; -} - static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd ) { return SPIFFS_errno( &fs ); } diff --git a/app/spiffs/spiffs_config.h b/app/spiffs/spiffs_config.h index 40394a3c..d4e161c1 100644 --- a/app/spiffs/spiffs_config.h +++ b/app/spiffs/spiffs_config.h @@ -20,19 +20,19 @@ // Set generic spiffs debug output call. #ifndef SPIFFS_DBG -#define SPIFFS_DBG(...) //dbg_printf(__VA_ARGS__) +#define SPIFFS_DBG(...) //printf(__VA_ARGS__) #endif // Set spiffs debug output call for garbage collecting. #ifndef SPIFFS_GC_DBG -#define SPIFFS_GC_DBG(...) //dbg_printf(__VA_ARGS__) +#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__) #endif // Set spiffs debug output call for caching. #ifndef SPIFFS_CACHE_DBG -#define SPIFFS_CACHE_DBG(...) //dbg_printf(__VA_ARGS__) +#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__) #endif // Set spiffs debug output call for system consistency checks. #ifndef SPIFFS_CHECK_DBG -#define SPIFFS_CHECK_DBG(...) //dbg_printf(__VA_ARGS__) +#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__) #endif // Enable/disable API functions to determine exact number of bytes @@ -211,7 +211,7 @@ #endif #if SPIFFS_TEST_VISUALISATION #ifndef spiffs_printf -#define spiffs_printf(...) dbg_printf(__VA_ARGS__) +#define spiffs_printf(...) printf(__VA_ARGS__) #endif // spiffs_printf argument for a free page #ifndef SPIFFS_TEST_VIS_FREE_STR diff --git a/app/spiffs/spiffs_nucleus.c b/app/spiffs/spiffs_nucleus.c index 6c6b61d7..b4d32e95 100644 --- a/app/spiffs/spiffs_nucleus.c +++ b/app/spiffs/spiffs_nucleus.c @@ -292,60 +292,27 @@ s32_t spiffs_probe( SPIFFS_CHECK_RES(res); } + // check that we have sane number of blocks + if (bix_count[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS; // check that the order is correct, take aborted erases in calculation - // Note that bix_count[0] should be blockcnt, [1] should be blockcnt - 1 - // and [2] should be blockcnt - 3 // first block aborted erase - int fs_size; - if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 2) { - fs_size = bix_count[1]+1; - } else + if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) { + return (bix_count[1]+1) * cfg->log_block_size; + } // second block aborted erase - if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 3) { - fs_size = bix_count[0]; - } else + if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) { + return bix_count[0] * cfg->log_block_size; + } // third block aborted erase if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) { - fs_size = bix_count[0]; - } else + return bix_count[0] * cfg->log_block_size; + } // no block has aborted erase - if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 2) { - fs_size = bix_count[0]; - } else { - return SPIFFS_ERR_PROBE_NOT_A_FS; + if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) { + return bix_count[0] * cfg->log_block_size; } - // check that we have sane number of blocks - if (fs_size < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS; - - dummy_fs.block_count = fs_size; - - // Now verify that there is at least one good block at the end - for (bix = fs_size - 1; bix >= 3; bix--) { - spiffs_obj_id end_magic; - paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix); -#if SPIFFS_HAL_CALLBACK_EXTRA - // not any proper fs to report here, so callback with null - // (cross fingers that no-one gets angry) - res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&end_magic); -#else - res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&end_magic); -#endif - if (res < 0) { - return SPIFFS_ERR_PROBE_NOT_A_FS; - } - if (end_magic == (spiffs_obj_id)(-1)) { - if (bix < fs_size - 1) { - return SPIFFS_ERR_PROBE_NOT_A_FS; - } - } else if (end_magic != SPIFFS_MAGIC(&dummy_fs, bix)) { - return SPIFFS_ERR_PROBE_NOT_A_FS; - } else { - break; - } - } - - return fs_size * cfg->log_block_size; + return SPIFFS_ERR_PROBE_NOT_A_FS; } #endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 diff --git a/app/spiffs/spiffs_nucleus.h b/app/spiffs/spiffs_nucleus.h index f6acaacf..df7a9d8e 100644 --- a/app/spiffs/spiffs_nucleus.h +++ b/app/spiffs/spiffs_nucleus.h @@ -137,7 +137,7 @@ ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) #else // SPIFFS_USE_MAGIC_LENGTH #define SPIFFS_MAGIC(fs, bix) \ - ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - ((bix) < 3 ? (1<<(bix)) - 1 : (bix)<<2)))) + ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix)))) #endif // SPIFFS_USE_MAGIC_LENGTH #endif // SPIFFS_USE_MAGIC diff --git a/app/u8glib/u8g.h b/app/u8glib/u8g.h index c1d6e02b..6103ffdc 100644 --- a/app/u8glib/u8g.h +++ b/app/u8glib/u8g.h @@ -1178,6 +1178,9 @@ struct _u8g_t u8g_state_cb state_cb; u8g_box_t current_page; /* current box of the visible page */ + + uint8_t i2c_addr; + uint8_t use_delay; }; #define u8g_GetFontAscent(u8g) ((u8g)->font_ref_ascent) diff --git a/app/user/Makefile b/app/user/Makefile index 87f4be55..50da583a 100644 --- a/app/user/Makefile +++ b/app/user/Makefile @@ -24,7 +24,7 @@ STD_CFLAGS=-std=gnu11 -Wimplicit # makefile at its root level - these are then overridden # for a subtree within the makefile rooted therein # -DEFINES += -DESP_INIT_DATA_DEFAULT="\"$(SDK_DIR)/bin/esp_init_data_default.bin\"" +#DEFINES += ############################################################# # Recursion Magic - Don't touch this!! diff --git a/app/user/user_main.c b/app/user/user_main.c index 320969b5..3c0189b4 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -29,21 +29,11 @@ #include "rtc/rtctime.h" #endif -static task_handle_t input_sig; -static uint8 input_sig_flag = 0; +#define SIG_LUA 0 +#define SIG_UARTINPUT 1 +#define TASK_QUEUE_LEN 4 -/* Contents of esp_init_data_default.bin */ -extern const uint32_t init_data[]; -extern const uint32_t init_data_end[]; -__asm__( - /* Place in .text for same reason as user_start_trampoline */ - ".section \".text\"\n" - ".align 4\n" - "init_data:\n" - ".incbin \"" ESP_INIT_DATA_DEFAULT "\"\n" - "init_data_end:\n" - ".previous\n" -); +static os_event_t *taskQueue; /* Note: the trampoline *must* be explicitly put into the .text segment, since * by the time it is invoked the irom has not yet been mapped. This naturally @@ -60,31 +50,6 @@ void TEXT_SECTION_ATTR user_start_trampoline (void) rtctime_early_startup (); #endif - /* Re-implementation of default init data deployment. The SDK does not - * appear to be laying down its own version of init data anymore, so - * we have to do it again. To see whether we need to, we read out - * the flash size and do a test for esp_init_data based on that size. - * If it's missing, we need to initialize it *right now* before the SDK - * starts up and gets stuck at "rf_cal[0] !=0x05,is 0xFF". - * If the size byte is wrong, then we'll end up fixing up the init data - * again on the next boot, after we've corrected the size byte. - * Only remaining issue is lack of spare code bytes in iram, so this - * is deliberately quite terse and not as readable as one might like. - */ - SPIFlashInfo sfi; - SPIRead (0, (uint32_t *)(&sfi), sizeof (sfi)); // Cache read not enabled yet, safe to use - if (sfi.size < 2) // Compensate for out-of-order 4mbit vs 2mbit values - sfi.size ^= 1; - uint32_t flash_end_addr = (256 * 1024) << sfi.size; - uint32_t init_data_hdr = 0xffffffff; - uint32_t init_data_addr = flash_end_addr - 4 * SPI_FLASH_SEC_SIZE; - SPIRead (init_data_addr, &init_data_hdr, sizeof (init_data_hdr)); - if (init_data_hdr == 0xffffffff) - { - SPIEraseSector (init_data_addr); - SPIWrite (init_data_addr, init_data, 4 * (init_data_end - init_data)); - } - call_user_start (); } @@ -93,18 +58,17 @@ static void start_lua(task_param_t param, uint8 priority) { char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL }; NODE_DBG("Task task_lua started.\n"); lua_main( 2, lua_argv ); - // Only enable UART interrupts once we've successfully started up, - // otherwise the task queue might fill up with input events and prevent - // the start_lua task from being posted. - ETS_UART_INTR_ENABLE(); } static void handle_input(task_param_t flag, uint8 priority) { - (void)priority; - if (flag & 0x8000) { - input_sig_flag = flag & 0x4000 ? 1 : 0; - } - lua_handle_input (flag & 0x01); +// c_printf("HANDLE_INPUT: %u %u\n", flag, priority); REMOVE + lua_handle_input (flag); +} + +static task_handle_t input_sig; + +task_handle_t user_get_input_sig(void) { + return input_sig; } bool user_process_input(bool force) { @@ -128,7 +92,9 @@ void nodemcu_init(void) // Fit hardware real flash size. flash_rom_set_size_byte(flash_safe_get_size_byte()); + // Reboot to get SDK to use (or write) init data at new location system_restart (); + // Don't post the start_lua task, we're about to reboot... return; } @@ -141,7 +107,7 @@ void nodemcu_init(void) #ifdef BUILD_SPIFFS if (!vfs_mount("/FLASH", 0)) { // Failed to mount -- try reformat - dbg_printf("Formatting file system. Please wait...\n"); + c_printf("Formatting file system. Please wait...\n"); if (!vfs_format()) { NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" ); NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" ); @@ -152,8 +118,7 @@ void nodemcu_init(void) #endif // endpoint_setup(); - if (!task_post_low(task_get_id(start_lua),'s')) - NODE_ERR("Failed to post the start_lua task!\n"); + task_post_low(task_get_id(start_lua),'s'); } #ifdef LUA_USE_MODULES_WIFI @@ -181,7 +146,7 @@ void user_rf_pre_init(void) uint32 user_rf_cal_sector_set(void) { - enum ext_flash_size_map size_map = system_get_flash_size_map(); + enum flash_size_map size_map = system_get_flash_size_map(); uint32 rf_cal_sec = 0; switch (size_map) { @@ -200,18 +165,9 @@ user_rf_cal_sector_set(void) case FLASH_SIZE_32M_MAP_512_512: case FLASH_SIZE_32M_MAP_1024_1024: - case FLASH_SIZE_32M_MAP_2048_2048: rf_cal_sec = 1024 - 5; break; - case FLASH_SIZE_64M_MAP: - rf_cal_sec = 2048 - 5; - break; - - case FLASH_SIZE_128M_MAP: - rf_cal_sec = 4096 - 5; - break; - default: rf_cal_sec = 0; break; @@ -235,7 +191,7 @@ void user_init(void) UartBautRate br = BIT_RATE_DEFAULT; input_sig = task_get_id(handle_input); - uart_init (br, br, input_sig, &input_sig_flag); + uart_init (br, br, input_sig); #ifndef NODE_DEBUG system_set_os_print(0); diff --git a/app/websocket/websocketclient.c b/app/websocket/websocketclient.c index 834996e7..d9fa29ce 100644 --- a/app/websocket/websocketclient.c +++ b/app/websocket/websocketclient.c @@ -47,10 +47,18 @@ #define PORT_INSECURE 80 #define PORT_MAX_VALUE 65535 -#define WS_INIT_REQUEST "GET %s HTTP/1.1\r\n"\ - "Host: %s:%d\r\n" +// TODO: user agent configurable +#define WS_INIT_HEADERS "GET %s HTTP/1.1\r\n"\ + "Host: %s:%d\r\n"\ + "Upgrade: websocket\r\n"\ + "Connection: Upgrade\r\n"\ + "User-Agent: ESP8266\r\n"\ + "Sec-Websocket-Key: %s\r\n"\ + "Sec-WebSocket-Protocol: chat\r\n"\ + "Sec-WebSocket-Version: 13\r\n"\ + "\r\n" -#define WS_INIT_REQUEST_LENGTH 30 +#define WS_INIT_HEADERS_LENGTH 169 #define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" #define WS_GUID_LENGTH 36 @@ -69,13 +77,6 @@ #define WS_OPCODE_PING 0x9 #define WS_OPCODE_PONG 0xA -header_t DEFAULT_HEADERS[] = { - {"User-Agent", "ESP8266"}, - {"Sec-WebSocket-Protocol", "chat"}, - {0} -}; -header_t *EMPTY_HEADERS = DEFAULT_HEADERS + sizeof(DEFAULT_HEADERS) / sizeof(header_t) - 1; - static char *cryptoSha1(char *data, unsigned int len) { SHA1_CTX ctx; SHA1Init(&ctx); @@ -127,44 +128,6 @@ static void generateSecKeys(char **key, char **expectedKey) { os_free(keyEncrypted); } -static char *_strcpy(char *dst, char *src) { - while(*dst++ = *src++); - return dst - 1; -} - -static int headers_length(header_t *headers) { - int length = 0; - for(; headers->key; headers++) - length += strlen(headers->key) + strlen(headers->value) + 4; - return length; -} - -static char *sprintf_headers(char *buf, ...) { - char *dst = buf; - va_list args; - va_start(args, buf); - for(header_t *header_set = va_arg(args, header_t *); header_set; header_set = va_arg(args, header_t *)) - for(header_t *header = header_set; header->key; header++) { - va_list args2; - va_start(args2, buf); - for(header_t *header_set2 = va_arg(args2, header_t *); header_set2; header_set2 = va_arg(args2, header_t *)) - for(header_t *header2 = header_set2; header2->key; header2++) { - if(header == header2) - goto ok; - if(!strcasecmp(header->key, header2->key)) - goto skip; - } -ok: - dst = _strcpy(dst, header->key); - dst = _strcpy(dst, ": "); - dst = _strcpy(dst, header->value); - dst = _strcpy(dst, "\r\n"); -skip:; - } - dst = _strcpy(dst, "\r\n"); - return dst; -} - static void ws_closeSentCallback(void *arg) { NODE_DBG("ws_closeSentCallback \n"); struct espconn *conn = (struct espconn *) arg; @@ -489,7 +452,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) { } else if (opCode == WS_OPCODE_PONG) { // ping alarm was already reset... } else { - if (ws->onReceive) ws->onReceive(ws, payloadLength, payload, opCode); + if (ws->onReceive) ws->onReceive(ws, payload, opCode); } os_free(payload); } @@ -546,7 +509,7 @@ static void ws_initReceiveCallback(void *arg, char *buf, unsigned short len) { } // Check server has valid sec key - if (strstr(buf, ws->expectedSecKey) == NULL) { + if (strstr(buf, WS_HTTP_SEC_WEBSOCKET_ACCEPT) == NULL || strstr(buf, ws->expectedSecKey) == NULL) { NODE_DBG("Server has invalid response\n"); ws->knownFailureCode = -7; if (ws->isSecure) @@ -587,31 +550,12 @@ static void connect_callback(void *arg) { char *key; generateSecKeys(&key, &ws->expectedSecKey); - header_t headers[] = { - {"Upgrade", "websocket"}, - {"Connection", "Upgrade"}, - {"Sec-WebSocket-Key", key}, - {"Sec-WebSocket-Version", "13"}, - {0} - }; - - header_t *extraHeaders = ws->extraHeaders ? ws->extraHeaders : EMPTY_HEADERS; - - char buf[WS_INIT_REQUEST_LENGTH + strlen(ws->path) + strlen(ws->hostname) + - headers_length(DEFAULT_HEADERS) + headers_length(headers) + headers_length(extraHeaders) + 2]; - - int len = os_sprintf( - buf, - WS_INIT_REQUEST, - ws->path, - ws->hostname, - ws->port - ); - - len = sprintf_headers(buf + len, headers, extraHeaders, DEFAULT_HEADERS, 0) - buf; + char buf[WS_INIT_HEADERS_LENGTH + strlen(ws->path) + strlen(ws->hostname) + strlen(key)]; + int len = os_sprintf(buf, WS_INIT_HEADERS, ws->path, ws->hostname, ws->port, key); os_free(key); - NODE_DBG("request: %s", buf); + + NODE_DBG("connecting\n"); if (ws->isSecure) espconn_secure_send(conn, (uint8_t *) buf, len); else @@ -686,7 +630,7 @@ static void dns_callback(const char *hostname, ip_addr_t *addr, void *arg) { ws_info *ws = (ws_info *) conn->reverse; if (ws->conn == NULL || ws->connectionState == 4) { - return; + return; } if (addr == NULL) { diff --git a/app/websocket/websocketclient.h b/app/websocket/websocketclient.h index 0c029418..35746b48 100644 --- a/app/websocket/websocketclient.h +++ b/app/websocket/websocketclient.h @@ -40,14 +40,9 @@ struct ws_info; typedef void (*ws_onConnectionCallback)(struct ws_info *wsInfo); -typedef void (*ws_onReceiveCallback)(struct ws_info *wsInfo, int len, char *message, int opCode); +typedef void (*ws_onReceiveCallback)(struct ws_info *wsInfo, char *message, int opCode); typedef void (*ws_onFailureCallback)(struct ws_info *wsInfo, int errorCode); -typedef struct { - char *key; - char *value; -} header_t; - typedef struct ws_info { int connectionState; @@ -56,7 +51,6 @@ typedef struct ws_info { int port; char *path; char *expectedSecKey; - header_t *extraHeaders; struct espconn *conn; void *reservedData; diff --git a/docs/en/flash.md b/docs/en/flash.md index 3a5f6560..21771f63 100644 --- a/docs/en/flash.md +++ b/docs/en/flash.md @@ -1,20 +1,13 @@ -Below you'll find all necessary information to flash a NodeMCU firmware binary to ESP8266 or ESP8285. Note that this is a reference documentation and not a tutorial with fancy screen shots. Turn to your favorite search engine for those. Make sure you follow a recent tutorial rather than one that is several months old! +Adafruit provides a really nice [firmware flashing tutorial](https://learn.adafruit.com/building-and-running-micropython-on-the-esp8266/flash-firmware). Below you'll find just the basics for the two popular tools esptool and NodeMCU Flasher. !!! attention Keep in mind that the ESP8266 needs to be [put into flash mode](#putting-device-into-flash-mode) before you can flash a new firmware! -!!! important +## esptool.py +> A cute Python utility to communicate with the ROM bootloader in Espressif ESP8266. It is intended to be a simple, platform independent, open source replacement for XTCOM. - When switching between NodeMCU versions, see the notes about - [Upgrading Firmware](#upgrading-firmware). - -## Tool overview - -### esptool.py -> A Python-based, open source, platform independent, utility to communicate with the ROM bootloader in Espressif ESP8266. - -Source: [https://github.com/espressif/esptool](https://github.com/espressif/esptool) +Source: [https://github.com/themadinventor/esptool](https://github.com/themadinventor/esptool) Supported platforms: OS X, Linux, Windows, anything that runs Python @@ -22,33 +15,27 @@ Supported platforms: OS X, Linux, Windows, anything that runs Python Run the following command to flash an *aggregated* binary as is produced for example by the [cloud build service](build.md#cloud-build-service) or the [Docker image](build.md#docker-image). -`esptool.py --port write_flash -fm 0x00000 .bin` +`esptool.py --port write_flash -fm -fs 0x00000 .bin` -`mode` is `qio` for 512 kByte modules and `dio` for >=4 MByte modules (`qio` might work as well, YMMV). +- `mode` is `qio` for 512 kByte modules and `dio` for 4 MByte modules (`qio` might work as well, YMMV). +- `size` is given in bits. Specify `4m` for 512 kByte and `32m` for 4 MByte. -**Gotchas** +Check the [esptool flash modes documentation](https://github.com/themadinventor/esptool#flash-modes) for details and other options. -- See [below](#determine-flash-size) if you don't know or are uncertain about the capacity of the flash chip on your device. It might help to double check as e.g. some ESP-01 modules come with 512kB while others are equipped with 1MB. -- esptool.py is under heavy development. It's advised you run the latest version (check with `esptool.py version`). Since this documentation may not have been able to keep up refer to the [esptool flash modes documentation](https://github.com/themadinventor/esptool#flash-modes) for current options and parameters. -- In some uncommon cases, the [SDK init data](#sdk-init-data) may be invalid and NodeMCU may fail to boot. The easiest solution is to fully erase the chip before flashing: -`esptool.py --port erase_flash` - -### NodeMCU Flasher +## NodeMCU Flasher > A firmware Flash tool for NodeMCU...We are working on next version and will use QT framework. It will be cross platform and open-source. Source: [https://github.com/nodemcu/nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher) Supported platforms: Windows -Note that this tool was created by the initial developers of the NodeMCU firmware. It hasn't seen updates since September 2015 and is not maintained by the current NodeMCU *firmware* team. Be careful to not accidentally flash the very old default firmware the tool is shipped with. - ## Putting Device Into Flash Mode To enable ESP8266 firmware flashing GPIO0 pin must be pulled low before the device is reset. Conversely, for a normal boot, GPIO0 must be pulled high or floating. If you have a [NodeMCU dev kit](https://github.com/nodemcu/nodemcu-devkit-v1.0) then you don't need to do anything, as the USB connection can pull GPIO0 low by asserting DTR and reset your board by asserting RTS. -If you have an ESP-01 or other device without built-in USB, you will need to enable flashing yourself by pulling GPIO0 low or pressing a "flash" switch, while powering up or resetting the module. +If you have an ESP-01 or other device without built-in USB, you will need to enable flashing yourself by pulling GPIO0 low or pressing a "flash" switch. ## Which Files To Flash @@ -59,59 +46,55 @@ Otherwise, if you built your own firmware from source code: - `bin/0x00000.bin` to 0x00000 - `bin/0x10000.bin` to 0x10000 +Also, in some special circumstances, you may need to flash `blank.bin` or `esp_init_data_default.bin` to various addresses on the flash (depending on flash size and type), see [below](#upgrading-from-sdk-09x-firmware). + ## Upgrading Firmware -There are three potential issues that arise from upgrading (or downgrading!) firmware from one NodeMCU version to another: +!!! important -* Lua scripts written for one NodeMCU version (like 0.9.x) may not work error-free on a more recent firmware. For example, Espressif changed the `socket:send` operation to be asynchronous i.e. non-blocking. See [API documentation](modules/net.md#netsocketsend) for details. + It goes without saying that you shouldn't expect your NodeMCU 0.9.x Lua scripts to work error-free on a more recent firmware. Most notably Espressif changed the `socket:send` operation to be asynchronous i.e. non-blocking. See [API documentation](modules/net.md#netsocketsend) for details. -* The NodeMCU flash file system may need to be reformatted, particularly if its address has changed because the new firmware is different in size from the old firmware. If it is not automatically formatted then it should be valid and have the same contents as before the flash operation. You can still run [`file.format()`](modules/file.md#fileformat) manually to re-format your flash file system. You will know if you need to do this if your flash files exist but seem empty, or if data cannot be written to new files. However, this should be an exceptional case. -Formatting a file system on a large flash device (e.g. the 16MB parts) can take some time. So, on the first boot, you shouldn't get worried if nothing appears to happen for a minute. There's a message printed to console to make you aware of this. +Espressif changes the init data block (`esp_init_data_default.bin`) for their devices along the way with the SDK. So things break when a NodeMCU firmware with a certain SDK is flashed to a module which contains init data from a different SDK. Hence, this section applies to upgrading NodeMCU firmware just as well as *downgrading* firmware. -* The Espressif SDK Init Data may change between each NodeMCU firmware version, and may need to be erased or reflashed. See [SDK Init Data](#sdk-init-data) for details. Fully erasing the module before upgrading firmware will avoid this issue. +A typical case that often fails is when a module is upgraded from a 0.9.x firmware to a recent version. It might look like the new firmware is broken, but the reason for the missing Lua prompt is related to the big jump in SDK versions. -## SDK Init Data +If there is no init data block found during SDK startup, the SDK will install one itself. If there is a previous (potentially too old) init block, the SDK *probably* doesn't do anything with it but there is no documentation from Espressif on this topic. -!!! note +Hence, there are two strategies to update the SDK init data: - Normally, NodeMCU will take care of writing the SDK init data when needed. Most users can ignore this section. +- Erase flash completely. This will also erase the (Lua) files you uploaded to the device! The SDK will install the init data block during startup. +- Don't erase the flash but replace just the init data with a new file during the flashing procedure. For this you would download [SDK patch 1.5.4.1](http://bbs.espressif.com/download/file.php?id=1572) and extract `esp_init_data_default.bin` from there. -NodeMCU versions are compiled against specific versions of the Espressif SDK. The SDK reserves space in flash that is used to store calibration and other data. This data changes between SDK versions, and if it is invalid or not present, the firmware may not boot correctly. Symptoms include messages like `rf_cal[0] !=0x05,is 0xFF`, or endless reboot loops and/or fast blinking module LEDs. +When flashing a new firmware (particularly with a much different size), the flash filesystem may be reformatted as the firmware starts. If it is not automatically reformatted, then it should be valid and have the same contents as before the flash operation. You can still run [`file.format()`](modules/file.md#fileformat) to re-format your flash filesystem. You will know if you need to do this if your flash files exist but seem empty, or if data cannot be written to new files. However, this should be an exceptional case. -!!! tip +**esptool.py** - If you are seeing one or several of the above symptoms, ensure that your chip is fully erased before flashing, for example: - - `esptool.py --port erase_flash` - - Also verify that you are using an up-to-date NodeMCU release, as some early releases of NodeMCU 1.5.4.1 did not write the SDK init data to a freshly erased chip. - -Espressif refers to this area as "System Param" and it resides in the last four 4 kB sectors of flash. Since SDK 1.5.4.1 a fifth sector is reserved for RF calibration (and its placement is controlled by NodeMCU) as described by this [patch notice](http://bbs.espressif.com/viewtopic.php?f=46&t=2407). At minimum, Espressif states that the 4th sector from the end needs to be flashed with "init data", and the 2nd sector from the end should be blank. - -The default init data is provided as part of the SDK in the file `esp_init_data_default.bin`. NodeMCU will automatically flash this file to the right place on first boot if the sector appears to be empty. - -If you need to customize init data then first download the [Espressif SDK patch 1.5.4.1](http://bbs.espressif.com/download/file.php?id=1572) and extract `esp_init_data_default.bin`. Then flash that file just like you'd flash the firmware. The correct address for the init data depends on the capacity of the flash chip. - -- `0x7c000` for 512 kB, modules like most ESP-01, -03, -07 etc. -- `0xfc000` for 1 MB, modules like ESP8285, PSF-A85, some ESP-01, -03 etc. -- `0x1fc000` for 2 MB -- `0x3fc000` for 4 MB, modules like ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini - -See "4.1 Non-FOTA Flash Map" and "6.3 RF Initialization Configuration" of the [ESP8266 Getting Started Guide](https://espressif.com/en/support/explore/get-started/esp8266/getting-started-guide) for details on init data addresses and customization. - -## Determine flash size - -To determine the capacity of the flash chip *before* a firmware is installed you can run - -`esptool.py --port flash_id` - -It will return a manufacturer ID and a chip ID like so: +For [esptool.py](https://github.com/themadinventor/esptool) you specify the init data file as an additional file for the `write_flash` command. ``` -Connecting... -Manufacturer: e0 -Device: 4016 +esptool.py --port erase_flash +esptool.py --port write_flash 0x00000 .bin esp_init_data_default.bin ``` -The chip ID can then be looked up in [https://code.coreboot.org/p/flashrom/source/tree/HEAD/trunk/flashchips.h](https://code.coreboot.org/p/flashrom/source/tree/HEAD/trunk/flashchips.h). This leads to a manufacturer name and a chip model name/number e.g. `AMIC_A25LQ032`. That information can then be fed into your favorite search engine to find chip descriptions and data sheets. -By convention the last two or three digits in the module name denote the capacity in megabits. So, `A25LQ032` in the example above is a 32Mb(=4MB) module. +!!! note "Note:" + + The address for `esp_init_data_default.bin` depends on the size of your module's flash. + + - `0x7c000` for 512 kB, modules like ESP-01, -03, -07 etc. + - `0xfc000` for 1 MB, modules like ESP8285, PSF-A85 + - `0x1fc000` for 2 MB + - `0x3fc000` for 4 MB, modules like ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini + +**NodeMCU Flasher** + +The [NodeMCU Flasher](https://github.com/nodemcu/nodemcu-flasher) will download init data using a special path: +``` +INTERNAL://DEFAULT +``` + +Replace the provided (old) `esp_init_data_default.bin` with the one extracted above and use the flasher like you're used to. + +**References** + +* [2A-ESP8266__IOT_SDK_User_Manual__EN_v1.5.pdf, Chapter 6](http://bbs.espressif.com/viewtopic.php?f=51&t=1024) +* [SPI Flash ROM Layout (without OTA upgrades)](https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map#spi-flash-rom-layout-without-ota-upgrades) diff --git a/docs/en/index.md b/docs/en/index.md index 8000df91..c8731c50 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -44,7 +44,7 @@ print(gpio.read(pin)) ``` ## Getting Started -1. [Build the firmware](build.md) with the modules you need. +1. [Build the firmeware](build.md) with the modules you need. 1. [Flash the firmware](flash.md) to the chip. 1. [Upload code](upload.md) to the firmware. diff --git a/docs/en/modules/enduser-setup.md b/docs/en/modules/enduser-setup.md index ade57594..704d1dfc 100644 --- a/docs/en/modules/enduser-setup.md +++ b/docs/en/modules/enduser-setup.md @@ -7,29 +7,10 @@ This module provides a simple way of configuring ESP8266 chips without using a s ![enduser setup config dialog](../../img/enduser-setup.jpg "enduser setup config dialog") -After running [`enduser_setup.start()`](#enduser_setupstart), a wireless network named "SetupGadget_XXXXXX" will start. Connect to that SSID and then navigate to the root -of any website (e.g., `http://example.com/` will work, but do not use `.local` domains because it will fail on iOS). A web page similar to the picture above will load, allowing the -end user to provide their Wi-Fi information. - -After an IP address has been successfully obtained, then this module will stop as if [`enduser_setup.stop()`](#enduser_setupstop) had been called. There is a 10-second delay before -teardown to allow connected clients to obtain a last status message while the SoftAP is still active. - -Alternative HTML can be served by placing a file called `enduser_setup.html` on the filesystem. Everything needed by the web page must be included in this one file. This file will be kept -in RAM, so keep it as small as possible. The file can be gzip'd ahead of time to reduce the size (i.e., using `gzip -n` or `zopfli`), and when served, the End User Setup module will add -the appropriate `Content-Encoding` header to the response. *Note: Even if gzipped, the file still needs to be named `enduser_setup.html`.* - -The following HTTP endpoints exist: - -|Endpoint|Description| -|--------|-----------| -|/|Returns HTML for the web page. Will return the contents of `enduser_setup.html` if it exists on the filesystem, otherwise will return a page embedded into the firmware image.| -|/aplist|Forces the ESP8266 to perform a site survey across all channels, reporting access points that it can find. Return payload is a JSON array: `[{"ssid":"foobar","rssi":-36,"chan":3}]`| -|/generate_204|Returns a HTTP 204 status (expected by certain Android clients during Wi-Fi connectivity checks)| -|/status|Returns plaintext status description, used by the web page| -|/status.json|Returns a JSON payload containing the ESP8266's chip id in hexadecimal format and the status code: 0=Idle, 1=Connecting, 2=Wrong Password, 3=Network not Found, 4=Failed, 5=Success| -|/setwifi|Endpoint intended for services to use for setting the wifi credentials. Identical to `/update` except returns the same payload as `/status.json` instead of redirecting to `/`.| -|/update|Form submission target. Example: `http://example.com/update?wifi_ssid=foobar&wifi_password=CorrectHorseBatteryStaple`. Must be a GET request. Will redirect to `/` when complete. | +After running [`enduser_setup.start()`](#enduser_setupstart) a portal like the above can be accessed through a wireless network called SetupGadget_XXXXXX. The portal is used to submit the credentials for the WiFi of the enduser. +After an IP address has been successfully obtained this module will stop as if [`enduser_setup.stop()`](#enduser_setupstop) had been called. +Alternative HTML can be served by placing a file called `enduser_setup.html` in the filesystem. This file will be kept in RAM, so keep it as small as possible. ## enduser_setup.manual() @@ -72,7 +53,7 @@ Starts the captive portal. #### Parameters - `onConnected()` callback will be fired when an IP-address has been obtained, just before the enduser_setup module will terminate itself - `onError()` callback will be fired if an error is encountered. `err_num` is a number describing the error, and `string` contains a description of the error. - - `onDebug()` callback is disabled by default (controlled by `#define ENDUSER_SETUP_DEBUG_ENABLE` in `enduser_setup.c`). It is intended to be used to find internal issues in the module. `string` contains a description of what is going on. + - `onDebug()` callback is disabled by default. It is intended to be used to find internal issues in the module. `string` contains a description of what is going on. #### Returns `nil` diff --git a/docs/en/modules/file.md b/docs/en/modules/file.md index 9cbfa93e..8db14f1f 100644 --- a/docs/en/modules/file.md +++ b/docs/en/modules/file.md @@ -7,6 +7,8 @@ The file module provides access to the file system and its individual files. The file system is a flat file system, with no notion of subdirectories/folders. +Only one file can be open at any given time. + Besides the SPIFFS file system on internal flash, this module can also access FAT partitions on an external SD card is [FatFS is enabled](../sdcard.md). ```lua @@ -41,6 +43,30 @@ Current directory defaults to the root of internal SPIFFS (`/FLASH`) after syste #### Returns `true` on success, `false` otherwise +## file.close() + +Closes the open file, if any. + +#### Syntax +`file.close()` + +#### Parameters +none + +#### Returns +`nil` + +#### Example +```lua +-- open 'init.lua', print the first line. +if file.open("init.lua", "r") then + print(file.readline()) + file.close() +end +``` +#### See also +[`file.open()`](#fileopen) + ## file.exists() Determines whether the specified file exists. @@ -69,6 +95,34 @@ end #### See also [`file.list()`](#filelist) +## file.flush() + +Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()`](#fileclose) performs an implicit flush as well. + +#### Syntax +`file.flush()` + +#### Parameters +none + +#### Returns +`nil` + +#### Example +```lua +-- open 'init.lua' in 'a+' mode +if file.open("init.lua", "a+") then + -- write 'foo bar' to the end of the file + file.write('foo bar') + file.flush() + -- write 'baz' too + file.write('baz') + file.close() +end +``` +#### See also +[`file.close()`](#fileclose) + ## file.format() Format the file system. Completely erases any existing file system and writes a new one. Depending on the size of the flash chip in the ESP, this may take several seconds. @@ -226,9 +280,9 @@ When done with the file, it must be closed using `file.close()`. - "a+": append update mode, previous data is preserved, writing is only allowed at the end of file #### Returns -file object if file opened ok. `nil` if file not opened, or not exists (read modes). +`nil` if file not opened, or not exists (read modes). `true` if file opened ok. -#### Example (basic model) +#### Example ```lua -- open 'init.lua', print the first line. if file.open("init.lua", "r") then @@ -236,20 +290,71 @@ if file.open("init.lua", "r") then file.close() end ``` -#### Example (object model) +#### See also +- [`file.close()`](#fileclose) +- [`file.readline()`](#filereadline) + +## file.read() + +Read content from the open file. + +#### Syntax +`file.read([n_or_str])` + +#### Parameters +- `n_or_str`: + - if nothing passed in, read up to `LUAL_BUFFERSIZE` bytes (default 1024) or the entire file (whichever is smaller) + - if passed a number n, then read the file until the lesser of `n` bytes, `LUAL_BUFFERSIZE` bytes, or EOF is reached. Specifying a number larger than the buffer size will read the buffer size. + - if passed a string `str`, then read until `str` appears next in the file, `LUAL_BUFFERSIZE` bytes have been read, or EOF is reached + +#### Returns +File content as a string, or nil when EOF + +#### Example ```lua --- open 'init.lua', print the first line. -fd = file.open("init.lua", "r") -if fd then - print(fd:readline()) - fd:close(); fd = nil +-- print the first line of 'init.lua' +if file.open("init.lua", "r") then + print(file.read('\n')) + file.close() +end + +-- print the first 5 bytes of 'init.lua' +if file.open("init.lua", "r") then + print(file.read(5)) + file.close() end ``` #### See also -- [`file.close()`](#fileclose) +- [`file.open()`](#fileopen) - [`file.readline()`](#filereadline) +## file.readline() + +Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than `LUAL_BUFFERSIZE`, this function only returns the first `LUAL_BUFFERSIZE` bytes (this is 1024 bytes by default). + +#### Syntax +`file.readline()` + +#### Parameters +none + +#### Returns +File content in string, line by line, including EOL('\n'). Return `nil` when EOF. + +#### Example +```lua +-- print the first line of 'init.lua' +if file.open("init.lua", "r") then + print(file.readline()) + file.close() +end +``` +#### See also +- [`file.open()`](#fileopen) +- [`file.close()`](#fileclose) +- [`file.read()`](#filereade) + ## file.remove() Remove a file from the file system. The file must not be currently open. @@ -293,188 +398,12 @@ Renames a file. If a file is currently open, it will be closed first. file.rename("temp.lua","init.lua") ``` -# File access functions - -The `file` module provides several functions to access the content of a file after it has been opened with [`file.open()`](#fileopen). They can be used as part of a basic model or an object model: - -## Basic model -In the basic model there is max one file opened at a time. The file access functions operate on this file per default. If another file is opened, the previous default file needs to be closed beforehand. - -```lua --- open 'init.lua', print the first line. -if file.open("init.lua", "r") then - print(file.readline()) - file.close() -end -``` - -## Object model -Files are represented by file objects which are created by `file.open()`. File access functions are available as methods of this object, and multiple file objects can coexist. - -```lua -src = file.open("init.lua", "r") -if src then - dest = file.open("copy.lua", "w") - if dest then - local line - repeat - line = src:read() - if line then - dest:write(line) - end - until line == nil - dest:close(); dest = nil - end - src:close(); dest = nil -end -``` - -!!! Attention - - It is recommended to use only one single model within the application. Concurrent use of both models can yield unpredictable behavior: Closing the default file from basic model will also close the correspoding file object. Closing a file from object model will also close the default file if they are the same file. - -!!! Note - - The maximum number of open files on SPIFFS is determined at compile time by `SPIFFS_MAX_OPEN_FILES` in `user_config.h`. - -## file.close() -## file.obj:close() - -Closes the open file, if any. - -#### Syntax -`file.close()` - -`fd:close()` - -#### Parameters -none - -#### Returns -`nil` - -#### See also -[`file.open()`](#fileopen) - -## file.flush() -## file.obj:flush() - -Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()` / `fd:close()`](#fileclose) performs an implicit flush as well. - -#### Syntax -`file.flush()` - -`fd:flush()` - -#### Parameters -none - -#### Returns -`nil` - -#### Example (basic model) -```lua --- open 'init.lua' in 'a+' mode -if file.open("init.lua", "a+") then - -- write 'foo bar' to the end of the file - file.write('foo bar') - file.flush() - -- write 'baz' too - file.write('baz') - file.close() -end -``` - -#### See also -[`file.close()` / `file.obj:close()`](#fileclose) - -## file.read() -## file.obj:read() - -Read content from the open file. - -!!! note - - The function temporarily allocates 2 * (number of requested bytes) on the heap for buffering and processing the read data. Default chunk size (`FILE_READ_CHUNK`) is 1024 bytes and is regarded to be safe. Pushing this by 4x or more can cause heap overflows depending on the application. Consider this when selecting a value for parameter `n_or_char`. - -#### Syntax -`file.read([n_or_char])` - -`fd:read([n_or_char])` - -#### Parameters -- `n_or_char`: - - if nothing passed in, then read up to `FILE_READ_CHUNK` bytes or the entire file (whichever is smaller). - - if passed a number `n`, then read up to `n` bytes or the entire file (whichever is smaller). - - if passed a string containing the single character `char`, then read until `char` appears next in the file, `FILE_READ_CHUNK` bytes have been read, or EOF is reached. - -#### Returns -File content as a string, or nil when EOF - -#### Example (basic model) -```lua --- print the first line of 'init.lua' -if file.open("init.lua", "r") then - print(file.read('\n')) - file.close() -end -``` - -#### Example (object model) -```lua --- print the first 5 bytes of 'init.lua' -fd = file.open("init.lua", "r") -if fd then - print(fd:read(5)) - fd:close(); fd = nil -end -``` - -#### See also -- [`file.open()`](#fileopen) -- [`file.readline()` / `file.obj:readline()`](#filereadline) - -## file.readline() -## file.obj:readline() - -Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than 1024, this function only returns the first 1024 bytes. - -#### Syntax -`file.readline()` - -`fd:readline()` - -#### Parameters -none - -#### Returns -File content in string, line by line, including EOL('\n'). Return `nil` when EOF. - -#### Example (basic model) -```lua --- print the first line of 'init.lua' -if file.open("init.lua", "r") then - print(file.readline()) - file.close() -end -``` - -#### See also -- [`file.open()`](#fileopen) -- [`file.close()` / `file.obj:close()`](#fileclose) -- [`file.read()` / `file.obj:read()`](#fileread) - - ## file.seek() -## file.obj:seek() - Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence. #### Syntax `file.seek([whence [, offset]])` -`fd:seek([whence [, offset]])` - #### Parameters - `whence` - "set": base is position 0 (beginning of the file) @@ -487,7 +416,7 @@ If no parameters are given, the function simply returns the current file offset. #### Returns the resulting file position, or `nil` on error -#### Example (basic model) +#### Example ```lua if file.open("init.lua", "r") then -- skip the first 5 bytes of the file @@ -500,22 +429,19 @@ end [`file.open()`](#fileopen) ## file.write() -## file.obj:write() Write a string to the open file. #### Syntax `file.write(string)` -`fd:write(string)` - #### Parameters `string` content to be write to file #### Returns `true` if the write is ok, `nil` on error -#### Example (basic model) +#### Example ```lua -- open 'init.lua' in 'a+' mode if file.open("init.lua", "a+") then @@ -525,38 +451,24 @@ if file.open("init.lua", "a+") then end ``` -#### Example (object model) -```lua --- open 'init.lua' in 'a+' mode -fd = file.open("init.lua", "a+") -if fd then - -- write 'foo bar' to the end of the file - fd:write('foo bar') - fd:close() -end -``` - #### See also - [`file.open()`](#fileopen) -- [`file.writeline()` / `file.obj:writeline()`](#filewriteline) +- [`file.writeline()`](#filewriteline) ## file.writeline() -## file.obj:writeline() Write a string to the open file and append '\n' at the end. #### Syntax `file.writeline(string)` -`fd:writeline(string)` - #### Parameters `string` content to be write to file #### Returns `true` if write ok, `nil` on error -#### Example (basic model) +#### Example ```lua -- open 'init.lua' in 'a+' mode if file.open("init.lua", "a+") then @@ -568,4 +480,4 @@ end #### See also - [`file.open()`](#fileopen) -- [`file.readline()` / `file.obj:readline()`](#filereadline) +- [`file.readline()`](#filereadline) diff --git a/docs/en/modules/gpio.md b/docs/en/modules/gpio.md index 4eb7c6b8..2d7cbc1b 100644 --- a/docs/en/modules/gpio.md +++ b/docs/en/modules/gpio.md @@ -69,29 +69,28 @@ gpio.read(0) ## gpio.serout() -Serialize output based on a sequence of delay-times in µs. After each delay, the pin is toggled. After the last cycle and last delay the pin is not toggled. +Serialize output based on a sequence of delay-times in µs. After each delay, the pin is toggled. After the last repeat and last delay the pin is not toggled. The function works in two modes: * synchronous - for sub-50 µs resolution, restricted to max. overall duration, * asynchrounous - synchronous operation with less granularity but virtually unrestricted duration. -Whether the asynchronous mode is chosen is defined by presence of the `callback` parameter. If present and is of function type the function goes asynchronous and the callback function is invoked when sequence finishes. If the parameter is numeric the function still goes asynchronous but no callback is invoked when done. +Whether the asynchronous mode is chosen is defined by presence of the `callback` parameter. If present and is of function type the function goes asynchronous the callback function is invoked when sequence finishes. If the parameter is numeric the function still goes asynchronous but no callback is invoked when done. -For the asynchronous version, the minimum delay time should not be shorter than 50 μs and maximum delay time is 0x7fffff μs (~8.3 seconds). -In this mode the function does not block the stack and returns immediately before the output sequence is finalized. HW timer `FRC1_SOURCE` mode is used to change the states. As there is only a single hardware timer, there -are restrictions on which modules can be used at the same time. An error will be raised if the timer is already in use. +For asynchronous version minimum delay time should not be shorter than 50 μs and maximum delay time is 0x7fffff μs (~8.3 seconds). +In this mode the function does not block the stack and returns immediately before the output sequence is finalized. HW timer inf `FRC1_SOURCE` mode is used to change the states. -Note that the synchronous variant (no or nil `callback` parameter) function blocks the stack and as such any use of it must adhere to the SDK guidelines (also explained [here](../extn-developer-faq/#extension-developer-faq)). Failure to do so may lead to WiFi issues or outright to crashes/reboots. In short it means that the sum of all delay times multiplied by the number of cycles should not exceed 15 ms. +Note that the synchronous variant (no or nil `callback` parameter) function blocks the stach and as such any use of it must adhere to the SDK guidelines (also explained [here](https://nodemcu.readthedocs.io/en/dev/en/extn-developer-faq/#extension-developer-faq)). Failure to do so may lead to WiFi issues or outright to crashes/reboots. Shortly it means that sum of all delay times multiplied by the number of repeats should not exceed 15 ms. #### Syntax -`gpio.serout(pin, start_level, delay_times [, cycle_num[, callback]])` +`gpio.serout(pin, start_level, delay_times [, repeat_num[, callback]])` #### Parameters - `pin` pin to use, IO index - `start_level` level to start on, either `gpio.HIGH` or `gpio.LOW` - `delay_times` an array of delay times in µs between each toggle of the gpio pin. -- `cycle_num` an optional number of times to run through the sequence. (default is 1) -- `callback` an optional callback function or number, if present the function returns immediately and goes asynchronous. +- `repeat_num` an optional number of times to run through the sequence. +- `callback` an optional callback function or number, if present the function ruturns immediately and goes asynchronous. #### Returns diff --git a/docs/en/modules/net.md b/docs/en/modules/net.md index 0fc78cb7..5580cba8 100644 --- a/docs/en/modules/net.md +++ b/docs/en/modules/net.md @@ -135,21 +135,15 @@ Listen on port from IP address. #### Example ```lua --- server listens on 80, if data received, print data to console and send "hello world" back to caller --- 30s time out for a inactive client + -- 30s time out for a inactive client sv = net.createServer(net.TCP, 30) - -function receiver(sck, data) - print(data) - sck:close() -end - -if sv then - sv:listen(80, function(conn) - conn:on("receive", receiver) - conn:send("hello world") +-- server listens on 80, if data received, print data to console and send "hello world" back to caller +sv:listen(80, function(c) + c:on("receive", function(c, pl) + print(pl) end) -end + c:send("hello world") +end) ``` #### See also @@ -309,35 +303,32 @@ Multiple consecutive `send()` calls aren't guaranteed to work (and often don't) #### Example ```lua srv = net.createServer(net.TCP) - -function receiver(sck, data) - local response = {} - - -- if you're sending back HTML over HTTP you'll want something like this instead - -- local response = {"HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n"} - - response[#response + 1] = "lots of data" - response[#response + 1] = "even more data" - response[#response + 1] = "e.g. content read from a file" - - -- sends and removes the first element from the 'response' table - local function send(localSocket) - if #response > 0 - then localSocket:send(table.remove(response, 1)) - else - localSocket:close() - response = nil - end - end - - -- triggers the send() function again once the first chunk of data was sent - sck:on("sent", send) - - send(sck) -end - srv:listen(80, function(conn) - conn:on("receive", receiver) + conn:on("receive", function(sck, req) + local response = {} + + -- if you're sending back HTML over HTTP you'll want something like this instead + -- local response = {"HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n"} + + response[#response + 1] = "lots of data" + response[#response + 1] = "even more data" + response[#response + 1] = "e.g. content read from a file" + + -- sends and removes the first element from the 'response' table + local function send(sk) + if #response > 0 + then sk:send(table.remove(response, 1)) + else + sk:close() + response = nil + end + end + + -- triggers the send() function again once the first chunk of data was sent + sck:on("sent", send) + + send(sck) + end) end) ``` If you do not or can not keep all the data you send back in memory at one time (remember that `response` is an aggregation) you may use explicit callbacks instead of building up a table like so: diff --git a/docs/en/modules/ow.md b/docs/en/modules/ow.md index 459ba831..4cc23462 100644 --- a/docs/en/modules/ow.md +++ b/docs/en/modules/ow.md @@ -84,7 +84,7 @@ Reads multi bytes. #### Parameters - `pin` 1~12, I/O index -- `size` number of bytes to be read from slave device (up to 256) +- `size` number of bytes to be read from slave device #### Returns `string` bytes read from slave device diff --git a/docs/en/modules/rtctime.md b/docs/en/modules/rtctime.md index 860e1138..4fc01d7a 100644 --- a/docs/en/modules/rtctime.md +++ b/docs/en/modules/rtctime.md @@ -55,7 +55,7 @@ rtctime.dsleep(5000000, 4) For applications where it is necessary to take samples with high regularity, this function is useful. It provides an easy way to implement a "wake up on the next 5-minute boundary" scheme, without having to explicitly take into account how long the module has been active for etc before going back to sleep. #### Syntax -`rtctime.dsleep_aligned(aligned_us, minsleep_us [, option])` +`rtctime.dsleep(aligned_us, minsleep_us [, option])` #### Parameters - `aligned_us` boundary interval in microseconds diff --git a/docs/en/modules/sntp.md b/docs/en/modules/sntp.md index 9729aa49..987d7950 100644 --- a/docs/en/modules/sntp.md +++ b/docs/en/modules/sntp.md @@ -12,7 +12,7 @@ When compiled together with the [rtctime](rtctime.md) module it also offers seam Attempts to obtain time synchronization. -For best results you may want to to call this periodically in order to compensate for internal clock drift. As stated in the [rtctime](rtctime.md) module documentation it's advisable to sync time after deep sleep and it's necessary to sync after module reset (add it to [`init.lua`](../upload.md#initlua) after WiFi initialization). +For best results you may want to to call this periodically in order to compensate for internal clock drift. As stated in the [rtctime](rtctime.md) module documentation it's advisable to sync time after deep sleep and it's necessary to sync after module reset (add it to [`init.lua`](upload.md#initlua) after WiFi initialization). #### Syntax `sntp.sync([server_ip], [callback], [errcallback])` diff --git a/docs/en/modules/somfy.md b/docs/en/modules/somfy.md deleted file mode 100644 index f5dd8eba..00000000 --- a/docs/en/modules/somfy.md +++ /dev/null @@ -1,45 +0,0 @@ -# Somfy module -| Since | Origin / Contributor | Maintainer | Source | -| :----- | :-------------------- | :---------- | :------ | -| 2016-09-27 | [vsky279](https://github.com/vsky279) | [vsky279](https://github.com/vsky279) | [somfy.c](../../../app/modules/somfy.c)| - -This module provides a simple interface to control Somfy blinds via an RF transmitter (433.42 MHz). It is based on [Nickduino Somfy Remote Arduino skecth](https://github.com/Nickduino/Somfy_Remote). - -The hardware used is the standard 433 MHz RF transmitter. Unfortunately these chips are usually transmitting at he frequency of 433.92MHz so the crystal resonator should be replaced with the 433.42 MHz resonator though some reporting that it is working even with the original crystal. - -To understand details of the Somfy protocol please refer to [Somfy RTS protocol](https://pushstack.wordpress.com/somfy-rts-protocol/) and also discussion [here](https://forum.arduino.cc/index.php?topic=208346.0). - -The module is using hardware timer so it cannot be used at the same time with other NodeMCU modules using the hardware timer, i.e. `sigma delta`, `pcm`, `perf`, or `pwm` modules. - -## somfy.sendcommand() - -Builds an frame defined by Somfy protocol and sends it to the RF transmitter. - -#### Syntax -`somfy.sendcommand(pin, remote_address, command, rolling_code, repeat_count, call_back)` - -#### Parameters -- `pin` GPIO pin the RF transmitter is connected to. -- `remote_address` address of the remote control. The device to be controlled is programmed with the addresses of the remote controls it should listen to. -- `command` command to be transmitted. Can be one of `somfy.SOMFY_UP`, `somfy.SOMFY_DOWN`, `somfy.SOMFY_PROG`, `somfy.SOMFY_STOP` -- `rolling_code` The rolling code is increased every time a button is pressed. The receiver only accepts command if the rolling code is above the last received code and is not to far ahead of the last received code. This window is in the order of a 100 big. The rolling code needs to be stored in the EEPROM (i.e. filesystem) to survive the ESP8266 reset. -- `repeat_count` how many times the command is repeated -- `call_back` a function to be called after the command is transmitted. Allows chaining commands to set the blinds to a defined position. - -My original remote is [TELIS 4 MODULIS RTS](https://www.somfy.co.uk/products/1810765/telis-4-modulis-rts). This remote is working with the additional info - additional 56 bits that follow data (shortening the Inter-frame gap). It seems that the scrumbling alhorithm has not been revealed yet. - -When I send the `somfy.DOWN` command, repeating the frame twice (which seems to be the standard for a short button press), i.e. `repeat_count` equal to 2, the blinds go only 1 step down. This corresponds to the movement of the wheel on the original remote. The down button on the original remote sends also `somfy.DOWN` command but the additional info is different and this makes the blinds go full down. Fortunately it seems that repeating the frame 16 times makes the blinds go fully down. - -#### Returns -nil - -#### Example -To start with controlling your Somfy blinds you need to: - -- Choose an arbitrary remote address (different from your existing remote) - `123` in this example -- Choose a starting point for the rolling code. Any unsigned int works, 1 is a good start -- Long-press the program button of your existing remote control until your blind goes up and down slightly -- execute `somfy.sendcommand(4, 123, somfy.PROG, 1, 2)` - the blinds will react and your ESP8266 remote control is now registered -- running `somfy.sendcommand(4, 123, somfy.DOWN, 2, 16)` - fully closes the blinds - -For more elaborated example please refer to [`somfy.lua`](../../../lua_examples/somfy.lua). diff --git a/docs/en/modules/spi.md b/docs/en/modules/spi.md index abe3a98f..280a7e49 100644 --- a/docs/en/modules/spi.md +++ b/docs/en/modules/spi.md @@ -6,10 +6,6 @@ All transactions for sending and receiving are most-significant-bit first and least-significant last. For technical details of the underlying hardware refer to [metalphreak's ESP8266 HSPI articles](http://d.av.id.au/blog/tag/hspi/). -!!! note - - The ESP hardware provides two SPI busses, with IDs 0, and 1, which map to pins generally labelled SPI and HSPI. If you are using any kind of development board which provides flash, then bus ID 0 (SPI) is almost certainly used for communicating with the flash chip. You probably want to choose bus ID 1 (HSPI) for your communication, as you will have uncontended use of it. - ## High Level Functions The high level functions provide a send & receive API for half- and full-duplex mode. Sent and received data items are restricted to 1 - 32 bit diff --git a/docs/en/modules/u8g.md b/docs/en/modules/u8g.md index 8d022f31..3bbb46da 100644 --- a/docs/en/modules/u8g.md +++ b/docs/en/modules/u8g.md @@ -8,7 +8,7 @@ U8glib is a graphics library developed at [olikraus/u8glib](https://github.com/o I²C and SPI mode: - sh1106_128x64 -- ssd1306 - 128x32, 128x64, and 64x48 variants +- ssd1306 - 128x64 and 64x48 variants - ssd1309_128x64 - ssd1327_96x96_gr - uc1611 - dogm240 and dogxl240 variants @@ -107,7 +107,6 @@ Initialize a display via I²C. The init sequence would insert delays to match the display specs. These can destabilize the overall system if wifi service is blocked for too long. It is therefore advisable to disable such delays unless the specific use case can exclude wifi traffic while initializing the display driver. - `u8g.sh1106_128x64_i2c()` -- `u8g.ssd1306_128x32_i2c()` - `u8g.ssd1306_128x64_i2c()` - `u8g.ssd1306_64x48_i2c()` - `u8g.ssd1309_128x64_i2c()` @@ -147,7 +146,6 @@ The init sequence would insert delays to match the display specs. These can dest - `u8g.pcd8544_84x48_hw_spi()` - `u8g.pcf8812_96x65_hw_spi()` - `u8g.sh1106_128x64_hw_spi()` -- `u8g.ssd1306_128x32_hw_spi()` - `u8g.ssd1306_128x64_hw_spi()` - `u8g.ssd1306_64x48_hw_spi()` - `u8g.ssd1309_128x64_hw_spi()` @@ -204,30 +202,6 @@ disp = u8g.ssd1306_128x64_hw_spi(cs, dc, res) #### See also [I²C Display Drivers](#i2c-display-drivers) -## u8g.fb_rle -Initialize a virtual display that provides run-length encoded framebuffer contents to a Lua callback. - -The callback function can be used to process the framebuffer line by line. It's called with either `nil` as parameter to indicate the start of a new frame or with a string containing a line of the framebuffer with run-length encoding. First byte in the string specifies how many pairs of (x, len) follow, while each pair defines the start (leftmost x-coordinate) and length of a sequence of lit pixels. All other pixels in the line are dark. - -```lua -n = struct.unpack("B", rle_line) -print(n.." pairs") -for i = 0,n-1 do - print(string.format(" x: %d len: %d", struct.unpack("BB", rle_line, 1+1 + i*2))) -end -``` - -#### Syntax -`u8g.fb_rle(cb_fn, width, height)` - -#### Parameters -- `cb_fn([rle_line])` callback function. `rle_line` is a string containing a run-length encoded framebuffer line, or `nil` to indicate start of frame. -- `width` of display. Must be a multiple of 8, less than or equal to 248. -- `height` of display. Must be a multiple of 8, less than or equal to 248. - -#### Returns -u8g display object - ___ ## Constants diff --git a/docs/en/modules/websocket.md b/docs/en/modules/websocket.md index 5f319c7c..177c9abe 100644 --- a/docs/en/modules/websocket.md +++ b/docs/en/modules/websocket.md @@ -7,6 +7,10 @@ A websocket *client* module that implements [RFC6455](https://tools.ietf.org/htm The implementation supports fragmented messages, automatically respondes to ping requests and periodically pings if the server isn't communicating. +!!! note + + Currently, it is **not** possible to change the request headers, most notably the user agent. + **SSL/TLS support** Take note of constraints documented in the [net module](net.md). @@ -66,27 +70,6 @@ ws = nil -- fully dispose the client as lua will now gc it ``` -## websocket.client:config(params) - -Configures websocket client instance. - -#### Syntax -`websocket:config(params)` - -#### Parameters -- `params` table with configuration parameters. Following keys are recognized: - - `headers` table of extra request headers affecting every request - -#### Returns -`nil` - -#### Example -```lua -ws = websocket.createClient() -ws:config({headers={['User-Agent']='NodeMCU'}}) -``` - - ## websocket.client:connect() Attempts to estabilish a websocket connection to the given URL. diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index b34d8f00..dd2b9874 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -24,23 +24,6 @@ Gets the current WiFi channel. #### Returns current WiFi channel -## wifi.getdefaultmode() - -Gets default WiFi operation mode. - -#### Syntax -`wifi.getdefaultmode()` - -#### Parameters -`nil` - -#### Returns -The WiFi mode, as one of the `wifi.STATION`, `wifi.SOFTAP`, `wifi.STATIONAP` or `wifi.NULLMODE` constants. - -#### See also -[`wifi.getmode()`](#wifigetmode) -[`wifi.setmode()`](#wifisetmode) - ## wifi.getmode() Gets WiFi operation mode. @@ -55,7 +38,6 @@ Gets WiFi operation mode. The WiFi mode, as one of the `wifi.STATION`, `wifi.SOFTAP`, `wifi.STATIONAP` or `wifi.NULLMODE` constants. #### See also -[`wifi.getdefaultmode()`](#wifigetdefaultmode) [`wifi.setmode()`](#wifisetmode) ## wifi.getphymode() @@ -85,20 +67,18 @@ Configures the WiFi mode to use. NodeMCU can run in one of four WiFi modes: When using the combined Station + AP mode, the same channel will be used for both networks as the radio can only listen on a single channel. -NOTE: WiFi Mode configuration will be retained until changed even if device is turned off. +NOTE: WiFi Mode configuration will be retained until changed even if device is turned off. #### Syntax -`wifi.setmode(mode[, save])` +`wifi.setmode(mode)` #### Parameters -- `mode` value should be one of - - `wifi.STATION` for when the device is connected to a WiFi router. This is often done to give the device access to the Internet. - - `wifi.SOFTAP` for when the device is acting *only* as an access point. This will allow you to see the device in the list of WiFi networks (unless you hide the SSID, of course). In this mode your computer can connect to the device, creating a local area network. Unless you change the value, the NodeMCU device will be given a local IP address of 192.168.4.1 and assign your computer the next available IP address, such as 192.168.4.2. - - `wifi.STATIONAP` is the combination of `wifi.STATION` and `wifi.SOFTAP`. It allows you to create a local WiFi connection *and* connect to another WiFi router. - - `wifi.NULLMODE` changing WiFi mode to NULL_MODE will put wifi into a low power state similar to MODEM_SLEEP, provided `wifi.nullmodesleep(false)` has not been called. -- `save` choose whether or not to save wifi mode to flash - - `true` WiFi mode configuration **will** be retained through power cycle. (Default) - - `false` WiFi mode configuration **will not** be retained through power cycle. +`mode` value should be one of + +- `wifi.STATION` for when the device is connected to a WiFi router. This is often done to give the device access to the Internet. +- `wifi.SOFTAP` for when the device is acting *only* as an access point. This will allow you to see the device in the list of WiFi networks (unless you hide the SSID, of course). In this mode your computer can connect to the device, creating a local area network. Unless you change the value, the NodeMCU device will be given a local IP address of 192.168.4.1 and assign your computer the next available IP address, such as 192.168.4.2. +- `wifi.STATIONAP` is the combination of `wifi.STATION` and `wifi.SOFTAP`. It allows you to create a local WiFi connection *and* connect to another WiFi router. +- `wifi.NULLMODE` to switch off WiFi #### Returns current mode after setup @@ -110,12 +90,11 @@ wifi.setmode(wifi.STATION) #### See also [`wifi.getmode()`](#wifigetmode) -[`wifi.getdefaultmode()`](#wifigetdefaultmode) ## wifi.setphymode() Sets WiFi physical mode. - + - `wifi.PHYMODE_B` 802.11b, more range, low Transfer rate, more current draw - `wifi.PHYMODE_G` @@ -137,7 +116,7 @@ Information from the Espressif datasheet v4.3 `wifi.setphymode(mode)` #### Parameters -`mode` one of the following +`mode` one of the following - `wifi.PHYMODE_B` - `wifi.PHYMODE_G` @@ -151,23 +130,39 @@ physical mode after setup ## wifi.nullmodesleep() -Configures whether or not WiFi automatically goes to sleep in NULL_MODE. Enabled by default. - - !!! note - This function **does not** store it's setting in flash, if auto sleep in NULL_MODE is not desired, `wifi.nullmodesleep(false)` must be called after powerup, restart, or wake from deep sleep. +Configures whether or not WiFi automatically goes to sleep in NULL_MODE. Enabled by default. #### Syntax -`wifi.nullmodesleep([enable])` +`wifi.nullmodesleep(enable)` #### Parameters -- `enable` - - `true` Enable WiFi auto sleep in NULL_MODE. (Default setting) - - `false` Disable WiFi auto sleep in NULL_MODE. +- `enable` + - true: Enable WiFi auto sleep in NULL_MODE. (Default setting) + - false: Disable WiFi auto sleep in NULL_MODE. #### Returns -- `sleep_enabled` Current/New NULL_MODE sleep setting - - If `wifi.nullmodesleep()` is called with no arguments, current setting is returned. - - If `wifi.nullmodesleep()` is called with `enable` argument, confirmation of new setting is returned. +Current/new NULL_MODE sleep setting. + +## wifi.sleeptype() + +Configures the WiFi modem sleep type. + +#### Syntax +`wifi.sleeptype(type_wanted)` + +#### Parameters +`type_wanted` one of the following: + +- `wifi.NONE_SLEEP` to keep the modem on at all times +- `wifi.LIGHT_SLEEP` to allow the modem to power down under some circumstances +- `wifi.MODEM_SLEEP` to power down the modem as much as possible + +#### Returns +The actual sleep mode set, as one of `wifi.NONE_SLEEP`, `wifi.LIGHT_SLEEP` or `wifi.MODEM_SLEEP`. + +#### See also +- [`node.dsleep()`](node.md#nodedsleep) +- [`rtctime.dsleep()`](rtctime.md#rtctimedsleep) ## wifi.startsmart() @@ -246,95 +241,61 @@ wifi.sta.autoconnect(1) - [`wifi.sta.connect()`](#wifistaconnect) - [`wifi.sta.disconnect()`](#wifistadisconnect) -## wifi.sta.changeap() - -Select Access Point from list returned by `wifi.sta.getapinfo()` - -#### Syntax -`wifi.sta.changeap(ap_index)` - -#### Parameters -`ap_index` Index of Access Point you would like to change to. (Range:1-5) - - Corresponds to index used by [`wifi.sta.getapinfo()`](#wifistagetapinfo) and [`wifi.sta.getapindex()`](#wifistagetapindex) - -#### Returns -- `true` Success -- `false` Failure - -#### Example -```lua -wifi.sta.changeap(4) -``` - -#### See also -- [`wifi.sta.getapinfo()`](#wifistagetapinfo) -- [`wifi.sta.getapindex()`](#wifistagetapindex) - ## wifi.sta.config() Sets the WiFi station configuration. +NOTE: Station configuration will be retained until changed even if device is turned off. + #### Syntax -`wifi.sta.config(station_config)` +`wifi.sta.config(ssid, password[, auto[, bssid]])` #### Parameters -- `station_config` table containing configuration data for station - - `ssid` string which is less than 32 bytes. - - `pwd` string which is 8-64 or 0 bytes. Empty string indicates an open WiFi access point. - - `auto` defaults to true - - `true` to enable auto connect and connect to access point, hence with `auto=true` there's no need to call [`wifi.sta.connect()`](#wifistaconnect) - - `false` to disable auto connect and remain disconnected from access point - - `bssid` string that contains the MAC address of the access point (optional) + +- `ssid` string which is less than 32 bytes. +- `password` string which is 8-64 or 0 bytes. Empty string indicates an open WiFi access point. +- `auto` defaults to 1 + - 0 to disable auto connect and remain disconnected from access point + - 1 to enable auto connect and connect to access point, hence with `auto=1` there's no need to call [`wifi.sta.connect()`](#wifistaconnect) later +- `bssid` string that contains the MAC address of the access point (optional) - You can set BSSID if you have multiple access points with the same SSID. - Note: if you set BSSID for a specific SSID and would like to configure station to connect to the same SSID only without the BSSID requirement, you MUST first configure to station to a different SSID first, then connect to the desired SSID - The following formats are valid: - - "DE:C1:A5:51:F1:ED" + - "DE-C1-A5-51-F1-ED" - "AC-1D-1C-B1-0B-22" - "DE AD BE EF 7A C0" - - `save` Save station configuration to flash. - - `true` configuration **will** be retained through power cycle. - - `false` configuration **will not** be retained through power cycle. (Default) #### Returns -- `true` Success -- `false` Failure +`nil` #### Example ```lua ---connect to Access Point (DO NOT save config to flash) -station_cfg={} -station_cfg.ssid="NODE-AABBCC" -station_cfg.pwd="password" -wifi.sta.config(station_cfg) +-- Connect to access point automatically when in range, `auto` defaults to 1 +wifi.sta.config("myssid", "password") ---connect to Access Point (DO save config to flash) -station_cfg={} -station_cfg.ssid="NODE-AABBCC" -station_cfg.pwd="password" -station_cfg.save=true -wifi.sta.config(station_cfg) - ---connect to Access Point with specific MAC address -station_cfg={} -station_cfg.ssid="NODE-AABBCC" -station_cfg.pwd="password" -station_cfg.bssid="AA:BB:CC:DD:EE:FF" -wifi.sta.config(station_cfg) - ---configure station but don't connect to Access point -station_cfg={} -station_cfg.ssid="NODE-AABBCC" -station_cfg.pwd="password" -station_cfg.auto=false -wifi.sta.config(station_cfg) +-- Connect to Unsecured access point automatically when in range, `auto` defaults to 1 +wifi.sta.config("myssid", "") + +-- Connect to access point, User decides when to connect/disconnect to/from AP due to `auto=0` +wifi.sta.config("myssid", "mypassword", 0) +wifi.sta.connect() +-- ... do some WiFi stuff +wifi.sta.disconnect() + +-- Connect to specific access point automatically when in range, `auto` defaults to 1 +wifi.sta.config("myssid", "mypassword", "12:34:56:78:90:12") +-- Connect to specific access point, User decides when to connect/disconnect to/from AP due to `auto=0` +wifi.sta.config("myssid", "mypassword", 0, "12:34:56:78:90:12") +wifi.sta.connect() +-- ... do some WiFi stuff +wifi.sta.disconnect() ``` #### See also - [`wifi.sta.connect()`](#wifistaconnect) - [`wifi.sta.disconnect()`](#wifistadisconnect) -- [`wifi.sta.apinfo()`](#wifistaapinfo) ## wifi.sta.connect() @@ -357,10 +318,6 @@ none Disconnects from AP in station mode. - !!! note - Please note that disconnecting from Access Point does not reduce power consumption. - If power saving is your goal, please refer to the description for `wifi.NULLMODE` in the function [`wifi.setmode()`](#wifisetmode) for more details. - #### Syntax `wifi.sta.disconnect()` @@ -382,14 +339,14 @@ Registers callbacks for WiFi station status events. - `wifi.sta.eventMonReg(wifi_status[, function([previous_state])])` #### Parameters -- `wifi_status` WiFi status you would like to set a callback for: +- `wifi_status` WiFi status you would like to set a callback for: - `wifi.STA_IDLE` - `wifi.STA_CONNECTING` - `wifi.STA_WRONGPWD` - `wifi.STA_APNOTFOUND` - `wifi.STA_FAIL` - `wifi.STA_GOTIP` -- `function` callback function to perform when event occurs +- `function` callback function to perform when event occurs - Note: leaving field blank unregisters callback. - `previous_state` previous wifi_state(0 - 5) @@ -397,7 +354,7 @@ Registers callbacks for WiFi station status events. `nil` #### Example -```lua +```lua --register callback wifi.sta.eventMonReg(wifi.STA_IDLE, function() print("STATION_IDLE") end) wifi.sta.eventMonReg(wifi.STA_CONNECTING, function() print("STATION_CONNECTING") end) @@ -405,16 +362,16 @@ wifi.sta.eventMonReg(wifi.STA_WRONGPWD, function() print("STATION_WRONG_PASSWORD wifi.sta.eventMonReg(wifi.STA_APNOTFOUND, function() print("STATION_NO_AP_FOUND") end) wifi.sta.eventMonReg(wifi.STA_FAIL, function() print("STATION_CONNECT_FAIL") end) wifi.sta.eventMonReg(wifi.STA_GOTIP, function() print("STATION_GOT_IP") end) - + --register callback: use previous state wifi.sta.eventMonReg(wifi.STA_CONNECTING, function(previous_State) - if(previous_State==wifi.STA_GOTIP) then + if(previous_State==wifi.STA_GOTIP) then print("Station lost connection with access point\n\tAttempting to reconnect...") else print("STATION_CONNECTING") end end) - + --unregister callback wifi.sta.eventMonReg(wifi.STA_IDLE) ``` @@ -490,7 +447,7 @@ Scans AP list as a Lua table into callback function. #### Parameters - `cfg` table that contains scan configuration - - `ssid` SSID == nil, don't filter SSID + - `ssid` SSID == nil, don't filter SSID - `bssid` BSSID == nil, don't filter BSSID - `channel` channel == 0, scan all channels, otherwise scan set channel (default is 0) - `show_hidden` show_hidden == 1, get info for router with hidden SSID (default is 0) @@ -522,7 +479,7 @@ function listap(t) -- (SSID : Authmode, RSSI, BSSID, Channel) end end wifi.sta.getap(listap) - + -- print AP list in new format function listap(t) for k,v in pairs(t) do @@ -550,8 +507,8 @@ function listap(t) end end scan_cfg = {} -scan_cfg.ssid = "myssid" -scan_cfg.bssid = "AA:AA:AA:AA:AA:AA" +scan_cfg.ssid = "myssid" +scan_cfg.bssid = "AA:AA:AA:AA:AA:AA" scan_cfg.channel = 0 scan_cfg.show_hidden = 1 wifi.sta.getap(scan_cfg, 1, listap) @@ -563,10 +520,10 @@ function listap(t) print("CURRENT RSSI IS: "..rssi) end end -ssid, tmp, bssid_set, bssid=wifi.sta.getconfig() +ssid, tmp, bssid_set, bssid=wifi.sta.getconfig() scan_cfg = {} -scan_cfg.ssid = ssid +scan_cfg.ssid = ssid if bssid_set == 1 then scan_cfg.bssid = bssid else scan_cfg.bssid = nil end scan_cfg.channel = wifi.getchannel() scan_cfg.show_hidden = 0 @@ -578,88 +535,6 @@ wifi.sta.getap(scan_cfg, 1, listap) #### See also [`wifi.sta.getip()`](#wifistagetip) -## wifi.sta.getapindex() - -Get index of current Access Point stored in AP cache. - - -#### Syntax -`wifi.sta.getapindex()` - -#### Parameters -none - -#### Returns -`current_index` index of currently selected Access Point. (Range:1-5) - -#### Example -```lua -print("the index of the currently selected AP is: "..wifi.sta.getapindex()) -``` - -#### See also -- [`wifi.sta.getapindex()`](#wifistagetapindex) -- [`wifi.sta.apinfo()`](#wifistaapinfo) -- [`wifi.sta.apchange()`](#wifistaapchange) - -## wifi.sta.getapinfo() - -Get information of APs cached by ESP8266 station. - -!!! Note - Any Access Points configured with save disabled `wifi.sta.config({save=false})` will populate this list (appearing to overwrite APs stored in flash) until restart. - -#### Syntax -`wifi.sta.getapinfo()` - -#### Parameters -`nil` - -#### Returns -- `ap_info` - - `qty` quantity of APs returned - - `1-5` index of AP. (the index corresponds to index used by [`wifi.sta.changeap()`](#wifistachangeap) and [`wifi.sta.getapindex()`](#wifistagetapindex)) - - `ssid` ssid of Access Point - - `pwd` Password for Access Point - - If no password was configured, the `pwd` field will be `nil` - - `bssid` MAC address of Access Point - - If no MAC address was configured, the `bssid` field will be `nil` - - -#### Example -```lua ---print stored access point info -do - for k,v in pairs(wifi.sta.getapinfo()) do - if (type(v)=="table") then - print(" "..k.." : "..type(v)) - for k,v in pairs(v) do - print("\t\t"..k.." : "..v) - end - else - print(" "..k.." : "..v) - end - end -end - ---print stored access point info(formatted) -do - local x=wifi.sta.getapinfo() - local y=wifi.sta.getapindex() - print("\n Number of APs stored in flash:", x.qty) - print(string.format(" %-6s %-32s %-64s %-18s", "index:", "SSID:", "Password:", "BSSID:")) - for i=1, (x.qty), 1 do - print(string.format(" %s%-6d %-32s %-64s %-18s",(i==y and ">" or " "), i, x[i].ssid, x[i].pwd and x[i].pwd or type(nil), x[i].bssid and x[i].bssid or type(nil))) - end -end -``` - -#### See also -- [`wifi.sta.getapindex()`](#wifistagetapindex) -- [`wifi.sta.setaplimit()`](#wifistasetaplimit) -- [`wifi.sta.changeap()`](#wifistachangeap) -- [`wifi.sta.config()`](#wifistaconfig) - ## wifi.sta.getbroadcast() Gets the broadcast address in station mode. @@ -671,7 +546,7 @@ Gets the broadcast address in station mode. `nil` #### Returns -broadcast address as string, for example "192.168.0.255", +broadcast address as string, for example "192.168.0.255", returns `nil` if IP address = "0.0.0.0". #### See also @@ -679,39 +554,23 @@ returns `nil` if IP address = "0.0.0.0". ## wifi.sta.getconfig() -Gets the WiFi station configuration. +Gets the WiFi station configuration. #### Syntax `wifi.sta.getconfig()` #### Parameters -- `return_table` - - `true` returns data in a table - - `false` returns data in the old format (default) +none #### Returns -If `return_table` is `true`: -- `config_table` - - `ssid` ssid of Access Point. - - `pwd` password to Access Point. - - If no password was configured, the `pwd` field will be `nil` - - `bssid` MAC address of Access Point - - If no MAC address was configured, the `bssid` field will be `nil` +ssid, password, bssid_set, bssid -If `return_table` is `false`: -- ssid, password, bssid_set, bssid - - Note: If `bssid_set` is equal to `0` then `bssid` is irrelevant, +Note: If bssid_set is equal to 0 then bssid is irrelevant #### Example ```lua ---Get current Station configuration (NEW FORMAT) -do -local def_sta_config=wifi.sta.getconfig(true) -print(string.format("\tDefault station config\n\tssid:\"%s\"\tpassword:\"%s\"%s", def_sta_config.ssid, def_sta_config.pwd, (type(def_sta_config.bssid)=="string" and "\tbssid:\""..def_sta_config.bssid.."\"" or ""))) -end - ---Get current Station configuration (OLD FORMAT) +--Get current Station configuration ssid, password, bssid_set, bssid=wifi.sta.getconfig() print("\nCurrent Station configuration:\nSSID : "..ssid .."\nPassword : "..password @@ -721,55 +580,6 @@ ssid, password, bssid_set, bssid=nil, nil, nil, nil ``` #### See also -- [`wifi.sta.getdefaultconfig()`](#wifistagetdefaultconfig) -- [`wifi.sta.connect()`](#wifistaconnect) -- [`wifi.sta.disconnect()`](#wifistadisconnect) - -## wifi.sta.getdefaultconfig() - -Gets the default WiFi station configuration stored in flash. - -#### Syntax -`wifi.sta.getdefaultconfig(return_table)` - -#### Parameters -- `return_table` - - `true` returns data in a table - - `false` returns data in the old format (default) - -#### Returns -If `return_table` is `true`: -- `config_table` - - `ssid` ssid of Access Point. - - `pwd` password to Access Point. - - If no password was configured, the `pwd` field will be `nil` - - `bssid` MAC address of Access Point - - If no MAC address was configured, the `bssid` field will be `nil` - -If `return_table` is `false`: -- ssid, password, bssid_set, bssid - - Note: If `bssid_set` is equal to `0` then `bssid` is irrelevant, - -#### Example - -```lua ---Get default Station configuration (NEW FORMAT) -do - local def_sta_config=wifi.sta.getdefaultconfig(true) - print(string.format("\tDefault station config\n\tssid:\"%s\"\tpassword:\"%s\"%s", def_sta_config.ssid, def_sta_config.pwd, (type(def_sta_config.bssid)=="string" and "\tbssid:\""..def_sta_config.bssid.."\"" or ""))) -end - ---Get default Station configuration (OLD FORMAT) -ssid, password, bssid_set, bssid=wifi.sta.getdefaultconfig() -print("\nCurrent Station configuration:\nSSID : "..ssid -.."\nPassword : "..password -.."\nBSSID_set : "..bssid_set -.."\nBSSID: "..bssid.."\n") -ssid, password, bssid_set, bssid=nil, nil, nil, nil -``` - -#### See also -- [`wifi.sta.getconfig()`](#wifistagetconfig) - [`wifi.sta.connect()`](#wifistaconnect) - [`wifi.sta.disconnect()`](#wifistadisconnect) @@ -857,32 +667,6 @@ RSSI=wifi.sta.getrssi() print("RSSI is", RSSI) ``` -## wifi.sta.setaplimit() - -Set Maximum number of Access Points to store in flash. - - This value is written to flash - -!!! Attention - If 5 Access Points are stored and AP limit is set to 4, the AP at index 5 will remain until [`node.restore()`](node.md#noderestore) is called or AP limit is set to 5 and AP is overwritten. - -#### Syntax -`wifi.sta.setaplimit(qty)` - -#### Parameters -`qty` Quantity of Access Points to store in flash. Range: 1-5 (Default: 5) - -#### Returns -- `true` Success -- `false` Failure - -#### Example -```lua -wifi.sta.setaplimit(true) -``` - -#### See also -- [`wifi.sta.getapinfo()`](#wifistagetapinfo) - ## wifi.sta.sethostname() Sets station hostname. @@ -948,26 +732,6 @@ print(wifi.sta.setmac("DE:AD:BE:EF:7A:C0")) #### See also [`wifi.sta.setip()`](#wifistasetip) -## wifi.sta.sleeptype() - -Configures the WiFi modem sleep type to be used while station is connected to an Access Point. - - !!! note - Does not apply to `wifi.SOFTAP`, `wifi.STATIONAP` or `wifi.NULLMODE`. - -#### Syntax -`wifi.sta.sleeptype(type_wanted)` - -#### Parameters -`type_wanted` one of the following: - -- `wifi.NONE_SLEEP` to keep the modem on at all times -- `wifi.LIGHT_SLEEP` to allow the CPU to power down under some circumstances -- `wifi.MODEM_SLEEP` to power down the modem as much as possible - -#### Returns -The actual sleep mode set, as one of `wifi.NONE_SLEEP`, `wifi.LIGHT_SLEEP` or `wifi.MODEM_SLEEP`. - ## wifi.sta.status() Gets the current status in station mode. @@ -994,26 +758,22 @@ number: 0~5 Sets SSID and password in AP mode. Be sure to make the password at least 8 characters long! If you don't it will default to *no* password and not set the SSID! It will still work as an access point but use a default SSID like e.g. NODE-9997C3. +NOTE: SoftAP Configuration will be retained until changed even if device is turned off. + #### Syntax `wifi.ap.config(cfg)` #### Parameters -- `cfg` table to hold configuration - - `ssid` SSID chars 1-32 - - `pwd` password chars 8-64 - - `auth` authentication method, one of `wifi.OPEN` (default), `wifi.WPA_PSK`, `wifi.WPA2_PSK`, `wifi.WPA_WPA2_PSK` - - `channel` channel number 1-14 default = 6 - - `hidden` false = not hidden, true = hidden, default = false - - `max` maximum number of connections 1-4 default=4 - - `beacon` beacon interval time in range 100-60000, default = 100 - - `save` save configuration to flash. - - `true` configuration **will** be retained through power cycle. (Default) - - `false` configuration **will not** be retained through power cycle. - +- `ssid` SSID chars 1-32 +- `pwd` password chars 8-64 +- `auth` authentication method, one of `wifi.OPEN` (default), `wifi.WPA_PSK`, `wifi.WPA2_PSK`, `wifi.WPA_WPA2_PSK` +- `channel` channel number 1-14 default = 6 +- `hidden` 0 = not hidden, 1 = hidden, default 0 +- `max` maximal number of connections 1-4 default=4 +- `beacon` beacon interval time in range 100-60000, default = 100 #### Returns -- `true` Success -- `false` Failure +`nil` #### Example: ```lua @@ -1025,7 +785,7 @@ Sets SSID and password in AP mode. Be sure to make the password at least 8 chara ## wifi.ap.deauth() -Deauths (forcibly removes) a client from the ESP access point by sending a corresponding IEEE802.11 management packet (first) and removing the client from it's data structures (afterwards). +Deauths (forcibly removes) a client from the ESP access point by sending a corresponding IEEE802.11 management packet (first) and removing the client from it's data structures (afterwards). The IEEE802.11 reason code used is 2 for "Previous authentication no longer valid"(AUTH_EXPIRE). @@ -1043,11 +803,11 @@ Returns true unless called while the ESP is in the STATION opmode ```lua allowed_mac_list={"18:fe:34:00:00:00", "18:fe:34:00:00:01"} -wifi.eventmon.register(wifi.eventmon.AP_STACONNECTED, function(T) +wifi.eventmon.register(wifi.eventmon.AP_STACONNECTED, function(T) print("\n\tAP - STATION CONNECTED".."\n\tMAC: "..T.MAC.."\n\tAID: "..T.AID) if(allowed_mac_list~=nil) then - for _, v in pairs(allowed_mac_list) do - if(v == T.MAC) then return end + for _, v in pairs(allowed_mac_list) do + if(v == T.MAC) then return end end end wifi.ap.deauth(T.MAC) @@ -1071,7 +831,7 @@ Gets broadcast address in AP mode. none #### Returns -broadcast address in string, for example "192.168.0.255", +broadcast address in string, for example "192.168.0.255", returns `nil` if IP address = "0.0.0.0". #### Example @@ -1111,102 +871,6 @@ for mac,ip in pairs(wifi.ap.getclient()) do end ``` -## wifi.ap.getconfig() - -Gets the current SoftAP configuration. - -#### Syntax -`wifi.ap.getconfig(return_table)` - -#### Parameters -- `return_table` - - `true` returns data in a table - - `false` returns data in the old format (default) - -#### Returns -If `return_table` is true: -- `config_table` - - `ssid` Network name - - `pwd` Password - - If no password was configured, the `pwd` field will be `nil` - - `auth` Authentication Method (`wifi.OPEN`, `wifi.WPA_PSK`, `wifi.WPA2_PSK` or `wifi.WPA_WPA2_PSK`) - - `channel` Channel number - - `hidden` `false` = not hidden, `true` = hidden - - `max` Maximum number of client connections - - `beacon` Beacon interval - -If `return_table` is false: -ssid, password - Note: If bssid_set is equal to 0 then bssid is irrelevant - -#### Example - -```lua ---Get SoftAP configuration table (NEW FORMAT) -do - print("\n Current SoftAP configuration:") - for k,v in pairs(wifi.ap.getconfig(true)) do - print(" "..k.." :",v) - end -end - ---Get current SoftAP configuration (OLD FORMAT) -do - local ssid, password=wifi.ap.getconfig() - print("\n Current SoftAP configuration:\n SSID : "..ssid.. - "\n Password :",password) - ssid, password=nil, nil -end -``` - -## wifi.ap.getdefaultconfig() - -Gets the default SoftAP configuration stored in flash. - -#### Syntax -`wifi.ap.getdefaultconfig(return_table)` - -#### Parameters -- `return_table` - - `true` returns data in a table - - `false` returns data in the old format (default) - -#### Returns -If `return_table` is true: -- `config_table` - - `ssid` Network name - - `pwd` Password - - If no password was configured, the `pwd` field will be `nil` - - `auth` Authentication Method (`wifi.OPEN`, `wifi.WPA_PSK`, `wifi.WPA2_PSK` or `wifi.WPA_WPA2_PSK`) - - `channel` Channel number - - `hidden` `false` = not hidden, `true` = hidden - - `max` Maximum number of client connections - - `beacon` Beacon interval - -If `return_table` is false: -ssid, password - Note: If bssid_set is equal to 0 then bssid is irrelevant - -#### Example - -```lua ---Get default SoftAP configuration table (NEW FORMAT) -do - print("\n Default SoftAP configuration:") - for k,v in pairs(wifi.ap.getdefaultconfig(true)) do - print(" "..k.." :",v) - end -end - ---Get default SoftAP configuration (OLD FORMAT) -do - local ssid, password=wifi.ap.getdefaultconfig() - print("\n Default SoftAP configuration:\n SSID : "..ssid.. - "\n Password :",password) - ssid, password=nil, nil -end -``` - ## wifi.ap.getip() Gets IP address, netmask and gateway in AP mode. @@ -1413,39 +1077,39 @@ T: Table returned by event. #### Example ```lua - wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T) + wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T) print("\n\tSTA - CONNECTED".."\n\tSSID: "..T.SSID.."\n\tBSSID: ".. T.BSSID.."\n\tChannel: "..T.channel) end) - - wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T) + + wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T) print("\n\tSTA - DISCONNECTED".."\n\tSSID: "..T.SSID.."\n\tBSSID: ".. T.BSSID.."\n\treason: "..T.reason) end) - wifi.eventmon.register(wifi.eventmon.STA_AUTHMODE_CHANGE, Function(T) + wifi.eventmon.register(wifi.eventmon.STA_AUTHMODE_CHANGE, Function(T) print("\n\tSTA - AUTHMODE CHANGE".."\n\told_auth_mode: ".. - T.old_auth_mode.."\n\tnew_auth_mode: "..T.new_auth_mode) + T.old_auth_mode.."\n\tnew_auth_mode: "..T.new_auth_mode) end) - wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T) + wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T) print("\n\tSTA - GOT IP".."\n\tStation IP: "..T.IP.."\n\tSubnet mask: ".. T.netmask.."\n\tGateway IP: "..T.gateway) end) - wifi.eventmon.register(wifi.eventmon.STA_DHCP_TIMEOUT, function() + wifi.eventmon.register(wifi.eventmon.STA_DHCP_TIMEOUT, function() print("\n\tSTA - DHCP TIMEOUT") end) - wifi.eventmon.register(wifi.eventmon.AP_STACONNECTED, function(T) + wifi.eventmon.register(wifi.eventmon.AP_STACONNECTED, function(T) print("\n\tAP - STATION CONNECTED".."\n\tMAC: "..T.MAC.."\n\tAID: "..T.AID) end) - wifi.eventmon.register(wifi.eventmon.AP_STADISCONNECTED, function(T) + wifi.eventmon.register(wifi.eventmon.AP_STADISCONNECTED, function(T) print("\n\tAP - STATION DISCONNECTED".."\n\tMAC: "..T.MAC.."\n\tAID: "..T.AID) end) - wifi.eventmon.register(wifi.eventmon.AP_PROBEREQRECVED, function(T) + wifi.eventmon.register(wifi.eventmon.AP_PROBEREQRECVED, function(T) print("\n\tAP - STATION DISCONNECTED".."\n\tMAC: ".. T.MAC.."\n\tRSSI: "..T.RSSI) end) ``` diff --git a/docs/en/modules/ws2812.md b/docs/en/modules/ws2812.md index 21f32ae5..ed8a8a2f 100644 --- a/docs/en/modules/ws2812.md +++ b/docs/en/modules/ws2812.md @@ -71,7 +71,7 @@ ws2812.write(nil, string.char(0, 255, 0, 0, 255, 0)) -- turn the two first RGB l For more advanced animations, it is useful to keep a "framebuffer" of the strip, interact with it and flush it to the strip. -For this purpose, the ws2812 library offers a read/write buffer. This buffer has a `__tostring` method so that it can be printed. This is useful for debugging. +For this purpose, the ws2812 library offers a read/write buffer. #### Example Led chaser with a RGBW strip @@ -181,92 +181,6 @@ The number of given bytes must match the number of bytesPerLed of the buffer buffer:fill(0, 0, 0) -- fill the buffer with black for a RGB strip ``` -## ws2812.buffer:dump() -Returns the contents of the buffer (the pixel values) as a string. This can then be saved to a file or sent over a network. - -#### Syntax -`buffer:dump()` - -#### Returns -A string containing the pixel values. - -#### Example -```lua -local s = buffer:dump() -``` - -## ws2812.buffer:replace() -Inserts a string (or a buffer) into another buffer with an offset. -The buffer must have the same number of colors per led or an error will be thrown. - -#### Syntax -`buffer:replace(source[, offset])` - -#### Parameters - - `source` the pixel values to be set into the buffer. This is either a string or a buffer. - - `offset` the offset where the source is to be placed in the buffer. Default is 1. Negative values can be used. - -#### Returns -`nil` - -#### Example -```lua -buffer:replace(anotherbuffer:dump()) -- copy one buffer into another via a string -buffer:replace(anotherbuffer) -- copy one buffer into another -newbuffer = buffer.sub(1) -- make a copy of a buffer into a new buffer -``` - -## ws2812.buffer:mix() -This is a general method that loads data into a buffer that is a linear combination of data from other buffers. It can be used to copy a buffer or, -more usefully, do a cross fade. The pixel values are computed as integers and then range limited to [0, 255]. This means that negative -factors work as expected, and that the order of combining buffers does not matter. - -#### Syntax -`buffer:mix(factor1, buffer1, ...)` - -#### Parameters - - `factor1` This is the factor that the contents of `buffer1` are multiplied by. This factor is scaled by a factor of 256. Thus `factor1` value of 256 is a factor of 1.0. - - `buffer1` This is the source buffer. It must be of the same shape as the destination buffer. - -There can be any number of factor/buffer pairs. - -#### Returns -`nil` - -#### Example -```lua --- loads buffer with a crossfade between buffer1 and buffer2 -buffer:mix(256 - crossmix, buffer1, crossmix, buffer2) - --- multiplies all values in buffer by 0.75 --- This can be used in place of buffer:fade -buffer:mix(192, buffer) -``` - -## ws2812.buffer:power() -Computes the total energy requirement for the buffer. This is merely the total sum of all the pixel values (which assumes that each color in each -pixel consumes the same amount of power). A real WS2812 (or WS2811) has three constant current drivers of 20mA -- one for each of R, G and B. The -pulse width modulation will cause the *average* current to scale linearly with pixel value. - -#### Syntax -`buffer:power()` - -#### Returns -An integer which is the sum of all the pixel values. - -#### Example -```lua --- Dim the buffer to no more than the PSU can provide -local psu_current_ma = 1000 -local led_current_ma = 20 -local led_sum = psu_current_ma * 255 / led_current_ma - -local p = buffer:power() -if p > led_sum then - buffer:mix(256 * led_sum / p, buffer) -- power is now limited -end -``` - ## ws2812.buffer:fade() Fade in or out. Defaults to out. Multiply or divide each byte of each led with/by the given value. Useful for a fading effect. @@ -286,17 +200,14 @@ buffer:fade(2) buffer:fade(2, ws2812.FADE_IN) ``` ## ws2812.buffer:shift() -Shift the content of (a piece of) the buffer in positive or negative direction. This allows simple animation effects. A slice of the buffer can be specified by using the -standard start and end offset Lua notation. Negative values count backwards from the end of the buffer. +Shift the content of the buffer in positive or negative direction. This allows simple animation effects. #### Syntax -`buffer:shift(value [, mode[, i[, j]]])` +`buffer:shift(value [, mode])` #### Parameters - `value` number of pixels by which to rotate the buffer. Positive values rotate forwards, negative values backwards. - `mode` is the shift mode to use. Can be one of `ws2812.SHIFT_LOGICAL` or `ws2812.SHIFT_CIRCULAR`. In case of SHIFT\_LOGICAL, the freed pixels are set to 0 (off). In case of SHIFT\_CIRCULAR, the buffer is treated like a ring buffer, inserting the pixels falling out on one end again on the other end. Defaults to SHIFT\_LOGICAL. - - `i` is the first offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is 1. - - `j` is the last offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is -1. #### Returns `nil` @@ -305,42 +216,3 @@ standard start and end offset Lua notation. Negative values count backwards from ```lua buffer:shift(3) ``` - -## ws2812.buffer:sub() -This implements the extraction function like `string.sub`. The indexes are in leds and all the same rules apply. - -#### Syntax -`buffer1:sub(i[, j])` - -#### Parameters - - `i` This is the start of the extracted data. Negative values can be used. - - `j` this is the end of the extracted data. Negative values can be used. The default is -1. - -#### Returns -A buffer containing the extracted piece. - -#### Example -``` -b = buffer:sub(1,10) -``` - -## ws2812.buffer:__concat() -This implements the `..` operator to concatenate two buffers. They must have the same number of colors per led. - -#### Syntax -`buffer1 .. buffer2` - -#### Parameters - - `buffer1` this is the start of the resulting buffer - - `buffer2` this is the end of the resulting buffer - -#### Returns -The concatenated buffer. - -#### Example -``` -ws2812.write(buffer1 .. buffer2) -``` - - - diff --git a/lua_examples/somfy.lua b/lua_examples/somfy.lua deleted file mode 100644 index f3baa3ed..00000000 --- a/lua_examples/somfy.lua +++ /dev/null @@ -1,145 +0,0 @@ --- Somfy module example (beside somfy module requires also CJSON module) --- The rolling code number is stored in the file somfy.cfg. A cached write of the somfy.cfg file is implemented in order to reduce the number of write to the EEPROM memory. Together with the logic of the file module it should allow long lasting operation. - -config_file = "somfy." --- somfy.cfg looks like --- {"window1":{"rc":1,"address":123},"window2":{"rc":1,"address":124}} - -local tmr_cache = tmr.create() -local tmr_delay = tmr.create() - -pin = 4 -gpio.mode(pin, gpio.OUTPUT, gpio.PULLUP) - -function deepcopy(orig) - local orig_type = type(orig) - local copy - if orig_type == 'table' then - copy = {} - for orig_key, orig_value in next, orig, nil do - copy[deepcopy(orig_key)] = deepcopy(orig_value) - end - setmetatable(copy, deepcopy(getmetatable(orig))) - else -- number, string, boolean, etc - copy = orig - end - return copy -end - -function readconfig() - local cfg, ok, ln - if file.exists(config_file.."cfg") then - print("Reading config from "..config_file.."cfg") - file.open(config_file.."cfg", "r+") - ln = file.readline() - file.close() - else - if file.exists(config_file.."bak") then - print("Reading config from "..config_file.."bak") - file.open(config_file.."bak", "r+") - ln = file.readline() - file.close() - end - end - if not ln then ln = "{}" end - print("Configuration: "..ln) - config = cjson.decode(ln) - config_saved = deepcopy(config) -end - -function writeconfighard() - print("Saving config") - file.remove(config_file.."bak") - file.rename(config_file.."cfg", config_file.."bak") - file.open(config_file.."cfg", "w+") - local ok, cfg = pcall(cjson.encode, config) - if ok then - file.writeline(cfg) - else - print("Config not saved!") - end - file.close() - - config_saved = deepcopy(config) -end - -function writeconfig() - tmr.stop(tmr_cache) - local savenow = false - local savelater = false - ---print("Config: "..cjson.encode(config)) ---print("Config saved: "..cjson.encode(config)) - - local count = 0 - for _ in pairs(config_saved) do count = count + 1 end - if count == 0 then - config_saved = readconfig() - end - for remote,cfg in pairs(config_saved) do - savelater = savelater or not config[remote] or config[remote].rc > cfg.rc - savenow = savenow or not config[remote] or config[remote].rc > cfg.rc + 10 - end - savelater = savelater and not savenow - if savenow then - print("Saving config now!") - writeconfighard() - end - if savelater then - print("Saving config later") - tmr.alarm(tmr_cache, 65000, tmr.ALARM_SINGLE, writeconfighard) - end -end - ---======================================================================================================-- -function down(remote, cb, par) - par = par or {} - print("down: ".. remote) - config[remote].rc=config[remote].rc+1 - somfy.sendcommand(pin, config[remote].address, somfy.DOWN, config[remote].rc, 16, function() wait(100, cb, par) end) - writeconfig() -end - -function up(remote, cb, par) - par = par or {} - print("up: ".. remote) - config[remote].rc=config[remote].rc+1 - somfy.sendcommand(pin, config[remote].address, somfy.UP, config[remote].rc, 16, function() wait(100, cb, par) end) - writeconfig() -end - -function downStep(remote, cb, par) - par = par or {} - print("downStep: ".. remote) - config[remote].rc=config[remote].rc+1 - somfy.sendcommand(pin, config[remote].address, somfy.DOWN, config[remote].rc, 2, function() wait(300, cb, par) end) - writeconfig() -end - -function upStep(remote, cb, par) - par = par or {} - print("upStep: ".. remote) - config[remote].rc=config[remote].rc+1 - somfy.sendcommand(pin, config[remote].address, somfy.UP, config[remote].rc, 2, function() wait(300, cb, par) end) - writeconfig() -end - -function wait(ms, cb, par) - par = par or {} - print("wait: ".. ms) - if cb then tmr.alarm(tmr_delay, ms, tmr.ALARM_SINGLE, function () cb(unpack(par)) end) end -end - - ---======================================================================================================-- -if not config then readconfig() end -if #config == 0 then -- somfy.cfg does not exist - config = cjson.decode([[{"window1":{"rc":1,"address":123},"window2":{"rc":1,"address":124}}]]) - config_saved = deepcopy(config) -end -down('window1', - wait, {60000, - up, {'window1', - wait, {9000, - downStep, {'window1', downStep, {'window1', downStep, {'window1', downStep, {'window1', downStep, {'window1', downStep, {'window1', downStep, {'window1' -}}}}}}}}}}) diff --git a/sdk-overrides/include/user_interface.h b/sdk-overrides/include/user_interface.h index 61ce8356..b288f995 100644 --- a/sdk-overrides/include/user_interface.h +++ b/sdk-overrides/include/user_interface.h @@ -6,11 +6,5 @@ bool wifi_softap_deauth(uint8 mac[6]); uint8 get_fpm_auto_sleep_flag(void); -enum ext_flash_size_map { - FLASH_SIZE_32M_MAP_2048_2048 = 7, - FLASH_SIZE_64M_MAP = 8, - FLASH_SIZE_128M_MAP = 9 -}; - #endif /* SDK_OVERRIDES_INCLUDE_USER_INTERFACE_H_ */ diff --git a/tools/pr-build.sh b/tools/pr-build.sh index 7f715479..0bc34e94 100644 --- a/tools/pr-build.sh +++ b/tools/pr-build.sh @@ -21,7 +21,7 @@ cat user_config.h cd "$TRAVIS_BUILD_DIR"/ld || exit # increase irom0_0_seg size for all modules build -sed -E -i.bak 's@(.*irom0_0_seg *:.*len *=) *[^,]*(.*)@\1 0xD0000\2@' nodemcu.ld +sed -E -i.bak 's@(.*irom0_0_seg *:.*len *=) *[^,]*(.*)@\1 0xC0000\2@' nodemcu.ld cat nodemcu.ld # change to "root" directory no matter where the script was started from diff --git a/tools/spiffsimg/Makefile b/tools/spiffsimg/Makefile index 8656d114..82dfb797 100644 --- a/tools/spiffsimg/Makefile +++ b/tools/spiffsimg/Makefile @@ -2,7 +2,7 @@ SRCS=\ main.c \ ../../app/spiffs/spiffs_cache.c ../../app/spiffs/spiffs_check.c ../../app/spiffs/spiffs_gc.c ../../app/spiffs/spiffs_hydrogen.c ../../app/spiffs/spiffs_nucleus.c -CFLAGS=-g -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -I. -I../../app/spiffs -I../../app/include -DNODEMCU_SPIFFS_NO_INCLUDE --include spiffs_typedefs.h -Ddbg_printf=printf +CFLAGS=-g -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -I. -I../../app/spiffs -I../../app/include -DNODEMCU_SPIFFS_NO_INCLUDE --include spiffs_typedefs.h spiffsimg: $(SRCS) $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ From 22e1adc4b06c931797539b986c85e229e5942a5f Mon Sep 17 00:00:00 2001 From: Valera Date: Mon, 3 Apr 2017 14:34:32 +0300 Subject: [PATCH 11/11] Small fix in docs (#1897) dscription -> description --- docs/en/modules/mdns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/modules/mdns.md b/docs/en/modules/mdns.md index 2ea42470..315da841 100644 --- a/docs/en/modules/mdns.md +++ b/docs/en/modules/mdns.md @@ -23,7 +23,7 @@ The specific names are: - `port` The port number for the service. Default value is 80. - `service` The name of the service. Default value is 'http'. -- `dscription` A short phrase (under 63 characters) describing the service. Default is the hostname. +- `description` A short phrase (under 63 characters) describing the service. Default is the hostname. #### Returns `nil`