Commit 7b83bbb2 authored by Arnim Läuger's avatar Arnim Läuger Committed by GitHub
Browse files

Merge pull request #1519 from nodemcu/dev

Next 1.5.4.1 master drop
parents 8e48483c d96d7f23
......@@ -40,7 +40,7 @@ uint8_t u8g_dev_rot90_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg);
uint8_t u8g_dev_rot180_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg);
uint8_t u8g_dev_rot270_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg);
uint8_t u8g_dev_rot_dummy_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg)
uint8_t u8g_dev_rot_dummy_fn(u8g_t *u8g, u8g_dev_t*dev, uint8_t msg, void *arg)
{
return 0;
}
......
......@@ -75,7 +75,8 @@ uint8_t global_SREG_backup;
/*===============================================================*/
/* AVR */
#if defined(__AVR__)
#if defined(__AVR_XMEGA__)
#elif defined(__AVR__)
#define U8G_ATMEGA_HW_SPI
/* remove the definition for attiny */
......
......@@ -12,7 +12,8 @@
#include "platform.h"
#include "c_string.h"
#include "c_stdlib.h"
#include "flash_fs.h"
#include "c_stdio.h"
#include "vfs.h"
#include "flash_api.h"
#include "user_interface.h"
#include "user_exceptions.h"
......@@ -22,6 +23,7 @@
#include "driver/uart.h"
#include "task/task.h"
#include "mem.h"
#include "espconn.h"
#ifdef LUA_USE_MODULES_RTCTIME
#include "rtc/rtctime.h"
......@@ -98,11 +100,15 @@ void nodemcu_init(void)
}
#endif // defined(FLASH_SAFE_API)
#if defined ( BUILD_SPIFFS )
if (!fs_mount()) {
#if defined ( CLIENT_SSL_ENABLE ) && defined ( SSL_BUFFER_SIZE )
espconn_secure_set_size(ESPCONN_CLIENT, SSL_BUFFER_SIZE);
#endif
#ifdef BUILD_SPIFFS
if (!vfs_mount("/FLASH", 0)) {
// Failed to mount -- try reformat
c_printf("Formatting file system. Please wait...\n");
if (!fs_format()) {
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" );
}
......
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
GEN_LIBS = libwebsocket.a
endif
STD_CFLAGS=-std=gnu11 -Wimplicit
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
INCLUDES += -I ./include
INCLUDES += -I ../include
INCLUDES += -I ../libc
INCLUDES += -I ../../include
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
/* Websocket client implementation
*
* Copyright (c) 2016 Luís Fonseca <miguelluisfonseca@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "osapi.h"
#include "user_interface.h"
#include "espconn.h"
#include "mem.h"
#include "limits.h"
#include "stdlib.h"
#include "c_types.h"
#include "c_string.h"
#include "c_stdlib.h"
#include "c_stdio.h"
#include "websocketclient.h"
// Depends on 'crypto' module for sha1
#include "../crypto/digests.h"
#include "../crypto/mech.h"
#define PROTOCOL_SECURE "wss://"
#define PROTOCOL_INSECURE "ws://"
#define PORT_SECURE 443
#define PORT_INSECURE 80
#define PORT_MAX_VALUE 65535
// TODO: user agent configurable
#define WS_INIT_HEADERS "GET %s HTTP/1.1\r\n"\
"Host: %s:%d\r\n"\
"Upgrade: websocket\r\n"\
"Connection: Upgrade\r\n"\
"User-Agent: ESP8266\r\n"\
"Sec-Websocket-Key: %s\r\n"\
"Sec-WebSocket-Protocol: chat\r\n"\
"Sec-WebSocket-Version: 13\r\n"\
"\r\n"
#define WS_INIT_HEADERS_LENGTH 169
#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
#define WS_GUID_LENGTH 36
#define WS_HTTP_SWITCH_PROTOCOL_HEADER "HTTP/1.1 101"
#define WS_HTTP_SEC_WEBSOCKET_ACCEPT "Sec-WebSocket-Accept:"
#define WS_CONNECT_TIMEOUT_MS 10 * 1000
#define WS_PING_INTERVAL_MS 30 * 1000
#define WS_FORCE_CLOSE_TIMEOUT_MS 5 * 1000
#define WS_UNHEALTHY_THRESHOLD 2
#define WS_OPCODE_CONTINUATION 0x0
#define WS_OPCODE_TEXT 0x1
#define WS_OPCODE_BINARY 0x2
#define WS_OPCODE_CLOSE 0x8
#define WS_OPCODE_PING 0x9
#define WS_OPCODE_PONG 0xA
static char *cryptoSha1(char *data, unsigned int len) {
SHA1_CTX ctx;
SHA1Init(&ctx);
SHA1Update(&ctx, data, len);
uint8_t *digest = (uint8_t *) c_zalloc(20);
SHA1Final(digest, &ctx);
return (char *) digest; // Requires free
}
static const char *bytes64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static char *base64Encode(char *data, unsigned int len) {
int blen = (len + 2) / 3 * 4;
char *out = (char *) c_zalloc(blen + 1);
out[blen] = '\0';
int j = 0, i;
for (i = 0; i < len; i += 3) {
int a = data[i];
int b = (i + 1 < len) ? data[i + 1] : 0;
int c = (i + 2 < len) ? data[i + 2] : 0;
out[j++] = bytes64[a >> 2];
out[j++] = bytes64[((a & 3) << 4) | (b >> 4)];
out[j++] = (i + 1 < len) ? bytes64[((b & 15) << 2) | (c >> 6)] : 61;
out[j++] = (i + 2 < len) ? bytes64[(c & 63)] : 61;
}
return out; // Requires free
}
static void generateSecKeys(char **key, char **expectedKey) {
char rndData[16];
int i;
for (i = 0; i < 16; i++) {
rndData[i] = (char) os_random();
}
*key = base64Encode(rndData, 16);
// expectedKey = b64(sha1(keyB64 + GUID))
char keyWithGuid[24 + WS_GUID_LENGTH];
memcpy(keyWithGuid, *key, 24);
memcpy(keyWithGuid + 24, WS_GUID, WS_GUID_LENGTH);
char *keyEncrypted = cryptoSha1(keyWithGuid, 24 + WS_GUID_LENGTH);
*expectedKey = base64Encode(keyEncrypted, 20);
os_free(keyEncrypted);
}
static void ws_closeSentCallback(void *arg) {
NODE_DBG("ws_closeSentCallback \n");
struct espconn *conn = (struct espconn *) arg;
ws_info *ws = (ws_info *) conn->reverse;
if (ws == NULL) {
NODE_DBG("ws is unexpectly null\n");
return;
}
ws->knownFailureCode = -6;
if (ws->isSecure)
espconn_secure_disconnect(conn);
else
espconn_disconnect(conn);
}
static void ws_sendFrame(struct espconn *conn, int opCode, const char *data, unsigned short len) {
NODE_DBG("ws_sendFrame %d %d\n", opCode, len);
ws_info *ws = (ws_info *) conn->reverse;
if (ws->connectionState == 4) {
NODE_DBG("already in closing state\n");
return;
} else if (ws->connectionState != 3) {
NODE_DBG("can't send message while not in a connected state\n");
return;
}
char *b = c_zalloc(10 + len); // 10 bytes = worst case scenario for framming
if (b == NULL) {
NODE_DBG("Out of memory when receiving message, disconnecting...\n");
ws->knownFailureCode = -16;
if (ws->isSecure)
espconn_secure_disconnect(conn);
else
espconn_disconnect(conn);
return;
}
b[0] = 1 << 7; // has fin
b[0] += opCode;
b[1] = 1 << 7; // has mask
int bufOffset;
if (len < 126) {
b[1] += len;
bufOffset = 2;
} else if (len < 0x10000) {
b[1] += 126;
b[2] = len >> 8;
b[3] = len;
bufOffset = 4;
} else {
b[1] += 127;
b[2] = len >> 24;
b[3] = len >> 16;
b[4] = len >> 8;
b[5] = len;
bufOffset = 6;
}
// Random mask:
b[bufOffset] = (char) os_random();
b[bufOffset + 1] = (char) os_random();
b[bufOffset + 2] = (char) os_random();
b[bufOffset + 3] = (char) os_random();
bufOffset += 4;
// Copy data to buffer
memcpy(b + bufOffset, data, len);
// Apply mask to encode payload
int i;
for (i = 0; i < len; i++) {
b[bufOffset + i] ^= b[bufOffset - 4 + i % 4];
}
bufOffset += len;
NODE_DBG("b[0] = %d \n", b[0]);
NODE_DBG("b[1] = %d \n", b[1]);
NODE_DBG("b[2] = %d \n", b[2]);
NODE_DBG("b[3] = %d \n", b[3]);
NODE_DBG("b[4] = %d \n", b[4]);
NODE_DBG("b[5] = %d \n", b[5]);
NODE_DBG("b[6] = %d \n", b[6]);
NODE_DBG("b[7] = %d \n", b[7]);
NODE_DBG("b[8] = %d \n", b[8]);
NODE_DBG("b[9] = %d \n", b[9]);
NODE_DBG("sending message\n");
if (ws->isSecure)
espconn_secure_send(conn, (uint8_t *) b, bufOffset);
else
espconn_send(conn, (uint8_t *) b, bufOffset);
os_free(b);
}
static void ws_sendPingTimeout(void *arg) {
NODE_DBG("ws_sendPingTimeout \n");
struct espconn *conn = (struct espconn *) arg;
ws_info *ws = (ws_info *) conn->reverse;
if (ws->unhealthyPoints == WS_UNHEALTHY_THRESHOLD) {
// several pings were sent but no pongs nor messages
ws->knownFailureCode = -19;
if (ws->isSecure)
espconn_secure_disconnect(conn);
else
espconn_disconnect(conn);
return;
}
ws_sendFrame(conn, WS_OPCODE_PING, NULL, 0);
ws->unhealthyPoints += 1;
}
static void ws_receiveCallback(void *arg, char *buf, unsigned short len) {
NODE_DBG("ws_receiveCallback %d \n", len);
struct espconn *conn = (struct espconn *) arg;
ws_info *ws = (ws_info *) conn->reverse;
ws->unhealthyPoints = 0; // received data, connection is healthy
os_timer_disarm(&ws->timeoutTimer); // reset ping check
os_timer_arm(&ws->timeoutTimer, WS_PING_INTERVAL_MS, true);
char *b = buf;
if (ws->frameBuffer != NULL) { // Append previous frameBuffer with new content
NODE_DBG("Appending new frameBuffer to old one \n");
ws->frameBuffer = c_realloc(ws->frameBuffer, ws->frameBufferLen + len);
if (ws->frameBuffer == NULL) {
NODE_DBG("Failed to allocate new framebuffer, disconnecting...\n");
ws->knownFailureCode = -8;
if (ws->isSecure)
espconn_secure_disconnect(conn);
else
espconn_disconnect(conn);
return;
}
memcpy(ws->frameBuffer + ws->frameBufferLen, b, len);
ws->frameBufferLen += len;
len = ws->frameBufferLen;
b = ws->frameBuffer;
NODE_DBG("New frameBufferLen: %d\n", len);
}
while (b != NULL) { // several frames can be present, b pointer will be moved to the next frame
NODE_DBG("b[0] = %d \n", b[0]);
NODE_DBG("b[1] = %d \n", b[1]);
NODE_DBG("b[2] = %d \n", b[2]);
NODE_DBG("b[3] = %d \n", b[3]);
NODE_DBG("b[4] = %d \n", b[4]);
NODE_DBG("b[5] = %d \n", b[5]);
NODE_DBG("b[6] = %d \n", b[6]);
NODE_DBG("b[7] = %d \n", b[7]);
int isFin = b[0] & 0x80 ? 1 : 0;
int opCode = b[0] & 0x0f;
int hasMask = b[1] & 0x80 ? 1 : 0;
uint64_t payloadLength = b[1] & 0x7f;
int bufOffset = 2;
if (payloadLength == 126) {
payloadLength = (b[2] << 8) + b[3];
bufOffset = 4;
} else if (payloadLength == 127) { // this will clearly not hold in heap, abort??
payloadLength = (b[2] << 24) + (b[3] << 16) + (b[4] << 8) + b[5];
bufOffset = 6;
}
if (hasMask) {
int maskOffset = bufOffset;
bufOffset += 4;
int i;
for (i = 0; i < payloadLength; i++) {
b[bufOffset + i] ^= b[maskOffset + i % 4]; // apply mask to decode payload
}
}
if (payloadLength > len - bufOffset) {
NODE_DBG("INCOMPLETE Frame \n");
if (ws->frameBuffer == NULL) {
NODE_DBG("Allocing new frameBuffer \n");
ws->frameBuffer = c_zalloc(len);
if (ws->frameBuffer == NULL) {
NODE_DBG("Failed to allocate framebuffer, disconnecting... \n");
ws->knownFailureCode = -9;
if (ws->isSecure)
espconn_secure_disconnect(conn);
else
espconn_disconnect(conn);
return;
}
memcpy(ws->frameBuffer, b, len);
ws->frameBufferLen = len;
}
break; // since the buffer were already concat'ed, wait for the next receive
}
if (!isFin) {
NODE_DBG("PARTIAL frame! Should concat payload and later restore opcode\n");
if(ws->payloadBuffer == NULL) {
NODE_DBG("Allocing new payloadBuffer \n");
ws->payloadBuffer = c_zalloc(payloadLength);
if (ws->payloadBuffer == NULL) {
NODE_DBG("Failed to allocate payloadBuffer, disconnecting...\n");
ws->knownFailureCode = -10;
if (ws->isSecure)
espconn_secure_disconnect(conn);
else
espconn_disconnect(conn);
return;
}
memcpy(ws->payloadBuffer, b + bufOffset, payloadLength);
ws->frameBufferLen = payloadLength;
ws->payloadOriginalOpCode = opCode;
} else {
NODE_DBG("Appending new payloadBuffer to old one \n");
ws->payloadBuffer = c_realloc(ws->payloadBuffer, ws->payloadBufferLen + payloadLength);
if (ws->payloadBuffer == NULL) {
NODE_DBG("Failed to allocate new framebuffer, disconnecting...\n");
ws->knownFailureCode = -11;
if (ws->isSecure)
espconn_secure_disconnect(conn);
else
espconn_disconnect(conn);
return;
}
memcpy(ws->payloadBuffer + ws->payloadBufferLen, b + bufOffset, payloadLength);
ws->payloadBufferLen += payloadLength;
}
} else {
char *payload;
if (opCode == WS_OPCODE_CONTINUATION) {
NODE_DBG("restoring original opcode\n");
if (ws->payloadBuffer == NULL) {
NODE_DBG("Got FIN continuation frame but didn't receive any beforehand, disconnecting...\n");
ws->knownFailureCode = -15;
if (ws->isSecure)
espconn_secure_disconnect(conn);
else
espconn_disconnect(conn);
return;
}
// concat buffer with payload
payload = c_zalloc(ws->payloadBufferLen + payloadLength);
if (payload == NULL) {
NODE_DBG("Failed to allocate new framebuffer, disconnecting...\n");
ws->knownFailureCode = -12;
if (ws->isSecure)
espconn_secure_disconnect(conn);
else
espconn_disconnect(conn);
return;
}
memcpy(payload, ws->payloadBuffer, ws->payloadBufferLen);
memcpy(payload + ws->payloadBufferLen, b + bufOffset, payloadLength);
os_free(ws->payloadBuffer); // free previous buffer
ws->payloadBuffer = NULL;
payloadLength += ws->payloadBufferLen;
ws->payloadBufferLen = 0;
opCode = ws->payloadOriginalOpCode;
ws->payloadOriginalOpCode = 0;
} else {
int extensionDataOffset = 0;
if (opCode == WS_OPCODE_CLOSE && payloadLength > 0) {
unsigned int reasonCode = b[bufOffset] << 8 + b[bufOffset + 1];
NODE_DBG("Closing due to: %d\n", reasonCode); // Must not be shown to client as per spec
extensionDataOffset += 2;
}
payload = c_zalloc(payloadLength - extensionDataOffset + 1);
if (payload == NULL) {
NODE_DBG("Failed to allocate payload, disconnecting...\n");
ws->knownFailureCode = -13;
if (ws->isSecure)
espconn_secure_disconnect(conn);
else
espconn_disconnect(conn);
return;
}
memcpy(payload, b + bufOffset + extensionDataOffset, payloadLength - extensionDataOffset);
payload[payloadLength - extensionDataOffset] = '\0';
}
NODE_DBG("isFin %d \n", isFin);
NODE_DBG("opCode %d \n", opCode);
NODE_DBG("hasMask %d \n", hasMask);
NODE_DBG("payloadLength %d \n", payloadLength);
NODE_DBG("len %d \n", len);
NODE_DBG("bufOffset %d \n", bufOffset);
if (opCode == WS_OPCODE_CLOSE) {
NODE_DBG("Closing message: %s\n", payload); // Must not be shown to client as per spec
espconn_regist_sentcb(conn, ws_closeSentCallback);
ws_sendFrame(conn, WS_OPCODE_CLOSE, (const char *) (b + bufOffset), (unsigned short) payloadLength);
ws->connectionState = 4;
} else if (opCode == WS_OPCODE_PING) {
ws_sendFrame(conn, WS_OPCODE_PONG, (const char *) (b + bufOffset), (unsigned short) payloadLength);
} else if (opCode == WS_OPCODE_PONG) {
// ping alarm was already reset...
} else {
if (ws->onReceive) ws->onReceive(ws, payload, opCode);
}
os_free(payload);
}
bufOffset += payloadLength;
NODE_DBG("bufOffset %d \n", bufOffset);
if (bufOffset == len) { // (bufOffset > len) won't happen here because it's being checked earlier
b = NULL;
if (ws->frameBuffer != NULL) { // the last frame inside buffer was processed
os_free(ws->frameBuffer);
ws->frameBuffer = NULL;
ws->frameBufferLen = 0;
}
} else {
len -= bufOffset;
b += bufOffset; // move b to next frame
if (ws->frameBuffer != NULL) {
NODE_DBG("Reallocing frameBuffer to remove consumed frame\n");
ws->frameBuffer = c_realloc(ws->frameBuffer, ws->frameBufferLen + len);
if (ws->frameBuffer == NULL) {
NODE_DBG("Failed to allocate new frame buffer, disconnecting...\n");
ws->knownFailureCode = -14;
if (ws->isSecure)
espconn_secure_disconnect(conn);
else
espconn_disconnect(conn);
return;
}
memcpy(ws->frameBuffer + ws->frameBufferLen, b, len);
ws->frameBufferLen += len;
b = ws->frameBuffer;
}
}
}
}
static void ws_initReceiveCallback(void *arg, char *buf, unsigned short len) {
NODE_DBG("ws_initReceiveCallback %d \n", len);
struct espconn *conn = (struct espconn *) arg;
ws_info *ws = (ws_info *) conn->reverse;
// Check server is switch protocols
if (strstr(buf, WS_HTTP_SWITCH_PROTOCOL_HEADER) == NULL) {
NODE_DBG("Server is not switching protocols\n");
ws->knownFailureCode = -17;
if (ws->isSecure)
espconn_secure_disconnect(conn);
else
espconn_disconnect(conn);
return;
}
// Check server has valid sec key
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)
espconn_secure_disconnect(conn);
else
espconn_disconnect(conn);
return;
}
NODE_DBG("Server response is valid, it's now a websocket!\n");
os_timer_disarm(&ws->timeoutTimer);
os_timer_setfn(&ws->timeoutTimer, (os_timer_func_t *) ws_sendPingTimeout, conn);
os_timer_arm(&ws->timeoutTimer, WS_PING_INTERVAL_MS, true);
espconn_regist_recvcb(conn, ws_receiveCallback);
if (ws->onConnection) ws->onConnection(ws);
char *data = strstr(buf, "\r\n\r\n");
unsigned short dataLength = len - (data - buf) - 4;
NODE_DBG("dataLength = %d\n", len - (data - buf) - 4);
if (data != NULL && dataLength > 0) { // handshake already contained a frame
ws_receiveCallback(arg, data + 4, dataLength);
}
}
static void connect_callback(void *arg) {
NODE_DBG("Connected\n");
struct espconn *conn = (struct espconn *) arg;
ws_info *ws = (ws_info *) conn->reverse;
ws->connectionState = 3;
espconn_regist_recvcb(conn, ws_initReceiveCallback);
char *key;
generateSecKeys(&key, &ws->expectedSecKey);
char buf[WS_INIT_HEADERS_LENGTH + strlen(ws->path) + strlen(ws->hostname) + strlen(key)];
int len = os_sprintf(buf, WS_INIT_HEADERS, ws->path, ws->hostname, ws->port, key);
os_free(key);
NODE_DBG("connecting\n");
if (ws->isSecure)
espconn_secure_send(conn, (uint8_t *) buf, len);
else
espconn_send(conn, (uint8_t *) buf, len);
}
static void disconnect_callback(void *arg) {
NODE_DBG("disconnect_callback\n");
struct espconn *conn = (struct espconn *) arg;
ws_info *ws = (ws_info *) conn->reverse;
ws->connectionState = 4;
os_timer_disarm(&ws->timeoutTimer);
NODE_DBG("ws->hostname %d\n", ws->hostname);
os_free(ws->hostname);
NODE_DBG("ws->path %d\n ", ws->path);
os_free(ws->path);
if (ws->expectedSecKey != NULL) {
os_free(ws->expectedSecKey);
}
if (ws->frameBuffer != NULL) {
os_free(ws->frameBuffer);
}
if (ws->payloadBuffer != NULL) {
os_free(ws->payloadBuffer);
}
if (conn->proto.tcp != NULL) {
os_free(conn->proto.tcp);
}
NODE_DBG("conn %d\n", conn);
espconn_delete(conn);
NODE_DBG("freeing conn1 \n");
os_free(conn);
ws->conn = NULL;
if (ws->onFailure) {
if (ws->knownFailureCode) ws->onFailure(ws, ws->knownFailureCode);
else ws->onFailure(ws, -99);
}
}
static void ws_connectTimeout(void *arg) {
NODE_DBG("ws_connectTimeout\n");
struct espconn *conn = (struct espconn *) arg;
ws_info *ws = (ws_info *) conn->reverse;
ws->knownFailureCode = -18;
disconnect_callback(arg);
}
static void error_callback(void * arg, sint8 errType) {
NODE_DBG("error_callback %d\n", errType);
struct espconn *conn = (struct espconn *) arg;
ws_info *ws = (ws_info *) conn->reverse;
ws->knownFailureCode = ((int) errType) - 100;
disconnect_callback(arg);
}
static void dns_callback(const char *hostname, ip_addr_t *addr, void *arg) {
NODE_DBG("dns_callback\n");
struct espconn *conn = (struct espconn *) arg;
ws_info *ws = (ws_info *) conn->reverse;
if (ws->conn == NULL || ws->connectionState == 4) {
return;
}
if (addr == NULL) {
ws->knownFailureCode = -5;
disconnect_callback(arg);
return;
}
ws->connectionState = 2;
os_memcpy(conn->proto.tcp->remote_ip, addr, 4);
espconn_regist_connectcb(conn, connect_callback);
espconn_regist_disconcb(conn, disconnect_callback);
espconn_regist_reconcb(conn, error_callback);
// Set connection timeout timer
os_timer_disarm(&ws->timeoutTimer);
os_timer_setfn(&ws->timeoutTimer, (os_timer_func_t *) ws_connectTimeout, conn);
os_timer_arm(&ws->timeoutTimer, WS_CONNECT_TIMEOUT_MS, false);
if (ws->isSecure) {
NODE_DBG("secure connecting \n");
espconn_secure_connect(conn);
}
else {
NODE_DBG("insecure connecting \n");
espconn_connect(conn);
}
NODE_DBG("DNS found %s " IPSTR " \n", hostname, IP2STR(addr));
}
void ws_connect(ws_info *ws, const char *url) {
NODE_DBG("ws_connect called\n");
if (ws == NULL) {
NODE_DBG("ws_connect ws_info argument is null!");
return;
}
if (url == NULL) {
NODE_DBG("url is null!");
return;
}
// Extract protocol - either ws or wss
bool isSecure = c_strncasecmp(url, PROTOCOL_SECURE, strlen(PROTOCOL_SECURE)) == 0;
if (isSecure) {
url += strlen(PROTOCOL_SECURE);
} else {
if (c_strncasecmp(url, PROTOCOL_INSECURE, strlen(PROTOCOL_INSECURE)) != 0) {
NODE_DBG("Failed to extract protocol from: %s\n", url);
if (ws->onFailure) ws->onFailure(ws, -1);
return;
}
url += strlen(PROTOCOL_INSECURE);
}
// Extract path - it should start with '/'
char *path = c_strchr(url, '/');
// Extract hostname, possibly including port
char hostname[256];
if (path) {
if (path - url >= sizeof(hostname)) {
NODE_DBG("Hostname too large");
if (ws->onFailure) ws->onFailure(ws, -2);
return;
}
memcpy(hostname, url, path - url);
hostname[path - url] = '\0';
} else {
// no path found, assuming the url only refers to the hostname and possibly the port
memcpy(hostname, url, strlen(url));
hostname[strlen(url)] = '\0';
path = "/";
}
// Extract port from hostname, if available
char *portInHostname = strchr(hostname, ':');
int port;
if (portInHostname) {
port = atoi(portInHostname + 1);
if (port <= 0 || port > PORT_MAX_VALUE) {
NODE_DBG("Invalid port number\n");
if (ws->onFailure) ws->onFailure(ws, -3);
return;
}
hostname[strlen(hostname) - strlen(portInHostname)] = '\0'; // remove port from hostname
} else {
port = isSecure ? PORT_SECURE : PORT_INSECURE;
}
if (strlen(hostname) == 0) {
NODE_DBG("Failed to extract hostname\n");
if (ws->onFailure) ws->onFailure(ws, -4);
return;
}
NODE_DBG("secure protocol = %d\n", isSecure);
NODE_DBG("hostname = %s\n", hostname);
NODE_DBG("port = %d\n", port);
NODE_DBG("path = %s\n", path);
// Prepare internal ws_info
ws->connectionState = 1;
ws->isSecure = isSecure;
ws->hostname = c_strdup(hostname);
ws->port = port;
ws->path = c_strdup(path);
ws->expectedSecKey = NULL;
ws->knownFailureCode = 0;
ws->frameBuffer = NULL;
ws->frameBufferLen = 0;
ws->payloadBuffer = NULL;
ws->payloadBufferLen = 0;
ws->payloadOriginalOpCode = 0;
ws->unhealthyPoints = 0;
// Prepare espconn
struct espconn *conn = (struct espconn *) c_zalloc(sizeof(struct espconn));
conn->type = ESPCONN_TCP;
conn->state = ESPCONN_NONE;
conn->proto.tcp = (esp_tcp *) c_zalloc(sizeof(esp_tcp));
conn->proto.tcp->local_port = espconn_port();
conn->proto.tcp->remote_port = ws->port;
conn->reverse = ws;
ws->conn = conn;
// Attempt to resolve hostname address
ip_addr_t addr;
err_t result = espconn_gethostbyname(conn, hostname, &addr, dns_callback);
if (result == ESPCONN_INPROGRESS) {
NODE_DBG("DNS pending\n");
} else {
dns_callback(hostname, &addr, conn);
}
return;
}
void ws_send(ws_info *ws, int opCode, const char *message, unsigned short length) {
NODE_DBG("ws_send\n");
ws_sendFrame(ws->conn, opCode, message, length);
}
static void ws_forceCloseTimeout(void *arg) {
NODE_DBG("ws_forceCloseTimeout\n");
struct espconn *conn = (struct espconn *) arg;
ws_info *ws = (ws_info *) conn->reverse;
if (ws->connectionState == 0 || ws->connectionState == 4) {
return;
}
if (ws->isSecure)
espconn_secure_disconnect(ws->conn);
else
espconn_disconnect(ws->conn);
}
void ws_close(ws_info *ws) {
NODE_DBG("ws_close\n");
if (ws->connectionState == 0 || ws->connectionState == 4) {
return;
}
ws->knownFailureCode = 0; // no error as user requested to close
if (ws->connectionState == 1) {
disconnect_callback(ws->conn);
} else {
ws_sendFrame(ws->conn, WS_OPCODE_CLOSE, NULL, 0);
os_timer_disarm(&ws->timeoutTimer);
os_timer_setfn(&ws->timeoutTimer, (os_timer_func_t *) ws_forceCloseTimeout, ws->conn);
os_timer_arm(&ws->timeoutTimer, WS_FORCE_CLOSE_TIMEOUT_MS, false);
}
}
/* Websocket client implementation
*
* Copyright (c) 2016 Luís Fonseca <miguelluisfonseca@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _WEBSOCKET_H_
#define _WEBSOCKET_H_
#include "osapi.h"
#include "user_interface.h"
#include "espconn.h"
#include "mem.h"
#include "limits.h"
#include "stdlib.h"
#if defined(USES_SDK_BEFORE_V140)
#define espconn_send espconn_sent
#define espconn_secure_send espconn_secure_sent
#endif
struct ws_info;
typedef void (*ws_onConnectionCallback)(struct ws_info *wsInfo);
typedef void (*ws_onReceiveCallback)(struct ws_info *wsInfo, char *message, int opCode);
typedef void (*ws_onFailureCallback)(struct ws_info *wsInfo, int errorCode);
typedef struct ws_info {
int connectionState;
bool isSecure;
char *hostname;
int port;
char *path;
char *expectedSecKey;
struct espconn *conn;
void *reservedData;
int knownFailureCode;
char *frameBuffer;
int frameBufferLen;
char *payloadBuffer;
int payloadBufferLen;
int payloadOriginalOpCode;
os_timer_t timeoutTimer;
int unhealthyPoints;
ws_onConnectionCallback onConnection;
ws_onReceiveCallback onReceive;
ws_onFailureCallback onFailure;
} ws_info;
/*
* Attempts to estabilish a websocket connection to the given url.
*/
void ws_connect(ws_info *wsInfo, const char *url);
/*
* Sends a message with a given opcode.
*/
void ws_send(ws_info *wsInfo, int opCode, const char *message, unsigned short length);
/*
* Disconnects existing conection and frees memory.
*/
void ws_close(ws_info *wsInfo);
#endif // _WEBSOCKET_H_
......@@ -78,7 +78,12 @@ esptool.py --port <serial-port-of-ESP8266> write_flash <flash options> 0x00000 <
!!! note "Note:"
The address for `esp_init_data_default.bin` depends on the size of your module's flash. ESP-01, -03, -07 etc. with 512 kByte flash require `0x7c000`. Init data goes to `0x3fc000` on an ESP-12E with 4 MByte flash.
The address for `esp_init_data_default.bin` depends on the size of your module's flash.
- `0x7c000` for 512 kB, modules like ESP-01, -03, -07 etc.
- `0xfc000` for 1 MB, modules like ESP8285, PSF-A85
- `0x1fc000` for 2 MB
- `0x3fc000` for 4 MB, modules like ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini
**NodeMCU Flasher**
......
......@@ -19,13 +19,13 @@ Send ABGR data in 8 bits to a APA102 chain.
- `data_pin` any GPIO pin 0, 1, 2, ...
- `clock_pin` any GPIO pin 0, 1, 2, ...
- `string` payload to be sent to one or more APA102 LEDs.
It should be composed from a AGRB quadruplet per element.
It should be composed from a ABGR quadruplet per element.
- `A1` the first pixel's Intensity channel (0-31)
- `B1` the first pixel's Blue channel (0-255)<br />
- `G1` the first pixel's Green channel (0-255)
- `R1` the first pixel's Red channel (0-255)
... You can connect a lot of APA102 ...
- `A2`, `G2`, `R2`, `B2` are the next APA102s Intensity, Blue, Green and channel parameters
- `A2`, `B2`, `G2`, `R2` are the next APA102s Intensity, Blue, Green and Red channel parameters
#### Returns
`nil`
......@@ -33,9 +33,9 @@ Send ABGR data in 8 bits to a APA102 chain.
#### Example
```lua
a = 31
b = 0
g = 0
r = 255
b = 0
led_abgr = string.char(a, g, r, b, a, g, r, b)
led_abgr = string.char(a, b, g, r, a, b, g, r)
apa102.write(2, 3, leds_abgr) -- turn two APA102s to red, connected to data_pin 2 and clock_pin 3
```
# BME280 module
| Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ |
| 2016-02-21 | [vsky279](https://github.com/vsky279) | [vsky279](https://github.com/vsky279) | [bit.c](../../../app/modules/bme280.c)|
| 2016-02-21 | [vsky279](https://github.com/vsky279) | [vsky279](https://github.com/vsky279) | [bme280.c](../../../app/modules/bme280.c)|
This module provides a simple interface to [BME280/BMP280 temperature/air presssure/humidity sensors](http://www.bosch-sensortec.com/bst/products/all_products/bme280) (Bosch Sensortec).
......@@ -23,8 +23,8 @@ altitude in meters of measurement point
## bme280.baro()
Reads the sensor and returns the air temperature in hectopascals as an integer multiplied with 1000 or `nil` when readout is not successful.
Current temperature is needed to calculate the air pressure so temperature reading is performed prior reading pressure data. Second returned variable is therefore current temperature.
Reads the sensor and returns the air pressure in hectopascals as an integer multiplied with 1000 or `nil` when readout is not successful.
Current temperature is needed to calculate the air pressure so temperature reading is performed prior reading pressure data. Second returned variable is therefore current air temperature.
#### Syntax
`bme280.baro()`
......
......@@ -5,6 +5,16 @@
The crypto modules provides various functions for working with cryptographic algorithms.
The following encryption/decryption algorithms/modes are supported:
- `"AES-ECB"` for 128-bit AES in ECB mode (NOT recommended)
- `"AES-CBC"` for 128-bit AES in CBC mode
The following hash algorithms are supported:
- MD2 (not available by default, has to be explicitly enabled in `app/include/user_config.h`)
- MD5
- SHA1
- SHA256, SHA384, SHA512 (unless disabled in `app/include/user_config.h`)
## crypto.encrypt()
Encrypts Lua strings.
......@@ -13,9 +23,7 @@ Encrypts Lua strings.
`crypto.encrypt(algo, key, plain [, iv])`
#### Parameters
- `algo` the name of the encryption algorithm to use, one of
- `"AES-ECB"` for 128-bit AES in ECB mode
- `"AES-CBC"` for 128-bit AES in CBC mode
- `algo` the name of a supported encryption algorithm to use
- `key` the encryption key as a string; for AES encryption this *MUST* be 16 bytes long
- `plain` the string to encrypt; it will be automatically zero-padded to a 16-byte boundary if necessary
- `iv` the initilization vector, if using AES-CBC; defaults to all-zero if not given
......@@ -40,9 +48,7 @@ Decrypts previously encrypted data.
`crypto.decrypt(algo, key, cipher [, iv])`
#### Parameters
- `algo` the name of the encryption algorithm to use, one of
- `"AES-ECB"` for 128-bit AES in ECB mode
- `"AES-CBC"` for 128-bit AES in CBC mode
- `algo` the name of a supported encryption algorithm to use
- `key` the encryption key as a string; for AES encryption this *MUST* be 16 bytes long
- `cipher` the cipher text to decrypt (as obtained from `crypto.encrypt()`)
- `iv` the initilization vector, if using AES-CBC; defaults to all-zero if not given
......@@ -75,13 +81,6 @@ Compute a cryptographic hash of a a file.
- `algo` the hash algorithm to use, case insensitive string
- `filename` the path to the file to hash
Supported hash algorithms are:
- MD2 (not available by default, has to be explicitly enabled in `app/include/user_config.h`)
- MD5
- SHA1
- SHA256, SHA384, SHA512 (unless disabled in `app/include/user_config.h`)
#### Returns
A binary string containing the message digest. To obtain the textual version (ASCII hex characters), please use [`crypto.toHex()`](#cryptotohex ).
......@@ -101,13 +100,6 @@ Compute a cryptographic hash of a Lua string.
`algo` the hash algorithm to use, case insensitive string
`str` string to hash contents of
Supported hash algorithms are:
- MD2 (not available by default, has to be explicitly enabled in `app/include/user_config.h`)
- MD5
- SHA1
- SHA256, SHA384, SHA512 (unless disabled in `app/include/user_config.h`)
#### Returns
A binary string containing the message digest. To obtain the textual version (ASCII hex characters), please use [`crypto.toHex()`](#cryptotohex ).
......@@ -126,13 +118,6 @@ Create a digest/hash object that can have any number of strings added to it. Obj
#### Parameters
`algo` the hash algorithm to use, case insensitive string
Supported hash algorithms are:
- MD2 (not available by default, has to be explicitly enabled in `app/include/user_config.h`)
- MD5
- SHA1
- SHA256, SHA384, SHA512 (unless disabled in `app/include/user_config.h`)
#### Returns
Userdata object with `update` and `finalize` functions available.
......@@ -157,13 +142,6 @@ Compute a [HMAC](https://en.wikipedia.org/wiki/Hash-based_message_authentication
- `str` data to calculate the hash for
- `key` key to use for signing, may be a binary string
Supported hash algorithms are:
- MD2 (not available by default, has to be explicitly enabled in `app/include/user_config.h`)
- MD5
- SHA1
- SHA256, SHA384, SHA512 (unless disabled in `app/include/user_config.h`)
#### Returns
A binary string containing the HMAC signature. Use [`crypto.toHex()`](#cryptotohex ) to obtain the textual version.
......@@ -172,6 +150,30 @@ A binary string containing the HMAC signature. Use [`crypto.toHex()`](#cryptotoh
print(crypto.toHex(crypto.hmac("sha1","abc","mysecret")))
```
## crypto.new_hmac()
Create a hmac object that can have any number of strings added to it. Object has `update` and `finalize` functions.
#### Syntax
`hmacobj = crypto.new_hmac(algo, key)`
#### Parameters
- `algo` the hash algorithm to use, case insensitive string
- `key` the key to use (may be a binary string)
#### Returns
Userdata object with `update` and `finalize` functions available.
#### Example
```lua
hmacobj = crypto.new_hmac("SHA1", "s3kr3t")
hmacobj:update("FirstString"))
hmacobj:update("SecondString"))
digest = hmacobj:finalize()
print(crypto.toHex(digest))
```
## crypto.mask()
Applies an XOR mask to a Lua string. Note that this is not a proper cryptographic mechanism, but some protocols may use it nevertheless.
......
......@@ -5,10 +5,44 @@
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 directories/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).
```lua
-- open file in flash:
if file.open("init.lua") then
print(file.read())
file.close()
end
-- or with full pathspec
file.open("/FLASH/init.lua")
-- open file on SD card
if file.open("/SD0/somefile.txt") then
print(file.read())
file.close()
end
```
## file.chdir()
Change current directory (and drive). This will be used when no drive/directory is prepended to filenames.
Current directory defaults to the root of internal SPIFFS (`/FLASH`) after system start.
#### Syntax
`file.chdir(dir)`
#### Parameters
`dir` directory name - `/FLASH`, `/SD0`, `/SD1`, etc.
#### Returns
`true` on success, `false` otherwise
## file.close()
Closes the open file, if any.
......@@ -25,9 +59,10 @@ none
#### Example
```lua
-- open 'init.lua', print the first line.
file.open("init.lua", "r")
print(file.readline())
file.close()
if file.open("init.lua", "r") then
print(file.readline())
file.close()
end
```
#### See also
[`file.open()`](#fileopen)
......@@ -76,13 +111,14 @@ none
#### Example
```lua
-- open 'init.lua' in 'a+' mode
file.open("init.lua", "a+")
-- write 'foo bar' to the end of the file
file.write('foo bar')
file.flush()
-- write 'baz' too
file.write('baz')
file.close()
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)
......@@ -91,6 +127,8 @@ file.close()
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.
Not supported for SD cards.
#### Syntax
`file.format()`
......@@ -107,6 +145,8 @@ none
Returns the flash address and physical size of the file system area, in bytes.
Not supported for SD cards.
#### Syntax
`file.fscfg()`
......@@ -124,7 +164,7 @@ print(string.format("0x%x", file.fscfg()))
## file.fsinfo()
Return size information for the file system, in bytes.
Return size information for the file system. The unit is Byte for SPIFFS and kByte for FatFS.
#### Syntax
`file.fsinfo()`
......@@ -142,7 +182,7 @@ none
```lua
-- get file system info
remaining, used, total=file.fsinfo()
print("\nFile system info:\nTotal : "..total.." Bytes\nUsed : "..used.." Bytes\nRemain: "..remaining.." Bytes\n")
print("\nFile system info:\nTotal : "..total.." (k)Bytes\nUsed : "..used.." (k)Bytes\nRemain: "..remaining.." (k)Bytes\n")
```
## file.list()
......@@ -166,6 +206,60 @@ for k,v in pairs(l) do
end
```
## file.mount()
Mounts a FatFs volume on SD card.
Not supported for internal flash.
#### Syntax
`file.mount(ldrv[, pin])`
#### Parameters
- `ldrv` name of the logical drive, `SD0:`, `SD1:`, etc.
- `pin` 1~12, IO index for SS/CS, defaults to 8 if omitted.
#### Returns
Volume object
#### Example
```lua
vol = file.mount("SD0:")
vol:umount()
```
## file.on()
Registers callback functions.
Trigger events are:
- `rtc` deliver current date & time to the file system. Function is expected to return a table containing the fields `year`, `mon`, `day`, `hour`, `min`, `sec` of current date and time. Not supported for internal flash.
#### Syntax
`file.on(event[, function()])`
#### Parameters
- `event` string
- `function()` callback function. Unregisters the callback if `function()` is omitted.
#### Returns
`nil`
#### Example
```lua
sntp.sync(server_ip,
function()
print("sntp time sync ok")
file.on("rtc",
function()
return rtctime.epoch2cal(rtctime.get())
end)
end)
```
#### See also
[`rtctime.epoch2cal()`](rtctime.md#rtctimepoch2cal)
## file.open()
Opens a file for access, potentially creating it (for write modes).
......@@ -191,9 +285,10 @@ When done with the file, it must be closed using `file.close()`.
#### Example
```lua
-- open 'init.lua', print the first line.
file.open("init.lua", "r")
print(file.readline())
file.close()
if file.open("init.lua", "r") then
print(file.readline())
file.close()
end
```
#### See also
- [`file.close()`](#fileclose)
......@@ -218,14 +313,16 @@ File content as a string, or nil when EOF
#### Example
```lua
-- print the first line of 'init.lua'
file.open("init.lua", "r")
print(file.read('\n'))
file.close()
if file.open("init.lua", "r") then
print(file.read('\n'))
file.close()
end
-- print the first 5 bytes of 'init.lua'
file.open("init.lua", "r")
print(file.read(5))
file.close()
if file.open("init.lua", "r") then
print(file.read(5))
file.close()
end
```
#### See also
......@@ -248,9 +345,10 @@ File content in string, line by line, including EOL('\n'). Return `nil` when EOF
#### Example
```lua
-- print the first line of 'init.lua'
file.open("init.lua", "r")
print(file.readline())
file.close()
if file.open("init.lua", "r") then
print(file.readline())
file.close()
end
```
#### See also
- [`file.open()`](#fileopen)
......@@ -320,11 +418,12 @@ the resulting file position, or `nil` on error
#### Example
```lua
file.open("init.lua", "r")
-- skip the first 5 bytes of the file
file.seek("set", 5)
print(file.readline())
file.close()
if file.open("init.lua", "r") then
-- skip the first 5 bytes of the file
file.seek("set", 5)
print(file.readline())
file.close()
end
```
#### See also
[`file.open()`](#fileopen)
......@@ -345,10 +444,11 @@ Write a string to the open file.
#### Example
```lua
-- open 'init.lua' in 'a+' mode
file.open("init.lua", "a+")
-- write 'foo bar' to the end of the file
file.write('foo bar')
file.close()
if file.open("init.lua", "a+") then
-- write 'foo bar' to the end of the file
file.write('foo bar')
file.close()
end
```
#### See also
......@@ -371,10 +471,11 @@ Write a string to the open file and append '\n' at the end.
#### Example
```lua
-- open 'init.lua' in 'a+' mode
file.open("init.lua", "a+")
-- write 'foo bar' to the end of the file
file.writeline('foo bar')
file.close()
if file.open("init.lua", "a+") then
-- write 'foo bar' to the end of the file
file.writeline('foo bar')
file.close()
end
```
#### See also
......
# gdbstub Module
| Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ |
| 2016-09-18 | [Philip Gladstone](https://github.com/pjsg) | [Philip Gladstone](https://github.com/pjsg) | [gdbstub.c](../../../app/modules/gdbstub.c)|
This module provides basic source code debugging of the firmware when used in conjunction with a version of gdb built for the lx106. If you
enable this module, then fatal errors (like invalid memory reads) will trap into the gdbstub. This uses UART0 to talk to GDB. If
this happens while the UART0 is connected to a terminal (or some IDE like esplorer) then you will see a string starting with `$T` and a few
more characters after that. This is the signal that a trap has happened, and control should be passed to gdb.
`GDB` can then be started at connected to the nodemcu platform. If this is connected to the host system via a serial port, then the following
(or close variant) ought to work:
```
gdb bin/firmwarefile.bin
target remote /dev/ttyUSB0
```
At this point, you can just poke around and see what happened, but you cannot continue execution.
In order to do interactive debugging, add a call to `gdbstub.brk()` in your lua code. This will trigger a break instruction and
will trap into gdb as above. However, continuation is supported from a break instruction and so you can single step, set breakpoints, etc.
Note that the lx106 processor as configured by Espressif only supports a single hardware breakpoint -- so this means that you
can only put a single breakpoint in flash code. You can single step as much as you like.
## gdbstub.brk()
Enters gdb by executing a `break 0,0` instruction.
#### Syntax
`gdbstub.brk()`
## gdbstub.gdboutput()
Controls whether system output is encapsulated in gdb remote debugging protocol. This turns out not to be as useful as you
would hope -- mostly because you can't send input to the nodemcu board. Also because you really only should make this call
*after* you get gdb running and connected to the nodemcu. The example below first does the break and then switches to
redirect the output. This works (but you are unable to send any more console input).
#### Syntax
`gdbstub.gdboutput(enable)`
#### Parameters
- `enable` If true, then output is wrapped in gdb remote debugging protocol. If false, then it is sent straight to the Uart.
#### Example
function entergdb()
gdbstub.brk()
gdbstub.gdboutput(1)
print("Active")
end
entergdb()
#### Notes
Once you attach gdb to the nodemcu, then any further output from the nodemcu will be discarded (as it does not
match the gdb remote debugging protocol). This may (or may not) be a problem. If you want to run under gdb and see
the output from the nodemcu, then call `gdbstub.gdboutput(1)` and then output will be wrapped in the gdb protocol and display
on the gdb console. You don't want to do this until gdb is attached as each packet requires an explicit ack in order to continue.
......@@ -18,7 +18,7 @@ temperature multiplied with 10 (integer)
#### Example
```lua
hmc5883.init(1, 2)
hmc58831.init(1, 2)
local x,y,z = hmc5883l.read()
print(string.format("x = %d, y = %d, z = %d", x, y, z))
```
......
......@@ -11,13 +11,9 @@ Basic HTTP *client* module that provides an interface to do GET/POST/PUT/DELETE
Each request method takes a callback which is invoked when the response has been received from the server. The first argument is the status code, which is either a regular HTTP status code, or -1 to denote a DNS, connection or out-of-memory failure, or a timeout (currently at 10 seconds).
For each operation it is also possible to include custom headers. Note that following headers *can not* be overridden however:
For each operation it is possible to provide custom HTTP headers or override standard headers. By default the `Host` header is deduced from the URL and `User-Agent` is `ESP8266`. Note, however, that the `Connection` header *can not* be overridden! It is always set to `close`.
- Host
- Connection
- User-Agent
The `Host` header is taken from the URL itself, the `Connection` is always set to `close`, and the `User-Agent` is `ESP8266`.
HTTP redirects (HTTP status 300-308) are followed automatically up to a limit of 20 to avoid the dreaded redirect loops.
**SSL/TLS support**
......
......@@ -104,7 +104,7 @@ Firmware from before 05 Jan 2016 have a maximum sleeptime of ~35 minutes.
- 0, init data byte 108 is valuable
- \> 0, init data byte 108 is valueless
- 0, RF_CAL or not after deep-sleep wake up, depends on init data byte 108
- 1, RF_CAL after deep-sleep wake up, there will belarge current
- 1, RF_CAL after deep-sleep wake up, there will be large current
- 2, no RF_CAL after deep-sleep wake up, there will only be small current
- 4, disable RF after deep-sleep wake up, just like modem sleep, there will be the smallest current
......@@ -136,6 +136,19 @@ none
#### Returns
flash ID (number)
## node.flashsize()
Returns the flash chip size in bytes. On 4MB modules like ESP-12 the return value is 4194304 = 4096KB.
#### Syntax
`node.flashsize()`
#### Parameters
none
#### Returns
flash size in bytes (integer)
## node.heap()
Returns the current available heap size in bytes. Note that due to fragmentation, actual allocations of this size may not be possible.
......@@ -200,54 +213,6 @@ sk:on("receive", function(conn, payload) node.input(payload) end)
#### See also
[`node.output()`](#nodeoutput)
## node.key() --deprecated
Defines action to take on button press (on the old devkit 0.9), button connected to GPIO 16.
This function is only available if the firmware was compiled with DEVKIT_VERSION_0_9 defined.
#### Syntax
`node.key(type, function())`
#### Parameters
- `type`: type is either string "long" or "short". long: press the key for 3 seconds, short: press shortly(less than 3 seconds)
- `function`: user defined function which is called when key is pressed. If nil, remove the user defined function. Default function: long: change LED blinking rate, short: reset chip
#### Returns
`nil`
#### Example
```lua
node.key("long", function() print('hello world') end)
```
#### See also
[`node.led()`](#nodeled-deprecated)
## node.led() --deprecated
Sets the on/off time for the LED (on the old devkit 0.9), with the LED connected to GPIO16, multiplexed with [`node.key()`](#nodekey-deprecated).
This function is only available if the firmware was compiled with DEVKIT_VERSION_0_9 defined.
#### Syntax
`node.led(low, high)`
#### Parameters
- `low` LED off time, LED keeps on when low=0. Unit: milliseconds, time resolution: 80~100ms
- `high` LED on time. Unit: milliseconds, time resolution: 80~100ms
#### Returns
`nil`
#### Example
```lua
-- turn led on forever.
node.led(0)
```
#### See also
[`node.key()`](#nodekey-deprecated)
## node.output()
Redirects the Lua interpreter output to a callback function. Optionally also prints it to the serial console.
......
......@@ -24,6 +24,7 @@ This is a companion module to the [rtcmem](rtcmem.md) and [SNTP](sntp.md) module
## rtctime.dsleep()
Puts the ESP8266 into deep sleep mode, like [`node.dsleep()`](node.md#nodedsleep). It differs from [`node.dsleep()`](node.md#nodedsleep) in the following ways:
- Time is kept across the deep sleep. I.e. [`rtctime.get()`](#rtctimeget) will keep working (provided time was available before the sleep).
- This call never returns. The module is put to sleep immediately. This is both to support accurate time keeping and to reduce power consumption.
- The time slept will generally be considerably more accurate than with [`node.dsleep()`](node.md#nodedsleep).
......
......@@ -9,7 +9,11 @@ It is aimed at setting up regularly occurring tasks, timing out operations, and
What the tmr module is *not* however, is a time keeping module. While most timeouts are expressed in milliseconds or even microseconds, the accuracy is limited and compounding errors would lead to rather inaccurate time keeping. Consider using the [rtctime](rtctime.md) module for "wall clock" time.
NodeMCU provides 7 timers, numbered 0-6. It is currently up to the user to keep track of which timers are used for what.
NodeMCU provides 7 static timers, numbered 0-6, and dynamic timer creation function [`tmr.create()`](#tmrcreate).
!!! attention
Static timers are deprecated and will be removed later.
## tmr.alarm()
......@@ -18,7 +22,7 @@ This is a convenience function combining [`tmr.register()`](#tmrregister) and [`
To free up the resources with this timer when done using it, call [`tmr.unregister()`](#tmrunregister) on it. For one-shot timers this is not necessary, unless they were stopped before they expired.
#### Parameters
- `id` timer id (0-6)
- `id`/`ref` timer id (0-6) or object
- `interval_ms` timer interval in milliseconds. Maximum value is 6870947 (1:54:30.947).
- `mode` timer mode:
- `tmr.ALARM_SINGLE` a one-shot alarm (and no need to call [`tmr.unregister()`](#tmrunregister))
......@@ -33,10 +37,46 @@ To free up the resources with this timer when done using it, call [`tmr.unregist
if not tmr.alarm(0, 5000, tmr.ALARM_SINGLE, function() print("hey there") end) then print("whoopsie") end
```
#### See also
- [`tmr.create()`](#tmrcreate)
- [`tmr.register()`](#tmrregister)
- [`tmr.start()`](#tmrstart)
- [`tmr.unregister()`](#tmrunregister)
## tmr.create()
Creates a dynamic timer object.
Dynamic timer can be used instead of numeric ID in control functions. Also can be controlled in object-oriented way.
Functions supported in timer object:
- [`t:alarm()`](#tmralarm)
- [`t:register()`](#tmrregister)
- [`t:start()`](#tmrstart)
- [`t:stop()`](#tmrstop)
- [`t:unregister()`](#tmrunregister)
- [`t:state()`](#tmrstate)
- [`t:interval()`](#tmrinterval)
#### Parameters
none
#### Returns
`timer` object
#### Example
```lua
local mytimer = tmr.create()
-- oo calling
mytimer:register(5000, tmr.ALARM_SINGLE, function (t) print("expired"); t:unregister() end)
mytimer:start()
-- with self parameter
tmr.register(mytimer, 5000, tmr.ALARM_SINGLE, function (t) print("expired"); tmr.unregister(t) end)
tmr.start(mytimer)
```
## tmr.delay()
Busyloops the processor for a specified number of microseconds.
......@@ -64,10 +104,10 @@ tmr.delay(100)
Changes a registered timer's expiry interval.
#### Syntax
`tmr.interval(id, interval_ms)`
`tmr.interval(id/ref, interval_ms)`
#### Parameters
- `id` timer id (0-6)
- `id`/`ref` timer id (0-6) or object
- `interval_ms` new timer interval in milliseconds. Maximum value is 6870947 (1:54:30.947).
#### Returns
......@@ -105,10 +145,10 @@ Configures a timer and registers the callback function to call on expiry.
To free up the resources with this timer when done using it, call [`tmr.unregister()`](#tmrunregister) on it. For one-shot timers this is not necessary, unless they were stopped before they expired.
#### Syntax
`tmr.register(id, interval_ms, mode, func)`
`tmr.register(id/ref, interval_ms, mode, func)`
#### Parameters
- `id` timer id (0-6)
- `id`/`ref` timer id (0-6) or object
- `interval_ms` timer interval in milliseconds. Maximum value is 6870947 (1:54:30.947).
- `mode` timer mode:
- `tmr.ALARM_SINGLE` a one-shot alarm (and no need to call [`tmr.unregister()`](#tmrunregister))
......@@ -126,7 +166,8 @@ tmr.register(0, 5000, tmr.ALARM_SINGLE, function() print("hey there") end)
tmr.start(0)
```
#### See also
[`tmr.alarm()`](#tmralarm)
- [`tmr.create()`](#tmrcreate)
- [`tmr.alarm()`](#tmralarm)
## tmr.softwd()
......@@ -158,10 +199,10 @@ complex_stuff_which_might_never_call_the_callback(on_success_callback)
Starts or restarts a previously configured timer.
#### Syntax
`tmr.start(id)`
`tmr.start(id/ref)`
#### Parameters
`id` timer id (0-6)
`id`/`ref` timer id (0-6) or object
#### Returns
`true` if the timer was started, `false` on error
......@@ -172,6 +213,7 @@ tmr.register(0, 5000, tmr.ALARM_SINGLE, function() print("hey there") end)
if not tmr.start(0) then print("uh oh") end
```
#### See also
- [`tmr.create()`](#tmrcreate)
- [`tmr.register()`](#tmrregister)
- [`tmr.stop()`](#tmrstop)
- [`tmr.unregister()`](#tmrunregister)
......@@ -181,10 +223,10 @@ if not tmr.start(0) then print("uh oh") end
Checks the state of a timer.
#### Syntax
`tmr.state(id)`
`tmr.state(id/ref)`
#### Parameters
`id` timer id (0-6)
`id`/`ref` timer id (0-6) or object
#### Returns
(bool, int) or `nil`
......@@ -201,10 +243,10 @@ running, mode = tmr.state(0)
Stops a running timer, but does *not* unregister it. A stopped timer can be restarted with [`tmr.start()`](#tmrstart).
#### Syntax
`tmr.stop(id)`
`tmr.stop(id/ref)`
#### Parameters
`id` timer id (0-6)
`id`/`ref` timer id (0-6) or object
#### Returns
`true` if the timer was stopped, `false` on error
......@@ -243,10 +285,10 @@ Stops the timer (if running) and unregisters the associated callback.
This isn't necessary for one-shot timers (`tmr.ALARM_SINGLE`), as those automatically unregister themselves when fired.
#### Syntax
`tmr.unregister(id)`
`tmr.unregister(id/ref)`
#### Parameters
`id` timer id (0-6)
`id`/`ref` timer id (0-6) or object
#### Returns
`nil`
......
......@@ -28,7 +28,7 @@ SPI only:
- uc1611 - dogm240 and dogxl240 variants
- uc1701 - dogs102 and mini12864 variants
This integration is based on [v1.18.1](https://github.com/olikraus/U8glib_Arduino/releases/tag/1.18.1).
This integration is based on [v1.19.1](https://github.com/olikraus/U8glib_Arduino/releases/tag/1.19.1).
## Overview
### I²C Connection
......@@ -347,6 +347,9 @@ See [u8glib nextPage()](https://github.com/olikraus/u8glib/wiki/userreference#ne
## u8g.disp:setColorIndex()
See [u8glib setColorIndex()](https://github.com/olikraus/u8glib/wiki/userreference#setcolortndex).
## u8g.disp:setContrast()
See [u8glib setContrast()](https://github.com/olikraus/u8glib/wiki/userreference#setcontrast).
## u8g.disp:setDefaultBackgroundColor()
See [u8glib setDefaultBackgroundColor()](https://github.com/olikraus/u8glib/wiki/userreference#setdefaultbackgroundcolor).
......@@ -437,7 +440,6 @@ See [u8glib undoScale()](https://github.com/olikraus/u8glib/wiki/userreference#u
- setCursorPos()
- setCursorStyle()
- General functions
- setContrast()
- setPrintPos()
- setHardwareBackup()
- setRGB()
......
# Websocket Module
| Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ |
| 2016-08-02 | [Luís Fonseca](https://github.com/luismfonseca) | [Luís Fonseca](https://github.com/luismfonseca) | [websocket.c](../../../app/modules/websocket.c)|
A websocket *client* module that implements [RFC6455](https://tools.ietf.org/html/rfc6455) (version 13) and provides a simple interface to send and receive messages.
The implementation supports fragmented messages, automatically respondes to ping requests and periodically pings if the server isn't communicating.
!!! note
Currently, it is **not** possible to change the request headers, most notably the user agent.
**SSL/TLS support**
Take note of constraints documented in the [net module](net.md).
## websocket.createClient()
Creates a new websocket client. This client should be stored in a variable and will provide all the functions to handle a connection.
When the connection becomes closed, the same client can still be reused - the callback functions are kept - and you can connect again to any server.
Before disposing the client, make sure to call `ws:close()`.
#### Syntax
`websocket.createClient()`
#### Parameters
none
#### Returns
`websocketclient`
#### Example
```lua
local ws = websocket.createClient()
-- ...
ws:close()
ws = nil
```
## websocket.client:close()
Closes a websocket connection. The client issues a close frame and attemtps to gracefully close the websocket.
If server doesn't reply, the connection is terminated after a small timeout.
This function can be called even if the websocket isn't connected.
This function must *always* be called before disposing the reference to the websocket client.
#### Syntax
`websocket:close()`
#### Parameters
none
#### Returns
`nil`
#### Example
```lua
ws = websocket.createClient()
ws:close()
ws:close() -- nothing will happen
ws = nil -- fully dispose the client as lua will now gc it
```
## websocket.client:connect()
Attempts to estabilish a websocket connection to the given URL.
#### Syntax
`websocket:connect(url)`
#### Parameters
- `url` the URL for the websocket.
#### Returns
`nil`
#### Example
```lua
ws = websocket.createClient()
ws:connect('ws://echo.websocket.org')
```
If it fails, an error will be delivered via `websocket:on("close", handler)`.
## websocket.client:on()
Registers the callback function to handle websockets events (there can be only one handler function registered per event type).
#### Syntax
`websocket:on(eventName, function(ws, ...))`
#### Parameters
- `eventName` the type of websocket event to register the callback function. Those events are: `connection`, `receive` and `close`.
- `function(ws, ...)` callback function.
The function first parameter is always the websocketclient.
Other arguments are required depending on the event type. See example for more details.
If `nil`, any previously configured callback is unregistered.
#### Returns
`nil`
#### Example
```lua
local ws = websocket.createClient()
ws:on("connection", function(ws)
print('got ws connection')
end)
ws:on("receive", function(_, msg, opcode)
print('got message:', msg, opcode) -- opcode is 1 for text message, 2 for binary
end)
ws:on("close", function(_, status)
print('connection closed', status)
ws = nil -- required to lua gc the websocket client
end)
ws:connect('ws://echo.websocket.org')
```
Note that the close callback is also triggered if any error occurs.
The status code for the close, if not 0 then it represents an error, as described in the following table.
| Status Code | Explanation |
| :----------- | :----------- |
| 0 | User requested close or the connection was terminated gracefully |
| -1 | Failed to extract protocol from URL |
| -2 | Hostname is too large (>256 chars) |
| -3 | Invalid port number (must be >0 and <= 65535) |
| -4 | Failed to extract hostname |
| -5 | DNS failed to lookup hostname |
| -6 | Server requested termination |
| -7 | Server sent invalid handshake HTTP response (i.e. server sent a bad key) |
| -8 to -14 | Failed to allocate memory to receive message |
| -15 | Server not following FIN bit protocol correctly |
| -16 | Failed to allocate memory to send message |
| -17 | Server is not switching protocols |
| -18 | Connect timeout |
| -19 | Server is not responding to health checks nor communicating |
| -99 to -999 | Well, something bad has happenned |
## websocket.client:send()
Sends a message through the websocket connection.
#### Syntax
`websocket:send(message, opcode)`
#### Parameters
- `message` the data to send.
- `opcode` optionally set the opcode (default: 1, text message)
#### Returns
`nil` or an error if socket is not connected
#### Example
```lua
ws = websocket.createClient()
ws:on("connection", function()
ws:send('hello!')
end)
ws:connect('ws://echo.websocket.org')
```
......@@ -128,6 +128,21 @@ physical mode after setup
#### See also
[`wifi.getphymode()`](#wifigetphymode)
## wifi.nullmodesleep()
Configures whether or not WiFi automatically goes to sleep in NULL_MODE. Enabled by default.
#### Syntax
`wifi.nullmodesleep(enable)`
#### Parameters
- `enable`
- true: Enable WiFi auto sleep in NULL_MODE. (Default setting)
- false: Disable WiFi auto sleep in NULL_MODE.
#### Returns
Current/new NULL_MODE sleep setting.
## wifi.sleeptype()
Configures the WiFi modem sleep type.
......
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