Commit ecf9c644 authored by Marcel Stör's avatar Marcel Stör
Browse files

Revert "Next 1.5.4.1 master drop (#1627)"

This reverts commit 04ce0adf.
parent ec265a6c
......@@ -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
......
......@@ -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
// 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;
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;
}
// 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;
}
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
......
......@@ -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
......
......@@ -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)
......
......@@ -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!!
......
......@@ -29,21 +29,11 @@
#include "rtc/rtctime.h"
#endif
static task_handle_t input_sig;
static uint8 input_sig_flag = 0;
/* 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"
);
#define SIG_LUA 0
#define SIG_UARTINPUT 1
#define TASK_QUEUE_LEN 4
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);
......
......@@ -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"
#define WS_INIT_REQUEST_LENGTH 30
// 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_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) {
......
......@@ -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;
......
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
When switching between NodeMCU versions, see the notes about
[Upgrading Firmware](#upgrading-firmware).
## Tool overview
## 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.
### 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 <serial-port-of-ESP8266> write_flash -fm <mode> 0x00000 <nodemcu-firmware>.bin`
`mode` is `qio` for 512&nbsp;kByte modules and `dio` for >=4&nbsp;MByte modules (`qio` might work as well, YMMV).
`esptool.py --port <serial-port-of-ESP8266> write_flash -fm <mode> -fs <size> 0x00000 <nodemcu-firmware>.bin`
**Gotchas**
- `mode` is `qio` for 512&nbsp;kByte modules and `dio` for 4&nbsp;MByte modules (`qio` might work as well, YMMV).
- `size` is given in bits. Specify `4m` for 512&nbsp;kByte and `32m` for 4&nbsp;MByte.
- 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 <serial-port-of-ESP8266> erase_flash`
Check the [esptool flash modes documentation](https://github.com/themadinventor/esptool#flash-modes) for details and other options.
### 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
## Upgrading Firmware
There are three potential issues that arise from upgrading (or downgrading!) firmware from one NodeMCU version to another:
* 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.
* 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.
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).
* 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.
## SDK Init Data
!!! note
Normally, NodeMCU will take care of writing the SDK init data when needed. Most users can ignore this section.
## Upgrading Firmware
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.
!!! important
!!! tip
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.
If you are seeing one or several of the above symptoms, ensure that your chip is fully erased before flashing, for example:
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.
`esptool.py --port <serial-port-of-ESP8266> erase_flash`
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.
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.
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.
Espressif refers to this area as "System Param" and it resides in the last four 4&nbsp;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.
Hence, there are two strategies to update the SDK init data:
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.
- 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.
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.
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.
- `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
**esptool.py**
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.
For [esptool.py](https://github.com/themadinventor/esptool) you specify the init data file as an additional file for the `write_flash` command.
## Determine flash size
```
esptool.py --port <serial-port-of-ESP8266> erase_flash
esptool.py --port <serial-port-of-ESP8266> write_flash <flash options> 0x00000 <nodemcu-firmware>.bin <init-data-address> esp_init_data_default.bin
```
To determine the capacity of the flash chip *before* a firmware is installed you can run
!!! note "Note:"
`esptool.py --port <serial-port> flash_id`
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
It will return a manufacturer ID and a chip ID like so:
**NodeMCU Flasher**
The [NodeMCU Flasher](https://github.com/nodemcu/nodemcu-flasher) will download init data using a special path:
```
Connecting...
Manufacturer: e0
Device: 4016
INTERNAL://DEFAULT
```
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.
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)
......@@ -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.
......
......@@ -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`
......
......@@ -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,245 +290,120 @@ if file.open("init.lua", "r") then
file.close()
end
```
#### Example (object model)
```lua
-- 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.close()`](#fileclose)
- [`file.readline()`](#filereadline)
## file.remove()
Remove a file from the file system. The file must not be currently open.
###Syntax
`file.remove(filename)`
#### Parameters
`filename` file to remove
#### Returns
`nil`
#### Example
```lua
-- remove "foo.lua" from file system.
file.remove("foo.lua")
```
#### See also
[`file.open()`](#fileopen)
## file.rename()
## file.read()
Renames a file. If a file is currently open, it will be closed first.
Read content from the open file.
#### Syntax
`file.rename(oldname, newname)`
`file.read([n_or_str])`
#### Parameters
- `oldname` old file name
- `newname` new file name
- `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
`true` on success, `false` on error.
File content as a string, or nil when EOF
#### Example
```lua
-- rename file 'temp.lua' to 'init.lua'.
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.
-- print the first line of 'init.lua'
if file.open("init.lua", "r") then
print(file.readline())
print(file.read('\n'))
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
-- print the first 5 bytes of 'init.lua'
if file.open("init.lua", "r") then
print(file.read(5))
file.close()
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.open()`](#fileopen)
- [`file.readline()`](#filereadline)
## file.flush()
## file.obj:flush()
## file.readline()
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.
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.flush()`
`fd:flush()`
`file.readline()`
#### Parameters
none
#### Returns
`nil`
File content in string, line by line, including EOL('\n'). Return `nil` when EOF.
#### Example (basic model)
#### 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')
-- print the first line of 'init.lua'
if file.open("init.lua", "r") then
print(file.readline())
file.close()
end
```
#### See also
[`file.close()` / `file.obj:close()`](#fileclose)
## file.read()
## file.obj:read()
Read content from the open file.
!!! note
- [`file.open()`](#fileopen)
- [`file.close()`](#fileclose)
- [`file.read()`](#filereade)
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`.
## file.remove()
#### Syntax
`file.read([n_or_char])`
Remove a file from the file system. The file must not be currently open.
`fd:read([n_or_char])`
###Syntax
`file.remove(filename)`
#### 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.
`filename` file to remove
#### Returns
File content as a string, or nil when EOF
`nil`
#### 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
#### 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
-- remove "foo.lua" from file system.
file.remove("foo.lua")
```
#### See also
- [`file.open()`](#fileopen)
- [`file.readline()` / `file.obj:readline()`](#filereadline)
[`file.open()`](#fileopen)
## file.readline()
## file.obj:readline()
## file.rename()
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.
Renames a file. If a file is currently open, it will be closed first.
#### Syntax
`file.readline()`
`fd:readline()`
`file.rename(oldname, newname)`
#### Parameters
none
- `oldname` old file name
- `newname` new file name
#### Returns
File content in string, line by line, including EOL('\n'). Return `nil` when EOF.
`true` on success, `false` on error.
#### Example
#### Example (basic model)
```lua
-- print the first line of 'init.lua'
if file.open("init.lua", "r") then
print(file.readline())
file.close()
end
-- rename file 'temp.lua' to 'init.lua'.
file.rename("temp.lua","init.lua")
```
#### 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)
......@@ -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
......
......@@ -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
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"}
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
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)
-- 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:
......
......@@ -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
......
......@@ -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
......
......@@ -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])`
......
# 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).
......@@ -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
......
......@@ -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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment