Commit 81ec3665 authored by Marcel Stör's avatar Marcel Stör Committed by GitHub
Browse files

Merge pull request #1653 from nodemcu/dev-for-drop

New December master drop
parents ecf9c644 85c3a249
...@@ -20,19 +20,19 @@ ...@@ -20,19 +20,19 @@
// Set generic spiffs debug output call. // Set generic spiffs debug output call.
#ifndef SPIFFS_DBG #ifndef SPIFFS_DBG
#define SPIFFS_DBG(...) //printf(__VA_ARGS__) #define SPIFFS_DBG(...) //dbg_printf(__VA_ARGS__)
#endif #endif
// Set spiffs debug output call for garbage collecting. // Set spiffs debug output call for garbage collecting.
#ifndef SPIFFS_GC_DBG #ifndef SPIFFS_GC_DBG
#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__) #define SPIFFS_GC_DBG(...) //dbg_printf(__VA_ARGS__)
#endif #endif
// Set spiffs debug output call for caching. // Set spiffs debug output call for caching.
#ifndef SPIFFS_CACHE_DBG #ifndef SPIFFS_CACHE_DBG
#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__) #define SPIFFS_CACHE_DBG(...) //dbg_printf(__VA_ARGS__)
#endif #endif
// Set spiffs debug output call for system consistency checks. // Set spiffs debug output call for system consistency checks.
#ifndef SPIFFS_CHECK_DBG #ifndef SPIFFS_CHECK_DBG
#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__) #define SPIFFS_CHECK_DBG(...) //dbg_printf(__VA_ARGS__)
#endif #endif
// Enable/disable API functions to determine exact number of bytes // Enable/disable API functions to determine exact number of bytes
...@@ -211,7 +211,7 @@ ...@@ -211,7 +211,7 @@
#endif #endif
#if SPIFFS_TEST_VISUALISATION #if SPIFFS_TEST_VISUALISATION
#ifndef spiffs_printf #ifndef spiffs_printf
#define spiffs_printf(...) printf(__VA_ARGS__) #define spiffs_printf(...) dbg_printf(__VA_ARGS__)
#endif #endif
// spiffs_printf argument for a free page // spiffs_printf argument for a free page
#ifndef SPIFFS_TEST_VIS_FREE_STR #ifndef SPIFFS_TEST_VIS_FREE_STR
......
...@@ -292,27 +292,60 @@ s32_t spiffs_probe( ...@@ -292,27 +292,60 @@ s32_t spiffs_probe(
SPIFFS_CHECK_RES(res); 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 // 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 // first block aborted erase
if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) { int fs_size;
return (bix_count[1]+1) * cfg->log_block_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 // second block aborted erase
if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) { if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 3) {
return bix_count[0] * cfg->log_block_size; fs_size = bix_count[0];
} } else
// third block aborted erase // third block aborted erase
if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) { 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 // no block has aborted erase
if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) { if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 2) {
return bix_count[0] * cfg->log_block_size; fs_size = bix_count[0];
} else {
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; 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 #endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
......
...@@ -137,7 +137,7 @@ ...@@ -137,7 +137,7 @@
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs)))
#else // SPIFFS_USE_MAGIC_LENGTH #else // SPIFFS_USE_MAGIC_LENGTH
#define SPIFFS_MAGIC(fs, bix) \ #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_LENGTH
#endif // SPIFFS_USE_MAGIC #endif // SPIFFS_USE_MAGIC
......
...@@ -1178,9 +1178,6 @@ struct _u8g_t ...@@ -1178,9 +1178,6 @@ struct _u8g_t
u8g_state_cb state_cb; u8g_state_cb state_cb;
u8g_box_t current_page; /* current box of the visible page */ 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) #define u8g_GetFontAscent(u8g) ((u8g)->font_ref_ascent)
......
...@@ -24,7 +24,7 @@ STD_CFLAGS=-std=gnu11 -Wimplicit ...@@ -24,7 +24,7 @@ STD_CFLAGS=-std=gnu11 -Wimplicit
# makefile at its root level - these are then overridden # makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein # 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!! # Recursion Magic - Don't touch this!!
......
...@@ -29,11 +29,21 @@ ...@@ -29,11 +29,21 @@
#include "rtc/rtctime.h" #include "rtc/rtctime.h"
#endif #endif
#define SIG_LUA 0 static task_handle_t input_sig;
#define SIG_UARTINPUT 1 static uint8 input_sig_flag = 0;
#define TASK_QUEUE_LEN 4
/* Contents of esp_init_data_default.bin */
static os_event_t *taskQueue; 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 /* 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 * 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) ...@@ -50,6 +60,31 @@ void TEXT_SECTION_ATTR user_start_trampoline (void)
rtctime_early_startup (); rtctime_early_startup ();
#endif #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 (); call_user_start ();
} }
...@@ -58,17 +93,18 @@ static void start_lua(task_param_t param, uint8 priority) { ...@@ -58,17 +93,18 @@ static void start_lua(task_param_t param, uint8 priority) {
char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL }; char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL };
NODE_DBG("Task task_lua started.\n"); NODE_DBG("Task task_lua started.\n");
lua_main( 2, lua_argv ); 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) { static void handle_input(task_param_t flag, uint8 priority) {
// c_printf("HANDLE_INPUT: %u %u\n", flag, priority); REMOVE (void)priority;
lua_handle_input (flag); if (flag & 0x8000) {
} input_sig_flag = flag & 0x4000 ? 1 : 0;
}
static task_handle_t input_sig; lua_handle_input (flag & 0x01);
task_handle_t user_get_input_sig(void) {
return input_sig;
} }
bool user_process_input(bool force) { bool user_process_input(bool force) {
...@@ -92,9 +128,7 @@ void nodemcu_init(void) ...@@ -92,9 +128,7 @@ void nodemcu_init(void)
// Fit hardware real flash size. // Fit hardware real flash size.
flash_rom_set_size_byte(flash_safe_get_size_byte()); 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 (); system_restart ();
// Don't post the start_lua task, we're about to reboot... // Don't post the start_lua task, we're about to reboot...
return; return;
} }
...@@ -107,7 +141,7 @@ void nodemcu_init(void) ...@@ -107,7 +141,7 @@ void nodemcu_init(void)
#ifdef BUILD_SPIFFS #ifdef BUILD_SPIFFS
if (!vfs_mount("/FLASH", 0)) { if (!vfs_mount("/FLASH", 0)) {
// Failed to mount -- try reformat // Failed to mount -- try reformat
c_printf("Formatting file system. Please wait...\n"); dbg_printf("Formatting file system. Please wait...\n");
if (!vfs_format()) { if (!vfs_format()) {
NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" ); NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" );
NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" ); NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" );
...@@ -118,7 +152,8 @@ void nodemcu_init(void) ...@@ -118,7 +152,8 @@ void nodemcu_init(void)
#endif #endif
// endpoint_setup(); // 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 #ifdef LUA_USE_MODULES_WIFI
...@@ -146,7 +181,7 @@ void user_rf_pre_init(void) ...@@ -146,7 +181,7 @@ void user_rf_pre_init(void)
uint32 uint32
user_rf_cal_sector_set(void) 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; uint32 rf_cal_sec = 0;
switch (size_map) { switch (size_map) {
...@@ -165,9 +200,18 @@ user_rf_cal_sector_set(void) ...@@ -165,9 +200,18 @@ user_rf_cal_sector_set(void)
case FLASH_SIZE_32M_MAP_512_512: case FLASH_SIZE_32M_MAP_512_512:
case FLASH_SIZE_32M_MAP_1024_1024: case FLASH_SIZE_32M_MAP_1024_1024:
case FLASH_SIZE_32M_MAP_2048_2048:
rf_cal_sec = 1024 - 5; rf_cal_sec = 1024 - 5;
break; break;
case FLASH_SIZE_64M_MAP:
rf_cal_sec = 2048 - 5;
break;
case FLASH_SIZE_128M_MAP:
rf_cal_sec = 4096 - 5;
break;
default: default:
rf_cal_sec = 0; rf_cal_sec = 0;
break; break;
...@@ -191,7 +235,7 @@ void user_init(void) ...@@ -191,7 +235,7 @@ void user_init(void)
UartBautRate br = BIT_RATE_DEFAULT; UartBautRate br = BIT_RATE_DEFAULT;
input_sig = task_get_id(handle_input); 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 #ifndef NODE_DEBUG
system_set_os_print(0); system_set_os_print(0);
......
...@@ -47,18 +47,10 @@ ...@@ -47,18 +47,10 @@
#define PORT_INSECURE 80 #define PORT_INSECURE 80
#define PORT_MAX_VALUE 65535 #define PORT_MAX_VALUE 65535
// TODO: user agent configurable #define WS_INIT_REQUEST "GET %s HTTP/1.1\r\n"\
#define WS_INIT_HEADERS "GET %s HTTP/1.1\r\n"\ "Host: %s:%d\r\n"
"Host: %s:%d\r\n"\
"Upgrade: websocket\r\n"\ #define WS_INIT_REQUEST_LENGTH 30
"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 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
#define WS_GUID_LENGTH 36 #define WS_GUID_LENGTH 36
...@@ -77,6 +69,13 @@ ...@@ -77,6 +69,13 @@
#define WS_OPCODE_PING 0x9 #define WS_OPCODE_PING 0x9
#define WS_OPCODE_PONG 0xA #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) { static char *cryptoSha1(char *data, unsigned int len) {
SHA1_CTX ctx; SHA1_CTX ctx;
SHA1Init(&ctx); SHA1Init(&ctx);
...@@ -128,6 +127,44 @@ static void generateSecKeys(char **key, char **expectedKey) { ...@@ -128,6 +127,44 @@ static void generateSecKeys(char **key, char **expectedKey) {
os_free(keyEncrypted); 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) { static void ws_closeSentCallback(void *arg) {
NODE_DBG("ws_closeSentCallback \n"); NODE_DBG("ws_closeSentCallback \n");
struct espconn *conn = (struct espconn *) arg; struct espconn *conn = (struct espconn *) arg;
...@@ -452,7 +489,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) { ...@@ -452,7 +489,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) {
} else if (opCode == WS_OPCODE_PONG) { } else if (opCode == WS_OPCODE_PONG) {
// ping alarm was already reset... // ping alarm was already reset...
} else { } else {
if (ws->onReceive) ws->onReceive(ws, payload, opCode); if (ws->onReceive) ws->onReceive(ws, payloadLength, payload, opCode);
} }
os_free(payload); os_free(payload);
} }
...@@ -509,7 +546,7 @@ static void ws_initReceiveCallback(void *arg, char *buf, unsigned short len) { ...@@ -509,7 +546,7 @@ static void ws_initReceiveCallback(void *arg, char *buf, unsigned short len) {
} }
// Check server has valid sec key // 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"); NODE_DBG("Server has invalid response\n");
ws->knownFailureCode = -7; ws->knownFailureCode = -7;
if (ws->isSecure) if (ws->isSecure)
...@@ -550,12 +587,31 @@ static void connect_callback(void *arg) { ...@@ -550,12 +587,31 @@ static void connect_callback(void *arg) {
char *key; char *key;
generateSecKeys(&key, &ws->expectedSecKey); generateSecKeys(&key, &ws->expectedSecKey);
char buf[WS_INIT_HEADERS_LENGTH + strlen(ws->path) + strlen(ws->hostname) + strlen(key)]; header_t headers[] = {
int len = os_sprintf(buf, WS_INIT_HEADERS, ws->path, ws->hostname, ws->port, key); {"Upgrade", "websocket"},
{"Connection", "Upgrade"},
{"Sec-WebSocket-Key", key},
{"Sec-WebSocket-Version", "13"},
{0}
};
os_free(key); 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];
NODE_DBG("connecting\n"); 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("request: %s", buf);
if (ws->isSecure) if (ws->isSecure)
espconn_secure_send(conn, (uint8_t *) buf, len); espconn_secure_send(conn, (uint8_t *) buf, len);
else else
......
...@@ -40,9 +40,14 @@ ...@@ -40,9 +40,14 @@
struct ws_info; struct ws_info;
typedef void (*ws_onConnectionCallback)(struct ws_info *wsInfo); 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 void (*ws_onFailureCallback)(struct ws_info *wsInfo, int errorCode);
typedef struct {
char *key;
char *value;
} header_t;
typedef struct ws_info { typedef struct ws_info {
int connectionState; int connectionState;
...@@ -51,6 +56,7 @@ typedef struct ws_info { ...@@ -51,6 +56,7 @@ typedef struct ws_info {
int port; int port;
char *path; char *path;
char *expectedSecKey; char *expectedSecKey;
header_t *extraHeaders;
struct espconn *conn; struct espconn *conn;
void *reservedData; void *reservedData;
......
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 !!! 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! 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 !!! important
> 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
Source: [https://github.com/themadinventor/esptool](https://github.com/themadinventor/esptool) ### 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 Supported platforms: OS X, Linux, Windows, anything that runs Python
...@@ -15,27 +22,33 @@ 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). 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> -fs <size> 0x00000 <nodemcu-firmware>.bin` `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).
- `mode` is `qio` for 512&nbsp;kByte modules and `dio` for 4&nbsp;MByte modules (`qio` might work as well, YMMV). **Gotchas**
- `size` is given in bits. Specify `4m` for 512&nbsp;kByte and `32m` for 4&nbsp;MByte.
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 <serial-port-of-ESP8266> 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. > 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) Source: [https://github.com/nodemcu/nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher)
Supported platforms: Windows 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 ## 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. 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 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 ## Which Files To Flash
...@@ -46,55 +59,59 @@ Otherwise, if you built your own firmware from source code: ...@@ -46,55 +59,59 @@ Otherwise, if you built your own firmware from source code:
- `bin/0x00000.bin` to 0x00000 - `bin/0x00000.bin` to 0x00000
- `bin/0x10000.bin` to 0x10000 - `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 ## 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. Normally, NodeMCU will take care of writing the SDK init data when needed. Most users can ignore this section.
- 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.
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 <serial-port-of-ESP8266> erase_flash`
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
```
!!! note "Note:" 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.
The address for `esp_init_data_default.bin` depends on the size of your module's flash. 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.
- `0x7c000` for 512 kB, modules like ESP-01, -03, -07 etc. 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.
- `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** 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.
The [NodeMCU Flasher](https://github.com/nodemcu/nodemcu-flasher) will download init data using a special path: - `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.
INTERNAL://DEFAULT - `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
Replace the provided (old) `esp_init_data_default.bin` with the one extracted above and use the flasher like you're used to. To determine the capacity of the flash chip *before* a firmware is installed you can run
**References** `esptool.py --port <serial-port> flash_id`
It will return a manufacturer ID and a chip ID like so:
```
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.
* [2A-ESP8266__IOT_SDK_User_Manual__EN_v1.5.pdf, Chapter 6](http://bbs.espressif.com/viewtopic.php?f=51&t=1024) 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.
* [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)) ...@@ -44,7 +44,7 @@ print(gpio.read(pin))
``` ```
## Getting Started ## 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. [Flash the firmware](flash.md) to the chip.
1. [Upload code](upload.md) to the firmware. 1. [Upload code](upload.md) to the firmware.
......
...@@ -7,10 +7,29 @@ This module provides a simple way of configuring ESP8266 chips without using a s ...@@ -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") ![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 running [`enduser_setup.start()`](#enduser_setupstart), a wireless network named "SetupGadget_XXXXXX" will start. Connect to that SSID and then navigate to the root
After an IP address has been successfully obtained this module will stop as if [`enduser_setup.stop()`](#enduser_setupstop) had been called. 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() ## enduser_setup.manual()
...@@ -53,7 +72,7 @@ Starts the captive portal. ...@@ -53,7 +72,7 @@ Starts the captive portal.
#### Parameters #### Parameters
- `onConnected()` callback will be fired when an IP-address has been obtained, just before the enduser_setup module will terminate itself - `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. - `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 #### Returns
`nil` `nil`
......
...@@ -7,8 +7,6 @@ The file module provides access to the file system and its individual files. ...@@ -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. 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). 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 ```lua
...@@ -43,30 +41,6 @@ Current directory defaults to the root of internal SPIFFS (`/FLASH`) after syste ...@@ -43,30 +41,6 @@ Current directory defaults to the root of internal SPIFFS (`/FLASH`) after syste
#### Returns #### Returns
`true` on success, `false` otherwise `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() ## file.exists()
Determines whether the specified file exists. Determines whether the specified file exists.
...@@ -95,34 +69,6 @@ end ...@@ -95,34 +69,6 @@ end
#### See also #### See also
[`file.list()`](#filelist) [`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() ## 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. 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()`. ...@@ -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 - "a+": append update mode, previous data is preserved, writing is only allowed at the end of file
#### Returns #### 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 ```lua
-- open 'init.lua', print the first line. -- open 'init.lua', print the first line.
if file.open("init.lua", "r") then if file.open("init.lua", "r") then
...@@ -290,120 +236,245 @@ if file.open("init.lua", "r") then ...@@ -290,120 +236,245 @@ if file.open("init.lua", "r") then
file.close() file.close()
end 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 #### See also
- [`file.close()`](#fileclose) - [`file.close()`](#fileclose)
- [`file.readline()`](#filereadline) - [`file.readline()`](#filereadline)
## file.read() ## file.remove()
Read content from the open file. 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()
Renames a file. If a file is currently open, it will be closed first.
#### Syntax #### Syntax
`file.read([n_or_str])` `file.rename(oldname, newname)`
#### Parameters #### Parameters
- `n_or_str`: - `oldname` old file name
- if nothing passed in, read up to `LUAL_BUFFERSIZE` bytes (default 1024) or the entire file (whichever is smaller) - `newname` new file name
- 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 #### Returns
File content as a string, or nil when EOF `true` on success, `false` on error.
#### Example #### Example
```lua ```lua
-- print the first line of 'init.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.
if file.open("init.lua", "r") then if file.open("init.lua", "r") then
print(file.read('\n')) print(file.readline())
file.close() file.close()
end end
```
-- print the first 5 bytes of 'init.lua' ## Object model
if file.open("init.lua", "r") then 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.
print(file.read(5))
file.close() ```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 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 #### See also
- [`file.open()`](#fileopen) [`file.open()`](#fileopen)
- [`file.readline()`](#filereadline)
## file.readline() ## file.flush()
## file.obj:flush()
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). 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 #### Syntax
`file.readline()` `file.flush()`
`fd:flush()`
#### Parameters #### Parameters
none none
#### Returns #### Returns
File content in string, line by line, including EOL('\n'). Return `nil` when EOF. `nil`
#### Example #### Example (basic model)
```lua ```lua
-- print the first line of 'init.lua' -- open 'init.lua' in 'a+' mode
if file.open("init.lua", "r") then if file.open("init.lua", "a+") then
print(file.readline()) -- write 'foo bar' to the end of the file
file.write('foo bar')
file.flush()
-- write 'baz' too
file.write('baz')
file.close() file.close()
end end
``` ```
#### See also #### See also
- [`file.open()`](#fileopen) [`file.close()` / `file.obj:close()`](#fileclose)
- [`file.close()`](#fileclose)
- [`file.read()`](#filereade)
## file.remove() ## file.read()
## file.obj:read()
Remove a file from the file system. The file must not be currently open. Read content from the open file.
###Syntax !!! note
`file.remove(filename)`
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 #### Parameters
`filename` file to remove - `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 #### Returns
`nil` File content as a string, or nil when EOF
#### Example #### 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 ```lua
-- remove "foo.lua" from file system. -- print the first 5 bytes of 'init.lua'
file.remove("foo.lua") fd = file.open("init.lua", "r")
if fd then
print(fd:read(5))
fd:close(); fd = nil
end
``` ```
#### See also #### See also
[`file.open()`](#fileopen) - [`file.open()`](#fileopen)
- [`file.readline()` / `file.obj:readline()`](#filereadline)
## file.rename() ## file.readline()
## file.obj:readline()
Renames a file. If a file is currently open, it will be closed first. 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 #### Syntax
`file.rename(oldname, newname)` `file.readline()`
`fd:readline()`
#### Parameters #### Parameters
- `oldname` old file name none
- `newname` new file name
#### Returns #### Returns
`true` on success, `false` on error. File content in string, line by line, including EOL('\n'). Return `nil` when EOF.
#### Example
#### Example (basic model)
```lua ```lua
-- rename file 'temp.lua' to 'init.lua'. -- print the first line of 'init.lua'
file.rename("temp.lua","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.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. 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 #### Syntax
`file.seek([whence [, offset]])` `file.seek([whence [, offset]])`
`fd:seek([whence [, offset]])`
#### Parameters #### Parameters
- `whence` - `whence`
- "set": base is position 0 (beginning of the file) - "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. ...@@ -416,7 +487,7 @@ If no parameters are given, the function simply returns the current file offset.
#### Returns #### Returns
the resulting file position, or `nil` on error the resulting file position, or `nil` on error
#### Example #### Example (basic model)
```lua ```lua
if file.open("init.lua", "r") then if file.open("init.lua", "r") then
-- skip the first 5 bytes of the file -- skip the first 5 bytes of the file
...@@ -429,19 +500,22 @@ end ...@@ -429,19 +500,22 @@ end
[`file.open()`](#fileopen) [`file.open()`](#fileopen)
## file.write() ## file.write()
## file.obj:write()
Write a string to the open file. Write a string to the open file.
#### Syntax #### Syntax
`file.write(string)` `file.write(string)`
`fd:write(string)`
#### Parameters #### Parameters
`string` content to be write to file `string` content to be write to file
#### Returns #### Returns
`true` if the write is ok, `nil` on error `true` if the write is ok, `nil` on error
#### Example #### Example (basic model)
```lua ```lua
-- open 'init.lua' in 'a+' mode -- open 'init.lua' in 'a+' mode
if file.open("init.lua", "a+") then if file.open("init.lua", "a+") then
...@@ -451,24 +525,38 @@ if file.open("init.lua", "a+") then ...@@ -451,24 +525,38 @@ if file.open("init.lua", "a+") then
end 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 #### See also
- [`file.open()`](#fileopen) - [`file.open()`](#fileopen)
- [`file.writeline()`](#filewriteline) - [`file.writeline()` / `file.obj:writeline()`](#filewriteline)
## file.writeline() ## file.writeline()
## file.obj:writeline()
Write a string to the open file and append '\n' at the end. Write a string to the open file and append '\n' at the end.
#### Syntax #### Syntax
`file.writeline(string)` `file.writeline(string)`
`fd:writeline(string)`
#### Parameters #### Parameters
`string` content to be write to file `string` content to be write to file
#### Returns #### Returns
`true` if write ok, `nil` on error `true` if write ok, `nil` on error
#### Example #### Example (basic model)
```lua ```lua
-- open 'init.lua' in 'a+' mode -- open 'init.lua' in 'a+' mode
if file.open("init.lua", "a+") then if file.open("init.lua", "a+") then
...@@ -480,4 +568,4 @@ end ...@@ -480,4 +568,4 @@ end
#### See also #### See also
- [`file.open()`](#fileopen) - [`file.open()`](#fileopen)
- [`file.readline()`](#filereadline) - [`file.readline()` / `file.obj:readline()`](#filereadline)
...@@ -69,28 +69,29 @@ gpio.read(0) ...@@ -69,28 +69,29 @@ gpio.read(0)
## gpio.serout() ## 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: The function works in two modes:
* synchronous - for sub-50 µs resolution, restricted to max. overall duration, * synchronous - for sub-50 µs resolution, restricted to max. overall duration,
* asynchrounous - synchronous operation with less granularity but virtually unrestricted 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). 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 inf `FRC1_SOURCE` mode is used to change the states. 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 #### Syntax
`gpio.serout(pin, start_level, delay_times [, repeat_num[, callback]])` `gpio.serout(pin, start_level, delay_times [, cycle_num[, callback]])`
#### Parameters #### Parameters
- `pin` pin to use, IO index - `pin` pin to use, IO index
- `start_level` level to start on, either `gpio.HIGH` or `gpio.LOW` - `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. - `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. - `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 ruturns immediately and goes asynchronous. - `callback` an optional callback function or number, if present the function returns immediately and goes asynchronous.
#### Returns #### Returns
......
...@@ -135,15 +135,21 @@ Listen on port from IP address. ...@@ -135,15 +135,21 @@ Listen on port from IP address.
#### Example #### Example
```lua ```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 -- server listens on 80, if data received, print data to console and send "hello world" back to caller
sv:listen(80, function(c) -- 30s time out for a inactive client
c:on("receive", function(c, pl) sv = net.createServer(net.TCP, 30)
print(pl)
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) end)
c:send("hello world") end
end)
``` ```
#### See also #### See also
...@@ -303,8 +309,8 @@ Multiple consecutive `send()` calls aren't guaranteed to work (and often don't) ...@@ -303,8 +309,8 @@ Multiple consecutive `send()` calls aren't guaranteed to work (and often don't)
#### Example #### Example
```lua ```lua
srv = net.createServer(net.TCP) srv = net.createServer(net.TCP)
srv:listen(80, function(conn)
conn:on("receive", function(sck, req) function receiver(sck, data)
local response = {} local response = {}
-- if you're sending back HTML over HTTP you'll want something like this instead -- if you're sending back HTML over HTTP you'll want something like this instead
...@@ -315,11 +321,11 @@ srv:listen(80, function(conn) ...@@ -315,11 +321,11 @@ srv:listen(80, function(conn)
response[#response + 1] = "e.g. content read from a file" response[#response + 1] = "e.g. content read from a file"
-- sends and removes the first element from the 'response' table -- sends and removes the first element from the 'response' table
local function send(sk) local function send(localSocket)
if #response > 0 if #response > 0
then sk:send(table.remove(response, 1)) then localSocket:send(table.remove(response, 1))
else else
sk:close() localSocket:close()
response = nil response = nil
end end
end end
...@@ -328,7 +334,10 @@ srv:listen(80, function(conn) ...@@ -328,7 +334,10 @@ srv:listen(80, function(conn)
sck:on("sent", send) sck:on("sent", send)
send(sck) send(sck)
end) end
srv:listen(80, function(conn)
conn:on("receive", receiver)
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: 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. ...@@ -84,7 +84,7 @@ Reads multi bytes.
#### Parameters #### Parameters
- `pin` 1~12, I/O index - `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 #### Returns
`string` bytes read from slave device `string` bytes read from slave device
......
...@@ -55,7 +55,7 @@ rtctime.dsleep(5000000, 4) ...@@ -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. 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 #### Syntax
`rtctime.dsleep(aligned_us, minsleep_us [, option])` `rtctime.dsleep_aligned(aligned_us, minsleep_us [, option])`
#### Parameters #### Parameters
- `aligned_us` boundary interval in microseconds - `aligned_us` boundary interval in microseconds
......
...@@ -12,7 +12,7 @@ When compiled together with the [rtctime](rtctime.md) module it also offers seam ...@@ -12,7 +12,7 @@ When compiled together with the [rtctime](rtctime.md) module it also offers seam
Attempts to obtain time synchronization. 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 #### Syntax
`sntp.sync([server_ip], [callback], [errcallback])` `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,6 +6,10 @@ ...@@ -6,6 +6,10 @@
All transactions for sending and receiving are most-significant-bit first and least-significant last. 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/). 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 ## High Level Functions
The high level functions provide a send & receive API for half- and 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 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 ...@@ -8,7 +8,7 @@ U8glib is a graphics library developed at [olikraus/u8glib](https://github.com/o
I²C and SPI mode: I²C and SPI mode:
- sh1106_128x64 - sh1106_128x64
- ssd1306 - 128x64 and 64x48 variants - ssd1306 - 128x32, 128x64, and 64x48 variants
- ssd1309_128x64 - ssd1309_128x64
- ssd1327_96x96_gr - ssd1327_96x96_gr
- uc1611 - dogm240 and dogxl240 variants - uc1611 - dogm240 and dogxl240 variants
...@@ -107,6 +107,7 @@ Initialize a display via I²C. ...@@ -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. 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.sh1106_128x64_i2c()`
- `u8g.ssd1306_128x32_i2c()`
- `u8g.ssd1306_128x64_i2c()` - `u8g.ssd1306_128x64_i2c()`
- `u8g.ssd1306_64x48_i2c()` - `u8g.ssd1306_64x48_i2c()`
- `u8g.ssd1309_128x64_i2c()` - `u8g.ssd1309_128x64_i2c()`
...@@ -146,6 +147,7 @@ The init sequence would insert delays to match the display specs. These can dest ...@@ -146,6 +147,7 @@ The init sequence would insert delays to match the display specs. These can dest
- `u8g.pcd8544_84x48_hw_spi()` - `u8g.pcd8544_84x48_hw_spi()`
- `u8g.pcf8812_96x65_hw_spi()` - `u8g.pcf8812_96x65_hw_spi()`
- `u8g.sh1106_128x64_hw_spi()` - `u8g.sh1106_128x64_hw_spi()`
- `u8g.ssd1306_128x32_hw_spi()`
- `u8g.ssd1306_128x64_hw_spi()` - `u8g.ssd1306_128x64_hw_spi()`
- `u8g.ssd1306_64x48_hw_spi()` - `u8g.ssd1306_64x48_hw_spi()`
- `u8g.ssd1309_128x64_hw_spi()` - `u8g.ssd1309_128x64_hw_spi()`
...@@ -202,6 +204,30 @@ disp = u8g.ssd1306_128x64_hw_spi(cs, dc, res) ...@@ -202,6 +204,30 @@ disp = u8g.ssd1306_128x64_hw_spi(cs, dc, res)
#### See also #### See also
[I²C Display Drivers](#i2c-display-drivers) [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 ## 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