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

New December master drop
This commit is contained in:
Marcel Stör 2016-12-07 22:25:56 +01:00 committed by GitHub
commit 81ec3665cb
67 changed files with 4342 additions and 1424 deletions

View File

@ -4,16 +4,22 @@
The following is a set of guidelines for contributing to NodeMCU on GitHub. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request.
It is appreciated but optional if you raise an issue _before_ you start changing NodeMCU, discussing the proposed change; emphasing that the you are proposing to develop the patch yourself, and outlining the strategy for implementation. This type of discussion is what we should be doing on the issues list and it is better to do this before or in parallel to developing the patch rather than having "you should have done it this way" type of feedback on the PR itself.
It is appreciated if you raise an issue _before_ you start changing NodeMCU, discussing the proposed change; emphasing that the you are proposing to develop the patch yourself, and outlining the strategy for implementation. This type of discussion is what we should be doing on the issues list and it is better to do this before or in parallel to developing the patch rather than having "you should have done it this way" type of feedback on the PR itself.
### Table Of Contents
* [General remarks](#general-remarks)
* [Development environment setup](#development-environment-setup)
* [Writing Documentation](#writing-documentation)
* [Working with Git and GitHub](#working-with-git-and-github)
* [General flow](#general-flow)
* [Keeping your fork in sync](#keeping-your-fork-in-sync)
* [Commit messages](#commit-messages)
* [For collaborators](#for-collaborators)
* [Handling releases](#handling-release)
## General remarks
We are a friendly and welcoming community and look forward to your contributions. Once your contribution is integrated into this repository we feel responsible for it. Therefore, be prepared for constructive feedback. Before we merge anything we need to ensure that it fits in and is consistent with the rest of NodeMCU.
If you made something really cool but won't spend time to integrate it into this upstream project please still share it in your fork on GitHub. If you mention it in an issues we'll take a look at it anyway.
## Development environment setup
Use the platform and tools you feel most comfortable with. There are no constraints imposed by this project. You have (at least) two options to set up the toolchain to build the NodeMCU firmware:
@ -52,7 +58,7 @@ Avoid intermediate merge commits. [Rebase](https://www.atlassian.com/git/tutoria
1. `git checkout <branch-name>`
1. Make changes to the code base and commit them using e.g. `git commit -a -m 'Look ma, I did it'`
1. When you're done:
1. [Squash your commits](http://www.andrewconnell.com/blog/squash-multiple-git-commits-into-one). There are [several ways](http://stackoverflow.com/a/5201642/131929) of doing this.
1. Think about [squashing (some of) your commits](http://www.andrewconnell.com/blog/squash-multiple-git-commits-into-one). There are [several ways](http://stackoverflow.com/a/5201642/131929) to do this. There's no need to squash everything into a single commit as GitHub offers to do this when we merge your changes. However, you might want to trim your commit history to relevant chunks.
1. Bring your fork up-to-date with the NodeMCU upstream repo ([see below](#keeping-your-fork-in-sync)). Then rebase your branch on `dev` running `git rebase dev`.
1. `git push`
1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/) (PR) on GitHub.
@ -90,3 +96,15 @@ Further paragraphs come after blank lines.
Don't forget to [reference affected issues](https://help.github.com/articles/closing-issues-via-commit-messages/) in the commit message to have them closed automatically on GitHub.
[Amend](https://help.github.com/articles/changing-a-commit-message/) your commit messages if necessary to make sure what the world sees on GitHub is as expressive and meaningful as possible.
## For collaborators
### Handling releases
- Create a [milestone](https://github.com/nodemcu/nodemcu-firmware/milestones) right after you cut a new release. Give it a meaningful name if you already have an idea what the scope of the upcoming release is going to be. Also set the due date to ~2 months in the future.
- Add this milestone to every PR before you merge it. Also add the milestone to PRs you want to see land in this milestone.
- Add notes to the description of the milestone in the course of the ~2 months it lives.
- Be careful and reluctant to merge PRs once we're past the 6-weeks mark of a milestone. Ideally we don't merge anything in the last 2 weeks.
- Cutting a release
- Create an annotated tag like so: `git tag -a <SDK-version>-master_<yyyyMMdd> -m ""`, `git push --tags`
- Create a new [release](https://github.com/nodemcu/nodemcu-firmware/releases) based on the tag you just pushed. The version name is the same as the tag name.
- Write release notes. Mention breaking changes explicitly. Since every PR that went into this release is linked to from the milestone it should be fairly easy to include important changes in the release notes.

View File

@ -207,14 +207,14 @@ sdk_patched: sdk_extracted $(TOP_DIR)/sdk/.patched-$(SDK_VER)
$(TOP_DIR)/sdk/.extracted-$(SDK_BASE_VER): $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip
mkdir -p "$(dir $@)"
(cd "$(dir $@)" && rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip ESP8266_NONOS_SDK/lib/* ESP8266_NONOS_SDK/ld/eagle.rom.addr.v6.ld ESP8266_NONOS_SDK/include/* )
(cd "$(dir $@)" && rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip ESP8266_NONOS_SDK/lib/* ESP8266_NONOS_SDK/ld/eagle.rom.addr.v6.ld ESP8266_NONOS_SDK/include/* ESP8266_NONOS_SDK/bin/esp_init_data_default.bin)
mv $(dir $@)/ESP8266_NONOS_SDK $(dir $@)/esp_iot_sdk_v$(SDK_VER)
rm -f $(SDK_DIR)/lib/liblwip.a
touch $@
$(TOP_DIR)/sdk/.patched-$(SDK_VER): $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VER).zip
mkdir -p "$(dir $@)/patch"
(cd "$(dir $@)/patch" && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VER)*.zip *.a && mv *.a $(SDK_DIR)/lib/)
(cd "$(dir $@)/patch" && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VER)*.zip *.a esp_init_data_default.bin && mv *.a $(SDK_DIR)/lib/ && mv esp_init_data_default.bin $(SDK_DIR)/bin/)
rmdir $(dir $@)/patch
rm -f $(SDK_DIR)/lib/liblwip.a
touch $@

View File

@ -33,6 +33,8 @@ LOCAL uint8 pinSCL = 15;
LOCAL void ICACHE_FLASH_ATTR
i2c_master_setDC(uint8 SDA, uint8 SCL)
{
uint8 sclLevel;
SDA &= 0x01;
SCL &= 0x01;
m_nLastSDA = SDA;
@ -47,6 +49,11 @@ i2c_master_setDC(uint8 SDA, uint8 SCL)
} else {
I2C_MASTER_SDA_HIGH_SCL_HIGH();
}
if(1 == SCL) {
do {
sclLevel = GPIO_INPUT_GET(GPIO_ID_PIN(I2C_MASTER_SCL_GPIO));
} while(sclLevel == 0);
}
}
/******************************************************************************

View File

@ -30,6 +30,8 @@
// For event signalling
static task_handle_t sig = 0;
static uint8 *sig_flag;
static uint8 isr_flag = 0;
// UartDev is defined and initialized in rom code.
extern UartDevice UartDev;
@ -277,8 +279,12 @@ uart0_rx_intr_handler(void *para)
got_input = true;
}
if (got_input && sig)
task_post_low (sig, false);
if (got_input && sig) {
if (isr_flag == *sig_flag) {
isr_flag ^= 0x01;
task_post_low (sig, 0x8000 | isr_flag << 14 | false);
}
}
}
static void
@ -316,21 +322,21 @@ uart_stop_autobaud()
* Description : user interface for init uart
* Parameters : UartBautRate uart0_br - uart0 bautrate
* UartBautRate uart1_br - uart1 bautrate
* uint8 task_prio - task priority to signal on input
* os_signal_t sig_input - signal to post
* uint8 *flag_input - flag of consumer task
* Returns : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR
uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input)
uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input, uint8 *flag_input)
{
sig = sig_input;
sig_flag = flag_input;
// rom use 74880 baut_rate, here reinitialize
UartDev.baut_rate = uart0_br;
uart_config(UART0);
UartDev.baut_rate = uart1_br;
uart_config(UART1);
ETS_UART_INTR_ENABLE();
#ifdef BIT_RATE_AUTOBAUD
uart_init_autobaud(0);
#endif

View File

@ -13,6 +13,7 @@
*/
#include "osapi.h"
#include "../libc/c_stdio.h"
#include "user_interface.h"
#include "espconn.h"
#include "mem.h"
@ -94,7 +95,7 @@ static int ICACHE_FLASH_ATTR http_chunked_decode( const char * chunked, char * d
char * endstr;
/* [chunk-size] */
i = strtoul( str + j, NULL, 16 );
HTTPCLIENT_DEBUG( "Chunk Size:%d\r\n", i );
HTTPCLIENT_DEBUG( "Chunk Size:%d", i );
if ( i <= 0 )
break;
/* [chunk-size-end-ptr] */
@ -137,7 +138,7 @@ static void ICACHE_FLASH_ATTR http_receive_callback( void * arg, char * buf, uns
char * new_buffer;
if ( new_size > BUFFER_SIZE_MAX || NULL == (new_buffer = (char *) os_malloc( new_size ) ) )
{
HTTPCLIENT_DEBUG( "Response too long (%d)\n", new_size );
HTTPCLIENT_ERR( "Response too long (%d)", new_size );
req->buffer[0] = '\0'; /* Discard the buffer to avoid using an incomplete response. */
if ( req->secure )
espconn_secure_disconnect( conn );
@ -163,12 +164,12 @@ static void ICACHE_FLASH_ATTR http_send_callback( void * arg )
if ( req->post_data == NULL )
{
HTTPCLIENT_DEBUG( "All sent\n" );
HTTPCLIENT_DEBUG( "All sent" );
}
else
{
/* The headers were sent, now send the contents. */
HTTPCLIENT_DEBUG( "Sending request body\n" );
HTTPCLIENT_DEBUG( "Sending request body" );
if ( req->secure )
espconn_secure_send( conn, (uint8_t *) req->post_data, strlen( req->post_data ) );
else
@ -181,7 +182,7 @@ static void ICACHE_FLASH_ATTR http_send_callback( void * arg )
static void ICACHE_FLASH_ATTR http_connect_callback( void * arg )
{
HTTPCLIENT_DEBUG( "Connected\n" );
HTTPCLIENT_DEBUG( "Connected" );
struct espconn * conn = (struct espconn *) arg;
request_args_t * req = (request_args_t *) conn->reverse;
espconn_regist_recvcb( conn, http_receive_callback );
@ -250,7 +251,7 @@ static void ICACHE_FLASH_ATTR http_connect_callback( void * arg )
}
req->headers = NULL;
HTTPCLIENT_DEBUG( "Sending request header\n" );
HTTPCLIENT_DEBUG( "Sending request header" );
}
static void http_free_req( request_args_t * req)
@ -272,7 +273,7 @@ static void http_free_req( request_args_t * req)
static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg )
{
HTTPCLIENT_DEBUG( "Disconnected\n" );
HTTPCLIENT_DEBUG( "Disconnected" );
struct espconn *conn = (struct espconn *) arg;
if ( conn == NULL )
@ -295,7 +296,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg )
if ( req->buffer == NULL )
{
HTTPCLIENT_DEBUG( "Buffer probably shouldn't be NULL\n" );
HTTPCLIENT_DEBUG( "Buffer probably shouldn't be NULL" );
}
else if ( req->buffer[0] != '\0' )
{
@ -305,7 +306,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg )
if (( os_strncmp( req->buffer, version_1_0, strlen( version_1_0 ) ) != 0 ) &&
( os_strncmp( req->buffer, version_1_1, strlen( version_1_1 ) ) != 0 ))
{
HTTPCLIENT_DEBUG( "Invalid version in %s\n", req->buffer );
HTTPCLIENT_ERR( "Invalid version in %s", req->buffer );
}
else
{
@ -326,7 +327,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg )
char *locationOffsetEnd = (char *) os_strstr(locationOffset, "\r\n");
if ( locationOffsetEnd == NULL ) {
HTTPCLIENT_DEBUG( "Found Location header but was incomplete\n" );
HTTPCLIENT_ERR( "Found Location header but was incomplete" );
http_status = -1;
} else {
*locationOffsetEnd = '\0';
@ -371,7 +372,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg )
return;
}
} else {
HTTPCLIENT_DEBUG("Too many redirections\n");
HTTPCLIENT_ERR("Too many redirections");
http_status = -1;
}
} else {
@ -379,7 +380,7 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg )
if (NULL == body) {
/* Find missing body */
HTTPCLIENT_DEBUG("Body shouldn't be NULL\n");
HTTPCLIENT_ERR("Body shouldn't be NULL");
/* To avoid NULL body */
body = "";
} else {
@ -411,16 +412,9 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg )
}
static void ICACHE_FLASH_ATTR http_error_callback( void *arg, sint8 errType )
{
HTTPCLIENT_DEBUG( "Disconnected with error\n" );
http_disconnect_callback( arg );
}
static void ICACHE_FLASH_ATTR http_timeout_callback( void *arg )
{
HTTPCLIENT_DEBUG( "Connection timeout\n" );
HTTPCLIENT_ERR( "Connection timeout" );
struct espconn * conn = (struct espconn *) arg;
if ( conn == NULL )
{
@ -439,13 +433,20 @@ static void ICACHE_FLASH_ATTR http_timeout_callback( void *arg )
}
static void ICACHE_FLASH_ATTR http_error_callback( void *arg, sint8 errType )
{
HTTPCLIENT_ERR( "Disconnected with error: %d", errType );
http_timeout_callback( arg );
}
static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_t * addr, void * arg )
{
request_args_t * req = (request_args_t *) arg;
if ( addr == NULL )
{
HTTPCLIENT_DEBUG( "DNS failed for %s\n", hostname );
HTTPCLIENT_ERR( "DNS failed for %s", hostname );
if ( req->callback_handle != NULL )
{
req->callback_handle( "", -1, "" );
@ -454,7 +455,7 @@ static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_
}
else
{
HTTPCLIENT_DEBUG( "DNS found %s " IPSTR "\n", hostname, IP2STR( addr ) );
HTTPCLIENT_DEBUG( "DNS found %s " IPSTR, hostname, IP2STR( addr ) );
struct espconn * conn = (struct espconn *) os_zalloc( sizeof(struct espconn) );
conn->type = ESPCONN_TCP;
@ -489,7 +490,7 @@ static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_
void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool secure, const char * method, const char * path, const char * headers, const char * post_data, http_callback_t callback_handle, int redirect_follow_count )
{
HTTPCLIENT_DEBUG( "DNS request\n" );
HTTPCLIENT_DEBUG( "DNS request" );
request_args_t * req = (request_args_t *) os_zalloc( sizeof(request_args_t) );
req->hostname = esp_strdup( hostname );
@ -512,7 +513,7 @@ void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool s
if ( error == ESPCONN_INPROGRESS )
{
HTTPCLIENT_DEBUG( "DNS pending\n" );
HTTPCLIENT_DEBUG( "DNS pending" );
}
else if ( error == ESPCONN_OK )
{
@ -523,9 +524,9 @@ void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool s
{
if ( error == ESPCONN_ARG )
{
HTTPCLIENT_DEBUG( "DNS arg error %s\n", hostname );
HTTPCLIENT_ERR( "DNS arg error %s", hostname );
}else {
HTTPCLIENT_DEBUG( "DNS error code %d\n", error );
HTTPCLIENT_ERR( "DNS error code %d", error );
}
http_dns_callback( hostname, NULL, req ); /* Handle all DNS errors the same way. */
}
@ -561,7 +562,7 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons
}
else
{
HTTPCLIENT_DEBUG( "URL is not HTTP or HTTPS %s\n", url );
HTTPCLIENT_ERR( "URL is not HTTP or HTTPS %s", url );
return;
}
@ -578,7 +579,7 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons
}
if (path - url >= sizeof(hostname)) {
HTTPCLIENT_DEBUG( "hostname is too long %s\n", url );
HTTPCLIENT_ERR( "hostname is too long %s", url );
return;
}
@ -592,7 +593,7 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons
port = atoi( colon + 1 );
if ( port == 0 )
{
HTTPCLIENT_DEBUG( "Port error %s\n", url );
HTTPCLIENT_ERR( "Port error %s", url );
return;
}
@ -606,10 +607,10 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons
path = "/";
}
HTTPCLIENT_DEBUG( "hostname=%s\n", hostname );
HTTPCLIENT_DEBUG( "port=%d\n", port );
HTTPCLIENT_DEBUG( "method=%s\n", method );
HTTPCLIENT_DEBUG( "path=%s\n", path );
HTTPCLIENT_DEBUG( "hostname=%s", hostname );
HTTPCLIENT_DEBUG( "port=%d", port );
HTTPCLIENT_DEBUG( "method=%s", method );
HTTPCLIENT_DEBUG( "path=%s", path );
http_raw_request( hostname, port, secure, method, path, headers, post_data, callback_handle, redirect_follow_count);
}
@ -645,10 +646,11 @@ void ICACHE_FLASH_ATTR http_put( const char * url, const char * headers, const c
void ICACHE_FLASH_ATTR http_callback_example( char * response, int http_status, char * full_response )
{
os_printf( "http_status=%d\n", http_status );
dbg_printf( "http_status=%d\n", http_status );
if ( http_status != HTTP_STATUS_GENERIC_ERROR )
{
os_printf( "strlen(full_response)=%d\n", strlen( full_response ) );
os_printf( "response=%s<EOF>\n", response );
dbg_printf( "strlen(full_response)=%d\n", strlen( full_response ) );
dbg_printf( "response=%s<EOF>\n", response );
}
}

View File

@ -10,18 +10,25 @@
#ifndef __HTTPCLIENT_H__
#define __HTTPCLIENT_H__
#if defined(GLOBAL_DEBUG_ON)
#define HTTPCLIENT_DEBUG_ON
static const char log_prefix[] = "HTTP client: ";
#if defined(DEVELOP_VERSION)
#define HTTPCLIENT_DEBUG_ON
#endif
#if defined(HTTPCLIENT_DEBUG_ON)
#define HTTPCLIENT_DEBUG(format, ...) os_printf(format, ##__VA_ARGS__)
#define HTTPCLIENT_DEBUG(format, ...) dbg_printf("%s"format"\n", log_prefix, ##__VA_ARGS__)
#else
#define HTTPCLIENT_DEBUG(format, ...)
#define HTTPCLIENT_DEBUG(...)
#endif
#if defined(NODE_ERROR)
#define HTTPCLIENT_ERR(format, ...) NODE_ERR("%s"format"\n", log_prefix, ##__VA_ARGS__)
#else
#define HTTPCLIENT_ERR(...)
#endif
#if defined(USES_SDK_BEFORE_V140)
#define espconn_send espconn_sent
#define espconn_secure_send espconn_secure_sent
#define espconn_send espconn_sent
#define espconn_secure_send espconn_secure_sent
#endif
/*

View File

@ -101,7 +101,7 @@ typedef struct {
int buff_uart_no; //indicate which uart use tx/rx buffer
} UartDevice;
void uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input);
void uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input, uint8 *flag_input);
void uart0_alt(uint8 on);
void uart0_sendStr(const char *str);
void uart0_putc(const char c);

View File

@ -149,5 +149,7 @@ void uart_div_modify(int no, unsigned int freq);
/* Returns 0 on success, 1 on failure */
uint8_t SPIRead(uint32_t src_addr, uint32_t *des_addr, uint32_t size);
uint8_t SPIWrite(uint32_t dst_addr, const uint32_t *src, uint32_t size);
uint8_t SPIEraseSector(uint32_t sector);
#endif

View File

@ -2,5 +2,6 @@
#define _SECTIONS_H_
#define TEXT_SECTION_ATTR __attribute__((section(".text")))
#define RAM_CONST_SECTION_ATTR __attribute((section(".data")))
#endif

View File

@ -24,6 +24,7 @@
//
// I2C based displays go into here:
// U8G_DISPLAY_TABLE_ENTRY(sh1106_128x64_i2c) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x32_i2c) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_i2c) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1306_64x48_i2c) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1309_128x64_i2c) \
@ -40,6 +41,7 @@
// U8G_DISPLAY_TABLE_ENTRY(pcd8544_84x48_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(pcf8812_96x65_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(sh1106_128x64_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x32_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1306_64x48_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1309_128x64_hw_spi) \
@ -75,6 +77,10 @@
U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_hw_spi) \
#undef U8G_DISPLAY_TABLE_ENTRY
// Special display device to provide run-length encoded framebuffer contents
// to a Lua callback:
//#define U8G_DISPLAY_FB_RLE
//
// ***************************************************************************

View File

@ -36,13 +36,13 @@ extern void luaL_assertfail(const char *file, int line, const char *message);
#define NODE_ERROR
#ifdef NODE_DEBUG
#define NODE_DBG c_printf
#define NODE_DBG dbg_printf
#else
#define NODE_DBG
#endif /* NODE_DEBUG */
#ifdef NODE_ERROR
#define NODE_ERR c_printf
#define NODE_ERR dbg_printf
#else
#define NODE_ERR
#endif /* NODE_ERROR */
@ -76,6 +76,9 @@ extern void luaL_assertfail(const char *file, int line, const char *message);
// maximum length of a filename
#define FS_OBJ_NAME_LEN 31
// maximum number of open files for SPIFFS
#define SPIFFS_MAX_OPEN_FILES 4
// Uncomment this next line for fastest startup
// It reduces the format time dramatically
// #define SPIFFS_MAX_FILESYSTEM_SIZE 32768

View File

@ -53,6 +53,7 @@
//#define LUA_USE_MODULES_RTCTIME
//#define LUA_USE_MODULES_SIGMA_DELTA
//#define LUA_USE_MODULES_SNTP
//#define LUA_USE_MODULES_SOMFY
#define LUA_USE_MODULES_SPI
//#define LUA_USE_MODULES_STRUCT
//#define LUA_USE_MODULES_SWITEC

View File

@ -63,6 +63,8 @@ extern void output_redirect(const char *str);
int c_sprintf(char* s,const char *fmt, ...);
#endif
extern void dbg_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
#define c_vsprintf ets_vsprintf
#define c_printf(...) do { \
unsigned char __print_buf[BUFSIZ]; \

164
app/libc/dbg_printf.c Normal file
View File

@ -0,0 +1,164 @@
/* $NetBSD: printf.c,v 1.12 1997/06/26 19:11:48 drochner Exp $ */
/*-
* Copyright (c) 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)printf.c 8.1 (Berkeley) 6/11/93
*/
// This version uses almost no stack, and does not suffer from buffer
// overflows. The downside is that it does not implement a wide range
// of formatting characters.
#include <c_stdlib.h>
#include <c_types.h>
#include <c_stdarg.h>
#include "driver/uart.h"
static void kprintn (void (*)(const char), uint32_t, int, int, char);
static void kdoprnt (void (*)(const char), const char *, va_list);
void
dbg_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
kdoprnt(uart0_putc, fmt, ap);
va_end(ap);
}
void
dbg_vprintf(const char *fmt, va_list ap)
{
kdoprnt(uart0_putc, fmt, ap);
}
void
kdoprnt(void (*put)(const char), const char *fmt, va_list ap)
{
register char *p;
register int ch, n;
unsigned long ul;
int lflag, set;
char zwidth;
char width;
for (;;) {
while ((ch = *fmt++) != '%') {
if (ch == '\0')
return;
put(ch);
}
lflag = 0;
width = 0;
zwidth = ' ';
reswitch: switch (ch = *fmt++) {
case '\0':
/* XXX print the last format character? */
return;
case 'l':
lflag = 1;
goto reswitch;
case 'c':
ch = va_arg(ap, int);
put(ch & 0x7f);
break;
case 's':
p = va_arg(ap, char *);
if (p == 0) {
p = "<null>";
}
while ((ch = *p++))
put(ch);
break;
case 'd':
ul = lflag ?
va_arg(ap, long) : va_arg(ap, int);
if ((long)ul < 0) {
put('-');
ul = -(long)ul;
}
kprintn(put, ul, 10, width, zwidth);
break;
case 'o':
ul = lflag ?
va_arg(ap, uint32_t) : va_arg(ap, uint32_t);
kprintn(put, ul, 8, width, zwidth);
break;
case 'u':
ul = lflag ?
va_arg(ap, uint32_t) : va_arg(ap, uint32_t);
kprintn(put, ul, 10, width, zwidth);
break;
case 'x':
ul = lflag ?
va_arg(ap, uint32_t) : va_arg(ap, uint32_t);
kprintn(put, ul, 16, width, zwidth);
break;
default:
if (ch >= '0' && ch <= '9') {
if (ch == '0' && width == 0 && zwidth == ' ') {
zwidth = '0';
} else {
width = width * 10 + ch - '0';
}
goto reswitch;
}
put('%');
if (lflag)
put('l');
put(ch);
}
}
va_end(ap);
}
static void
kprintn(void (*put)(const char), unsigned long ul, int base, int width, char padchar)
{
/* hold a long in base 8 */
char *p, buf[(sizeof(long) * 8 / 3) + 2];
p = buf;
do {
*p++ = "0123456789abcdef"[ul % base];
} while (ul /= base);
while (p - buf < width--) {
put(padchar);
}
do {
put(*--p);
} while (p > buf);
}

View File

@ -807,7 +807,7 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
}
LUALIB_API void luaL_assertfail(const char *file, int line, const char *message) {
c_printf("ASSERT@%s(%d): %s\n", file, line, message);
dbg_printf("ASSERT@%s(%d): %s\n", file, line, message);
}
static int panic (lua_State *L) {

View File

@ -343,28 +343,19 @@ static int read_line (lua_State *L, int f) {
static int read_line (lua_State *L, int f) {
luaL_Buffer b;
luaL_buffinit(L, &b);
char *p = luaL_prepbuffer(&b);
signed char c = EOF;
int i = 0;
do{
signed char c;
do {
c = (signed char)vfs_getc(f);
if(c==EOF){
if (c==EOF) {
break;
}
p[i++] = c;
}while((c!=EOF) && (c!='\n') && (i<LUAL_BUFFERSIZE) );
if (c != '\n') {
luaL_addchar(&b, c);
}
} while (c != '\n');
if(i>0 && p[i-1] == '\n')
i--; /* do not include `eol' */
if(i==0){
luaL_pushresult(&b); /* close buffer */
return (lua_objlen(L, -1) > 0); /* check whether read something */
}
luaL_addsize(&b, i);
luaL_pushresult(&b); /* close buffer */
return 1; /* read at least an `eol' */
return (lua_objlen(L, -1) > 0); /* check whether read something */
}
#endif

View File

@ -346,7 +346,7 @@ extern int readline4lua(const char *prompt, char *buffer, int length);
** (A format string with one argument is enough for Lua...)
*/
#if !defined(LUA_USE_STDIO)
#define luai_writestringerror(s,p) c_printf((s), (p))
#define luai_writestringerror(s,p) dbg_printf((s), (p))
#endif // defined(LUA_USE_STDIO)
@ -556,7 +556,7 @@ extern int readline4lua(const char *prompt, char *buffer, int length);
** For example: If set to 4K a call to string.gsub will need more than
** 5k C stack space.
*/
#define LUAL_BUFFERSIZE BUFSIZ
#define LUAL_BUFFERSIZE 256
/* }================================================================== */

View File

@ -31,6 +31,7 @@
* @author Robert Foss <dev@robertfoss.se>
*
* Additions & fixes: Johny Mattsson <jmattsson@dius.com.au>
* Jason Follas <jfollas@gmail.com>
*/
#include "module.h"
@ -50,6 +51,8 @@
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define LITLEN(strliteral) (sizeof (strliteral) -1)
#define STRINGIFY(x) #x
#define NUMLEN(x) (sizeof(STRINGIFY(x)) - 1)
#define ENDUSER_SETUP_ERR_FATAL (1 << 0)
#define ENDUSER_SETUP_ERR_NONFATAL (1 << 1)
@ -59,7 +62,7 @@
#define ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND 2
#define ENDUSER_SETUP_ERR_UNKOWN_ERROR 3
#define ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN 4
#define ENDUSER_SETUP_ERR_MAX_NUMBER 5
/**
* DNS Response Packet:
@ -78,159 +81,19 @@ static const char dns_body[] = { 0x00, 0x01, 0x00, 0x01,
0xC0, 0x0C, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x04 };
static const char http_html_filename[] = "enduser_setup.html";
static const char http_header_200[] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nContent-Type: text/html\r\n"; /* Note single \r\n here! */
static const char http_header_204[] = "HTTP/1.1 204 No Content\r\n\r\n";
static const char http_header_302[] = "HTTP/1.1 302 Moved\r\nLocation: /\r\n\r\n";
static const char http_header_401[] = "HTTP/1.1 401 Bad request\r\n\r\n";
static const char http_header_404[] = "HTTP/1.1 404 Not found\r\n\r\n";
static const char http_header_500[] = "HTTP/1.1 500 Internal Error\r\n\r\n";
static const char http_header_200[] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nConnection:close\r\nContent-Type:text/html\r\n"; /* Note single \r\n here! */
static const char http_header_204[] = "HTTP/1.1 204 No Content\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
static const char http_header_302[] = "HTTP/1.1 302 Moved\r\nLocation: /\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
static const char http_header_400[] = "HTTP/1.1 400 Bad request\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
static const char http_header_404[] = "HTTP/1.1 404 Not found\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
static const char http_header_405[] = "HTTP/1.1 405 Method Not Allowed\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
static const char http_header_500[] = "HTTP/1.1 500 Internal Error\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
/* The below is the un-minified version of the http_html_backup[] string.
* Minified using https://kangax.github.io/html-minifier/
* Note: using method="get" due to iOS not always sending body in same
* packet as the HTTP header, and us thus missing it in that case
*/
#if 0
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta name="viewport" content="width=380">
<title>WiFi Login</title>
<style media="screen" type="text/css">
*{margin:0;padding:0}
html{height:100%;background:linear-gradient(rgba(196,102,0,.2),rgba(155,89,182,.2)),url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAAA8AgMAAACm+SSwAAAADFBMVEVBR1FFS1VHTlg8Q0zU/YXIAAADVElEQVQ4yy1TTYvTUBQ9GTKiYNoodsCF4MK6U4TZChOhiguFWHyBFzqlLl4hoeNvEBeCrlrhBVKq1EUKLTP+hvi1GyguXqBdiZCBzGqg20K8L3hDQnK55+OeJNguHx6UujYl3dL5ALn4JOIUluAqeAWciyGaSdvngOWzNT+G0UyGUOxVOAdqkjXDCbBiUyjZ5QzYEbGadYAi6kHxth+kthXNVNCDofwhGv1D4QGGiM9iAjbCHgr2iUUpDJbs+VPQ4xAr2fX7KXbkOJMdok965Ksb+6lrjdkem8AshIuHm9Nyu19uTunYlOXDTQqi8VgeH0kBXH2xq/ouiMZPzuMukymutrBmulUTovC6HqNFW2ZOiqlpSXZOTvSUeUPxChjxol8BLbRy4gJuhV7OR4LRVBs3WQ9VVAU7SXgK2HeUrOj7bC8YsUgr3lEV/TXB7hK90EBnxaeg1Ov15bY80M736ekCGesGAaGvG0Ct4WRkVQVHIgIM9xJgvSFfPay8Q6GNv7VpR7xUnkvhnMQCJDYkYOtNLihV70tCU1Sk+BQrpoP+HLHUrJkuta40C6LP5GvBv+Hqo10ATxxFrTPvNdPr7XwgQud6RvQN/sXjBGzqbU27wcj9cgsyvSTrpyXV8gKpXeNJU3aFl7MOdldzV4+HfO19jBa5f2IjWwx1OLHIvFHkqbBj20ro1g7nDfY1DpScvDRUNARgjMMVO0zoMjKxJ6uWCPP+YRAWbGoaN8kXYHmLjB9FXLGOazfFVCvOgqzfnicNPrHtPKlex2ye824gMza0cTZ2sS2Xm7Qst/UfFw8O6vVtmUKxZy9xFgzMys5cJ5fxZw4y37Ufk1Dsfb8MqOjYxE3ZMWxiDcO0PYUaD2ys+8OW1pbB7/e3sfZeGVCL0Q2aMjjPdm2sxADuejZxHJAd8dO9DSUdA0V8/NggRRanDkBrANn8yHlEQOn/MmwoQfQF7xgmKDnv520bS/pgylP67vf3y2V5sCwfoCEMkZClgOfJAFX9eXefR2RpnmRs4CDVPceaRfoFzCkJVJX27vWZnoqyvmtXU3+dW1EIXIu8Qg5Qta4Zlv7drUCoWe8/8MXzaEwux7ESE9h6qnHj3mIO0/D9RvzfxPmjWiQ1vbeSk4rrHwhAre35EEVaAAAAAElFTkSuQmCC)}
body{font-family:arial,verdana}
div{position:absolute;margin:auto;top:-150px;right:0;bottom:0;left:0;width:320px;height:304px}
form{width:320px;text-align:center;position:relative}
form fieldset{background:#fff;border:0 none;border-radius:5px;box-shadow:0 0 15px 1px rgba(0,0,0,.4);padding:20px 30px;box-sizing:border-box}
form input{padding:15px;border:1px solid #ccc;border-radius:3px;margin-bottom:10px;width:100%;box-sizing:border-box;font-family:montserrat;color:#2C3E50;font-size:13px}
form .action-button{border:0 none;border-radius:3px;cursor:pointer;}
#msform .submit:focus,form .action-button:hover{box-shadow:0 0 0 2px #fff,0 0 0 3px #27AE60;}
#formFrame{display: none;}
#aplist{display: block;}
select{width:100%;margin-bottom: 20px;padding: 10px 5px; border:1px solid #ccc;display:none;}
.fs-title{font-size:15px;text-transform:uppercase;color:#2C3E50;margin-bottom:10px}
.fs-subtitle{font-weight:400;font-size:13px;color:#666;margin-bottom:20px}
.fs-status{font-weight:400;font-size:13px;color:#666;margin-bottom:10px;padding-top:20px; border-top:1px solid #ccc}
.submit{width:100px;background: #27AE60; color: #fff;font-weight:700;margin:10px 5px; padding: 10px 5px; }
</style>
</head>
<body>
<div>
<form id="credentialsForm" method="get" action="/update" target="formFrame">
<fieldset>
<iframe id="formFrame" src="" name="formFrame"></iframe> <!-- Used to submit data, needed to prevent re-direction after submission -->
<h2 class="fs-title">WiFi Login</h2>
<h3 class="fs-subtitle">Connect gadget to your WiFi network</h3>
<input id="wifi_ssid" autocorrect="off" autocapitalize="none" name="wifi_ssid" placeholder="WiFi Name">
<select id="aplist" name="aplist" size="1" disabled>
<option>Scanning for networks...</option>
</select>
<input name="wifi_password" placeholder="Password" type="password">
<input type=submit name=save class="action-button submit" value="Save">
<h3 class="fs-status">Status: <span id="status">Updating...</span></h3>
</fieldset>
<h3 id="dbg"></h3>
</form>
</div>
<script>
function fetch(url, method, callback)
{
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=check_ready;
function check_ready()
{
if (xhr.readyState === 4)
{
callback(xhr.status === 200 ? xhr.responseText : null);
}
}
xhr.open(method, url, true);
xhr.send();
}
function new_status(stat)
{
if (stat)
{
var e = document.getElementById("status");
e.innerHTML = stat;
}
}
function new_status_repeat(stat)
{
new_status(stat);
setTimeout(refresh_status, 750);
}
function new_ap_list(json)
{
if (json)
{
var list = JSON.parse(json);
list.sort(function(a, b){ return b.rssi - a.rssi; });
var ssids = list.map(function(a) { return a.ssid; }).filter(function(item, pos, self) { return self.indexOf(item)==pos; });
var sel = document.getElementById("aplist");
sel.innerHTML = "";
sel.setAttribute("size", Math.max(Math.min(3, list.length), 1));
sel.removeAttribute("disabled");
for (var i = 0; i < ssids.length; ++i)
{
var o = document.createElement("option");
o.innerHTML = ssids[i];
sel.options.add(o);
}
sel.style.display = 'block';
}
}
function new_ap_list_repeat(json)
{
new_ap_list(json);
setTimeout(refresh_ap_list, 3000);
}
function refresh_status()
{
fetch('/status','GET', new_status_repeat);
}
function refresh_ap_list()
{
fetch('/aplist','GET', new_ap_list_repeat);
}
function set_ssid_field() {
var sel = document.getElementById("aplist");
document.getElementById("wifi_ssid").value = sel.value;
}
window.onload = function()
{
refresh_status();
refresh_ap_list();
document.getElementById("aplist").onclick = set_ssid_field;
document.getElementById("aplist").onchange = set_ssid_field;
document.getElementById("credentialsForm").addEventListener("submit", function(){
fetch('/status','GET', new_status);
});
}
</script>
</body>
</html>
#endif
static const char http_html_backup[] =
"<!DOCTYPE html><meta http-equiv=content-type content='text/html; charset=UTF-8'><meta charset=utf-8><meta name=viewport content='width=380'><title>WiFi Login</title><style media=screen type=text/css>*{margin:0;padding:0}html{height:100%;background:linear-gradient(rgba(196,102,0,.2),rgba(155,89,182,.2)),url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAAA8AgMAAACm+SSwAAAADFBMVEVBR1FFS1VHTlg8Q0zU/YXIAAADVElEQVQ4yy1TTYvTUBQ9GTKiYNoodsCF4MK6U4TZChOhiguFWHyBFzqlLl4hoeNvEBeCrlrhBVKq1EUKLTP+hvi1GyguXqBdiZCBzGqg20K8L3hDQnK55+OeJNguHx6UujYl3dL5ALn4JOIUluAqeAWciyGaSdvngOWzNT+G0UyGUOxVOAdqkjXDCbBiUyjZ5QzYEbGadYAi6kHxth+kthXNVNCDofwhGv1D4QGGiM9iAjbCHgr2iUUpDJbs+VPQ4xAr2fX7KXbkOJMdok965Ksb+6lrjdkem8AshIuHm9Nyu19uTunYlOXDTQqi8VgeH0kBXH2xq/ouiMZPzuMukymutrBmulUTovC6HqNFW2ZOiqlpSXZOTvSUeUPxChjxol8BLbRy4gJuhV7OR4LRVBs3WQ9VVAU7SXgK2HeUrOj7bC8YsUgr3lEV/TXB7hK90EBnxaeg1Ov15bY80M736ekCGesGAaGvG0Ct4WRkVQVHIgIM9xJgvSFfPay8Q6GNv7VpR7xUnkvhnMQCJDYkYOtNLihV70tCU1Sk+BQrpoP+HLHUrJkuta40C6LP5GvBv+Hqo10ATxxFrTPvNdPr7XwgQud6RvQN/sXjBGzqbU27wcj9cgsyvSTrpyXV8gKpXeNJU3aFl7MOdldzV4+HfO19jBa5f2IjWwx1OLHIvFHkqbBj20ro1g7nDfY1DpScvDRUNARgjMMVO0zoMjKxJ6uWCPP+YRAWbGoaN8kXYHmLjB9FXLGOazfFVCvOgqzfnicNPrHtPKlex2ye824gMza0cTZ2sS2Xm7Qst/UfFw8O6vVtmUKxZy9xFgzMys5cJ5fxZw4y37Ufk1Dsfb8MqOjYxE3ZMWxiDcO0PYUaD2ys+8OW1pbB7/e3sfZeGVCL0Q2aMjjPdm2sxADuejZxHJAd8dO9DSUdA0V8/NggRRanDkBrANn8yHlEQOn/MmwoQfQF7xgmKDnv520bS/pgylP67vf3y2V5sCwfoCEMkZClgOfJAFX9eXefR2RpnmRs4CDVPceaRfoFzCkJVJX27vWZnoqyvmtXU3+dW1EIXIu8Qg5Qta4Zlv7drUCoWe8/8MXzaEwux7ESE9h6qnHj3mIO0/D9RvzfxPmjWiQ1vbeSk4rrHwhAre35EEVaAAAAAElFTkSuQmCC)}body{font-family:arial,verdana}div{position:absolute;margin:auto;top:-150px;right:0;bottom:0;left:0;width:320px;height:304px}form{width:320px;text-align:center;position:relative}form fieldset{background:#fff;border:0 none;border-radius:5px;box-shadow:0 0 15px 1px rgba(0,0,0,.4);padding:20px 30px;box-sizing:border-box}form input{padding:15px;border:1px solid #ccc;border-radius:3px;margin-bottom:10px;width:100%;box-sizing:border-box;font-family:montserrat;color:#2C3E50;font-size:13px}form .action-button{border:0 none;border-radius:3px;cursor:pointer}#msform .submit:focus,form .action-button:hover{box-shadow:0 0 0 2px #fff,0 0 0 3px #27AE60}#formFrame{display:none}#aplist{display:block}select{width:100%;margin-bottom:20px;padding:10px 5px;border:1px solid #ccc;display:none}.fs-title{font-size:15px;text-transform:uppercase;color:#2C3E50;margin-bottom:10px}.fs-subtitle{font-weight:400;font-size:13px;color:#666;margin-bottom:20px}.fs-status{font-weight:400;font-size:13px;color:#666;margin-bottom:10px;padding-top:20px;border-top:1px solid #ccc}.submit{width:100px;background:#27AE60;color:#fff;font-weight:700;margin:10px 5px;padding:10px 5px}</style><div><form id=credentialsForm action=/update target=formFrame><fieldset><iframe id=formFrame src=''name=formFrame></iframe><h2 class=fs-title>WiFi Login</h2><h3 class=fs-subtitle>Connect gadget to your WiFi network</h3><input id=wifi_ssid autocorrect=off autocapitalize=none name=wifi_ssid placeholder='WiFi Name'><select id=aplist name=aplist size=1 disabled><option>Scanning for networks...</select><input name=wifi_password placeholder=Password type=password> <input type=submit name=save class='action-button submit'value=Save><h3 class=fs-status>Status: <span id=status>Updating...</span></h3></fieldset><h3 id=dbg></h3></form></div><script>function fetch(t,e,n){function s(){4===i.readyState&&n(200===i.status?i.responseText:null)}var i=new XMLHttpRequest;i.onreadystatechange=s,i.open(e,t,!0),i.send()}function new_status(t){if(t){var e=document.getElementById('status');e.innerHTML=t}}function new_status_repeat(t){new_status(t),setTimeout(refresh_status,750)}function new_ap_list(t){if(t){var e=JSON.parse(t);e.sort(function(t,e){return e.rssi-t.rssi});var n=e.map(function(t){return t.ssid}).filter(function(t,e,n){return n.indexOf(t)==e}),s=document.getElementById('aplist');s.innerHTML='',s.setAttribute('size',Math.max(Math.min(3,e.length),1)),s.removeAttribute('disabled');for(var i=0;i<n.length;++i){var a=document.createElement('option');a.innerHTML=n[i],s.options.add(a)}s.style.display='block'}}function new_ap_list_repeat(t){new_ap_list(t),setTimeout(refresh_ap_list,3e3)}function refresh_status(){fetch('/status','GET',new_status_repeat)}function refresh_ap_list(){fetch('/aplist','GET',new_ap_list_repeat)}function set_ssid_field(){var t=document.getElementById('aplist');document.getElementById('wifi_ssid').value=t.value}window.onload=function(){refresh_status(),refresh_ap_list(),document.getElementById('aplist').onclick=set_ssid_field,document.getElementById('aplist').onchange=set_ssid_field,document.getElementById('credentialsForm').addEventListener('submit',function(){fetch('/status','GET',new_status)})}</script>";
static const char http_header_content_len_fmt[] = "Content-length:%5d\r\n\r\n";
static const char http_html_gzip_contentencoding[] = "Content-Encoding: gzip\r\n";
// Externally defined: static const char http_html_backup[] = ...
#include "eus/http_html_backup.def"
typedef struct scan_listener
{
@ -250,9 +113,15 @@ typedef struct
int lua_err_cb_ref;
int lua_dbg_cb_ref;
scan_listener_t *scan_listeners;
uint8_t softAPchannel;
uint8_t success;
uint8_t callbackDone;
uint8_t lastStationStatus;
uint8_t connecting;
} enduser_setup_state_t;
static enduser_setup_state_t *state;
static bool manual = false;
static task_handle_t do_station_cfg_handle;
@ -340,7 +209,7 @@ static void enduser_setup_check_station_start(void)
ENDUSER_SETUP_DEBUG("enduser_setup_check_station_start");
os_timer_setfn(&(state->check_station_timer), enduser_setup_check_station, NULL);
os_timer_arm(&(state->check_station_timer), 1*1000, TRUE);
os_timer_arm(&(state->check_station_timer), 3*1000, TRUE);
}
@ -377,13 +246,60 @@ static void enduser_setup_check_station(void *p)
has_ip |= ((char *) &ip)[i];
}
uint8_t currChan = wifi_get_channel();
if (has_ip == 0)
{
return;
// No IP Address yet, so check the reported status
uint8_t curr_status = wifi_station_get_connect_status();
char buf[20];
c_sprintf(buf, "status=%d,chan=%d", curr_status, currChan);
ENDUSER_SETUP_DEBUG(buf);
if (curr_status == 2 || curr_status == 3 || curr_status == 4)
{
state->connecting = 0;
// If the status is an error status and the channel changed, then cache the
// status to state since the Station won't be able to report the same status
// after switching the channel back to the SoftAP's
if (currChan != state->softAPchannel) {
state->lastStationStatus = curr_status;
ENDUSER_SETUP_DEBUG("Turning off Station due to different channel than AP");
wifi_station_disconnect();
wifi_set_opmode(SOFTAP_MODE);
enduser_setup_ap_start();
}
}
return;
}
state->success = 1;
state->lastStationStatus = 5; // We have an IP Address, so the status is 5 (as of SDK 1.5.1)
state->connecting = 0;
#if ENDUSER_SETUP_DEBUG_ENABLE
char debuginfo[100];
c_sprintf(debuginfo, "AP_CHAN: %d, STA_CHAN: %d", state->softAPchannel, currChan);
ENDUSER_SETUP_DEBUG(debuginfo);
#endif
if (currChan == state->softAPchannel)
{
enduser_setup_connected_callback();
state->callbackDone = 1;
}
else
{
ENDUSER_SETUP_DEBUG("Turning off Station due to different channel than AP");
wifi_station_disconnect();
wifi_set_opmode(SOFTAP_MODE);
enduser_setup_ap_start();
}
enduser_setup_check_station_stop();
enduser_setup_connected_callback();
/* Trigger shutdown, but allow time for HTTP client to fetch last status. */
if (!manual)
@ -411,6 +327,8 @@ static void enduser_setup_check_station(void *p)
/* Callback on timeout to hard-close a connection */
static err_t force_abort (void *arg, struct tcp_pcb *pcb)
{
ENDUSER_SETUP_DEBUG("force_abort");
(void)arg;
tcp_poll (pcb, 0, 0);
tcp_abort (pcb);
@ -420,6 +338,8 @@ static err_t force_abort (void *arg, struct tcp_pcb *pcb)
/* Callback to detect a remote-close of a connection */
static err_t handle_remote_close (void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
ENDUSER_SETUP_DEBUG("handle_remote_close");
(void)arg; (void)err;
if (p) /* server sent us data, just ACK and move on */
{
@ -438,6 +358,8 @@ static err_t handle_remote_close (void *arg, struct tcp_pcb *pcb, struct pbuf *p
/* Set up a deferred close of a connection, as discussed above. */
static inline void deferred_close (struct tcp_pcb *pcb)
{
ENDUSER_SETUP_DEBUG("deferred_close");
tcp_poll (pcb, force_abort, 15); /* ~3sec from now */
tcp_recv (pcb, handle_remote_close);
tcp_sent (pcb, 0);
@ -446,6 +368,8 @@ static inline void deferred_close (struct tcp_pcb *pcb)
/* Convenience function to queue up a close-after-send. */
static err_t close_once_sent (void *arg, struct tcp_pcb *pcb, u16_t len)
{
ENDUSER_SETUP_DEBUG("close_once_sent");
(void)arg; (void)len;
deferred_close (pcb);
return ERR_OK;
@ -473,7 +397,6 @@ static int enduser_setup_srch_str(const char *str, const char *srch_str)
}
}
/**
* Load HTTP Payload
*
@ -495,14 +418,26 @@ static int enduser_setup_http_load_payload(void)
err2 = vfs_lseek(f, 0, VFS_SEEK_SET);
}
const char cl_hdr[] = "Content-length:%5d\r\n\r\n";
const size_t cl_len = LITLEN(cl_hdr) + 3; /* room to expand %4d */
char cl_hdr[30];
size_t ce_len = 0;
c_sprintf(cl_hdr, http_header_content_len_fmt, file_len);
size_t cl_len = c_strlen(cl_hdr);
if (!f || err != VFS_RES_OK || err2 != VFS_RES_OK)
if (!f || err == VFS_RES_ERR || err2 == VFS_RES_ERR)
{
ENDUSER_SETUP_DEBUG("enduser_setup_http_load_payload unable to load file enduser_setup.html, loading backup HTML.");
int payload_len = LITLEN(http_header_200) + cl_len + LITLEN(http_html_backup);
c_sprintf(cl_hdr, http_header_content_len_fmt, sizeof(http_html_backup));
cl_len = c_strlen(cl_hdr);
if (http_html_backup[0] == 0x1f && http_html_backup[1] == 0x8b)
{
ce_len = c_strlen(http_html_gzip_contentencoding);
ENDUSER_SETUP_DEBUG("Content is gzipped");
}
int payload_len = LITLEN(http_header_200) + cl_len + ce_len + LITLEN(http_html_backup);
state->http_payload_len = payload_len;
state->http_payload_data = (char *) c_malloc(payload_len);
if (state->http_payload_data == NULL)
@ -513,13 +448,25 @@ static int enduser_setup_http_load_payload(void)
int offset = 0;
c_memcpy(&(state->http_payload_data[offset]), &(http_header_200), LITLEN(http_header_200));
offset += LITLEN(http_header_200);
offset += c_sprintf(state->http_payload_data + offset, cl_hdr, LITLEN(http_html_backup));
c_memcpy(&(state->http_payload_data[offset]), &(http_html_backup), LITLEN(http_html_backup));
return 1;
if (ce_len > 0)
offset += c_sprintf(state->http_payload_data + offset, http_html_gzip_contentencoding, ce_len);
c_memcpy(&(state->http_payload_data[offset]), &(cl_hdr), cl_len);
offset += cl_len;
c_memcpy(&(state->http_payload_data[offset]), &(http_html_backup), sizeof(http_html_backup));
return 1;
}
int payload_len = LITLEN(http_header_200) + cl_len + file_len;
char magic[2];
vfs_read(f, magic, 2);
if (magic[0] == 0x1f && magic[1] == 0x8b)
{
ce_len = c_strlen(http_html_gzip_contentencoding);
ENDUSER_SETUP_DEBUG("Content is gzipped");
}
int payload_len = LITLEN(http_header_200) + cl_len + ce_len + file_len;
state->http_payload_len = payload_len;
state->http_payload_data = (char *) c_malloc(payload_len);
if (state->http_payload_data == NULL)
@ -527,13 +474,19 @@ static int enduser_setup_http_load_payload(void)
return 2;
}
vfs_lseek(f, 0, VFS_SEEK_SET);
int offset = 0;
c_memcpy(&(state->http_payload_data[offset]), &(http_header_200), LITLEN(http_header_200));
offset += LITLEN(http_header_200);
offset += c_sprintf(state->http_payload_data + offset, cl_hdr, file_len);
if (ce_len > 0)
offset += c_sprintf(state->http_payload_data + offset, http_html_gzip_contentencoding, ce_len);
c_memcpy(&(state->http_payload_data[offset]), &(cl_hdr), cl_len);
offset += cl_len;
vfs_read(f, &(state->http_payload_data[offset]), file_len);
vfs_close(f);
vfs_close(f);
return 0;
}
@ -605,6 +558,9 @@ static int enduser_setup_http_urldecode(char *dst, const char *src, int src_len,
*/
static void do_station_cfg (task_param_t param, uint8_t prio)
{
ENDUSER_SETUP_DEBUG("do_station_cfg");
state->connecting = 1;
struct station_config *cnf = (struct station_config *)param;
(void)prio;
@ -635,6 +591,9 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data
{
ENDUSER_SETUP_DEBUG("enduser_setup_http_handle_credentials");
state->success = 0;
state->lastStationStatus = 0;
char *name_str = (char *) ((uint32_t)strstr(&(data[6]), "wifi_ssid="));
char *pwd_str = (char *) ((uint32_t)strstr(&(data[6]), "wifi_password="));
if (name_str == NULL || pwd_str == NULL)
@ -663,7 +622,7 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data
int err;
err = enduser_setup_http_urldecode(cnf->ssid, name_str_start, name_str_len, sizeof(cnf->ssid));
err |= enduser_setup_http_urldecode(cnf->password, pwd_str_start, pwd_str_len, sizeof(cnf->password));
if (err != 0)
if (err != 0 || c_strlen(cnf->ssid) == 0)
{
ENDUSER_SETUP_DEBUG("Unable to decode HTTP parameter to valid password or SSID");
return 1;
@ -694,7 +653,7 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data
static int enduser_setup_http_serve_header(struct tcp_pcb *http_client, const char *header, uint32_t header_len)
{
ENDUSER_SETUP_DEBUG("enduser_setup_http_serve_header");
err_t err = tcp_write (http_client, header, header_len, TCP_WRITE_FLAG_COPY);
if (err != ERR_OK)
{
@ -708,8 +667,9 @@ static int enduser_setup_http_serve_header(struct tcp_pcb *http_client, const ch
static err_t streamout_sent (void *arg, struct tcp_pcb *pcb, u16_t len)
{
(void)len;
ENDUSER_SETUP_DEBUG("streamout_sent");
(void)len;
unsigned offs = (unsigned)arg;
if (!state || !state->http_payload_data)
@ -754,6 +714,7 @@ static err_t streamout_sent (void *arg, struct tcp_pcb *pcb, u16_t len)
static int enduser_setup_http_serve_html(struct tcp_pcb *http_client)
{
ENDUSER_SETUP_DEBUG("enduser_setup_http_serve_html");
if (state->http_payload_data == NULL)
{
enduser_setup_http_load_payload();
@ -774,7 +735,7 @@ static int enduser_setup_http_serve_html(struct tcp_pcb *http_client)
}
static void serve_status(struct tcp_pcb *conn)
static void enduser_setup_serve_status(struct tcp_pcb *conn)
{
ENDUSER_SETUP_DEBUG("enduser_setup_serve_status");
@ -782,11 +743,12 @@ static void serve_status(struct tcp_pcb *conn)
"HTTP/1.1 200 OK\r\n"
"Cache-control:no-cache\r\n"
"Connection:close\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Content-type:text/plain\r\n"
"Content-length: %d\r\n"
"\r\n"
"%s%s";
const char *state[] =
"%s";
const char *states[] =
{
"Idle.",
"Connecting to \"%s\".",
@ -796,8 +758,8 @@ static void serve_status(struct tcp_pcb *conn)
"Connected to \"%s\" (%s)."
};
const size_t num_states = sizeof(state)/sizeof(state[0]);
uint8_t curr_state = wifi_station_get_connect_status ();
const size_t num_states = sizeof(states)/sizeof(states[0]);
uint8_t curr_state = state->lastStationStatus > 0 ? state->lastStationStatus : wifi_station_get_connect_status ();
if (curr_state < num_states)
{
switch (curr_state)
@ -807,22 +769,21 @@ static void serve_status(struct tcp_pcb *conn)
case STATION_NO_AP_FOUND:
case STATION_GOT_IP:
{
const char *s = state[curr_state];
const char *s = states[curr_state];
struct station_config config;
wifi_station_get_config(&config);
config.ssid[31] = '\0';
struct ip_info ip_info;
struct ip_info ip_info;
wifi_get_ip_info(STATION_IF , &ip_info);
char ip_addr[16];
ip_addr[0] = '\0';
if (curr_state == STATION_GOT_IP)
{
c_sprintf (ip_addr, "%d.%d.%d.%d", IP2STR(&ip_info.ip.addr));
}
wifi_get_ip_info(STATION_IF , &ip_info);
char ip_addr[16];
ip_addr[0] = '\0';
if (curr_state == STATION_GOT_IP)
{
c_sprintf (ip_addr, "%d.%d.%d.%d", IP2STR(&ip_info.ip.addr));
}
int state_len = c_strlen(s);
int ip_len = c_strlen(ip_addr);
@ -844,7 +805,7 @@ static void serve_status(struct tcp_pcb *conn)
/* Handle non-formatted strings */
default:
{
const char *s = state[curr_state];
const char *s = states[curr_state];
int status_len = c_strlen(s);
int buf_len = sizeof(fmt) + status_len + 10; //10 = (9+1), 1 byte is '\0' and 9 are reserved for length field
char buf[buf_len];
@ -862,12 +823,76 @@ static void serve_status(struct tcp_pcb *conn)
}
}
static void enduser_setup_serve_status_as_json (struct tcp_pcb *http_client)
{
ENDUSER_SETUP_DEBUG("enduser_setup_serve_status_as_json");
// If the station is currently shut down because of wi-fi channel issue, use the cached status
uint8_t curr_status = state->lastStationStatus > 0 ? state->lastStationStatus : wifi_station_get_connect_status ();
char json_payload[64];
c_sprintf(json_payload, "{\"deviceid\":\"%06X\", \"status\":%d}", system_get_chip_id(), curr_status);
const char fmt[] =
"HTTP/1.1 200 OK\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Content-Type: application/json\r\n"
"Content-Length: %d\r\n"
"\r\n"
"%s";
int len = c_strlen(json_payload);
char buf[c_strlen(fmt) + NUMLEN(len) + len - 4];
len = c_sprintf (buf, fmt, len, json_payload);
enduser_setup_http_serve_header (http_client, buf, len);
}
static void enduser_setup_handle_OPTIONS (struct tcp_pcb *http_client, char *data, unsigned short data_len)
{
ENDUSER_SETUP_DEBUG("enduser_setup_handle_OPTIONS");
const char json[] =
"HTTP/1.1 200 OK\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"Content-Type: application/json\r\n"
"Content-Length: 0\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Access-Control-Allow-Methods: GET\r\n"
"Access-Control-Allow-Age: 300\r\n"
"\r\n";
const char others[] =
"HTTP/1.1 200 OK\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"Content-Length: 0\r\n"
"\r\n";
int type = 0;
if (c_strncmp(data, "GET ", 4) == 0)
{
if (c_strncmp(data + 4, "/aplist", 7) == 0 || c_strncmp(data + 4, "/setwifi?", 9) == 0 || c_strncmp(data + 4, "/status.json", 12) == 0)
{
enduser_setup_http_serve_header (http_client, json, c_strlen(json));
return;
}
}
enduser_setup_http_serve_header (http_client, others, c_strlen(others));
return;
}
/* --- WiFi AP scanning support -------------------------------------------- */
static void free_scan_listeners (void)
{
ENDUSER_SETUP_DEBUG("free_scan_listeners");
if (!state || !state->scan_listeners)
{
return;
@ -886,6 +911,8 @@ static void free_scan_listeners (void)
static void remove_scan_listener (scan_listener_t *l)
{
ENDUSER_SETUP_DEBUG("remove_scan_listener");
if (state)
{
scan_listener_t **sl = &state->scan_listeners;
@ -921,6 +948,8 @@ static char *escape_ssid (char *dst, const char *src)
static void notify_scan_listeners (const char *payload, size_t sz)
{
ENDUSER_SETUP_DEBUG("notify_scan_listeners");
if (!state)
{
return;
@ -944,6 +973,8 @@ static void notify_scan_listeners (const char *payload, size_t sz)
static void on_scan_done (void *arg, STATUS status)
{
ENDUSER_SETUP_DEBUG("on_scan_done");
if (!state || !state->scan_listeners)
{
return;
@ -961,13 +992,14 @@ static void on_scan_done (void *arg, STATUS status)
"HTTP/1.1 200 OK\r\n"
"Connection:close\r\n"
"Cache-control:no-cache\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Content-type:application/json\r\n"
"Content-length:%4d\r\n"
"\r\n";
const size_t hdr_sz = sizeof (header_fmt) +1 -1; /* +expand %4d, -\0 */
/* To be able to safely escape a pathological SSID, we need 2*32 bytes */
const size_t max_entry_sz = sizeof("{\"ssid\":\"\",\"rssi\":},") + 2*32 + 6;
const size_t max_entry_sz = 27 + 2*32 + 6; // {"ssid":"","rssi":,"chan":}
const size_t alloc_sz = hdr_sz + num_nets * max_entry_sz + 3;
char *http = os_zalloc (alloc_sz);
if (!http)
@ -996,6 +1028,12 @@ static void on_scan_done (void *arg, STATUS status)
p += sizeof (entry_mid) -1;
p += c_sprintf (p, "%d", wn->rssi);
const char entry_chan[] = ",\"chan\":";
strcpy (p, entry_chan);
p += sizeof (entry_chan) -1;
p += c_sprintf (p, "%d", wn->channel);
*p++ = '}';
}
@ -1006,6 +1044,8 @@ static void on_scan_done (void *arg, STATUS status)
http[hdr_sz] = '['; /* Rewrite the \0 with the correct start of body */
notify_scan_listeners (http, hdr_sz + body_sz);
ENDUSER_SETUP_DEBUG(http + hdr_sz);
c_free (http);
return;
}
@ -1048,6 +1088,9 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
pbuf_free (p);
err_t ret = ERR_OK;
ENDUSER_SETUP_DEBUG(data);
if (c_strncmp(data, "GET ", 4) == 0)
{
if (c_strncmp(data + 4, "/ ", 2) == 0)
@ -1061,38 +1104,52 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
goto free_out; /* streaming now in progress */
}
}
else if (c_strncmp(data + 4, "/aplist ", 8) == 0)
else if (c_strncmp(data + 4, "/aplist", 7) == 0)
{
scan_listener_t *l = os_malloc (sizeof (scan_listener_t));
if (!l)
// Don't do an AP Scan while station is trying to connect to Wi-Fi
if (state->connecting == 0)
{
ENDUSER_SETUP_ERROR("out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL);
}
bool already = (state->scan_listeners != NULL);
tcp_arg (http_client, l);
/* TODO: check if also need a tcp_err() cb, or if recv() is enough */
l->conn = http_client;
l->next = state->scan_listeners;
state->scan_listeners = l;
if (!already)
{
if (!wifi_station_scan(NULL, on_scan_done))
scan_listener_t *l = os_malloc (sizeof (scan_listener_t));
if (!l)
{
enduser_setup_http_serve_header(http_client, http_header_500, LITLEN(http_header_500));
deferred_close (l->conn);
l->conn = 0;
free_scan_listeners();
ENDUSER_SETUP_ERROR("out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL);
}
bool already = (state->scan_listeners != NULL);
tcp_arg (http_client, l);
/* TODO: check if also need a tcp_err() cb, or if recv() is enough */
l->conn = http_client;
l->next = state->scan_listeners;
state->scan_listeners = l;
if (!already)
{
if (!wifi_station_scan(NULL, on_scan_done))
{
enduser_setup_http_serve_header(http_client, http_header_500, LITLEN(http_header_500));
deferred_close (l->conn);
l->conn = 0;
free_scan_listeners();
}
}
goto free_out; /* request queued */
}
else
{
// Return No Content status to the caller
enduser_setup_http_serve_header(http_client, http_header_204, LITLEN(http_header_204));
}
goto free_out; /* request queued */
}
else if (c_strncmp(data + 4, "/status ", 8) == 0)
else if (c_strncmp(data + 4, "/status.json", 12) == 0)
{
serve_status(http_client);
enduser_setup_serve_status_as_json(http_client);
}
else if (c_strncmp(data + 4, "/status", 7) == 0)
{
enduser_setup_serve_status(http_client);
}
else if (c_strncmp(data + 4, "/update?", 8) == 0)
{
switch (enduser_setup_http_handle_credentials(data, data_len))
@ -1101,14 +1158,29 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
enduser_setup_http_serve_header(http_client, http_header_302, LITLEN(http_header_302));
break;
case 1:
enduser_setup_http_serve_header(http_client, http_header_401, LITLEN(http_header_401));
enduser_setup_http_serve_header(http_client, http_header_400, LITLEN(http_header_400));
break;
default:
ENDUSER_SETUP_ERROR("http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
break;
}
}
else if (c_strncmp(data + 4, "/generate_204 ", 14) == 0)
else if (c_strncmp(data + 4, "/setwifi?", 9) == 0)
{
switch (enduser_setup_http_handle_credentials(data, data_len))
{
case 0:
enduser_setup_serve_status_as_json(http_client);
break;
case 1:
enduser_setup_http_serve_header(http_client, http_header_400, LITLEN(http_header_400));
break;
default:
ENDUSER_SETUP_ERROR("http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
break;
}
}
else if (c_strncmp(data + 4, "/generate_204", 13) == 0)
{
/* Convince Android devices that they have internet access to avoid pesky dialogues. */
enduser_setup_http_serve_header(http_client, http_header_204, LITLEN(http_header_204));
@ -1116,13 +1188,16 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
else
{
ENDUSER_SETUP_DEBUG("serving 404");
ENDUSER_SETUP_DEBUG(data + 4);
enduser_setup_http_serve_header(http_client, http_header_404, LITLEN(http_header_404));
}
}
else /* not GET */
else if (c_strncmp(data, "OPTIONS ", 8) == 0)
{
enduser_setup_http_serve_header(http_client, http_header_401, LITLEN(http_header_401));
enduser_setup_handle_OPTIONS(http_client, data, data_len);
}
else /* not GET or OPTIONS */
{
enduser_setup_http_serve_header(http_client, http_header_405, LITLEN(http_header_405));
}
deferred_close (http_client);
@ -1135,6 +1210,7 @@ free_out:
static err_t enduser_setup_http_connectcb(void *arg, struct tcp_pcb *pcb, err_t err)
{
ENDUSER_SETUP_DEBUG("enduser_setup_http_connectcb");
if (!state)
{
ENDUSER_SETUP_DEBUG("connect callback but no state?!");
@ -1208,7 +1284,7 @@ static void enduser_setup_http_stop(void)
static void enduser_setup_ap_stop(void)
{
ENDUSER_SETUP_DEBUG("enduser_setup_station_stop");
ENDUSER_SETUP_DEBUG("enduser_setup_ap_stop");
wifi_set_opmode(~SOFTAP_MODE & wifi_get_opmode());
}
@ -1234,15 +1310,47 @@ static void enduser_setup_ap_start(void)
cnf.ssid[ssid_name_len] = '_';
c_sprintf(cnf.ssid + ssid_name_len + 1, "%02X%02X%02X", mac[3], mac[4], mac[5]);
cnf.ssid_len = ssid_name_len + 7;
cnf.channel = 1;
cnf.channel = state == NULL? 1 : state->softAPchannel;
cnf.authmode = AUTH_OPEN;
cnf.ssid_hidden = 0;
cnf.max_connection = 5;
cnf.beacon_interval = 100;
wifi_set_opmode(STATIONAP_MODE);
wifi_softap_set_config(&cnf);
#if ENDUSER_SETUP_DEBUG_ENABLE
char debuginfo[100];
c_sprintf(debuginfo, "SSID: %s, CHAN: %d", cnf.ssid, cnf.channel);
ENDUSER_SETUP_DEBUG(debuginfo);
#endif
}
static void on_initial_scan_done (void *arg, STATUS status)
{
ENDUSER_SETUP_DEBUG("on_initial_scan_done");
if (!state)
{
return;
}
int8_t rssi = -100;
if (status == OK)
{
for (struct bss_info *wn = arg; wn; wn = wn->next.stqe_next)
{
if (wn->rssi > rssi)
{
state->softAPchannel = wn->channel;
rssi = wn->rssi;
}
}
}
enduser_setup_ap_start();
enduser_setup_check_station_start();
}
static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned short recv_len)
{
@ -1255,6 +1363,31 @@ static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned
uint32_t dns_reply_static_len = (uint32_t) sizeof(dns_header) + (uint32_t) sizeof(dns_body) + 2 + 4; // dns_id=2bytes, ip=4bytes
uint32_t dns_reply_len = dns_reply_static_len + qname_len;
#if ENDUSER_SETUP_DEBUG_ENABLE
char *qname = c_malloc(qname_len + 12);
if (qname != NULL)
{
c_sprintf(qname, "DNS QUERY = %s", &(recv_data[12]));
uint32_t p;
int i, j;
for(i=12;i<(int)strlen(qname);i++)
{
p=qname[i];
for(j=0;j<(int)p;j++)
{
qname[i]=qname[i+1];
i=i+1;
}
qname[i]='.';
}
qname[i-1]='\0';
ENDUSER_SETUP_DEBUG(qname);
c_free(qname);
}
#endif
uint8_t if_mode = wifi_get_opmode();
if ((if_mode & SOFTAP_MODE) == 0)
{
@ -1305,6 +1438,14 @@ static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned
{
ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Can't execute transmission.", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_FATAL);
}
else if (err == ESPCONN_MAXNUM)
{
ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Buffer full. Discarding...", ENDUSER_SETUP_ERR_MAX_NUMBER, ENDUSER_SETUP_ERR_NONFATAL);
}
else if (err == ESPCONN_IF)
{
ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Send UDP data failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL);
}
else if (err != 0)
{
ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. espconn_send failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL);
@ -1408,6 +1549,7 @@ static void enduser_setup_dns_stop(void)
static int enduser_setup_init(lua_State *L)
{
// Note: Normal to not see this debug message on first invocation because debug callback is set below
ENDUSER_SETUP_DEBUG("enduser_setup_init");
if (state != NULL)
@ -1452,12 +1594,19 @@ static int enduser_setup_init(lua_State *L)
{
lua_pushvalue (L, 3);
state->lua_dbg_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
ENDUSER_SETUP_DEBUG("enduser_setup_init: Debug callback has been defined");
}
else
{
state->lua_dbg_cb_ref = LUA_NOREF;
}
state->softAPchannel = 1;
state->success = 0;
state->callbackDone = 0;
state->lastStationStatus = 0;
state->connecting = 0;
return 0;
}
@ -1475,6 +1624,7 @@ static int enduser_setup_manual(lua_State *L)
static int enduser_setup_start(lua_State *L)
{
// Note: The debug callback is set in enduser_setup_init. It's normal to not see this debug message on first invocation.
ENDUSER_SETUP_DEBUG("enduser_setup_start");
if (!do_station_cfg_handle)
@ -1487,10 +1637,14 @@ static int enduser_setup_start(lua_State *L)
goto failed;
}
enduser_setup_check_station_start();
if (!manual)
{
enduser_setup_ap_start();
ENDUSER_SETUP_DEBUG("Performing AP Scan to identify likely AP's channel");
wifi_station_scan(NULL, on_initial_scan_done);
}
else
{
enduser_setup_check_station_start();
}
if(enduser_setup_dns_start())
@ -1529,6 +1683,12 @@ static int enduser_setup_stop(lua_State* L)
{
enduser_setup_ap_stop();
}
if (state->success && !state->callbackDone)
{
wifi_set_opmode(STATION_MODE | wifi_get_opmode());
wifi_station_connect();
enduser_setup_connected_callback();
}
enduser_setup_dns_stop();
enduser_setup_http_stop();
enduser_setup_free();

View File

@ -0,0 +1,315 @@
<!DOCTYPE html>
<html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>WiFi Login</title>
<style type=text/css>
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
font-family: sans-serif;
text-align: center;
background: #444d44;
}
#content {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 320px;
height: 480px;
margin: auto;
}
input,
button,
select {
-webkit-appearance: none;
border-radius: 0;
}
fieldset {
border: 0;
box-shadow: 0 0 15px 1px rgba(0, 0, 0, .4);
box-sizing: border-box;
padding: 20px 30px;
background: #fff;
min-height: 320px;
margin: -1px;
}
input {
border: 1px solid #ccc;
margin-bottom: 10px;
width: 100%;
box-sizing: border-box;
color: #222;
font: 16px monospace;
padding: 15px;
}
select {
font: 16px monospace;
background-color: transparent;
padding: 15px;
}
button {
color: #fff;
border: 0;
border-radius: 3px;
cursor: pointer;
display: block;
font: 16px sans-serif;
text-decoration: none;
padding: 10px 5px;
background: #31b457;
width: 100%;
}
button:focus,
button:hover {
box-shadow: 0 0 0 2px #fff, 0 0 0 3px #31b457;
}
h3 {
font-size: 16px;
color: #666;
margin-bottom: 20px;
}
h4 {
color: #ccc;
padding: 10px;
}
.utility {
float: right;
clear: both;
max-width: 75%;
font-size: 13px;
color: #222;
margin: 10px 0;
padding: 5px 10px;
background: #ccc;
}
.utility:focus,
.utility:hover {
box-shadow: 0 0 0 2px #fff, 0 0 0 3px #ccc;
}
#dropdown,
#f2,
#f3,
#bk2 {
display: none;
}
#dropdown {
position: relative;
width: 100%;
overflow: auto;
height: 51px;
margin-bottom: 10px;
}
#aplist {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
bottom: 0;
border: 1px solid #ccc;
font-family: monospace;
padding: 10px 5px;
}
#arrow {
color: #888;
position: absolute;
right: 8px;
top: 15px;
}
#i {
text-align: justify;
}
</style>
</head>
<body>
<div id=content>
<fieldset>
<div id=deviceId></div>
<div id=f1>
<h3>Connect device to your Wi-Fi</h3>
<button id=networks type=button class=utility></button>
<div id=dropdown>
<span id=arrow>&#x25bc;</span>
<select id=aplist name=aplist></select>
</div>
<input id=ssid type=text autocorrect=off autocapitalize=none placeholder='Wi-Fi Name' />
<input id=wifi_password type=text autocorrect=off autocapitalize=none autocomplete=off placeholder=Password />
<button id=submit type=button>Save</button>
</div>
<div id=f2>
<h1>Success!</h1>
<div id=i>
<h3>Your device has successfully connected to the Wi-Fi network.</h3>
</div>
</div>
<div id=f3>
<h2>Trying...</h2>
<button id=bk2 type=button class='utility'>Go Back to Wi-Fi Setup</button>
</div>
</fieldset>
<h4 id='st'>Updating Status...</h4>
</div>
<script>
var $ = function (selector) { return document.querySelector(selector); };
var ab = $('#networks'), ap = $('#aplist');
var stopAll = false, ra, rs;
function show(f, y) {
if (y == null) y = f;
$(f).style.display = y == f ? 'block' : 'none';
}
function hide(f) {
$(f).style.display = 'none';
}
function to(cb, x) {
return setTimeout(cb, 1000 * x);
}
function refr() {
if (!stopAll)
fetch('/status.json?n=' + Math.random(), 'GET', newSt, 2);
}
function cur(f) {
show('#f1', f);
show('#f2', f);
show('#f3', f);
}
function newSt(s, d) {
clearTimeout(rs);
rs = to(refr, 3);
if (s != 200) {
$('#st').innerText = 'Awaiting Status (' + s + ')';
} else {
if (typeof d === 'string') {
d = JSON.parse(d);
}
$('#deviceId').innerText = d.deviceid;
var c = d.pairing;
var s = [
'Idle',
'Connecting...',
'Failed - wrong password',
'Failed - network not found',
'Failed',
'Wi-Fi successfully connected!'
][d.status];
$('#st').innerText = s;
if (d.status === 5) {
cur('#f2');
stopAll = true;
clearTimeout(ra);
} else if (d.status > 1) {
cur('#f1');
}
}
}
function submit() {
var url = '/setwifi?wifi_ssid=' + encodeURIComponent($('#ssid').value) + '&wifi_password=' + encodeURIComponent($('#wifi_password').value);
clearTimeout(rs);
fetch(url, 'GET', newSt, 2);
cur('#f3');
}
function fetch(url, method, callback, time_out) {
var xhr = new XMLHttpRequest();
xhr.onloadend = function () {
callback(xhr.status, xhr.responseText);
}
xhr.ontimeout = function () {
callback(-1, null);
}
xhr.open(method, url, true);
xhr.setRequestHeader('Accept', 'application/json');
xhr.timeout = (time_out || 10) * 1000;
xhr.send();
}
function gotAp(s, json) {
var list;
if (s === 200 && json != null) {
if (typeof json === 'string' && json.length > 0) {
list = JSON.parse(json);
} else if (typeof json === 'object') {
list = json;
}
list.sort(function (a, b) {
return b.rssi - a.rssi;
});
var ops = '<option>Select a Network...</option>';
for (var i = 0; i < list.length; ++i) {
ops += '<option>' + list[i].ssid + '</option>';
}
ap.innerHTML = ops;
ab.disabled = false;
togAp(null, true);
ab.onclick = togAp;
} else {
ab.innerText = 'No networks found (' + s + ')';
ra = to(refrAp, 5);
}
}
function togAp(ev, force) {
if (!force || ap.style.display == 'block') {
hide('#dropdown');
show('#ssid');
ab.innerText = 'Scan for Networks';
ab.onclick = refrAp;
} else {
show('#dropdown');
hide('#ssid');
ab.innerText = 'Manual Entry';
}
}
function refrAp() {
ab.innerText = 'Searching for networks...';
ab.disabled = true;
ap.innerHTML = '<option disabled>Scanning...</option>';
if (!stopAll)
fetch('/aplist?n=' + Math.random(), 'GET', gotAp, 10);
}
window.onload = function() {
ab.innerText = 'Scan for Networks';
ab.onclick = refrAp;
$('#aplist').onchange = function () {
$('#ssid').value = $('#aplist').value;
};
$('#submit').onclick = submit;
$('#bk2').onclick = function () {
cur('#f1')
}
rs = to(refr, 0.5);
}
</script>
</body>
</html>

Binary file not shown.

View File

@ -0,0 +1,203 @@
static const char http_html_backup[] = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x95, 0x59,
0xfd, 0x72, 0xdb, 0x36, 0x12, 0xff, 0xbb, 0x37, 0x73, 0xef, 0xb0, 0x1e,
0xf7, 0x42, 0xb9, 0xa1, 0x64, 0x7d, 0xd8, 0x69, 0xc6, 0x12, 0xd5, 0xc9,
0xe5, 0x92, 0x26, 0x37, 0x49, 0xda, 0xa9, 0xdd, 0xe9, 0x75, 0x32, 0x99,
0x0e, 0x44, 0x82, 0x26, 0x62, 0x0a, 0xe0, 0x01, 0xa0, 0x65, 0x5d, 0xea,
0x77, 0x3f, 0x2c, 0x01, 0x5a, 0x86, 0x40, 0x87, 0xb5, 0x5a, 0x57, 0x04,
0xb0, 0xdf, 0xfb, 0xc3, 0xee, 0x52, 0x5d, 0x1c, 0xfc, 0xeb, 0xa7, 0x97,
0x17, 0xbf, 0xff, 0xfc, 0x0a, 0x0a, 0xbd, 0x2e, 0x97, 0x7f, 0xff, 0xdb,
0xc2, 0x7d, 0xe3, 0x13, 0x25, 0x99, 0x79, 0xfa, 0x66, 0xb1, 0xa6, 0x9a,
0x00, 0x27, 0x6b, 0x9a, 0x44, 0xd7, 0x8c, 0x6e, 0x2a, 0x21, 0x75, 0x04,
0xa9, 0xe0, 0x9a, 0x72, 0x9d, 0x44, 0x1b, 0x96, 0xe9, 0x22, 0xc9, 0xe8,
0x35, 0x4b, 0xe9, 0xb0, 0x59, 0xc4, 0xc0, 0x38, 0xd3, 0x8c, 0x94, 0x43,
0x95, 0x92, 0x92, 0x26, 0x93, 0xd1, 0x38, 0x6a, 0x04, 0x69, 0xa6, 0x4b,
0xba, 0xfc, 0x8d, 0xbd, 0x66, 0xf0, 0x4e, 0x5c, 0x32, 0xbe, 0x38, 0xb6,
0x3b, 0x78, 0xa6, 0xf4, 0xb6, 0xa4, 0xa0, 0xb7, 0x15, 0x4d, 0x34, 0xbd,
0xd1, 0xc7, 0xa9, 0x52, 0xb8, 0xff, 0xcd, 0x77, 0xf0, 0x05, 0xbf, 0xbe,
0x59, 0x13, 0x69, 0x38, 0xce, 0x60, 0x3c, 0x6f, 0x96, 0x15, 0xc9, 0x32,
0xc6, 0x2f, 0xdb, 0xf5, 0x2d, 0xfe, 0x07, 0xff, 0xd0, 0xfc, 0x18, 0x1f,
0x56, 0x22, 0xdb, 0x3a, 0xd6, 0x82, 0xb2, 0xcb, 0x42, 0x9f, 0xc1, 0x64,
0x3c, 0xfe, 0x87, 0xe5, 0xce, 0x8d, 0xf1, 0xc3, 0x9c, 0xac, 0x59, 0xb9,
0x3d, 0x03, 0x45, 0xb8, 0x1a, 0x2a, 0x2a, 0x59, 0x6e, 0x0f, 0x51, 0xfd,
0x90, 0x94, 0xec, 0xd2, 0x68, 0x4b, 0x29, 0xd7, 0x54, 0xda, 0xfd, 0x15,
0x49, 0xaf, 0x2e, 0xa5, 0xa8, 0x79, 0x76, 0x06, 0x87, 0x27, 0x27, 0x27,
0xd9, 0xc9, 0x89, 0xaf, 0xfb, 0xd0, 0xc5, 0xc4, 0xa9, 0xad, 0x84, 0x62,
0x9a, 0x09, 0x23, 0x85, 0xac, 0x94, 0x28, 0x6b, 0x4d, 0x9d, 0x7c, 0x51,
0xdd, 0xb9, 0x21, 0xad, 0x65, 0x6e, 0xb5, 0x12, 0x5a, 0x8b, 0xf5, 0xdd,
0xb2, 0xa4, 0xf9, 0xee, 0xac, 0x89, 0xec, 0x19, 0xcc, 0xa6, 0xe3, 0xea,
0x66, 0xee, 0xb9, 0x75, 0xf2, 0x1c, 0xb7, 0xbc, 0x20, 0x91, 0x5a, 0x0b,
0xdf, 0x36, 0xc6, 0xab, 0x5a, 0xdb, 0xc0, 0xd4, 0x46, 0x09, 0x6f, 0x1e,
0x15, 0x2d, 0x69, 0xda, 0x9a, 0x3b, 0xdc, 0xd0, 0xd5, 0x15, 0x33, 0x9e,
0x57, 0x15, 0x25, 0x92, 0xf0, 0x94, 0x9e, 0x01, 0x17, 0x9c, 0xb6, 0xa6,
0xc9, 0x8c, 0xca, 0xa1, 0x24, 0x19, 0xab, 0x55, 0x10, 0xf5, 0x9c, 0xd1,
0x32, 0x53, 0xd4, 0x89, 0x72, 0xc4, 0xf7, 0xdc, 0xba, 0x19, 0xaa, 0x82,
0x64, 0x62, 0x63, 0xb6, 0xcc, 0x3f, 0x93, 0xd3, 0xea, 0x06, 0x26, 0xe6,
0x4f, 0x5e, 0xae, 0xc8, 0x60, 0x1c, 0x83, 0xfd, 0x77, 0x74, 0x72, 0x74,
0x8f, 0x9e, 0xfd, 0xaf, 0x49, 0xaf, 0xd3, 0x6b, 0xb6, 0xf6, 0xf2, 0x8e,
0x71, 0x80, 0x19, 0x7a, 0x1e, 0x26, 0x27, 0xcf, 0x73, 0x17, 0x0f, 0xc6,
0x87, 0x36, 0x4c, 0x18, 0xb9, 0x20, 0x4c, 0x43, 0x63, 0x44, 0x18, 0xa6,
0x7d, 0x2f, 0xd0, 0x52, 0x93, 0x3f, 0x96, 0xc1, 0x61, 0x9a, 0xa6, 0xf7,
0x25, 0x0c, 0xdb, 0x84, 0x4d, 0x9c, 0x68, 0x97, 0x26, 0x07, 0xb5, 0x5e,
0x67, 0x52, 0x51, 0x0a, 0x69, 0xec, 0x9d, 0x4e, 0xa7, 0x3b, 0x5c, 0x1a,
0xe6, 0x67, 0x46, 0xe3, 0x5a, 0x70, 0xa1, 0x2a, 0x92, 0x52, 0xdf, 0x6f,
0x0c, 0x9e, 0x6f, 0xb3, 0x9f, 0xc5, 0x6e, 0x09, 0x7e, 0x84, 0x86, 0x4e,
0xaf, 0x36, 0x59, 0x36, 0x04, 0x92, 0x72, 0xdd, 0xa7, 0xc4, 0xa1, 0xc6,
0x29, 0xb1, 0xfc, 0x2e, 0xce, 0x41, 0xc2, 0x03, 0xb0, 0xcc, 0xaa, 0xd6,
0xdf, 0x5a, 0x2a, 0x64, 0xac, 0x04, 0xdb, 0x5d, 0xab, 0x8c, 0xa9, 0xaa,
0x24, 0x5b, 0x13, 0x9d, 0x52, 0xa4, 0x57, 0x41, 0x1c, 0x3a, 0x6f, 0x67,
0x46, 0x53, 0x21, 0x89, 0xbd, 0x5c, 0x0e, 0xa3, 0x9e, 0xf9, 0x88, 0x8d,
0xd3, 0x4e, 0x68, 0xcc, 0x26, 0xab, 0x93, 0xd3, 0xef, 0xc3, 0x5c, 0x75,
0x38, 0x7b, 0x96, 0x8b, 0xb4, 0x56, 0xf1, 0xbd, 0x8d, 0x42, 0x5c, 0x53,
0x09, 0x5f, 0x3a, 0x41, 0x3d, 0x86, 0xa9, 0x51, 0x8a, 0x21, 0x89, 0xed,
0x1a, 0xdd, 0xde, 0xe9, 0xf3, 0x6a, 0xd4, 0xec, 0x5e, 0xb2, 0x10, 0x1c,
0xd4, 0xfa, 0xea, 0x83, 0xe2, 0xd9, 0xb3, 0x67, 0x9d, 0x58, 0xb3, 0x30,
0xf6, 0xe4, 0x9d, 0x78, 0x79, 0x71, 0x30, 0xf5, 0x23, 0x12, 0x30, 0x8d,
0x6a, 0xcd, 0x4a, 0xa6, 0xb7, 0xad, 0x29, 0xa5, 0x20, 0x26, 0xe4, 0x4d,
0x3d, 0x72, 0x76, 0x94, 0x94, 0x48, 0xc4, 0xac, 0x2e, 0x5a, 0x3b, 0x6e,
0x86, 0x2e, 0x64, 0xdf, 0x9f, 0xda, 0x88, 0x79, 0x1e, 0xcc, 0xaa, 0x6e,
0x58, 0xb7, 0x1e, 0x58, 0x2b, 0x82, 0x02, 0xde, 0x14, 0x83, 0xee, 0x7b,
0xec, 0xfc, 0x08, 0x8d, 0x76, 0xa9, 0xf1, 0xb6, 0x1e, 0x9f, 0x9c, 0x50,
0xfe, 0x61, 0x26, 0x45, 0x65, 0xb8, 0x78, 0xdc, 0xac, 0xf2, 0xa9, 0xfb,
0x9e, 0xd9, 0xef, 0xd5, 0xd5, 0xd4, 0x29, 0x70, 0xa0, 0x75, 0xe8, 0xeb,
0x96, 0x11, 0xb4, 0x01, 0x49, 0x4b, 0xa2, 0xd9, 0x35, 0x7d, 0xa0, 0x52,
0xa0, 0xfd, 0x26, 0x0d, 0x1b, 0x57, 0xbe, 0xbd, 0x1a, 0x7f, 0xea, 0x2a,
0x55, 0x77, 0xe5, 0xf1, 0xf5, 0x93, 0xaa, 0x64, 0xaa, 0xa7, 0x09, 0x85,
0xda, 0xc3, 0x26, 0xa9, 0x45, 0x15, 0xb4, 0xa2, 0xa0, 0x4d, 0xf5, 0xd4,
0x49, 0xaf, 0xd1, 0x7a, 0x05, 0x29, 0xb8, 0xae, 0xa1, 0x1f, 0x52, 0x8a,
0xcd, 0x1e, 0xb2, 0x9f, 0x3f, 0x7f, 0x3e, 0xff, 0xaa, 0x5f, 0xae, 0xa1,
0x3e, 0xb7, 0xd2, 0x9c, 0x13, 0x93, 0x50, 0x38, 0x83, 0x2f, 0x41, 0xb3,
0xff, 0x5c, 0x2b, 0xcd, 0xf2, 0xed, 0x1d, 0xe5, 0xe2, 0xb8, 0x19, 0x4a,
0x70, 0x2a, 0x3a, 0x76, 0xc3, 0x90, 0x79, 0xc4, 0xc1, 0x62, 0x89, 0xa7,
0x19, 0xbb, 0x06, 0x96, 0x25, 0xae, 0xe9, 0x2f, 0x91, 0x6b, 0xe1, 0x1a,
0xa1, 0x5d, 0xb5, 0x24, 0x6e, 0x38, 0x7a, 0x9b, 0x2d, 0x17, 0xc7, 0x66,
0xc7, 0x3f, 0xcb, 0x27, 0xb8, 0xc6, 0x8d, 0x62, 0xb6, 0x7c, 0x29, 0x38,
0xc7, 0x5a, 0x6e, 0x19, 0x40, 0x0b, 0xd8, 0x8a, 0x5a, 0xc2, 0x6f, 0x6c,
0xf8, 0x9a, 0x19, 0x1b, 0x66, 0x2d, 0xa9, 0xad, 0x47, 0xc8, 0xce, 0xa9,
0xde, 0x08, 0x79, 0xa5, 0xec, 0xe4, 0xe4, 0xf6, 0xd3, 0x92, 0x28, 0x95,
0xb8, 0x8b, 0x61, 0x94, 0xda, 0x6d, 0xc7, 0xdc, 0x2a, 0x76, 0x30, 0xb5,
0xdb, 0xb8, 0x6f, 0x52, 0xd3, 0x88, 0x6c, 0xe2, 0xbe, 0x7c, 0x72, 0x78,
0x33, 0x3d, 0x5d, 0xa5, 0x73, 0x13, 0x84, 0x8a, 0xec, 0x88, 0x6c, 0xb3,
0x41, 0x32, 0x07, 0x33, 0x9c, 0x0b, 0xdd, 0xb3, 0xd1, 0x64, 0x8f, 0x1d,
0xb5, 0xf3, 0xd6, 0x3e, 0xdb, 0xce, 0x6a, 0xf8, 0x94, 0x62, 0xd9, 0x6e,
0xce, 0x6b, 0xb0, 0x9e, 0x0a, 0x29, 0x0d, 0x5b, 0x22, 0xf2, 0xdc, 0xae,
0x49, 0xc5, 0x34, 0x29, 0x4d, 0x59, 0x49, 0xf0, 0x7e, 0x41, 0x55, 0x92,
0x94, 0x16, 0xa2, 0xcc, 0xa8, 0x4c, 0xa2, 0x26, 0x18, 0xf0, 0xc1, 0xa8,
0x8d, 0xe0, 0x38, 0x90, 0xbe, 0x61, 0x39, 0xfb, 0xa3, 0x32, 0xee, 0x9b,
0xa8, 0x3c, 0x52, 0x8d, 0xa3, 0x59, 0x57, 0x25, 0xd5, 0x14, 0x89, 0x3c,
0xbd, 0x3f, 0xb7, 0x32, 0x8f, 0xc3, 0x24, 0xa8, 0x7a, 0xb5, 0x66, 0xfa,
0x7e, 0x0a, 0x96, 0xe7, 0xe4, 0x9a, 0xfa, 0x81, 0xef, 0xca, 0xfd, 0xf4,
0x2e, 0xf7, 0x93, 0xe5, 0x79, 0x9d, 0xa6, 0x54, 0xa9, 0x03, 0x93, 0xe7,
0xc9, 0x5e, 0xaa, 0x98, 0x5d, 0x5b, 0x8c, 0xfc, 0x8e, 0x88, 0x70, 0x00,
0x29, 0x88, 0x02, 0x65, 0xf9, 0xf2, 0xba, 0x2c, 0xb7, 0x90, 0x5a, 0x00,
0xd1, 0x0c, 0xb1, 0xa3, 0x0b, 0x0a, 0x36, 0x5a, 0x0e, 0x25, 0x23, 0xc4,
0x50, 0x98, 0x9c, 0x2e, 0xcb, 0xee, 0xc8, 0x8a, 0xe9, 0xf2, 0x42, 0x6e,
0xcd, 0x3d, 0x1d, 0x8d, 0x90, 0x7d, 0x1a, 0x7a, 0x8f, 0x55, 0x31, 0x44,
0x5f, 0xe4, 0xe0, 0x17, 0x2d, 0x7f, 0x14, 0xf0, 0x4f, 0x53, 0xd5, 0xd1,
0x22, 0x6b, 0xcd, 0x39, 0xd5, 0x75, 0xd5, 0x1d, 0x1c, 0x7c, 0xf2, 0xee,
0xd0, 0xa2, 0x38, 0x41, 0x1d, 0x91, 0xd2, 0xd1, 0xf2, 0xd7, 0x2a, 0x23,
0xda, 0x58, 0x02, 0xe7, 0x9a, 0xe8, 0x5a, 0x59, 0x83, 0x4e, 0x90, 0xec,
0x8e, 0x7d, 0xa1, 0x52, 0xc9, 0x2a, 0x64, 0x05, 0xf7, 0xb9, 0x26, 0x12,
0xbe, 0x85, 0x04, 0xf2, 0x9a, 0xa7, 0x58, 0x30, 0x60, 0x60, 0x31, 0x2a,
0xe4, 0x11, 0x7c, 0x01, 0x69, 0x4c, 0x91, 0x1c, 0x32, 0x91, 0xd6, 0x6b,
0xca, 0xf5, 0xe8, 0xbf, 0x35, 0x95, 0xdb, 0x73, 0x47, 0xb0, 0xa3, 0x9c,
0xc3, 0xed, 0xdc, 0x17, 0x49, 0x56, 0x46, 0xe6, 0xb7, 0x83, 0xe8, 0xb0,
0xbd, 0x80, 0xd1, 0x51, 0x0c, 0xa4, 0x72, 0x9b, 0xf6, 0x3e, 0x44, 0x47,
0x7b, 0x4c, 0x4a, 0x8b, 0xea, 0x45, 0x59, 0xa2, 0x35, 0xa4, 0x54, 0x34,
0x06, 0x49, 0xcc, 0x9f, 0x32, 0x54, 0x77, 0x74, 0x3b, 0x3b, 0x55, 0x21,
0x36, 0x83, 0x3c, 0x86, 0xad, 0xb1, 0xd3, 0x1d, 0xbb, 0x0f, 0xcb, 0x61,
0xb0, 0x85, 0x24, 0x01, 0x6e, 0x72, 0x7e, 0x04, 0x5b, 0x94, 0x37, 0xf7,
0x48, 0x8c, 0x11, 0xf9, 0xd1, 0xa8, 0xa9, 0x5e, 0x23, 0xdb, 0xaa, 0x90,
0xa8, 0xe1, 0xc9, 0xe1, 0x07, 0x88, 0x9a, 0x51, 0x2b, 0x82, 0x33, 0x88,
0x10, 0xf8, 0xd1, 0x3d, 0xe6, 0xdb, 0x0e, 0x4b, 0x0a, 0x96, 0xd1, 0x41,
0x8e, 0x66, 0xf4, 0xea, 0xf8, 0x6b, 0x02, 0xb5, 0x18, 0xa4, 0xab, 0x18,
0x6e, 0x02, 0x91, 0x2e, 0x1d, 0x8a, 0xea, 0x0b, 0xb6, 0xa6, 0xa2, 0xd6,
0x48, 0x87, 0x2d, 0x69, 0x0c, 0xdf, 0x19, 0xf2, 0x1e, 0xb1, 0x92, 0xe6,
0x72, 0xd0, 0x19, 0xad, 0x03, 0x17, 0xf9, 0x23, 0xef, 0xc8, 0x72, 0x53,
0x9d, 0x16, 0x83, 0xe8, 0x58, 0x59, 0x4c, 0x7d, 0x56, 0x82, 0xff, 0xc0,
0x93, 0x08, 0x9e, 0xc2, 0x7b, 0xa2, 0x8b, 0x91, 0x24, 0x3c, 0x13, 0xeb,
0x81, 0x49, 0x6e, 0xf4, 0xe3, 0xab, 0x8b, 0x28, 0x06, 0x4e, 0x37, 0xe7,
0x3a, 0x86, 0x69, 0x9f, 0x31, 0x69, 0x2d, 0xc3, 0x98, 0xd9, 0xa4, 0x46,
0x87, 0xf9, 0xc4, 0x48, 0xca, 0x9d, 0x88, 0xe0, 0x70, 0xfa, 0xb5, 0xc3,
0x19, 0x1e, 0xf6, 0x28, 0xb7, 0x46, 0x0e, 0x54, 0x0c, 0x59, 0x60, 0x41,
0x33, 0xd6, 0xb5, 0xd1, 0x95, 0x6a, 0x5f, 0x8d, 0x54, 0x90, 0x60, 0x7e,
0x30, 0x96, 0x31, 0xcc, 0xf0, 0x38, 0x0c, 0xa7, 0x82, 0x83, 0xc4, 0xcc,
0xa1, 0xe3, 0x40, 0x38, 0x7e, 0x10, 0xff, 0x88, 0xfd, 0x11, 0xe3, 0x9c,
0xca, 0x0b, 0x7a, 0xa3, 0x11, 0x16, 0x2f, 0x36, 0x84, 0xdd, 0xbb, 0xbb,
0x30, 0xc0, 0x08, 0x2b, 0xf3, 0x17, 0x1d, 0x45, 0x7b, 0x26, 0xdc, 0x02,
0x2d, 0x15, 0x0d, 0x44, 0x3b, 0xdd, 0x58, 0x69, 0x44, 0x0e, 0x99, 0xc1,
0xb2, 0x11, 0xab, 0xb4, 0x34, 0x42, 0xa3, 0xc0, 0x10, 0xf7, 0x41, 0x32,
0xf8, 0xf7, 0xf9, 0x4f, 0x1f, 0x46, 0x15, 0x91, 0x8a, 0x0e, 0xb2, 0x7d,
0x77, 0x5d, 0x00, 0xbb, 0xbd, 0x68, 0xdb, 0xf6, 0x9e, 0x2f, 0xd9, 0xc8,
0x1e, 0xb0, 0x2c, 0x08, 0x4e, 0x7b, 0xd1, 0xd3, 0x86, 0xac, 0x22, 0x0c,
0xad, 0x7b, 0x90, 0x0a, 0x43, 0xfd, 0xb1, 0xdb, 0xee, 0xe8, 0x6d, 0x56,
0xd2, 0x28, 0x7e, 0xe0, 0xd0, 0x8d, 0x09, 0xb6, 0x28, 0x3f, 0x48, 0xf5,
0x9a, 0xb0, 0x92, 0x66, 0x30, 0x84, 0x8d, 0x14, 0x26, 0xf0, 0x6d, 0x53,
0xec, 0xa7, 0x77, 0x25, 0x0d, 0xb8, 0xd0, 0x90, 0x8b, 0x9a, 0xf7, 0xb1,
0x3c, 0x78, 0x6c, 0xcb, 0x7d, 0x77, 0x83, 0x3a, 0x88, 0x42, 0xa6, 0x4f,
0x1f, 0xb3, 0x91, 0xbd, 0x87, 0x9f, 0xda, 0xa0, 0xf5, 0x43, 0x4b, 0x39,
0xd2, 0x10, 0x2a, 0xad, 0xb0, 0x06, 0x2a, 0xa7, 0x0f, 0x61, 0x04, 0x6f,
0xaa, 0xbd, 0x75, 0x01, 0x38, 0xdc, 0x67, 0x57, 0xb6, 0xb5, 0xac, 0x69,
0x40, 0xd4, 0x75, 0xaf, 0x48, 0x87, 0x2c, 0x07, 0x6c, 0xdf, 0xb4, 0x25,
0x4c, 0xfa, 0x0c, 0x9b, 0x44, 0x0f, 0xa0, 0xb6, 0x7b, 0xd9, 0x59, 0x10,
0xec, 0x84, 0x12, 0x16, 0x47, 0x84, 0x61, 0x2d, 0xd1, 0x35, 0x53, 0x01,
0xa9, 0xc6, 0xe9, 0xe9, 0x87, 0x66, 0x84, 0xc2, 0x29, 0xad, 0xa9, 0x82,
0x94, 0xa7, 0x22, 0xa3, 0xbf, 0xfe, 0xf2, 0xf6, 0xa5, 0x58, 0x57, 0x82,
0x53, 0xae, 0x07, 0x98, 0x06, 0x3c, 0x37, 0x89, 0xb8, 0x26, 0x65, 0x4d,
0x8f, 0xf0, 0x1a, 0x3f, 0xf1, 0x26, 0xaf, 0xaf, 0xb1, 0x7a, 0x84, 0x77,
0x32, 0xe6, 0xbd, 0x65, 0x2a, 0xac, 0xda, 0xc6, 0xf2, 0xee, 0xba, 0x1c,
0x06, 0x71, 0x16, 0xf5, 0xd5, 0xcc, 0x7b, 0x22, 0xd7, 0x54, 0x17, 0x22,
0x8b, 0x21, 0x25, 0x65, 0x89, 0x2f, 0xa4, 0x31, 0x68, 0x63, 0xc9, 0x1f,
0xc6, 0x94, 0xce, 0x00, 0xde, 0x14, 0x12, 0x12, 0x34, 0x00, 0xfe, 0xf3,
0xfe, 0xdd, 0x1b, 0xad, 0xab, 0x5f, 0xa8, 0x99, 0x23, 0x94, 0x1e, 0xec,
0xd9, 0x82, 0x84, 0x23, 0xc1, 0x4b, 0x41, 0x32, 0xca, 0x33, 0x6f, 0x26,
0xe9, 0xc4, 0x40, 0xab, 0x7e, 0x80, 0x7c, 0x16, 0x2f, 0x71, 0x23, 0x43,
0x52, 0x65, 0xe2, 0xa9, 0x28, 0xde, 0x80, 0x7d, 0x1d, 0xb7, 0x5d, 0x2a,
0xb5, 0x0d, 0xe4, 0x23, 0x54, 0x0e, 0x27, 0xb1, 0x1d, 0x2f, 0xfe, 0x82,
0xf8, 0x8a, 0xf2, 0x41, 0x1b, 0x32, 0x8c, 0x5f, 0x73, 0x4d, 0xba, 0x7c,
0x57, 0x54, 0xbb, 0xd0, 0xbc, 0xa1, 0x24, 0xa3, 0x26, 0x31, 0x2f, 0xd2,
0x94, 0x56, 0xda, 0xa4, 0x2f, 0x22, 0x55, 0x55, 0xb2, 0x94, 0xa0, 0x6d,
0xc7, 0xd8, 0x84, 0xa3, 0x2e, 0x01, 0x3b, 0x3f, 0x06, 0x6d, 0x46, 0xe0,
0xcf, 0x3f, 0x61, 0x62, 0x3a, 0xd1, 0x77, 0xcd, 0x98, 0xd0, 0xa9, 0x94,
0x67, 0x83, 0xbe, 0xe4, 0x5f, 0x0a, 0xfd, 0xa2, 0xc2, 0x86, 0x89, 0xaa,
0x3b, 0x93, 0x8c, 0x03, 0xdd, 0xbc, 0xab, 0x15, 0x26, 0x49, 0xd3, 0x0b,
0xe1, 0xc9, 0x93, 0x86, 0x19, 0x0e, 0xdc, 0x5c, 0xd6, 0xd3, 0xc0, 0x90,
0xd6, 0xeb, 0x61, 0xad, 0x80, 0x51, 0x49, 0xf9, 0xa5, 0x2e, 0x60, 0x09,
0xe3, 0x07, 0x2b, 0x03, 0x1a, 0xe3, 0x77, 0x36, 0xe4, 0xec, 0xa9, 0x39,
0x81, 0x62, 0xb1, 0xfa, 0x4c, 0x53, 0x1d, 0xf5, 0x69, 0x41, 0x86, 0x9e,
0xb6, 0xe9, 0xb1, 0x8c, 0x94, 0x90, 0x7a, 0xb0, 0x03, 0x1a, 0x89, 0x61,
0xe5, 0xeb, 0x08, 0x27, 0xbe, 0xd5, 0x48, 0x2a, 0xc5, 0x60, 0x08, 0xa4,
0x79, 0xe8, 0x52, 0x17, 0x78, 0xe7, 0x12, 0x23, 0x2a, 0x85, 0xe5, 0x6b,
0x21, 0x2a, 0x54, 0xb7, 0xb4, 0xc3, 0x3b, 0x10, 0xf8, 0x60, 0xbb, 0x58,
0xf3, 0x96, 0xe0, 0xce, 0xa2, 0x0e, 0x11, 0xb9, 0x90, 0x30, 0x40, 0x39,
0xcc, 0x48, 0x19, 0xcf, 0xcd, 0xd7, 0xc2, 0x7a, 0x61, 0xd3, 0x30, 0x87,
0xa7, 0x4f, 0xd9, 0x83, 0xd6, 0xa3, 0xee, 0xa7, 0xf7, 0x94, 0x63, 0xd5,
0x43, 0xe6, 0x8f, 0xec, 0xd3, 0x08, 0x2b, 0x24, 0x16, 0xc6, 0xaf, 0x6a,
0xbf, 0x0d, 0xb7, 0x48, 0x65, 0xdb, 0xdb, 0x9b, 0x8b, 0xf7, 0xef, 0x20,
0x41, 0x15, 0x1d, 0x7c, 0x64, 0x85, 0x33, 0x37, 0x59, 0x95, 0x34, 0x6b,
0xdf, 0x26, 0x3a, 0xa8, 0xb4, 0xb8, 0x34, 0xa8, 0x46, 0x34, 0x76, 0xdf,
0x49, 0x27, 0x49, 0xf0, 0xb4, 0x64, 0xe9, 0x15, 0x24, 0x96, 0xa1, 0x67,
0x1c, 0xf3, 0x58, 0xbd, 0x19, 0xef, 0x83, 0x68, 0x47, 0x07, 0x65, 0xe7,
0x86, 0xae, 0x29, 0xcf, 0x4b, 0x3e, 0xd9, 0x0d, 0x9b, 0x2f, 0xaa, 0xd8,
0xf4, 0xe8, 0xf9, 0x63, 0xba, 0x9a, 0xf3, 0x8f, 0x5e, 0xc7, 0x98, 0xc5,
0x94, 0x76, 0x8f, 0xfe, 0xcd, 0x11, 0x56, 0x09, 0x52, 0xed, 0xbf, 0xad,
0x24, 0xed, 0x9b, 0x50, 0x57, 0x82, 0xed, 0x6b, 0x4f, 0xd4, 0xfe, 0x9c,
0xe7, 0x17, 0x24, 0x7f, 0x2a, 0x77, 0xdd, 0x70, 0xde, 0x1f, 0xa2, 0xf3,
0x94, 0x70, 0x34, 0xb6, 0x45, 0xa7, 0x8a, 0xfa, 0x52, 0x82, 0xb1, 0xe9,
0xc9, 0x49, 0x60, 0x50, 0x60, 0x74, 0xe8, 0xd8, 0x63, 0x8c, 0x7e, 0x4f,
0x78, 0x4d, 0x4a, 0x78, 0xc5, 0xb5, 0xdc, 0x46, 0x8f, 0xca, 0x90, 0xb3,
0x3e, 0xec, 0x35, 0x41, 0x5c, 0x28, 0x91, 0x69, 0x61, 0xca, 0x20, 0x06,
0xe7, 0x0e, 0x45, 0xe6, 0xee, 0x3a, 0x7d, 0xdd, 0xc0, 0x0f, 0xe7, 0xb1,
0xe0, 0xfa, 0xb4, 0x57, 0x13, 0x5a, 0xae, 0x25, 0xa6, 0x80, 0x1b, 0x4d,
0x61, 0x61, 0x78, 0xf4, 0x3b, 0xa3, 0x7d, 0xcd, 0xff, 0xda, 0xeb, 0xa2,
0x6d, 0x2c, 0xf8, 0x0a, 0xfb, 0x40, 0x07, 0xda, 0x30, 0xc3, 0xb0, 0x71,
0x53, 0xc1, 0xbd, 0xfe, 0xec, 0x85, 0xec, 0xf1, 0x40, 0xea, 0x81, 0x90,
0xf7, 0x23, 0x05, 0x52, 0x16, 0x84, 0x5f, 0xd2, 0xbe, 0xf1, 0x60, 0x7f,
0xee, 0xf3, 0x7f, 0xec, 0x70, 0x9b, 0x9e, 0x9a, 0xdb, 0x40, 0xa9, 0x1d,
0x44, 0xad, 0x52, 0x67, 0x9e, 0xdd, 0x0a, 0x28, 0x57, 0x57, 0x53, 0x8f,
0xac, 0xd3, 0xb4, 0x70, 0x54, 0xf6, 0xd4, 0xe3, 0xa2, 0xfb, 0xcd, 0x76,
0x3c, 0x3a, 0x0d, 0x32, 0xb2, 0x38, 0xbe, 0xfb, 0xf5, 0x68, 0x71, 0xec,
0x7e, 0xe6, 0xc5, 0xc7, 0xe6, 0x7f, 0x89, 0xff, 0x1f, 0x01, 0x02, 0x3f,
0xe6, 0x2a, 0x1f, 0x00, 0x00
};
unsigned int http_html_backup_len = 2393;

View File

@ -0,0 +1,7 @@
#!/bin/sh
# Uses zopfli for better gzip compression
# sudo apt-get install zopfli
zopfli --gzip ./enduser_setup.html
xxd -i ./enduser_setup.html.gz | sed 's/unsigned char/static const char/; s/__enduser_setup_html_gz/http_html_backup/' > http_html_backup.def

View File

@ -2,15 +2,24 @@
#include "module.h"
#include "lauxlib.h"
#include "lmem.h"
#include "platform.h"
#include "c_types.h"
#include "vfs.h"
#include "c_string.h"
#include <alloca.h>
#define FILE_READ_CHUNK 1024
static int file_fd = 0;
static int file_fd_ref = LUA_NOREF;
static int rtc_cb_ref = LUA_NOREF;
typedef struct _file_fd_ud {
int fd;
} file_fd_ud;
static void table2tm( lua_State *L, vfs_time *tm )
{
@ -91,13 +100,48 @@ static int file_on(lua_State *L)
// Lua: close()
static int file_close( lua_State* L )
{
if(file_fd){
vfs_close(file_fd);
file_fd = 0;
int need_pop = FALSE;
file_fd_ud *ud;
if (lua_type( L, 1 ) != LUA_TUSERDATA) {
// fall back to last opened file
if (file_fd_ref != LUA_NOREF) {
lua_rawgeti( L, LUA_REGISTRYINDEX, file_fd_ref );
// top of stack is now default file descriptor
ud = (file_fd_ud *)luaL_checkudata(L, -1, "file.obj");
lua_pop( L, 1 );
} else {
// no default file currently opened
return 0;
}
} else {
ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
}
// unref default file descriptor
luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
file_fd_ref = LUA_NOREF;
if(ud->fd){
vfs_close(ud->fd);
// mark as closed
ud->fd = 0;
}
return 0;
}
static int file_obj_free( lua_State *L )
{
file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
if (ud->fd) {
// close file if it's still open
vfs_close(ud->fd);
ud->fd = 0;
}
return 0;
}
// Lua: format()
static int file_format( lua_State* L )
{
@ -130,10 +174,10 @@ static int file_fscfg (lua_State *L)
static int file_open( lua_State* L )
{
size_t len;
if(file_fd){
vfs_close(file_fd);
file_fd = 0;
}
// unref last file descriptor to allow gc'ing if not kept by user script
luaL_unref( L, LUA_REGISTRYINDEX, file_fd_ref );
file_fd_ref = LUA_NOREF;
const char *fname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( fname );
@ -146,7 +190,14 @@ static int file_open( lua_State* L )
if(!file_fd){
lua_pushnil(L);
} else {
lua_pushboolean(L, 1);
file_fd_ud *ud = (file_fd_ud *) lua_newuserdata( L, sizeof( file_fd_ud ) );
ud->fd = file_fd;
luaL_getmetatable( L, "file.obj" );
lua_setmetatable( L, -2 );
// store reference to opened file
lua_pushvalue( L, -1 );
file_fd_ref = luaL_ref( L, LUA_REGISTRYINDEX );
}
return 1;
}
@ -170,19 +221,36 @@ static int file_list( lua_State* L )
return 0;
}
static int file_seek (lua_State *L)
static int get_file_obj( lua_State *L, int *argpos )
{
if (lua_type( L, 1 ) == LUA_TUSERDATA) {
file_fd_ud *ud = (file_fd_ud *)luaL_checkudata(L, 1, "file.obj");
*argpos = 2;
return ud->fd;
} else {
*argpos = 1;
return file_fd;
}
}
#define GET_FILE_OBJ int argpos; \
int fd = get_file_obj( L, &argpos );
static int file_seek (lua_State *L)
{
GET_FILE_OBJ;
static const int mode[] = {VFS_SEEK_SET, VFS_SEEK_CUR, VFS_SEEK_END};
static const char *const modenames[] = {"set", "cur", "end", NULL};
if(!file_fd)
if(!fd)
return luaL_error(L, "open a file first");
int op = luaL_checkoption(L, 1, "cur", modenames);
long offset = luaL_optlong(L, 2, 0);
op = vfs_lseek(file_fd, offset, mode[op]);
int op = luaL_checkoption(L, argpos, "cur", modenames);
long offset = luaL_optlong(L, ++argpos, 0);
op = vfs_lseek(fd, offset, mode[op]);
if (op < 0)
lua_pushnil(L); /* error */
else
lua_pushinteger(L, vfs_tell(file_fd));
lua_pushinteger(L, vfs_tell(fd));
return 1;
}
@ -210,7 +278,6 @@ static int file_remove( lua_State* L )
const char *fname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( fname );
luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid");
file_close(L);
vfs_remove((char *)fname);
return 0;
}
@ -218,9 +285,11 @@ static int file_remove( lua_State* L )
// Lua: flush()
static int file_flush( lua_State* L )
{
if(!file_fd)
GET_FILE_OBJ;
if(!fd)
return luaL_error(L, "open a file first");
if(vfs_flush(file_fd) == 0)
if(vfs_flush(fd) == 0)
lua_pushboolean(L, 1);
else
lua_pushnil(L);
@ -231,10 +300,6 @@ static int file_flush( lua_State* L )
static int file_rename( lua_State* L )
{
size_t len;
if(file_fd){
vfs_close(file_fd);
file_fd = 0;
}
const char *oldname = luaL_checklstring( L, 1, &len );
const char *basename = vfs_basename( oldname );
@ -253,38 +318,64 @@ static int file_rename( lua_State* L )
}
// g_read()
static int file_g_read( lua_State* L, int n, int16_t end_char )
static int file_g_read( lua_State* L, int n, int16_t end_char, int fd )
{
if(n <= 0 || n > LUAL_BUFFERSIZE)
n = LUAL_BUFFERSIZE;
if(end_char < 0 || end_char >255)
end_char = EOF;
luaL_Buffer b;
if(!file_fd)
return luaL_error(L, "open a file first");
luaL_buffinit(L, &b);
char *p = luaL_prepbuffer(&b);
int i;
n = vfs_read(file_fd, p, n);
for (i = 0; i < n; ++i)
if (p[i] == end_char)
{
++i;
break;
}
if(i==0){
luaL_pushresult(&b); /* close buffer */
return (lua_objlen(L, -1) > 0); /* check whether read something */
static char *heap_mem = NULL;
// free leftover memory
if (heap_mem) {
luaM_free(L, heap_mem);
heap_mem = NULL;
}
vfs_lseek(file_fd, -(n - i), VFS_SEEK_CUR);
luaL_addsize(&b, i);
luaL_pushresult(&b); /* close buffer */
return 1; /* read at least an `eol' */
if(n <= 0)
n = FILE_READ_CHUNK;
if(end_char < 0 || end_char >255)
end_char = EOF;
if(!fd)
return luaL_error(L, "open a file first");
char *p;
int i;
if (n > LUAL_BUFFERSIZE) {
// get buffer from heap
p = heap_mem = luaM_malloc(L, n);
} else {
// small chunks go onto the stack
p = alloca(n);
}
n = vfs_read(fd, p, n);
// bypass search if no end character provided
if (n > 0 && end_char != EOF) {
for (i = 0; i < n; ++i)
if (p[i] == end_char)
{
++i;
break;
}
} else {
i = n;
}
if (i == 0 || n == VFS_RES_ERR) {
if (heap_mem) {
luaM_free(L, heap_mem);
heap_mem = NULL;
}
return 0;
}
vfs_lseek(fd, -(n - i), VFS_SEEK_CUR);
lua_pushlstring(L, p, i);
if (heap_mem) {
luaM_free(L, heap_mem);
heap_mem = NULL;
}
return 1;
}
// Lua: read()
@ -293,42 +384,46 @@ static int file_g_read( lua_State* L, int n, int16_t end_char )
// file.read('q') will read until 'q' or EOF is reached.
static int file_read( lua_State* L )
{
unsigned need_len = LUAL_BUFFERSIZE;
unsigned need_len = FILE_READ_CHUNK;
int16_t end_char = EOF;
size_t el;
if( lua_type( L, 1 ) == LUA_TNUMBER )
GET_FILE_OBJ;
if( lua_type( L, argpos ) == LUA_TNUMBER )
{
need_len = ( unsigned )luaL_checkinteger( L, 1 );
if( need_len > LUAL_BUFFERSIZE ){
need_len = LUAL_BUFFERSIZE;
}
need_len = ( unsigned )luaL_checkinteger( L, argpos );
}
else if(lua_isstring(L, 1))
else if(lua_isstring(L, argpos))
{
const char *end = luaL_checklstring( L, 1, &el );
const char *end = luaL_checklstring( L, argpos, &el );
if(el!=1){
return luaL_error( L, "wrong arg range" );
}
end_char = (int16_t)end[0];
}
return file_g_read(L, need_len, end_char);
return file_g_read(L, need_len, end_char, fd);
}
// Lua: readline()
static int file_readline( lua_State* L )
{
return file_g_read(L, LUAL_BUFFERSIZE, '\n');
GET_FILE_OBJ;
return file_g_read(L, LUAL_BUFFERSIZE, '\n', fd);
}
// Lua: write("string")
static int file_write( lua_State* L )
{
if(!file_fd)
GET_FILE_OBJ;
if(!fd)
return luaL_error(L, "open a file first");
size_t l, rl;
const char *s = luaL_checklstring(L, 1, &l);
rl = vfs_write(file_fd, s, l);
const char *s = luaL_checklstring(L, argpos, &l);
rl = vfs_write(fd, s, l);
if(rl==l)
lua_pushboolean(L, 1);
else
@ -339,13 +434,15 @@ static int file_write( lua_State* L )
// Lua: writeline("string")
static int file_writeline( lua_State* L )
{
if(!file_fd)
GET_FILE_OBJ;
if(!fd)
return luaL_error(L, "open a file first");
size_t l, rl;
const char *s = luaL_checklstring(L, 1, &l);
rl = vfs_write(file_fd, s, l);
const char *s = luaL_checklstring(L, argpos, &l);
rl = vfs_write(fd, s, l);
if(rl==l){
rl = vfs_write(file_fd, "\n", 1);
rl = vfs_write(fd, "\n", 1);
if(rl==1)
lua_pushboolean(L, 1);
else
@ -420,6 +517,20 @@ static int file_vol_umount( lua_State *L )
}
static const LUA_REG_TYPE file_obj_map[] =
{
{ LSTRKEY( "close" ), LFUNCVAL( file_close ) },
{ LSTRKEY( "read" ), LFUNCVAL( file_read ) },
{ LSTRKEY( "readline" ), LFUNCVAL( file_readline ) },
{ LSTRKEY( "write" ), LFUNCVAL( file_write ) },
{ LSTRKEY( "writeline" ), LFUNCVAL( file_writeline ) },
{ LSTRKEY( "seek" ), LFUNCVAL( file_seek ) },
{ LSTRKEY( "flush" ), LFUNCVAL( file_flush ) },
{ LSTRKEY( "__gc" ), LFUNCVAL( file_obj_free ) },
{ LSTRKEY( "__index" ), LROVAL( file_obj_map ) },
{ LNILKEY, LNILVAL }
};
static const LUA_REG_TYPE file_vol_map[] =
{
{ LSTRKEY( "umount" ), LFUNCVAL( file_vol_umount )},
@ -459,6 +570,7 @@ static const LUA_REG_TYPE file_map[] = {
int luaopen_file( lua_State *L ) {
luaL_rometatable( L, "file.vol", (void *)file_vol_map );
luaL_rometatable( L, "file.obj", (void *)file_obj_map );
return 0;
}

View File

@ -190,19 +190,20 @@ static void seroutasync_done (task_param_t arg)
{
lua_State *L = lua_getstate();
luaM_freearray(L, serout.delay_table, serout.tablelen, uint32);
if (serout.lua_done_ref != LUA_REFNIL) { // we're here so serout.lua_done_ref != LUA_NOREF
serout.delay_table = NULL;
if (serout.lua_done_ref != LUA_NOREF) {
lua_rawgeti (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
serout.lua_done_ref = LUA_NOREF;
if (lua_pcall(L, 0, 0, 0)) {
// Uncaught Error. Print instead of sudden reset
luaL_error(L, "error: %s", lua_tostring(L, -1));
}
luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
}
}
static void ICACHE_RAM_ATTR seroutasync_cb(os_param_t p) {
(void) p;
NODE_DBG("%d\t%d\t%d\t%d\t%d\t%d\t%d\n", serout.repeats, serout.index, serout.level, serout.pin, serout.tablelen, serout.delay_table[serout.index], system_get_time()); // timing is delayed for short timings when debug output is enabled
if (serout.index < serout.tablelen) {
GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[serout.pin]), serout.level);
serout.level = serout.level==LOW ? HIGH : LOW;
@ -220,17 +221,25 @@ static int lgpio_serout( lua_State* L )
serout.pin = luaL_checkinteger( L, 1 );
serout.level = luaL_checkinteger( L, 2 );
serout.repeats = luaL_optint( L, 4, 1 )-1;
luaL_unref (L, LUA_REGISTRYINDEX, serout.lua_done_ref);
uint8_t is_async = FALSE;
if (!lua_isnoneornil(L, 5)) {
if (lua_isnumber(L, 5)) {
serout.lua_done_ref = LUA_REFNIL;
serout.lua_done_ref = LUA_NOREF;
} else {
lua_pushvalue(L, 5);
serout.lua_done_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
is_async = TRUE;
} else {
serout.lua_done_ref = LUA_NOREF;
}
if (serout.delay_table) {
luaM_freearray(L, serout.delay_table, serout.tablelen, uint32);
serout.delay_table = NULL;
}
luaL_argcheck(L, platform_gpio_exists(serout.pin), 1, "Invalid pin");
luaL_argcheck(L, serout.level==HIGH || serout.level==LOW, 2, "Wrong level type" );
luaL_argcheck(L, lua_istable( L, 3 ) &&
@ -244,7 +253,7 @@ static int lgpio_serout( lua_State* L )
lua_pop( L, 1 );
}
if (serout.lua_done_ref != LUA_NOREF) { // async version for duration above 15 mSec
if (is_async) { // async version for duration above 15 mSec
if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, TRUE)) {
// Failed to init the timer
luaL_error(L, "Unable to initialize timer");
@ -296,6 +305,7 @@ int luaopen_gpio( lua_State *L ) {
platform_gpio_init(task_get_id(gpio_intr_callback_task));
#endif
serout.done_taskid = task_get_id((task_callback_t) seroutasync_done);
serout.lua_done_ref = LUA_NOREF;
return 0;
}

View File

@ -14,13 +14,13 @@ static int http_callback_registry = LUA_NOREF;
static void http_callback( char * response, int http_status, char * full_response )
{
#if defined(HTTPCLIENT_DEBUG_ON)
c_printf( "http_status=%d\n", http_status );
dbg_printf( "http_status=%d\n", http_status );
if ( http_status != HTTP_STATUS_GENERIC_ERROR )
{
if (full_response != NULL) {
c_printf( "strlen(full_response)=%d\n", strlen( full_response ) );
dbg_printf( "strlen(full_response)=%d\n", strlen( full_response ) );
}
c_printf( "response=%s<EOF>\n", response );
dbg_printf( "response=%s<EOF>\n", response );
}
#endif
if (http_callback_registry != LUA_NOREF)

View File

@ -72,6 +72,7 @@ typedef struct lmqtt_userdata
uint8_t secure;
#endif
bool connected; // indicate socket connected, not mqtt prot connected.
bool keepalive_sent;
ETSTimer mqttTimer;
tConnState connState;
}lmqtt_userdata;
@ -212,9 +213,9 @@ static void mqtt_connack_fail(lmqtt_userdata * mud, int reason_code)
{
return;
}
lua_State *L = lua_getstate();
lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_connect_fail_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata(client) to callback func in lua
lua_pushinteger(L, reason_code);
@ -241,12 +242,12 @@ static sint8 mqtt_send_if_possible(struct espconn *pesp_conn)
#ifdef CLIENT_SSL_ENABLE
if( mud->secure )
{
espconn_status = espconn_secure_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length );
espconn_status = espconn_secure_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length );
}
else
#endif
{
espconn_status = espconn_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length );
espconn_status = espconn_send( pesp_conn, pending_msg->msg.data, pending_msg->msg.length );
}
mud->keep_alive_tick = 0;
}
@ -275,7 +276,7 @@ static void mqtt_socket_received(void *arg, char *pdata, unsigned short len)
READPACKET:
if(length > MQTT_BUF_SIZE || length <= 0)
return;
return;
// c_memcpy(in_buffer, pdata, length);
uint8_t temp_buffer[MQTT_BUF_SIZE];
@ -287,7 +288,7 @@ READPACKET:
case MQTT_CONNECT_SENDING:
case MQTT_CONNECT_SENT:
mud->event_timeout = 0;
if(mqtt_get_type(in_buffer) != MQTT_MSG_TYPE_CONNACK){
NODE_DBG("MQTT: Invalid packet\r\n");
mud->connState = MQTT_INIT;
@ -301,16 +302,16 @@ READPACKET:
{
espconn_disconnect(pesp_conn);
}
mqtt_connack_fail(mud, MQTT_CONN_FAIL_NOT_A_CONNACK_MSG);
break;
} else if (mqtt_get_connect_ret_code(in_buffer) != MQTT_CONNACK_ACCEPTED) {
NODE_DBG("MQTT: CONNACK REFUSED (CODE: %d)\n", mqtt_get_connect_ret_code(in_buffer));
mud->connState = MQTT_INIT;
#ifdef CLIENT_SSL_ENABLE
if(mud->secure)
{
@ -320,12 +321,12 @@ READPACKET:
#endif
{
espconn_disconnect(pesp_conn);
}
}
mqtt_connack_fail(mud, mqtt_get_connect_ret_code(in_buffer));
break;
} else {
mud->connState = MQTT_DATA;
NODE_DBG("MQTT: Connected\r\n");
@ -454,6 +455,7 @@ READPACKET:
break;
case MQTT_MSG_TYPE_PINGRESP:
// Ignore
mud->keepalive_sent = 0;
NODE_DBG("MQTT: PINGRESP received\r\n");
break;
}
@ -585,7 +587,7 @@ void mqtt_socket_timer(void *arg)
NODE_DBG("timer, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q)));
if(mud->event_timeout > 0){
NODE_DBG("event_timeout: %d.\n", mud->event_timeout);
mud->event_timeout --;
mud->event_timeout --;
if(mud->event_timeout > 0){
return;
} else {
@ -605,7 +607,7 @@ void mqtt_socket_timer(void *arg)
NODE_DBG("sSend MQTT_CONNECT failed.\n");
mud->connState = MQTT_INIT;
mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_SENDING);
#ifdef CLIENT_SSL_ENABLE
if(mud->secure)
{
@ -620,7 +622,7 @@ void mqtt_socket_timer(void *arg)
} else if(mud->connState == MQTT_CONNECT_SENT) { // wait for CONACK time out.
NODE_DBG("MQTT_CONNECT timeout.\n");
mud->connState = MQTT_INIT;
#ifdef CLIENT_SSL_ENABLE
if(mud->secure)
{
@ -630,7 +632,7 @@ void mqtt_socket_timer(void *arg)
#endif
{
espconn_disconnect(mud->pesp_conn);
}
}
mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_RECEIVING);
} else if(mud->connState == MQTT_DATA){
msg_queue_t *pending_msg = msg_peek(&(mud->mqtt_state.pending_msg_q));
@ -640,13 +642,20 @@ void mqtt_socket_timer(void *arg)
// no queued event.
mud->keep_alive_tick ++;
if(mud->keep_alive_tick > mud->mqtt_state.connect_info->keepalive){
uint8_t temp_buffer[MQTT_BUF_SIZE];
mqtt_msg_init(&mud->mqtt_state.mqtt_connection, temp_buffer, MQTT_BUF_SIZE);
NODE_DBG("\r\nMQTT: Send keepalive packet\r\n");
mqtt_message_t* temp_msg = mqtt_msg_pingreq(&mud->mqtt_state.mqtt_connection);
msg_queue_t *node = msg_enqueue( &(mud->mqtt_state.pending_msg_q), temp_msg,
0, MQTT_MSG_TYPE_PINGREQ, (int)mqtt_get_qos(temp_msg->data) );
mqtt_send_if_possible(mud->pesp_conn);
if (mud->keepalive_sent) {
// Oh dear -- keepalive timer expired and still no ack of previous message
mqtt_socket_reconnected(mud->pesp_conn, 0);
} else {
uint8_t temp_buffer[MQTT_BUF_SIZE];
mqtt_msg_init(&mud->mqtt_state.mqtt_connection, temp_buffer, MQTT_BUF_SIZE);
NODE_DBG("\r\nMQTT: Send keepalive packet\r\n");
mqtt_message_t* temp_msg = mqtt_msg_pingreq(&mud->mqtt_state.mqtt_connection);
msg_queue_t *node = msg_enqueue( &(mud->mqtt_state.pending_msg_q), temp_msg,
0, MQTT_MSG_TYPE_PINGREQ, (int)mqtt_get_qos(temp_msg->data) );
mud->keepalive_sent = 1;
mud->keep_alive_tick = 0; // Need to reset to zero in case flow control stopped.
mqtt_send_if_possible(mud->pesp_conn);
}
}
}
}
@ -675,6 +684,7 @@ static int mqtt_socket_client( lua_State* L )
// create a object
mud = (lmqtt_userdata *)lua_newuserdata(L, sizeof(lmqtt_userdata));
c_memset(mud, 0, sizeof(*mud));
// pre-initialize it, in case of errors
mud->self_ref = LUA_NOREF;
mud->cb_connect_ref = LUA_NOREF;
@ -685,18 +695,8 @@ static int mqtt_socket_client( lua_State* L )
mud->cb_suback_ref = LUA_NOREF;
mud->cb_unsuback_ref = LUA_NOREF;
mud->cb_puback_ref = LUA_NOREF;
mud->pesp_conn = NULL;
#ifdef CLIENT_SSL_ENABLE
mud->secure = 0;
#endif
mud->keep_alive_tick = 0;
mud->event_timeout = 0;
mud->connState = MQTT_INIT;
mud->connected = false;
c_memset(&mud->mqttTimer, 0, sizeof(ETSTimer));
c_memset(&mud->mqtt_state, 0, sizeof(mqtt_state_t));
c_memset(&mud->connect_info, 0, sizeof(mqtt_connect_info_t));
// set its metatable
luaL_getmetatable(L, "mqtt.socket");
@ -761,7 +761,7 @@ static int mqtt_socket_client( lua_State* L )
c_free(mud->connect_info.password);
mud->connect_info.password = NULL;
}
return luaL_error(L, "not enough memory");
return luaL_error(L, "not enough memory");
}
c_memcpy(mud->connect_info.client_id, clientId, idl);
@ -819,8 +819,8 @@ static int mqtt_delete( lua_State* L )
// ---- alloc-ed in mqtt_socket_lwt()
if(mud->connect_info.will_topic){
c_free(mud->connect_info.will_topic);
mud->connect_info.will_topic = NULL;
c_free(mud->connect_info.will_topic);
mud->connect_info.will_topic = NULL;
}
if(mud->connect_info.will_message){
@ -926,7 +926,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
if( dns_reconn_count >= 5 ){
NODE_ERR( "DNS Fail!\n" );
// Note: should delete the pesp_conn or unref self_ref here.
struct espconn *pesp_conn = arg;
if(pesp_conn != NULL) {
lmqtt_userdata *mud = (lmqtt_userdata *)pesp_conn->reverse;
@ -934,7 +934,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
mqtt_connack_fail(mud, MQTT_CONN_FAIL_DNS);
}
}
mqtt_socket_disconnected(arg); // although not connected, but fire disconnect callback to release every thing.
return -1;
}
@ -1070,7 +1070,7 @@ static int mqtt_socket_connect( lua_State* L )
}
stack++;
// call back function when a connection fails
if ((stack<=top) && (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION)){
lua_pushvalue(L, stack); // copy argument (func) to the top of stack
@ -1135,7 +1135,7 @@ static int mqtt_socket_close( lua_State* L )
// Send disconnect message
mqtt_message_t* temp_msg = mqtt_msg_disconnect(&mud->mqtt_state.mqtt_connection);
NODE_DBG("Send MQTT disconnect infomation, data len: %d, d[0]=%d \r\n", temp_msg->length, temp_msg->data[0]);
#ifdef CLIENT_SSL_ENABLE
if(mud->secure) {
espconn_status = espconn_secure_send(mud->pesp_conn, temp_msg->data, temp_msg->length);
@ -1150,7 +1150,7 @@ static int mqtt_socket_close( lua_State* L )
}
}
mud->connected = 0;
while (mud->mqtt_state.pending_msg_q) {
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
}
@ -1595,7 +1595,7 @@ static const LUA_REG_TYPE mqtt_socket_map[] = {
{ LNILKEY, LNILVAL }
};
static const LUA_REG_TYPE mqtt_map[] = {
{ LSTRKEY( "Client" ), LFUNCVAL( mqtt_socket_client ) },
@ -1609,7 +1609,7 @@ static const LUA_REG_TYPE mqtt_map[] = {
{ LSTRKEY( "CONNACK_REFUSED_ID_REJECTED" ), LNUMVAL( MQTT_CONNACK_REFUSED_ID_REJECTED ) },
{ LSTRKEY( "CONNACK_REFUSED_SERVER_UNAVAILABLE" ), LNUMVAL( MQTT_CONNACK_REFUSED_SERVER_UNAVAILABLE ) },
{ LSTRKEY( "CONNACK_REFUSED_BAD_USER_OR_PASS" ), LNUMVAL( MQTT_CONNACK_REFUSED_BAD_USER_OR_PASS ) },
{ LSTRKEY( "CONNACK_REFUSED_NOT_AUTHORIZED" ), LNUMVAL( MQTT_CONNACK_REFUSED_NOT_AUTHORIZED ) },
{ LSTRKEY( "CONNACK_REFUSED_NOT_AUTHORIZED" ), LNUMVAL( MQTT_CONNACK_REFUSED_NOT_AUTHORIZED ) },
{ LSTRKEY( "__metatable" ), LROVAL( mqtt_map ) },
{ LNILKEY, LNILVAL }

View File

@ -134,6 +134,8 @@ static int ow_read_bytes( lua_State *L )
if( size == 0 )
return 0;
luaL_argcheck(L, size <= LUAL_BUFFERSIZE, 2, "Attempt to read too many characters");
luaL_Buffer b;
luaL_buffinit( L, &b );
char *p = luaL_prepbuffer(&b);

View File

@ -53,7 +53,7 @@
#define MAX_ATTEMPTS 5
#if 0
# define sntp_dbg(...) c_printf(__VA_ARGS__)
# define sntp_dbg(...) dbg_printf(__VA_ARGS__)
#else
# define sntp_dbg(...)
#endif

248
app/modules/somfy.c Normal file
View File

@ -0,0 +1,248 @@
// ***************************************************************************
// Somfy module for ESP8266 with NodeMCU
//
// Written by Lukas Voborsky, @voborsky
// based on https://github.com/Nickduino/Somfy_Remote
// Somfy protocol description: https://pushstack.wordpress.com/somfy-rts-protocol/
// and discussion: https://forum.arduino.cc/index.php?topic=208346.0
//
// MIT license, http://opensource.org/licenses/MIT
// ***************************************************************************
//#define NODE_DEBUG
#include "os_type.h"
#include "osapi.h"
#include "sections.h"
#include "module.h"
#include "lauxlib.h"
#include "lmem.h"
#include "platform.h"
#include "hw_timer.h"
#include "user_interface.h"
#define SYMBOL 640 // symbol width in microseconds
#define SOMFY_UP 0x2
#define SOMFY_STOP 0x1
#define SOMFY_DOWN 0x4
#define SOMFY_PROG 0x8
#define DIRECT_WRITE_LOW(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 0))
#define DIRECT_WRITE_HIGH(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 1))
static const os_param_t TIMER_OWNER = 0x736f6d66; // "somf"
static task_handle_t done_taskid;
static uint8_t pin;
static uint8_t frame[7];
static uint8_t sync;
static uint8_t repeat;
//static uint32_t delay[10] = {9415, 89565, 4*SYMBOL, 4*SYMBOL, 4*SYMBOL, 4550, SYMBOL, SYMBOL, SYMBOL, 30415}; // in us
// the `delay` array of constants must be in RAM as it is accessed from the timer interrupt
static const RAM_CONST_SECTION_ATTR uint32_t delay[10] = {US_TO_RTC_TIMER_TICKS(9415), US_TO_RTC_TIMER_TICKS(89565), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4550), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(30415)}; // in ticks (no need to recalculate)
static uint8_t repeatindex;
static uint8_t signalindex;
static uint8_t subindex;
static uint8_t bitcondition;
int lua_done_ref; // callback when transmission is done
void buildFrame(uint8_t *frame, uint64_t remote, uint8_t button, uint16_t code) {
// NODE_DBG("remote: %x\n", remote);
// NODE_DBG("button: %x\n", button);
// NODE_DBG("rolling code: %x\n", code);
frame[0] = 0xA7; // Encryption key. Doesn't matter much
frame[1] = button << 4; // Which button did you press? The 4 LSB will be the checksum
frame[2] = code >> 8; // Rolling code (big endian)
frame[3] = code; // Rolling code
frame[4] = remote >> 16; // Remote address
frame[5] = remote >> 8; // Remote address
frame[6] = remote; // Remote address
// frame[7] = 0x80;
// frame[8] = 0x0;
// frame[9] = 0x0;
// NODE_DBG("Frame:\t\t\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]);
// Checksum calculation: a XOR of all the nibbles
uint8_t checksum = 0;
for(uint8_t i = 0; i < 7; i++) {
checksum = checksum ^ frame[i] ^ (frame[i] >> 4);
}
checksum &= 0b1111; // We keep the last 4 bits only
//Checksum integration
frame[1] |= checksum; // If a XOR of all the nibbles is equal to 0, the blinds will consider the checksum ok.
// NODE_DBG("With checksum:\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]);
// Obfuscation: a XOR of all the uint8_ts
for(uint8_t i = 1; i < 7; i++) {
frame[i] ^= frame[i-1];
}
// NODE_DBG("Obfuscated:\t\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]);
}
static void somfy_transmissionDone (task_param_t arg)
{
lua_State *L = lua_getstate();
lua_rawgeti (L, LUA_REGISTRYINDEX, lua_done_ref);
luaL_unref (L, LUA_REGISTRYINDEX, lua_done_ref);
lua_done_ref = LUA_NOREF;
lua_call (L, 0, 0);
}
static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
(void) p;
// NODE_DBG("%d\t%d\n", signalindex, subindex);
switch (signalindex) {
case 0:
subindex = 0;
if(sync == 2) { // Only with the first frame.
//Wake-up pulse & Silence
DIRECT_WRITE_HIGH(pin);
signalindex++;
// delayMicroseconds(9415);
break;
} else {
signalindex++; signalindex++; //no break means: go directly to step 3
}
case 1:
//Wake-up pulse & Silence
DIRECT_WRITE_LOW(pin);
signalindex++;
// delayMicroseconds(89565);
break;
case 2:
signalindex++;
// no break means go directly to step 3
// a "useless" step to allow repeating the hardware sync w/o the silence after wake-up pulse
case 3:
// Hardware sync: two sync for the first frame, seven for the following ones.
DIRECT_WRITE_HIGH(pin);
signalindex++;
// delayMicroseconds(4*SYMBOL);
break;
case 4:
DIRECT_WRITE_LOW(pin);
subindex++;
if (subindex < sync) {signalindex--;} else {signalindex++;}
// delayMicroseconds(4*SYMBOL);
break;
case 5:
// Software sync
DIRECT_WRITE_HIGH(pin);
signalindex++;
// delayMicroseconds(4550);
break;
case 6:
DIRECT_WRITE_LOW(pin);
signalindex++;
subindex=0;
// delayMicroseconds(SYMBOL);
break;
case 7:
//Data: bits are sent one by one, starting with the MSB.
bitcondition = ((frame[subindex/8] >> (7 - (subindex%8))) & 1) == 1;
if(bitcondition) {
DIRECT_WRITE_LOW(pin);
}
else {
DIRECT_WRITE_HIGH(pin);
}
signalindex++;
// delayMicroseconds(SYMBOL);
break;
case 8:
//Data: bits are sent one by one, starting with the MSB.
if(bitcondition) {
DIRECT_WRITE_HIGH(pin);
}
else {
DIRECT_WRITE_LOW(pin);
}
if (subindex<56) {
subindex++;
signalindex--;
}
else {
signalindex++;
}
// delayMicroseconds(SYMBOL);
break;
case 9:
DIRECT_WRITE_LOW(pin);
signalindex++;
// delayMicroseconds(30415); // Inter-frame silence
break;
case 10:
repeatindex++;
if (repeatindex<repeat) {
DIRECT_WRITE_HIGH(pin); //start repeat from step 3, but don't wait as after step 1
signalindex=4; subindex=0; sync=7;
} else {
platform_hw_timer_close(TIMER_OWNER);
if (lua_done_ref != LUA_NOREF) {
task_post_low (done_taskid, (task_param_t)0);
}
}
break;
}
if (signalindex<10) {
platform_hw_timer_arm_ticks(TIMER_OWNER, delay[signalindex-1]);
}
}
static int somfy_lua_sendcommand(lua_State* L) { // pin, remote, command, rolling_code, num_repeat, callback
if (!lua_isnumber(L, 4)) {
return luaL_error(L, "wrong arg range");
}
pin = luaL_checkinteger(L, 1);
uint64_t remote = luaL_checkinteger(L, 2);
uint8_t cmd = luaL_checkinteger(L, 3);
uint16_t code = luaL_checkinteger(L, 4);
repeat=luaL_optint( L, 5, 2 );
luaL_argcheck(L, platform_gpio_exists(pin), 1, "Invalid pin");
luaL_unref(L, LUA_REGISTRYINDEX, lua_done_ref);
if (!lua_isnoneornil(L, 6)) {
lua_pushvalue(L, 6);
lua_done_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else {
lua_done_ref = LUA_NOREF;
}
MOD_CHECK_ID(gpio, pin);
platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_PULLUP);
buildFrame(frame, remote, cmd, code);
if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, TRUE)) {
// Failed to init the timer
luaL_error(L, "Unable to initialize timer");
}
platform_hw_timer_set_func(TIMER_OWNER, sendCommand, 0);
sync=2;
signalindex=0; repeatindex=0;
sendCommand(0);
return 0;
}
static const LUA_REG_TYPE somfy_map[] = {
{ LSTRKEY( "UP" ), LNUMVAL( SOMFY_UP ) },
{ LSTRKEY( "DOWN" ), LNUMVAL( SOMFY_DOWN ) },
{ LSTRKEY( "PROG" ), LNUMVAL( SOMFY_PROG ) },
{ LSTRKEY( "STOP" ), LNUMVAL( SOMFY_STOP ) },
{ LSTRKEY( "sendcommand" ), LFUNCVAL(somfy_lua_sendcommand)},
{ LNILKEY, LNILVAL}
};
int luaopen_somfy( lua_State *L ) {
done_taskid = task_get_id((task_callback_t) somfy_transmissionDone);
return 0;
}
NODEMCU_MODULE(SOMFY, "somfy", somfy_map, luaopen_somfy);

View File

@ -2,24 +2,13 @@
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "c_stdlib.h"
#include "lmem.h"
#include "u8g.h"
#include "u8g_glue.h"
#include "u8g_config.h"
struct _lu8g_userdata_t
{
u8g_t u8g;
};
typedef struct _lu8g_userdata_t lu8g_userdata_t;
// shorthand macro for the u8g structure inside the userdata
#define LU8G (&(lud->u8g))
// helper function: retrieve and check userdata argument
static lu8g_userdata_t *get_lud( lua_State *L )
@ -780,214 +769,6 @@ static int lu8g_setFontLineSpacingFactor( lua_State *L )
}
// ------------------------------------------------------------
// comm functions
//
#define I2C_CMD_MODE 0x000
#define I2C_DATA_MODE 0x040
#define ESP_I2C_ID 0
static uint8_t do_i2c_start(uint8_t id, uint8_t sla)
{
platform_i2c_send_start( id );
// ignore return value -> tolerate missing ACK
platform_i2c_send_address( id, sla, PLATFORM_I2C_DIRECTION_TRANSMITTER );
return 1;
}
static uint8_t u8g_com_esp8266_ssd_start_sequence(u8g_t *u8g)
{
/* are we requested to set the a0 state? */
if ( u8g->pin_list[U8G_PI_SET_A0] == 0 )
return 1;
/* setup bus, might be a repeated start */
if ( do_i2c_start( ESP_I2C_ID, u8g->i2c_addr ) == 0 )
return 0;
if ( u8g->pin_list[U8G_PI_A0_STATE] == 0 )
{
// ignore return value -> tolerate missing ACK
if ( platform_i2c_send_byte( ESP_I2C_ID, I2C_CMD_MODE ) == 0 )
; //return 0;
}
else
{
platform_i2c_send_byte( ESP_I2C_ID, I2C_DATA_MODE );
}
u8g->pin_list[U8G_PI_SET_A0] = 0;
return 1;
}
static void lu8g_digital_write( u8g_t *u8g, uint8_t pin_index, uint8_t value )
{
uint8_t pin;
pin = u8g->pin_list[pin_index];
if ( pin != U8G_PIN_NONE )
platform_gpio_write( pin, value );
}
void u8g_Delay(u8g_t *u8g, uint16_t msec)
{
const uint16_t chunk = 50;
if (u8g->use_delay == 0)
return;
while (msec > chunk)
{
os_delay_us( chunk*1000 );
msec -= chunk;
}
if (msec > 0)
os_delay_us( msec*1000 );
}
void u8g_MicroDelay(void)
{
os_delay_us( 1 );
}
void u8g_10MicroDelay(void)
{
os_delay_us( 10 );
}
uint8_t u8g_com_esp8266_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
switch(msg)
{
case U8G_COM_MSG_STOP:
break;
case U8G_COM_MSG_INIT:
// we assume that the SPI interface was already initialized
// just care for the /CS and D/C pins
lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_HIGH );
platform_gpio_mode( u8g->pin_list[U8G_PI_CS], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT );
platform_gpio_mode( u8g->pin_list[U8G_PI_A0], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT );
break;
case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
lu8g_digital_write( u8g, U8G_PI_A0, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH );
break;
case U8G_COM_MSG_CHIP_SELECT:
if (arg_val == 0)
{
/* disable */
lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_HIGH );
}
else
{
/* enable */
//u8g_com_arduino_digital_write(u8g, U8G_PI_SCK, LOW);
lu8g_digital_write( u8g, U8G_PI_CS, PLATFORM_GPIO_LOW );
}
break;
case U8G_COM_MSG_RESET:
if ( u8g->pin_list[U8G_PI_RESET] != U8G_PIN_NONE )
lu8g_digital_write( u8g, U8G_PI_RESET, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH );
break;
case U8G_COM_MSG_WRITE_BYTE:
platform_spi_send( 1, 8, arg_val );
break;
case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
{
register uint8_t *ptr = arg_ptr;
while( arg_val > 0 )
{
platform_spi_send( 1, 8, *ptr++ );
arg_val--;
}
}
break;
}
return 1;
}
uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
switch(msg)
{
case U8G_COM_MSG_INIT:
// we assume that the i2c bus was already initialized
//u8g_i2c_init(u8g->pin_list[U8G_PI_I2C_OPTION]);
break;
case U8G_COM_MSG_STOP:
break;
case U8G_COM_MSG_RESET:
/* Currently disabled, but it could be enable. Previous restrictions have been removed */
/* u8g_com_arduino_digital_write(u8g, U8G_PI_RESET, arg_val); */
break;
case U8G_COM_MSG_CHIP_SELECT:
u8g->pin_list[U8G_PI_A0_STATE] = 0;
u8g->pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again, also forces start condition */
if ( arg_val == 0 )
{
/* disable chip, send stop condition */
platform_i2c_send_stop( ESP_I2C_ID );
}
else
{
/* enable, do nothing: any byte writing will trigger the i2c start */
}
break;
case U8G_COM_MSG_WRITE_BYTE:
//u8g->pin_list[U8G_PI_SET_A0] = 1;
if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 )
return platform_i2c_send_stop( ESP_I2C_ID ), 0;
// ignore return value -> tolerate missing ACK
if ( platform_i2c_send_byte( ESP_I2C_ID, arg_val) == 0 )
; //return platform_i2c_send_stop( ESP_I2C_ID ), 0;
// platform_i2c_send_stop( ESP_I2C_ID );
break;
case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
//u8g->pin_list[U8G_PI_SET_A0] = 1;
if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 )
return platform_i2c_send_stop( ESP_I2C_ID ), 0;
{
register uint8_t *ptr = arg_ptr;
while( arg_val > 0 )
{
// ignore return value -> tolerate missing ACK
if ( platform_i2c_send_byte( ESP_I2C_ID, *ptr++) == 0 )
; //return platform_i2c_send_stop( ESP_I2C_ID ), 0;
arg_val--;
}
}
// platform_i2c_send_stop( ESP_I2C_ID );
break;
case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
u8g->pin_list[U8G_PI_A0_STATE] = arg_val;
u8g->pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again */
break;
}
return 1;
}
// device destructor
static int lu8g_close_display( lua_State *L )
{
@ -996,6 +777,19 @@ static int lu8g_close_display( lua_State *L )
if ((lud = get_lud( L )) == NULL)
return 0;
if (lud->cb_ref != LUA_NOREF) {
// this is the fb_rle device
u8g_dev_t *fb_dev = LU8G->dev;
u8g_pb_t *fb_dev_pb = (u8g_pb_t *)(fb_dev->dev_mem);
uint8_t *fb_dev_buf = fb_dev_pb->buf;
luaM_free( L, fb_dev_buf );
luaM_free( L, fb_dev_pb );
luaM_free( L, fb_dev );
luaL_unref( L, lud->cb_ref, LUA_REGISTRYINDEX );
}
return 0;
}
@ -1016,9 +810,10 @@ static int lu8g_close_display( lua_State *L )
return luaL_error( L, "i2c address required" ); \
\
lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \
lud->cb_ref = LUA_NOREF; \
\
lud->u8g.i2c_addr = (uint8_t)addr; \
lud->u8g.use_delay = del > 0 ? 1 : 0; \
lud->i2c_addr = (uint8_t)addr; \
lud->use_delay = del > 0 ? 1 : 0; \
\
u8g_InitI2C( LU8G, &u8g_dev_ ## device, U8G_I2C_OPT_NONE); \
\
@ -1049,8 +844,9 @@ U8G_DISPLAY_TABLE_I2C
unsigned del = luaL_optinteger( L, 4, 0 ); \
\
lu8g_userdata_t *lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) ); \
lud->cb_ref = LUA_NOREF; \
\
lud->u8g.use_delay = del > 0 ? 1 : 0; \
lud->use_delay = del > 0 ? 1 : 0; \
\
u8g_InitHWSPI( LU8G, &u8g_dev_ ## device, cs, dc, res ); \
\
@ -1064,6 +860,53 @@ U8G_DISPLAY_TABLE_I2C
// Unroll the display table and insert binding functions for SPI based displays.
U8G_DISPLAY_TABLE_SPI
//
//
//
// This display forwards the framebuffer contents as run-length encoded chunks to a Lua callback
static int lu8g_fb_rle( lua_State *L ) {
lu8g_userdata_t *lud;
if ((lua_type( L, 1 ) != LUA_TFUNCTION) &&
(lua_type( L, 1 ) != LUA_TLIGHTFUNCTION)) {
luaL_typerror( L, 1, "function" );
}
int width = luaL_checkint( L, 2 );
int height = luaL_checkint( L, 3 );
luaL_argcheck( L, (width > 0) && (width < 256) && ((width % 8) == 0), 2, "invalid width" );
luaL_argcheck( L, (height > 0) && (height < 256) && ((height % 8) == 0), 3, "invalid height" );
// construct display device structures manually because width and height are configurable
uint8_t *fb_dev_buf = (uint8_t *)luaM_malloc( L, width );
u8g_pb_t *fb_dev_pb = (u8g_pb_t *)luaM_malloc( L, sizeof( u8g_pb_t ) );
fb_dev_pb->p.page_height = 8;
fb_dev_pb->p.total_height = height;
fb_dev_pb->p.page_y0 = 0;
fb_dev_pb->p.page_y1 = 0;
fb_dev_pb->p.page = 0;
fb_dev_pb->width = width;
fb_dev_pb->buf = fb_dev_buf;
u8g_dev_t *fb_dev = (u8g_dev_t *)luaM_malloc( L, sizeof( u8g_dev_t ) );
fb_dev->dev_fn = u8g_dev_gen_fb_fn;
fb_dev->dev_mem = fb_dev_pb;
fb_dev->com_fn = u8g_com_esp8266_fbrle_fn;
lud = (lu8g_userdata_t *) lua_newuserdata( L, sizeof( lu8g_userdata_t ) );
lua_pushvalue( L, 1 ); // copy argument (func) to the top of stack
lud->cb_ref = luaL_ref( L, LUA_REGISTRYINDEX );
/* set metatable for userdata */
luaL_getmetatable(L, "u8g.display");
lua_setmetatable(L, -2);
u8g_Init8BitFixedPort( LU8G, fb_dev, width, height, 0, 0, 0);
return 1;
}
//
// ***************************************************************************
@ -1137,6 +980,8 @@ static const LUA_REG_TYPE lu8g_map[] = {
#define U8G_FONT_TABLE_ENTRY(font) \
{ LSTRKEY( #font ), LUDATA( (void *)(u8g_ ## font) ) },
U8G_FONT_TABLE
//
{ LSTRKEY( "fb_rle" ), LFUNCVAL( lu8g_fb_rle ) },
// Options for circle/ ellipse drawing
{ LSTRKEY( "DRAW_UPPER_RIGHT" ), LNUMVAL( U8G_DRAW_UPPER_RIGHT ) },
{ LSTRKEY( "DRAW_UPPER_LEFT" ), LNUMVAL( U8G_DRAW_UPPER_LEFT ) },

347
app/modules/u8g_glue.c Normal file
View File

@ -0,0 +1,347 @@
/*
Functions for integrating U8glib into the nodemcu platform.
*/
#include "lauxlib.h"
#include "platform.h"
#include "c_stdlib.h"
#include "u8g.h"
#include "u8g_glue.h"
// ------------------------------------------------------------
// comm functions
//
#define I2C_CMD_MODE 0x000
#define I2C_DATA_MODE 0x040
#define ESP_I2C_ID 0
static uint8_t do_i2c_start(uint8_t id, uint8_t sla)
{
platform_i2c_send_start( id );
// ignore return value -> tolerate missing ACK
platform_i2c_send_address( id, sla, PLATFORM_I2C_DIRECTION_TRANSMITTER );
return 1;
}
static uint8_t u8g_com_esp8266_ssd_start_sequence(struct _lu8g_userdata_t *lu8g)
{
/* are we requested to set the a0 state? */
if ( lu8g->u8g.pin_list[U8G_PI_SET_A0] == 0 )
return 1;
/* setup bus, might be a repeated start */
if ( do_i2c_start( ESP_I2C_ID, lu8g->i2c_addr ) == 0 )
return 0;
if ( lu8g->u8g.pin_list[U8G_PI_A0_STATE] == 0 )
{
// ignore return value -> tolerate missing ACK
if ( platform_i2c_send_byte( ESP_I2C_ID, I2C_CMD_MODE ) == 0 )
; //return 0;
}
else
{
platform_i2c_send_byte( ESP_I2C_ID, I2C_DATA_MODE );
}
lu8g->u8g.pin_list[U8G_PI_SET_A0] = 0;
return 1;
}
static void lu8g_digital_write( struct _lu8g_userdata_t *lu8g, uint8_t pin_index, uint8_t value )
{
uint8_t pin;
pin = lu8g->u8g.pin_list[pin_index];
if ( pin != U8G_PIN_NONE )
platform_gpio_write( pin, value );
}
void u8g_Delay(u8g_t *u8g, uint16_t msec)
{
struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g;
const uint16_t chunk = 50;
if (lu8g->use_delay == 0)
return;
while (msec > chunk)
{
os_delay_us( chunk*1000 );
msec -= chunk;
}
if (msec > 0)
os_delay_us( msec*1000 );
}
void u8g_MicroDelay(void)
{
os_delay_us( 1 );
}
void u8g_10MicroDelay(void)
{
os_delay_us( 10 );
}
uint8_t u8g_com_esp8266_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g;
switch(msg)
{
case U8G_COM_MSG_STOP:
break;
case U8G_COM_MSG_INIT:
// we assume that the SPI interface was already initialized
// just care for the /CS and D/C pins
lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_HIGH );
platform_gpio_mode( lu8g->u8g.pin_list[U8G_PI_CS], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT );
platform_gpio_mode( lu8g->u8g.pin_list[U8G_PI_A0], PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT );
break;
case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
lu8g_digital_write( lu8g, U8G_PI_A0, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH );
break;
case U8G_COM_MSG_CHIP_SELECT:
if (arg_val == 0)
{
/* disable */
lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_HIGH );
}
else
{
/* enable */
lu8g_digital_write( lu8g, U8G_PI_CS, PLATFORM_GPIO_LOW );
}
break;
case U8G_COM_MSG_RESET:
if ( lu8g->u8g.pin_list[U8G_PI_RESET] != U8G_PIN_NONE )
lu8g_digital_write( lu8g, U8G_PI_RESET, arg_val == 0 ? PLATFORM_GPIO_LOW : PLATFORM_GPIO_HIGH );
break;
case U8G_COM_MSG_WRITE_BYTE:
platform_spi_send( 1, 8, arg_val );
break;
case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
{
register uint8_t *ptr = arg_ptr;
while( arg_val > 0 )
{
platform_spi_send( 1, 8, *ptr++ );
arg_val--;
}
}
break;
}
return 1;
}
uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
struct _lu8g_userdata_t *lu8g = (struct _lu8g_userdata_t *)u8g;
switch(msg)
{
case U8G_COM_MSG_INIT:
// we assume that the i2c bus was already initialized
//u8g_i2c_init(u8g->pin_list[U8G_PI_I2C_OPTION]);
break;
case U8G_COM_MSG_STOP:
break;
case U8G_COM_MSG_RESET:
/* Currently disabled, but it could be enable. Previous restrictions have been removed */
/* u8g_com_arduino_digital_write(u8g, U8G_PI_RESET, arg_val); */
break;
case U8G_COM_MSG_CHIP_SELECT:
lu8g->u8g.pin_list[U8G_PI_A0_STATE] = 0;
lu8g->u8g.pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again, also forces start condition */
if ( arg_val == 0 )
{
/* disable chip, send stop condition */
platform_i2c_send_stop( ESP_I2C_ID );
}
else
{
/* enable, do nothing: any byte writing will trigger the i2c start */
}
break;
case U8G_COM_MSG_WRITE_BYTE:
//u8g->pin_list[U8G_PI_SET_A0] = 1;
if ( u8g_com_esp8266_ssd_start_sequence(lu8g) == 0 )
return platform_i2c_send_stop( ESP_I2C_ID ), 0;
// ignore return value -> tolerate missing ACK
if ( platform_i2c_send_byte( ESP_I2C_ID, arg_val) == 0 )
; //return platform_i2c_send_stop( ESP_I2C_ID ), 0;
// platform_i2c_send_stop( ESP_I2C_ID );
break;
case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
//u8g->pin_list[U8G_PI_SET_A0] = 1;
if ( u8g_com_esp8266_ssd_start_sequence(lu8g) == 0 )
return platform_i2c_send_stop( ESP_I2C_ID ), 0;
{
register uint8_t *ptr = arg_ptr;
while( arg_val > 0 )
{
// ignore return value -> tolerate missing ACK
if ( platform_i2c_send_byte( ESP_I2C_ID, *ptr++) == 0 )
; //return platform_i2c_send_stop( ESP_I2C_ID ), 0;
arg_val--;
}
}
// platform_i2c_send_stop( ESP_I2C_ID );
break;
case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
lu8g->u8g.pin_list[U8G_PI_A0_STATE] = arg_val;
lu8g->u8g.pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again */
break;
}
return 1;
}
// ***************************************************************************
// Generic framebuffer device and RLE comm driver
//
uint8_t u8g_dev_gen_fb_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg)
{
switch(msg)
{
case U8G_DEV_MSG_PAGE_FIRST:
// tell comm driver to start new framebuffer
u8g_SetChipSelect(u8g, dev, 1);
break;
case U8G_DEV_MSG_PAGE_NEXT:
{
u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem);
if ( u8g_pb_WriteBuffer(pb, u8g, dev) == 0 )
return 0;
}
break;
}
return u8g_dev_pb8v1_base_fn(u8g, dev, msg, arg);
}
static int bit_at( uint8_t *buf, int line, int x )
{
uint8_t byte = buf[x];
return buf[x] & (1 << line) ? 1 : 0;
}
struct _lu8g_fbrle_item
{
uint8_t start_x;
uint8_t len;
};
struct _lu8g_fbrle_line
{
uint8_t num_valid;
struct _lu8g_fbrle_item items[0];
};
uint8_t u8g_com_esp8266_fbrle_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
struct _lu8g_userdata_t *lud = (struct _lu8g_userdata_t *)u8g;
switch(msg)
{
case U8G_COM_MSG_INIT:
// allocate memory -> done
// init buffer
break;
case U8G_COM_MSG_CHIP_SELECT:
if (arg_val == 1) {
// new frame starts
if (lud->cb_ref != LUA_NOREF) {
// fire callback with nil argument
lua_State *L = lua_getstate();
lua_rawgeti( L, LUA_REGISTRYINDEX, lud->cb_ref );
lua_pushnil( L );
lua_call( L, 1, 0 );
}
}
break;
case U8G_COM_MSG_WRITE_SEQ:
case U8G_COM_MSG_WRITE_SEQ_P:
{
uint8_t xwidth = u8g->pin_list[U8G_PI_EN];
size_t fbrle_line_size = sizeof( struct _lu8g_fbrle_line ) + sizeof( struct _lu8g_fbrle_item ) * (xwidth/2);
int num_lines = arg_val / (xwidth/8);
uint8_t *buf = (uint8_t *)arg_ptr;
struct _lu8g_fbrle_line *fbrle_line;
if (!(fbrle_line = (struct _lu8g_fbrle_line *)c_malloc( fbrle_line_size ))) {
break;
}
for (int line = 0; line < num_lines; line++) {
int start_run = -1;
fbrle_line->num_valid = 0;
for (int x = 0; x < xwidth; x++) {
if (bit_at( buf, line, x ) == 0) {
if (start_run >= 0) {
// inside run, end it and enter result
fbrle_line->items[fbrle_line->num_valid].start_x = start_run;
fbrle_line->items[fbrle_line->num_valid++].len = x - start_run;
//NODE_ERR( " line: %d x: %d len: %d\n", line, start_run, x - start_run );
start_run = -1;
}
} else {
if (start_run < 0) {
// outside run, start it
start_run = x;
}
}
if (fbrle_line->num_valid >= xwidth/2) break;
}
// active run?
if (start_run >= 0 && fbrle_line->num_valid < xwidth/2) {
fbrle_line->items[fbrle_line->num_valid].start_x = start_run;
fbrle_line->items[fbrle_line->num_valid++].len = xwidth - start_run;
}
// line done, trigger callback
if (lud->cb_ref != LUA_NOREF) {
lua_State *L = lua_getstate();
lua_rawgeti( L, LUA_REGISTRYINDEX, lud->cb_ref );
lua_pushlstring( L, (const char *)fbrle_line, fbrle_line_size );
lua_call( L, 1, 0 );
}
}
c_free( fbrle_line );
}
break;
}
return 1;
}

22
app/modules/u8g_glue.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef _U8G_GLUE_H_
#define _U8G_GLUE_H_
#include "u8g.h"
struct _lu8g_userdata_t
{
u8g_t u8g;
uint8_t i2c_addr;
uint8_t use_delay;
int cb_ref;
};
typedef struct _lu8g_userdata_t lu8g_userdata_t;
// shorthand macro for the u8g structure inside the userdata
#define LU8G (&(lud->u8g))
uint8_t u8g_com_esp8266_fbrle_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);
uint8_t u8g_dev_gen_fb_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg);
#endif

View File

@ -15,6 +15,7 @@
#include "c_types.h"
#include "c_string.h"
#include "c_stdlib.h"
#include "websocketclient.h"
@ -45,7 +46,7 @@ static void websocketclient_onConnectionCallback(ws_info *ws) {
}
}
static void websocketclient_onReceiveCallback(ws_info *ws, char *message, int opCode) {
static void websocketclient_onReceiveCallback(ws_info *ws, int len, char *message, int opCode) {
NODE_DBG("websocketclient_onReceiveCallback\n");
lua_State *L = lua_getstate();
@ -59,7 +60,7 @@ static void websocketclient_onReceiveCallback(ws_info *ws, char *message, int op
if (data->onReceive != LUA_NOREF) {
lua_rawgeti(L, LUA_REGISTRYINDEX, data->onReceive); // load the callback function
lua_rawgeti(L, LUA_REGISTRYINDEX, data->self_ref); // pass itself, #1 callback argument
lua_pushstring(L, message); // #2 callback argument
lua_pushlstring(L, message, len); // #2 callback argument
lua_pushnumber(L, opCode); // #3 callback argument
lua_call(L, 3, 0);
}
@ -102,6 +103,7 @@ static int websocket_createClient(lua_State *L) {
ws_info *ws = (ws_info *) lua_newuserdata(L, sizeof(ws_info));
ws->connectionState = 0;
ws->extraHeaders = NULL;
ws->onConnection = &websocketclient_onConnectionCallback;
ws->onReceive = &websocketclient_onReceiveCallback;
ws->onFailure = &websocketclient_onCloseCallback;
@ -118,7 +120,6 @@ static int websocketclient_on(lua_State *L) {
NODE_DBG("websocketclient_on\n");
ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT);
luaL_argcheck(L, ws, 1, "Client websocket expected");
ws_data *data = (ws_data *) ws->reservedData;
@ -170,7 +171,6 @@ static int websocketclient_connect(lua_State *L) {
NODE_DBG("websocketclient_connect is called.\n");
ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT);
luaL_argcheck(L, ws, 1, "Client websocket expected");
ws_data *data = (ws_data *) ws->reservedData;
@ -188,11 +188,61 @@ static int websocketclient_connect(lua_State *L) {
return 0;
}
static header_t *realloc_headers(header_t *headers, int new_size) {
if(headers) {
for(header_t *header = headers; header->key; header++) {
c_free(header->value);
c_free(header->key);
}
c_free(headers);
}
if(!new_size)
return NULL;
return (header_t *)c_malloc(sizeof(header_t) * (new_size + 1));
}
static int websocketclient_config(lua_State *L) {
NODE_DBG("websocketclient_config is called.\n");
ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT);
ws_data *data = (ws_data *) ws->reservedData;
luaL_checktype(L, 2, LUA_TTABLE);
lua_getfield(L, 2, "headers");
if(lua_istable(L, -1)) {
lua_pushnil(L);
int size = 0;
while(lua_next(L, -2)) {
size++;
lua_pop(L, 1);
}
ws->extraHeaders = realloc_headers(ws->extraHeaders, size);
if(ws->extraHeaders) {
header_t *header = ws->extraHeaders;
lua_pushnil(L);
while(lua_next(L, -2)) {
header->key = c_strdup(lua_tostring(L, -2));
header->value = c_strdup(lua_tostring(L, -1));
header++;
lua_pop(L, 1);
}
header->key = header->value = NULL;
}
}
lua_pop(L, 1); // pop headers
return 0;
}
static int websocketclient_send(lua_State *L) {
NODE_DBG("websocketclient_send is called.\n");
ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT);
luaL_argcheck(L, ws, 1, "Client websocket expected");
ws_data *data = (ws_data *) ws->reservedData;
@ -225,7 +275,8 @@ static int websocketclient_gc(lua_State *L) {
NODE_DBG("websocketclient_gc\n");
ws_info *ws = (ws_info *) luaL_checkudata(L, 1, METATABLE_WSCLIENT);
luaL_argcheck(L, ws, 1, "Client websocket expected");
ws->extraHeaders = realloc_headers(ws->extraHeaders, 0);
ws_data *data = (ws_data *) ws->reservedData;
@ -265,6 +316,7 @@ static const LUA_REG_TYPE websocket_map[] =
static const LUA_REG_TYPE websocketclient_map[] =
{
{ LSTRKEY("on"), LFUNCVAL(websocketclient_on) },
{ LSTRKEY("config"), LFUNCVAL(websocketclient_config) },
{ LSTRKEY("connect"), LFUNCVAL(websocketclient_connect) },
{ LSTRKEY("send"), LFUNCVAL(websocketclient_send) },
{ LSTRKEY("close"), LFUNCVAL(websocketclient_close) },

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,9 @@
#include "c_stdio.h"
#include "task/task.h"
//#define WIFI_DEBUG
//#define EVENT_DEBUG
void wifi_add_sprintf_field(lua_State* L, char* name, char* string, ...);
void wifi_add_int_field(lua_State* L, char* name, lua_Integer integer);
@ -37,7 +40,13 @@ static inline void unregister_lua_cb(lua_State* L, int* cb_ref)
void wifi_change_default_host_name(void);
#ifdef NODE_DEBUG
#if defined(WIFI_DEBUG) || defined(NODE_DEBUG)
#define WIFI_DBG(...) c_printf(__VA_ARGS__)
#else
#define WIFI_DBG(...) //c_printf(__VA_ARGS__)
#endif
#if defined(EVENT_DEBUG) || defined(NODE_DEBUG)
#define EVENT_DBG(...) c_printf(__VA_ARGS__)
#else
#define EVENT_DBG(...) //c_printf(__VA_ARGS__)

View File

@ -19,7 +19,6 @@
typedef struct {
int canary;
int size;
uint8_t colorsPerLed;
uint8_t values[0];
@ -132,8 +131,7 @@ static int ws2812_write(lua_State* L) {
}
else if (type == LUA_TUSERDATA)
{
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
buffer1 = buffer->values;
length1 = buffer->colorsPerLed*buffer->size;
@ -156,8 +154,7 @@ static int ws2812_write(lua_State* L) {
}
else if (type == LUA_TUSERDATA)
{
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 2);
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 2, "ws2812.buffer expected");
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer");
buffer2 = buffer->values;
length2 = buffer->colorsPerLed*buffer->size;
@ -173,14 +170,13 @@ static int ws2812_write(lua_State* L) {
return 0;
}
// Handle a buffer where we can store led values
static int ws2812_new_buffer(lua_State *L) {
const int leds = luaL_checkint(L, 1);
const int colorsPerLed = luaL_checkint(L, 2);
luaL_argcheck(L, leds > 0, 1, "should be a positive integer");
luaL_argcheck(L, colorsPerLed > 0, 2, "should be a positive integer");
static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) {
/* relative string position: negative means back from end */
if (pos < 0) pos += (ptrdiff_t)len + 1;
return (pos >= 0) ? pos : 0;
}
static ws2812_buffer *allocate_buffer(lua_State *L, int leds, int colorsPerLed) {
// Allocate memory
size_t size = sizeof(ws2812_buffer) + colorsPerLed*leds*sizeof(uint8_t);
ws2812_buffer * buffer = (ws2812_buffer*)lua_newuserdata(L, size);
@ -193,16 +189,26 @@ static int ws2812_new_buffer(lua_State *L) {
buffer->size = leds;
buffer->colorsPerLed = colorsPerLed;
// Store canary for future type checks
buffer->canary = CANARY_VALUE;
return buffer;
}
// Handle a buffer where we can store led values
static int ws2812_new_buffer(lua_State *L) {
const int leds = luaL_checkint(L, 1);
const int colorsPerLed = luaL_checkint(L, 2);
luaL_argcheck(L, leds > 0, 1, "should be a positive integer");
luaL_argcheck(L, colorsPerLed > 0, 2, "should be a positive integer");
ws2812_buffer * buffer = allocate_buffer(L, leds, colorsPerLed);
c_memset(buffer->values, 0, colorsPerLed * leds);
return 1;
}
static int ws2812_buffer_fill(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
// Grab colors
int i, j;
@ -230,11 +236,10 @@ static int ws2812_buffer_fill(lua_State* L) {
}
static int ws2812_buffer_fade(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
const int fade = luaL_checkinteger(L, 2);
unsigned direction = luaL_optinteger( L, 3, FADE_OUT );
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
luaL_argcheck(L, fade > 0, 2, "fade value should be a strict positive number");
uint8_t * p = &buffer->values[0];
@ -260,17 +265,25 @@ static int ws2812_buffer_fade(lua_State* L) {
static int ws2812_buffer_shift(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
const int shiftValue = luaL_checkinteger(L, 2);
const unsigned shift_type = luaL_optinteger( L, 3, SHIFT_LOGICAL );
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
luaL_argcheck(L, shiftValue > 0-buffer->size && shiftValue < buffer->size, 2, "shifting more elements than buffer size");
ptrdiff_t start = posrelat(luaL_optinteger(L, 4, 1), buffer->size);
ptrdiff_t end = posrelat(luaL_optinteger(L, 5, -1), buffer->size);
if (start < 1) start = 1;
if (end > (ptrdiff_t)buffer->size) end = (ptrdiff_t)buffer->size;
start--;
int size = end - start;
size_t offset = start * buffer->colorsPerLed;
luaL_argcheck(L, shiftValue > 0-size && shiftValue < size, 2, "shifting more elements than buffer size");
int shift = shiftValue >= 0 ? shiftValue : -shiftValue;
// check if we want to shift at all
if (shift == 0)
if (shift == 0 || size <= 0)
{
return 0;
}
@ -280,38 +293,38 @@ static int ws2812_buffer_shift(lua_State* L) {
size_t shift_len, remaining_len;
// calculate length of shift section and remaining section
shift_len = shift*buffer->colorsPerLed;
remaining_len = (buffer->size-shift)*buffer->colorsPerLed;
remaining_len = (size-shift)*buffer->colorsPerLed;
if (shiftValue > 0)
{
// Store the values which are moved out of the array (last n pixels)
c_memcpy(tmp_pixels, &buffer->values[(buffer->size-shift)*buffer->colorsPerLed], shift_len);
c_memcpy(tmp_pixels, &buffer->values[offset + (size-shift)*buffer->colorsPerLed], shift_len);
// Move pixels to end
os_memmove(&buffer->values[shift*buffer->colorsPerLed], &buffer->values[0], remaining_len);
os_memmove(&buffer->values[offset + shift*buffer->colorsPerLed], &buffer->values[offset], remaining_len);
// Fill beginning with temp data
if (shift_type == SHIFT_LOGICAL)
{
c_memset(&buffer->values[0], 0, shift_len);
c_memset(&buffer->values[offset], 0, shift_len);
}
else
{
c_memcpy(&buffer->values[0], tmp_pixels, shift_len);
c_memcpy(&buffer->values[offset], tmp_pixels, shift_len);
}
}
else
{
// Store the values which are moved out of the array (last n pixels)
c_memcpy(tmp_pixels, &buffer->values[0], shift_len);
c_memcpy(tmp_pixels, &buffer->values[offset], shift_len);
// Move pixels to end
os_memmove(&buffer->values[0], &buffer->values[shift*buffer->colorsPerLed], remaining_len);
os_memmove(&buffer->values[offset], &buffer->values[offset + shift*buffer->colorsPerLed], remaining_len);
// Fill beginning with temp data
if (shift_type == SHIFT_LOGICAL)
{
c_memset(&buffer->values[(buffer->size-shift)*buffer->colorsPerLed], 0, shift_len);
c_memset(&buffer->values[offset + (size-shift)*buffer->colorsPerLed], 0, shift_len);
}
else
{
c_memcpy(&buffer->values[(buffer->size-shift)*buffer->colorsPerLed], tmp_pixels, shift_len);
c_memcpy(&buffer->values[offset + (size-shift)*buffer->colorsPerLed], tmp_pixels, shift_len);
}
}
// Free memory
@ -320,13 +333,107 @@ static int ws2812_buffer_shift(lua_State* L) {
return 0;
}
static int ws2812_buffer_dump(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
lua_pushlstring(L, buffer->values, buffer->size * buffer->colorsPerLed);
return 1;
}
static int ws2812_buffer_replace(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
size_t l = buffer->size;
ptrdiff_t start = posrelat(luaL_optinteger(L, 3, 1), l);
uint8_t *src;
size_t srcLen;
if (lua_type(L, 2) == LUA_TSTRING) {
size_t length;
src = (uint8_t *) lua_tolstring(L, 2, &length);
srcLen = length / buffer->colorsPerLed;
} else {
ws2812_buffer * rhs = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer");
src = rhs->values;
srcLen = rhs->size;
luaL_argcheck(L, rhs->colorsPerLed == buffer->colorsPerLed, 2, "Buffers have different colors");
}
luaL_argcheck(L, srcLen + start - 1 <= buffer->size, 2, "Does not fit into destination");
c_memcpy(buffer->values + (start - 1) * buffer->colorsPerLed, src, srcLen * buffer->colorsPerLed);
return 0;
}
// buffer:mix(factor1, buffer1, ..)
// factor is 256 for 100%
// uses saturating arithmetic (one buffer at a time)
static int ws2812_buffer_mix(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
int pos = 2;
size_t cells = buffer->size * buffer->colorsPerLed;
int n_sources = (lua_gettop(L) - 1) / 2;
struct {
int factor;
const uint8_t *values;
} source[n_sources];
int src;
for (src = 0; src < n_sources; src++, pos += 2) {
int factor = luaL_checkinteger(L, pos);
ws2812_buffer *src_buffer = (ws2812_buffer*) luaL_checkudata(L, pos + 1, "ws2812.buffer");
luaL_argcheck(L, src_buffer->size == buffer->size && src_buffer->colorsPerLed == buffer->colorsPerLed, pos + 1, "Buffer not same shape");
source[src].factor = factor;
source[src].values = src_buffer->values;
}
size_t i;
for (i = 0; i < cells; i++) {
int val = 0;
for (src = 0; src < n_sources; src++) {
val += ((int)(source[src].values[i] * source[src].factor) >> 8);
}
if (val < 0) {
val = 0;
} else if (val > 255) {
val = 255;
}
buffer->values[i] = val;
}
return 0;
}
// Returns the total of all channels
static int ws2812_buffer_power(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
size_t cells = buffer->size * buffer->colorsPerLed;
size_t i;
int total = 0;
for (i = 0; i < cells; i++) {
total += buffer->values[i];
}
lua_pushnumber(L, total);
return 1;
}
static int ws2812_buffer_get(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
const int led = luaL_checkinteger(L, 2) - 1;
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range");
int i;
@ -339,10 +446,9 @@ static int ws2812_buffer_get(lua_State* L) {
}
static int ws2812_buffer_set(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
const int led = luaL_checkinteger(L, 2) - 1;
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range");
int type = lua_type(L, 3);
@ -387,24 +493,94 @@ static int ws2812_buffer_set(lua_State* L) {
}
static int ws2812_buffer_size(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
lua_pushnumber(L, buffer->size);
return 1;
}
static int ws2812_buffer_sub(lua_State* L) {
ws2812_buffer * lhs = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
size_t l = lhs->size;
ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l);
ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l);
if (start < 1) start = 1;
if (end > (ptrdiff_t)l) end = (ptrdiff_t)l;
if (start <= end) {
ws2812_buffer *result = allocate_buffer(L, end - start + 1, lhs->colorsPerLed);
c_memcpy(result->values, lhs->values + lhs->colorsPerLed * (start - 1), lhs->colorsPerLed * (end - start + 1));
} else {
ws2812_buffer *result = allocate_buffer(L, 0, lhs->colorsPerLed);
}
return 1;
}
static int ws2812_buffer_concat(lua_State* L) {
ws2812_buffer * lhs = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
ws2812_buffer * rhs = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer");
luaL_argcheck(L, lhs->colorsPerLed == rhs->colorsPerLed, 1, "Can only concatenate buffers with same colors");
int colorsPerLed = lhs->colorsPerLed;
int leds = lhs->size + rhs->size;
ws2812_buffer * buffer = allocate_buffer(L, leds, colorsPerLed);
c_memcpy(buffer->values, lhs->values, lhs->colorsPerLed * lhs->size);
c_memcpy(buffer->values + lhs->colorsPerLed * lhs->size, rhs->values, rhs->colorsPerLed * rhs->size);
return 1;
}
static int ws2812_buffer_tostring(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
luaL_Buffer result;
luaL_buffinit(L, &result);
luaL_addchar(&result, '[');
int i;
int p = 0;
for (i = 0; i < buffer->size; i++) {
int j;
if (i > 0) {
luaL_addchar(&result, ',');
}
luaL_addchar(&result, '(');
for (j = 0; j < buffer->colorsPerLed; j++, p++) {
if (j > 0) {
luaL_addchar(&result, ',');
}
char numbuf[5];
c_sprintf(numbuf, "%d", buffer->values[p]);
luaL_addstring(&result, numbuf);
}
luaL_addchar(&result, ')');
}
luaL_addchar(&result, ']');
luaL_pushresult(&result);
return 1;
}
static const LUA_REG_TYPE ws2812_buffer_map[] =
{
{ LSTRKEY( "dump" ), LFUNCVAL( ws2812_buffer_dump )},
{ LSTRKEY( "fade" ), LFUNCVAL( ws2812_buffer_fade )},
{ LSTRKEY( "fill" ), LFUNCVAL( ws2812_buffer_fill )},
{ LSTRKEY( "get" ), LFUNCVAL( ws2812_buffer_get )},
{ LSTRKEY( "replace" ), LFUNCVAL( ws2812_buffer_replace )},
{ LSTRKEY( "mix" ), LFUNCVAL( ws2812_buffer_mix )},
{ LSTRKEY( "power" ), LFUNCVAL( ws2812_buffer_power )},
{ LSTRKEY( "set" ), LFUNCVAL( ws2812_buffer_set )},
{ LSTRKEY( "size" ), LFUNCVAL( ws2812_buffer_size )},
{ LSTRKEY( "shift" ), LFUNCVAL( ws2812_buffer_shift )},
{ LSTRKEY( "size" ), LFUNCVAL( ws2812_buffer_size )},
{ LSTRKEY( "sub" ), LFUNCVAL( ws2812_buffer_sub )},
{ LSTRKEY( "__concat" ),LFUNCVAL( ws2812_buffer_concat )},
{ LSTRKEY( "__index" ), LROVAL( ws2812_buffer_map )},
{ LSTRKEY( "__tostring" ), LFUNCVAL( ws2812_buffer_tostring )},
{ LNILKEY, LNILVAL}
};

View File

@ -52,7 +52,7 @@
#include "nodemcu_mdns.h"
#if 0
#define MDNS_DBG(...) os_printf(...)
#define MDNS_DBG(...) dbg_printf(...)
#else
#define MDNS_DBG(...) do {} while (0)
#endif

View File

@ -355,11 +355,12 @@ sint32_t vfs_chdir( const char *path )
sint32_t vfs_errno( const char *name )
{
vfs_fs_fns *fs_fns;
const char *normname = normalize_path( name );
char *outname;
if (!name) name = ""; // current drive
const char *normname = normalize_path( name );
#ifdef BUILD_SPIFFS
if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) {
return fs_fns->ferrno( );

View File

@ -85,7 +85,7 @@ inline sint32_t vfs_flush( int fd ) {
// Returns: File size
inline uint32_t vfs_size( int fd ) {
vfs_file *f = (vfs_file *)fd;
return f && f->fns->size ? f->fns->size( f ) : 0;
return f ? f->fns->size( f ) : 0;
}
// vfs_ferrno - get file system specific errno

View File

@ -2,6 +2,8 @@
#include "platform.h"
#include "spiffs.h"
#include "spiffs_nucleus.h"
spiffs fs;
#define LOG_PAGE_SIZE 256
@ -10,9 +12,9 @@ spiffs fs;
#define MIN_BLOCKS_FS 4
static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
static u8_t spiffs_fds[32*4];
static u8_t spiffs_fds[sizeof(spiffs_fd) * SPIFFS_MAX_OPEN_FILES];
#if SPIFFS_CACHE
static u8_t spiffs_cache[(LOG_PAGE_SIZE+32)*2];
static u8_t myspiffs_cache[(LOG_PAGE_SIZE+32)*2];
#endif
static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) {
@ -168,8 +170,8 @@ static bool myspiffs_mount_internal(bool force_mount) {
spiffs_fds,
sizeof(spiffs_fds),
#if SPIFFS_CACHE
spiffs_cache,
sizeof(spiffs_cache),
myspiffs_cache,
sizeof(myspiffs_cache),
#else
0, 0,
#endif
@ -243,6 +245,7 @@ static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int
static sint32_t myspiffs_vfs_eof( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_tell( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_flush( const struct vfs_file *fd );
static uint32_t myspiffs_vfs_size( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd );
static sint32_t myspiffs_vfs_closedir( const struct vfs_dir *dd );
@ -295,7 +298,7 @@ static vfs_file_fns myspiffs_file_fns = {
.eof = myspiffs_vfs_eof,
.tell = myspiffs_vfs_tell,
.flush = myspiffs_vfs_flush,
.size = NULL,
.size = myspiffs_vfs_size,
.ferrno = myspiffs_vfs_ferrno
};
@ -430,13 +433,17 @@ static sint32_t myspiffs_vfs_close( const struct vfs_file *fd ) {
static sint32_t myspiffs_vfs_read( const struct vfs_file *fd, void *ptr, size_t len ) {
GET_FILE_FH(fd);
return SPIFFS_read( &fs, fh, ptr, len );
sint32_t n = SPIFFS_read( &fs, fh, ptr, len );
return n >= 0 ? n : VFS_RES_ERR;
}
static sint32_t myspiffs_vfs_write( const struct vfs_file *fd, const void *ptr, size_t len ) {
GET_FILE_FH(fd);
return SPIFFS_write( &fs, fh, (void *)ptr, len );
sint32_t n = SPIFFS_write( &fs, fh, (void *)ptr, len );
return n >= 0 ? n : VFS_RES_ERR;
}
static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int whence ) {
@ -456,7 +463,8 @@ static sint32_t myspiffs_vfs_lseek( const struct vfs_file *fd, sint32_t off, int
break;
}
return SPIFFS_lseek( &fs, fh, off, spiffs_whence );
sint32_t res = SPIFFS_lseek( &fs, fh, off, spiffs_whence );
return res >= 0 ? res : VFS_RES_ERR;
}
static sint32_t myspiffs_vfs_eof( const struct vfs_file *fd ) {
@ -477,6 +485,16 @@ static sint32_t myspiffs_vfs_flush( const struct vfs_file *fd ) {
return SPIFFS_fflush( &fs, fh ) >= 0 ? VFS_RES_OK : VFS_RES_ERR;
}
static uint32_t myspiffs_vfs_size( const struct vfs_file *fd ) {
GET_FILE_FH(fd);
int32_t curpos = SPIFFS_tell( &fs, fh );
int32_t size = SPIFFS_lseek( &fs, fh, 0, SPIFFS_SEEK_END );
(void) SPIFFS_lseek( &fs, fh, curpos, SPIFFS_SEEK_SET );
return size;
}
static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd ) {
return SPIFFS_errno( &fs );
}

View File

@ -20,19 +20,19 @@
// Set generic spiffs debug output call.
#ifndef SPIFFS_DBG
#define SPIFFS_DBG(...) //printf(__VA_ARGS__)
#define SPIFFS_DBG(...) //dbg_printf(__VA_ARGS__)
#endif
// Set spiffs debug output call for garbage collecting.
#ifndef SPIFFS_GC_DBG
#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__)
#define SPIFFS_GC_DBG(...) //dbg_printf(__VA_ARGS__)
#endif
// Set spiffs debug output call for caching.
#ifndef SPIFFS_CACHE_DBG
#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__)
#define SPIFFS_CACHE_DBG(...) //dbg_printf(__VA_ARGS__)
#endif
// Set spiffs debug output call for system consistency checks.
#ifndef SPIFFS_CHECK_DBG
#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__)
#define SPIFFS_CHECK_DBG(...) //dbg_printf(__VA_ARGS__)
#endif
// Enable/disable API functions to determine exact number of bytes
@ -211,7 +211,7 @@
#endif
#if SPIFFS_TEST_VISUALISATION
#ifndef spiffs_printf
#define spiffs_printf(...) printf(__VA_ARGS__)
#define spiffs_printf(...) dbg_printf(__VA_ARGS__)
#endif
// spiffs_printf argument for a free page
#ifndef SPIFFS_TEST_VIS_FREE_STR

View File

@ -292,27 +292,60 @@ s32_t spiffs_probe(
SPIFFS_CHECK_RES(res);
}
// check that we have sane number of blocks
if (bix_count[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS;
// check that the order is correct, take aborted erases in calculation
// Note that bix_count[0] should be blockcnt, [1] should be blockcnt - 1
// and [2] should be blockcnt - 3
// first block aborted erase
if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) {
return (bix_count[1]+1) * cfg->log_block_size;
}
int fs_size;
if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 2) {
fs_size = bix_count[1]+1;
} else
// second block aborted erase
if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) {
return bix_count[0] * cfg->log_block_size;
}
if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 3) {
fs_size = bix_count[0];
} else
// third block aborted erase
if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) {
return bix_count[0] * cfg->log_block_size;
}
fs_size = bix_count[0];
} else
// no block has aborted erase
if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) {
return bix_count[0] * cfg->log_block_size;
if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 2) {
fs_size = bix_count[0];
} else {
return SPIFFS_ERR_PROBE_NOT_A_FS;
}
return SPIFFS_ERR_PROBE_NOT_A_FS;
// check that we have sane number of blocks
if (fs_size < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS;
dummy_fs.block_count = fs_size;
// Now verify that there is at least one good block at the end
for (bix = fs_size - 1; bix >= 3; bix--) {
spiffs_obj_id end_magic;
paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix);
#if SPIFFS_HAL_CALLBACK_EXTRA
// not any proper fs to report here, so callback with null
// (cross fingers that no-one gets angry)
res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&end_magic);
#else
res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&end_magic);
#endif
if (res < 0) {
return SPIFFS_ERR_PROBE_NOT_A_FS;
}
if (end_magic == (spiffs_obj_id)(-1)) {
if (bix < fs_size - 1) {
return SPIFFS_ERR_PROBE_NOT_A_FS;
}
} else if (end_magic != SPIFFS_MAGIC(&dummy_fs, bix)) {
return SPIFFS_ERR_PROBE_NOT_A_FS;
} else {
break;
}
}
return fs_size * cfg->log_block_size;
}
#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0

View File

@ -137,7 +137,7 @@
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs)))
#else // SPIFFS_USE_MAGIC_LENGTH
#define SPIFFS_MAGIC(fs, bix) \
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix))))
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - ((bix) < 3 ? (1<<(bix)) - 1 : (bix)<<2))))
#endif // SPIFFS_USE_MAGIC_LENGTH
#endif // SPIFFS_USE_MAGIC

View File

@ -1178,9 +1178,6 @@ struct _u8g_t
u8g_state_cb state_cb;
u8g_box_t current_page; /* current box of the visible page */
uint8_t i2c_addr;
uint8_t use_delay;
};
#define u8g_GetFontAscent(u8g) ((u8g)->font_ref_ascent)

View File

@ -24,7 +24,7 @@ STD_CFLAGS=-std=gnu11 -Wimplicit
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
DEFINES += -DESP_INIT_DATA_DEFAULT="\"$(SDK_DIR)/bin/esp_init_data_default.bin\""
#############################################################
# Recursion Magic - Don't touch this!!

View File

@ -29,11 +29,21 @@
#include "rtc/rtctime.h"
#endif
#define SIG_LUA 0
#define SIG_UARTINPUT 1
#define TASK_QUEUE_LEN 4
static task_handle_t input_sig;
static uint8 input_sig_flag = 0;
static os_event_t *taskQueue;
/* Contents of esp_init_data_default.bin */
extern const uint32_t init_data[];
extern const uint32_t init_data_end[];
__asm__(
/* Place in .text for same reason as user_start_trampoline */
".section \".text\"\n"
".align 4\n"
"init_data:\n"
".incbin \"" ESP_INIT_DATA_DEFAULT "\"\n"
"init_data_end:\n"
".previous\n"
);
/* Note: the trampoline *must* be explicitly put into the .text segment, since
* by the time it is invoked the irom has not yet been mapped. This naturally
@ -50,6 +60,31 @@ void TEXT_SECTION_ATTR user_start_trampoline (void)
rtctime_early_startup ();
#endif
/* Re-implementation of default init data deployment. The SDK does not
* appear to be laying down its own version of init data anymore, so
* we have to do it again. To see whether we need to, we read out
* the flash size and do a test for esp_init_data based on that size.
* If it's missing, we need to initialize it *right now* before the SDK
* starts up and gets stuck at "rf_cal[0] !=0x05,is 0xFF".
* If the size byte is wrong, then we'll end up fixing up the init data
* again on the next boot, after we've corrected the size byte.
* Only remaining issue is lack of spare code bytes in iram, so this
* is deliberately quite terse and not as readable as one might like.
*/
SPIFlashInfo sfi;
SPIRead (0, (uint32_t *)(&sfi), sizeof (sfi)); // Cache read not enabled yet, safe to use
if (sfi.size < 2) // Compensate for out-of-order 4mbit vs 2mbit values
sfi.size ^= 1;
uint32_t flash_end_addr = (256 * 1024) << sfi.size;
uint32_t init_data_hdr = 0xffffffff;
uint32_t init_data_addr = flash_end_addr - 4 * SPI_FLASH_SEC_SIZE;
SPIRead (init_data_addr, &init_data_hdr, sizeof (init_data_hdr));
if (init_data_hdr == 0xffffffff)
{
SPIEraseSector (init_data_addr);
SPIWrite (init_data_addr, init_data, 4 * (init_data_end - init_data));
}
call_user_start ();
}
@ -58,17 +93,18 @@ static void start_lua(task_param_t param, uint8 priority) {
char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL };
NODE_DBG("Task task_lua started.\n");
lua_main( 2, lua_argv );
// Only enable UART interrupts once we've successfully started up,
// otherwise the task queue might fill up with input events and prevent
// the start_lua task from being posted.
ETS_UART_INTR_ENABLE();
}
static void handle_input(task_param_t flag, uint8 priority) {
// c_printf("HANDLE_INPUT: %u %u\n", flag, priority); REMOVE
lua_handle_input (flag);
}
static task_handle_t input_sig;
task_handle_t user_get_input_sig(void) {
return input_sig;
(void)priority;
if (flag & 0x8000) {
input_sig_flag = flag & 0x4000 ? 1 : 0;
}
lua_handle_input (flag & 0x01);
}
bool user_process_input(bool force) {
@ -92,9 +128,7 @@ void nodemcu_init(void)
// Fit hardware real flash size.
flash_rom_set_size_byte(flash_safe_get_size_byte());
// Reboot to get SDK to use (or write) init data at new location
system_restart ();
// Don't post the start_lua task, we're about to reboot...
return;
}
@ -107,7 +141,7 @@ void nodemcu_init(void)
#ifdef BUILD_SPIFFS
if (!vfs_mount("/FLASH", 0)) {
// Failed to mount -- try reformat
c_printf("Formatting file system. Please wait...\n");
dbg_printf("Formatting file system. Please wait...\n");
if (!vfs_format()) {
NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" );
NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" );
@ -118,7 +152,8 @@ void nodemcu_init(void)
#endif
// endpoint_setup();
task_post_low(task_get_id(start_lua),'s');
if (!task_post_low(task_get_id(start_lua),'s'))
NODE_ERR("Failed to post the start_lua task!\n");
}
#ifdef LUA_USE_MODULES_WIFI
@ -146,7 +181,7 @@ void user_rf_pre_init(void)
uint32
user_rf_cal_sector_set(void)
{
enum flash_size_map size_map = system_get_flash_size_map();
enum ext_flash_size_map size_map = system_get_flash_size_map();
uint32 rf_cal_sec = 0;
switch (size_map) {
@ -165,9 +200,18 @@ user_rf_cal_sector_set(void)
case FLASH_SIZE_32M_MAP_512_512:
case FLASH_SIZE_32M_MAP_1024_1024:
case FLASH_SIZE_32M_MAP_2048_2048:
rf_cal_sec = 1024 - 5;
break;
case FLASH_SIZE_64M_MAP:
rf_cal_sec = 2048 - 5;
break;
case FLASH_SIZE_128M_MAP:
rf_cal_sec = 4096 - 5;
break;
default:
rf_cal_sec = 0;
break;
@ -191,7 +235,7 @@ void user_init(void)
UartBautRate br = BIT_RATE_DEFAULT;
input_sig = task_get_id(handle_input);
uart_init (br, br, input_sig);
uart_init (br, br, input_sig, &input_sig_flag);
#ifndef NODE_DEBUG
system_set_os_print(0);

View File

@ -47,18 +47,10 @@
#define PORT_INSECURE 80
#define PORT_MAX_VALUE 65535
// TODO: user agent configurable
#define WS_INIT_HEADERS "GET %s HTTP/1.1\r\n"\
"Host: %s:%d\r\n"\
"Upgrade: websocket\r\n"\
"Connection: Upgrade\r\n"\
"User-Agent: ESP8266\r\n"\
"Sec-Websocket-Key: %s\r\n"\
"Sec-WebSocket-Protocol: chat\r\n"\
"Sec-WebSocket-Version: 13\r\n"\
"\r\n"
#define WS_INIT_REQUEST "GET %s HTTP/1.1\r\n"\
"Host: %s:%d\r\n"
#define WS_INIT_HEADERS_LENGTH 169
#define WS_INIT_REQUEST_LENGTH 30
#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
#define WS_GUID_LENGTH 36
@ -77,6 +69,13 @@
#define WS_OPCODE_PING 0x9
#define WS_OPCODE_PONG 0xA
header_t DEFAULT_HEADERS[] = {
{"User-Agent", "ESP8266"},
{"Sec-WebSocket-Protocol", "chat"},
{0}
};
header_t *EMPTY_HEADERS = DEFAULT_HEADERS + sizeof(DEFAULT_HEADERS) / sizeof(header_t) - 1;
static char *cryptoSha1(char *data, unsigned int len) {
SHA1_CTX ctx;
SHA1Init(&ctx);
@ -128,6 +127,44 @@ static void generateSecKeys(char **key, char **expectedKey) {
os_free(keyEncrypted);
}
static char *_strcpy(char *dst, char *src) {
while(*dst++ = *src++);
return dst - 1;
}
static int headers_length(header_t *headers) {
int length = 0;
for(; headers->key; headers++)
length += strlen(headers->key) + strlen(headers->value) + 4;
return length;
}
static char *sprintf_headers(char *buf, ...) {
char *dst = buf;
va_list args;
va_start(args, buf);
for(header_t *header_set = va_arg(args, header_t *); header_set; header_set = va_arg(args, header_t *))
for(header_t *header = header_set; header->key; header++) {
va_list args2;
va_start(args2, buf);
for(header_t *header_set2 = va_arg(args2, header_t *); header_set2; header_set2 = va_arg(args2, header_t *))
for(header_t *header2 = header_set2; header2->key; header2++) {
if(header == header2)
goto ok;
if(!strcasecmp(header->key, header2->key))
goto skip;
}
ok:
dst = _strcpy(dst, header->key);
dst = _strcpy(dst, ": ");
dst = _strcpy(dst, header->value);
dst = _strcpy(dst, "\r\n");
skip:;
}
dst = _strcpy(dst, "\r\n");
return dst;
}
static void ws_closeSentCallback(void *arg) {
NODE_DBG("ws_closeSentCallback \n");
struct espconn *conn = (struct espconn *) arg;
@ -452,7 +489,7 @@ static void ws_receiveCallback(void *arg, char *buf, unsigned short len) {
} else if (opCode == WS_OPCODE_PONG) {
// ping alarm was already reset...
} else {
if (ws->onReceive) ws->onReceive(ws, payload, opCode);
if (ws->onReceive) ws->onReceive(ws, payloadLength, payload, opCode);
}
os_free(payload);
}
@ -509,7 +546,7 @@ static void ws_initReceiveCallback(void *arg, char *buf, unsigned short len) {
}
// Check server has valid sec key
if (strstr(buf, WS_HTTP_SEC_WEBSOCKET_ACCEPT) == NULL || strstr(buf, ws->expectedSecKey) == NULL) {
if (strstr(buf, ws->expectedSecKey) == NULL) {
NODE_DBG("Server has invalid response\n");
ws->knownFailureCode = -7;
if (ws->isSecure)
@ -550,12 +587,31 @@ static void connect_callback(void *arg) {
char *key;
generateSecKeys(&key, &ws->expectedSecKey);
char buf[WS_INIT_HEADERS_LENGTH + strlen(ws->path) + strlen(ws->hostname) + strlen(key)];
int len = os_sprintf(buf, WS_INIT_HEADERS, ws->path, ws->hostname, ws->port, key);
header_t headers[] = {
{"Upgrade", "websocket"},
{"Connection", "Upgrade"},
{"Sec-WebSocket-Key", key},
{"Sec-WebSocket-Version", "13"},
{0}
};
header_t *extraHeaders = ws->extraHeaders ? ws->extraHeaders : EMPTY_HEADERS;
char buf[WS_INIT_REQUEST_LENGTH + strlen(ws->path) + strlen(ws->hostname) +
headers_length(DEFAULT_HEADERS) + headers_length(headers) + headers_length(extraHeaders) + 2];
int len = os_sprintf(
buf,
WS_INIT_REQUEST,
ws->path,
ws->hostname,
ws->port
);
len = sprintf_headers(buf + len, headers, extraHeaders, DEFAULT_HEADERS, 0) - buf;
os_free(key);
NODE_DBG("connecting\n");
NODE_DBG("request: %s", buf);
if (ws->isSecure)
espconn_secure_send(conn, (uint8_t *) buf, len);
else
@ -630,7 +686,7 @@ static void dns_callback(const char *hostname, ip_addr_t *addr, void *arg) {
ws_info *ws = (ws_info *) conn->reverse;
if (ws->conn == NULL || ws->connectionState == 4) {
return;
return;
}
if (addr == NULL) {

View File

@ -40,9 +40,14 @@
struct ws_info;
typedef void (*ws_onConnectionCallback)(struct ws_info *wsInfo);
typedef void (*ws_onReceiveCallback)(struct ws_info *wsInfo, char *message, int opCode);
typedef void (*ws_onReceiveCallback)(struct ws_info *wsInfo, int len, char *message, int opCode);
typedef void (*ws_onFailureCallback)(struct ws_info *wsInfo, int errorCode);
typedef struct {
char *key;
char *value;
} header_t;
typedef struct ws_info {
int connectionState;
@ -51,6 +56,7 @@ typedef struct ws_info {
int port;
char *path;
char *expectedSecKey;
header_t *extraHeaders;
struct espconn *conn;
void *reservedData;

View File

@ -1,13 +1,20 @@
Adafruit provides a really nice [firmware flashing tutorial](https://learn.adafruit.com/building-and-running-micropython-on-the-esp8266/flash-firmware). Below you'll find just the basics for the two popular tools esptool and NodeMCU Flasher.
Below you'll find all necessary information to flash a NodeMCU firmware binary to ESP8266 or ESP8285. Note that this is a reference documentation and not a tutorial with fancy screen shots. Turn to your favorite search engine for those. Make sure you follow a recent tutorial rather than one that is several months old!
!!! attention
Keep in mind that the ESP8266 needs to be [put into flash mode](#putting-device-into-flash-mode) before you can flash a new firmware!
## esptool.py
> A cute Python utility to communicate with the ROM bootloader in Espressif ESP8266. It is intended to be a simple, platform independent, open source replacement for XTCOM.
!!! important
Source: [https://github.com/themadinventor/esptool](https://github.com/themadinventor/esptool)
When switching between NodeMCU versions, see the notes about
[Upgrading Firmware](#upgrading-firmware).
## Tool overview
### esptool.py
> A Python-based, open source, platform independent, utility to communicate with the ROM bootloader in Espressif ESP8266.
Source: [https://github.com/espressif/esptool](https://github.com/espressif/esptool)
Supported platforms: OS X, Linux, Windows, anything that runs Python
@ -15,27 +22,33 @@ Supported platforms: OS X, Linux, Windows, anything that runs Python
Run the following command to flash an *aggregated* binary as is produced for example by the [cloud build service](build.md#cloud-build-service) or the [Docker image](build.md#docker-image).
`esptool.py --port <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).
- `size` is given in bits. Specify `4m` for 512&nbsp;kByte and `32m` for 4&nbsp;MByte.
`mode` is `qio` for 512&nbsp;kByte modules and `dio` for >=4&nbsp;MByte modules (`qio` might work as well, YMMV).
Check the [esptool flash modes documentation](https://github.com/themadinventor/esptool#flash-modes) for details and other options.
**Gotchas**
## NodeMCU Flasher
- See [below](#determine-flash-size) if you don't know or are uncertain about the capacity of the flash chip on your device. It might help to double check as e.g. some ESP-01 modules come with 512kB while others are equipped with 1MB.
- esptool.py is under heavy development. It's advised you run the latest version (check with `esptool.py version`). Since this documentation may not have been able to keep up refer to the [esptool flash modes documentation](https://github.com/themadinventor/esptool#flash-modes) for current options and parameters.
- In some uncommon cases, the [SDK init data](#sdk-init-data) may be invalid and NodeMCU may fail to boot. The easiest solution is to fully erase the chip before flashing:
`esptool.py --port <serial-port-of-ESP8266> erase_flash`
### NodeMCU Flasher
> A firmware Flash tool for NodeMCU...We are working on next version and will use QT framework. It will be cross platform and open-source.
Source: [https://github.com/nodemcu/nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher)
Supported platforms: Windows
Note that this tool was created by the initial developers of the NodeMCU firmware. It hasn't seen updates since September 2015 and is not maintained by the current NodeMCU *firmware* team. Be careful to not accidentally flash the very old default firmware the tool is shipped with.
## Putting Device Into Flash Mode
To enable ESP8266 firmware flashing GPIO0 pin must be pulled low before the device is reset. Conversely, for a normal boot, GPIO0 must be pulled high or floating.
If you have a [NodeMCU dev kit](https://github.com/nodemcu/nodemcu-devkit-v1.0) then you don't need to do anything, as the USB connection can pull GPIO0 low by asserting DTR and reset your board by asserting RTS.
If you have an ESP-01 or other device without built-in USB, you will need to enable flashing yourself by pulling GPIO0 low or pressing a "flash" switch.
If you have an ESP-01 or other device without built-in USB, you will need to enable flashing yourself by pulling GPIO0 low or pressing a "flash" switch, while powering up or resetting the module.
## Which Files To Flash
@ -46,55 +59,59 @@ Otherwise, if you built your own firmware from source code:
- `bin/0x00000.bin` to 0x00000
- `bin/0x10000.bin` to 0x10000
Also, in some special circumstances, you may need to flash `blank.bin` or `esp_init_data_default.bin` to various addresses on the flash (depending on flash size and type), see [below](#upgrading-from-sdk-09x-firmware).
## Upgrading Firmware
!!! important
There are three potential issues that arise from upgrading (or downgrading!) firmware from one NodeMCU version to another:
It goes without saying that you shouldn't expect your NodeMCU 0.9.x Lua scripts to work error-free on a more recent firmware. Most notably Espressif changed the `socket:send` operation to be asynchronous i.e. non-blocking. See [API documentation](modules/net.md#netsocketsend) for details.
* Lua scripts written for one NodeMCU version (like 0.9.x) may not work error-free on a more recent firmware. For example, Espressif changed the `socket:send` operation to be asynchronous i.e. non-blocking. See [API documentation](modules/net.md#netsocketsend) for details.
Espressif changes the init data block (`esp_init_data_default.bin`) for their devices along the way with the SDK. So things break when a NodeMCU firmware with a certain SDK is flashed to a module which contains init data from a different SDK. Hence, this section applies to upgrading NodeMCU firmware just as well as *downgrading* firmware.
* The NodeMCU flash file system may need to be reformatted, particularly if its address has changed because the new firmware is different in size from the old firmware. If it is not automatically formatted then it should be valid and have the same contents as before the flash operation. You can still run [`file.format()`](modules/file.md#fileformat) manually to re-format your flash file system. You will know if you need to do this if your flash files exist but seem empty, or if data cannot be written to new files. However, this should be an exceptional case.
Formatting a file system on a large flash device (e.g. the 16MB parts) can take some time. So, on the first boot, you shouldn't get worried if nothing appears to happen for a minute. There's a message printed to console to make you aware of this.
A typical case that often fails is when a module is upgraded from a 0.9.x firmware to a recent version. It might look like the new firmware is broken, but the reason for the missing Lua prompt is related to the big jump in SDK versions.
* The Espressif SDK Init Data may change between each NodeMCU firmware version, and may need to be erased or reflashed. See [SDK Init Data](#sdk-init-data) for details. Fully erasing the module before upgrading firmware will avoid this issue.
If there is no init data block found during SDK startup, the SDK will install one itself. If there is a previous (potentially too old) init block, the SDK *probably* doesn't do anything with it but there is no documentation from Espressif on this topic.
## SDK Init Data
Hence, there are two strategies to update the SDK init data:
!!! note
- Erase flash completely. This will also erase the (Lua) files you uploaded to the device! The SDK will install the init data block during startup.
- Don't erase the flash but replace just the init data with a new file during the flashing procedure. For this you would download [SDK patch 1.5.4.1](http://bbs.espressif.com/download/file.php?id=1572) and extract `esp_init_data_default.bin` from there.
Normally, NodeMCU will take care of writing the SDK init data when needed. Most users can ignore this section.
When flashing a new firmware (particularly with a much different size), the flash filesystem may be reformatted as the firmware starts. If it is not automatically reformatted, then it should be valid and have the same contents as before the flash operation. You can still run [`file.format()`](modules/file.md#fileformat) to re-format your flash filesystem. You will know if you need to do this if your flash files exist but seem empty, or if data cannot be written to new files. However, this should be an exceptional case.
NodeMCU versions are compiled against specific versions of the Espressif SDK. The SDK reserves space in flash that is used to store calibration and other data. This data changes between SDK versions, and if it is invalid or not present, the firmware may not boot correctly. Symptoms include messages like `rf_cal[0] !=0x05,is 0xFF`, or endless reboot loops and/or fast blinking module LEDs.
**esptool.py**
!!! tip
For [esptool.py](https://github.com/themadinventor/esptool) you specify the init data file as an additional file for the `write_flash` command.
If you are seeing one or several of the above symptoms, ensure that your chip is fully erased before flashing, for example:
`esptool.py --port <serial-port-of-ESP8266> erase_flash`
Also verify that you are using an up-to-date NodeMCU release, as some early releases of NodeMCU 1.5.4.1 did not write the SDK init data to a freshly erased chip.
Espressif refers to this area as "System Param" and it resides in the last four 4&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.
The default init data is provided as part of the SDK in the file `esp_init_data_default.bin`. NodeMCU will automatically flash this file to the right place on first boot if the sector appears to be empty.
If you need to customize init data then first download the [Espressif SDK patch 1.5.4.1](http://bbs.espressif.com/download/file.php?id=1572) and extract `esp_init_data_default.bin`. Then flash that file just like you'd flash the firmware. The correct address for the init data depends on the capacity of the flash chip.
- `0x7c000` for 512 kB, modules like most ESP-01, -03, -07 etc.
- `0xfc000` for 1 MB, modules like ESP8285, PSF-A85, some ESP-01, -03 etc.
- `0x1fc000` for 2 MB
- `0x3fc000` for 4 MB, modules like ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini
See "4.1 Non-FOTA Flash Map" and "6.3 RF Initialization Configuration" of the [ESP8266 Getting Started Guide](https://espressif.com/en/support/explore/get-started/esp8266/getting-started-guide) for details on init data addresses and customization.
## Determine flash size
To determine the capacity of the flash chip *before* a firmware is installed you can run
`esptool.py --port <serial-port> flash_id`
It will return a manufacturer ID and a chip ID like so:
```
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
Connecting...
Manufacturer: e0
Device: 4016
```
The chip ID can then be looked up in [https://code.coreboot.org/p/flashrom/source/tree/HEAD/trunk/flashchips.h](https://code.coreboot.org/p/flashrom/source/tree/HEAD/trunk/flashchips.h). This leads to a manufacturer name and a chip model name/number e.g. `AMIC_A25LQ032`. That information can then be fed into your favorite search engine to find chip descriptions and data sheets.
!!! note "Note:"
The address for `esp_init_data_default.bin` depends on the size of your module's flash.
- `0x7c000` for 512 kB, modules like ESP-01, -03, -07 etc.
- `0xfc000` for 1 MB, modules like ESP8285, PSF-A85
- `0x1fc000` for 2 MB
- `0x3fc000` for 4 MB, modules like ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini
**NodeMCU Flasher**
The [NodeMCU Flasher](https://github.com/nodemcu/nodemcu-flasher) will download init data using a special path:
```
INTERNAL://DEFAULT
```
Replace the provided (old) `esp_init_data_default.bin` with the one extracted above and use the flasher like you're used to.
**References**
* [2A-ESP8266__IOT_SDK_User_Manual__EN_v1.5.pdf, Chapter 6](http://bbs.espressif.com/viewtopic.php?f=51&t=1024)
* [SPI Flash ROM Layout (without OTA upgrades)](https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map#spi-flash-rom-layout-without-ota-upgrades)
By convention the last two or three digits in the module name denote the capacity in megabits. So, `A25LQ032` in the example above is a 32Mb(=4MB) module.

View File

@ -44,7 +44,7 @@ print(gpio.read(pin))
```
## Getting Started
1. [Build the firmeware](build.md) with the modules you need.
1. [Build the firmware](build.md) with the modules you need.
1. [Flash the firmware](flash.md) to the chip.
1. [Upload code](upload.md) to the firmware.

View File

@ -7,10 +7,29 @@ This module provides a simple way of configuring ESP8266 chips without using a s
![enduser setup config dialog](../../img/enduser-setup.jpg "enduser setup config dialog")
After running [`enduser_setup.start()`](#enduser_setupstart) a portal like the above can be accessed through a wireless network called SetupGadget_XXXXXX. The portal is used to submit the credentials for the WiFi of the enduser.
After an IP address has been successfully obtained this module will stop as if [`enduser_setup.stop()`](#enduser_setupstop) had been called.
After running [`enduser_setup.start()`](#enduser_setupstart), a wireless network named "SetupGadget_XXXXXX" will start. Connect to that SSID and then navigate to the root
of any website (e.g., `http://example.com/` will work, but do not use `.local` domains because it will fail on iOS). A web page similar to the picture above will load, allowing the
end user to provide their Wi-Fi information.
After an IP address has been successfully obtained, then this module will stop as if [`enduser_setup.stop()`](#enduser_setupstop) had been called. There is a 10-second delay before
teardown to allow connected clients to obtain a last status message while the SoftAP is still active.
Alternative HTML can be served by placing a file called `enduser_setup.html` on the filesystem. Everything needed by the web page must be included in this one file. This file will be kept
in RAM, so keep it as small as possible. The file can be gzip'd ahead of time to reduce the size (i.e., using `gzip -n` or `zopfli`), and when served, the End User Setup module will add
the appropriate `Content-Encoding` header to the response. *Note: Even if gzipped, the file still needs to be named `enduser_setup.html`.*
The following HTTP endpoints exist:
|Endpoint|Description|
|--------|-----------|
|/|Returns HTML for the web page. Will return the contents of `enduser_setup.html` if it exists on the filesystem, otherwise will return a page embedded into the firmware image.|
|/aplist|Forces the ESP8266 to perform a site survey across all channels, reporting access points that it can find. Return payload is a JSON array: `[{"ssid":"foobar","rssi":-36,"chan":3}]`|
|/generate_204|Returns a HTTP 204 status (expected by certain Android clients during Wi-Fi connectivity checks)|
|/status|Returns plaintext status description, used by the web page|
|/status.json|Returns a JSON payload containing the ESP8266's chip id in hexadecimal format and the status code: 0=Idle, 1=Connecting, 2=Wrong Password, 3=Network not Found, 4=Failed, 5=Success|
|/setwifi|Endpoint intended for services to use for setting the wifi credentials. Identical to `/update` except returns the same payload as `/status.json` instead of redirecting to `/`.|
|/update|Form submission target. Example: `http://example.com/update?wifi_ssid=foobar&wifi_password=CorrectHorseBatteryStaple`. Must be a GET request. Will redirect to `/` when complete. |
Alternative HTML can be served by placing a file called `enduser_setup.html` in the filesystem. This file will be kept in RAM, so keep it as small as possible.
## enduser_setup.manual()
@ -53,7 +72,7 @@ Starts the captive portal.
#### Parameters
- `onConnected()` callback will be fired when an IP-address has been obtained, just before the enduser_setup module will terminate itself
- `onError()` callback will be fired if an error is encountered. `err_num` is a number describing the error, and `string` contains a description of the error.
- `onDebug()` callback is disabled by default. It is intended to be used to find internal issues in the module. `string` contains a description of what is going on.
- `onDebug()` callback is disabled by default (controlled by `#define ENDUSER_SETUP_DEBUG_ENABLE` in `enduser_setup.c`). It is intended to be used to find internal issues in the module. `string` contains a description of what is going on.
#### Returns
`nil`

View File

@ -7,8 +7,6 @@ The file module provides access to the file system and its individual files.
The file system is a flat file system, with no notion of subdirectories/folders.
Only one file can be open at any given time.
Besides the SPIFFS file system on internal flash, this module can also access FAT partitions on an external SD card is [FatFS is enabled](../sdcard.md).
```lua
@ -43,30 +41,6 @@ Current directory defaults to the root of internal SPIFFS (`/FLASH`) after syste
#### Returns
`true` on success, `false` otherwise
## file.close()
Closes the open file, if any.
#### Syntax
`file.close()`
#### Parameters
none
#### Returns
`nil`
#### Example
```lua
-- open 'init.lua', print the first line.
if file.open("init.lua", "r") then
print(file.readline())
file.close()
end
```
#### See also
[`file.open()`](#fileopen)
## file.exists()
Determines whether the specified file exists.
@ -95,34 +69,6 @@ end
#### See also
[`file.list()`](#filelist)
## file.flush()
Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()`](#fileclose) performs an implicit flush as well.
#### Syntax
`file.flush()`
#### Parameters
none
#### Returns
`nil`
#### Example
```lua
-- open 'init.lua' in 'a+' mode
if file.open("init.lua", "a+") then
-- write 'foo bar' to the end of the file
file.write('foo bar')
file.flush()
-- write 'baz' too
file.write('baz')
file.close()
end
```
#### See also
[`file.close()`](#fileclose)
## file.format()
Format the file system. Completely erases any existing file system and writes a new one. Depending on the size of the flash chip in the ESP, this may take several seconds.
@ -280,9 +226,9 @@ When done with the file, it must be closed using `file.close()`.
- "a+": append update mode, previous data is preserved, writing is only allowed at the end of file
#### Returns
`nil` if file not opened, or not exists (read modes). `true` if file opened ok.
file object if file opened ok. `nil` if file not opened, or not exists (read modes).
#### Example
#### Example (basic model)
```lua
-- open 'init.lua', print the first line.
if file.open("init.lua", "r") then
@ -290,70 +236,19 @@ if file.open("init.lua", "r") then
file.close()
end
```
#### See also
- [`file.close()`](#fileclose)
- [`file.readline()`](#filereadline)
## file.read()
Read content from the open file.
#### Syntax
`file.read([n_or_str])`
#### Parameters
- `n_or_str`:
- if nothing passed in, read up to `LUAL_BUFFERSIZE` bytes (default 1024) or the entire file (whichever is smaller)
- if passed a number n, then read the file until the lesser of `n` bytes, `LUAL_BUFFERSIZE` bytes, or EOF is reached. Specifying a number larger than the buffer size will read the buffer size.
- if passed a string `str`, then read until `str` appears next in the file, `LUAL_BUFFERSIZE` bytes have been read, or EOF is reached
#### Returns
File content as a string, or nil when EOF
#### Example
#### Example (object model)
```lua
-- print the first line of 'init.lua'
if file.open("init.lua", "r") then
print(file.read('\n'))
file.close()
end
-- print the first 5 bytes of 'init.lua'
if file.open("init.lua", "r") then
print(file.read(5))
file.close()
-- open 'init.lua', print the first line.
fd = file.open("init.lua", "r")
if fd then
print(fd:readline())
fd:close(); fd = nil
end
```
#### See also
- [`file.open()`](#fileopen)
- [`file.readline()`](#filereadline)
## file.readline()
Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than `LUAL_BUFFERSIZE`, this function only returns the first `LUAL_BUFFERSIZE` bytes (this is 1024 bytes by default).
#### Syntax
`file.readline()`
#### Parameters
none
#### Returns
File content in string, line by line, including EOL('\n'). Return `nil` when EOF.
#### Example
```lua
-- print the first line of 'init.lua'
if file.open("init.lua", "r") then
print(file.readline())
file.close()
end
```
#### See also
- [`file.open()`](#fileopen)
- [`file.close()`](#fileclose)
- [`file.read()`](#filereade)
- [`file.readline()`](#filereadline)
## file.remove()
@ -398,12 +293,188 @@ Renames a file. If a file is currently open, it will be closed first.
file.rename("temp.lua","init.lua")
```
# File access functions
The `file` module provides several functions to access the content of a file after it has been opened with [`file.open()`](#fileopen). They can be used as part of a basic model or an object model:
## Basic model
In the basic model there is max one file opened at a time. The file access functions operate on this file per default. If another file is opened, the previous default file needs to be closed beforehand.
```lua
-- open 'init.lua', print the first line.
if file.open("init.lua", "r") then
print(file.readline())
file.close()
end
```
## Object model
Files are represented by file objects which are created by `file.open()`. File access functions are available as methods of this object, and multiple file objects can coexist.
```lua
src = file.open("init.lua", "r")
if src then
dest = file.open("copy.lua", "w")
if dest then
local line
repeat
line = src:read()
if line then
dest:write(line)
end
until line == nil
dest:close(); dest = nil
end
src:close(); dest = nil
end
```
!!! Attention
It is recommended to use only one single model within the application. Concurrent use of both models can yield unpredictable behavior: Closing the default file from basic model will also close the correspoding file object. Closing a file from object model will also close the default file if they are the same file.
!!! Note
The maximum number of open files on SPIFFS is determined at compile time by `SPIFFS_MAX_OPEN_FILES` in `user_config.h`.
## file.close()
## file.obj:close()
Closes the open file, if any.
#### Syntax
`file.close()`
`fd:close()`
#### Parameters
none
#### Returns
`nil`
#### See also
[`file.open()`](#fileopen)
## file.flush()
## file.obj:flush()
Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()` / `fd:close()`](#fileclose) performs an implicit flush as well.
#### Syntax
`file.flush()`
`fd:flush()`
#### Parameters
none
#### Returns
`nil`
#### Example (basic model)
```lua
-- open 'init.lua' in 'a+' mode
if file.open("init.lua", "a+") then
-- write 'foo bar' to the end of the file
file.write('foo bar')
file.flush()
-- write 'baz' too
file.write('baz')
file.close()
end
```
#### See also
[`file.close()` / `file.obj:close()`](#fileclose)
## file.read()
## file.obj:read()
Read content from the open file.
!!! note
The function temporarily allocates 2 * (number of requested bytes) on the heap for buffering and processing the read data. Default chunk size (`FILE_READ_CHUNK`) is 1024 bytes and is regarded to be safe. Pushing this by 4x or more can cause heap overflows depending on the application. Consider this when selecting a value for parameter `n_or_char`.
#### Syntax
`file.read([n_or_char])`
`fd:read([n_or_char])`
#### Parameters
- `n_or_char`:
- if nothing passed in, then read up to `FILE_READ_CHUNK` bytes or the entire file (whichever is smaller).
- if passed a number `n`, then read up to `n` bytes or the entire file (whichever is smaller).
- if passed a string containing the single character `char`, then read until `char` appears next in the file, `FILE_READ_CHUNK` bytes have been read, or EOF is reached.
#### Returns
File content as a string, or nil when EOF
#### Example (basic model)
```lua
-- print the first line of 'init.lua'
if file.open("init.lua", "r") then
print(file.read('\n'))
file.close()
end
```
#### Example (object model)
```lua
-- print the first 5 bytes of 'init.lua'
fd = file.open("init.lua", "r")
if fd then
print(fd:read(5))
fd:close(); fd = nil
end
```
#### See also
- [`file.open()`](#fileopen)
- [`file.readline()` / `file.obj:readline()`](#filereadline)
## file.readline()
## file.obj:readline()
Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than 1024, this function only returns the first 1024 bytes.
#### Syntax
`file.readline()`
`fd:readline()`
#### Parameters
none
#### Returns
File content in string, line by line, including EOL('\n'). Return `nil` when EOF.
#### Example (basic model)
```lua
-- print the first line of 'init.lua'
if file.open("init.lua", "r") then
print(file.readline())
file.close()
end
```
#### See also
- [`file.open()`](#fileopen)
- [`file.close()` / `file.obj:close()`](#fileclose)
- [`file.read()` / `file.obj:read()`](#fileread)
## file.seek()
## file.obj:seek()
Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence.
#### Syntax
`file.seek([whence [, offset]])`
`fd:seek([whence [, offset]])`
#### Parameters
- `whence`
- "set": base is position 0 (beginning of the file)
@ -416,7 +487,7 @@ If no parameters are given, the function simply returns the current file offset.
#### Returns
the resulting file position, or `nil` on error
#### Example
#### Example (basic model)
```lua
if file.open("init.lua", "r") then
-- skip the first 5 bytes of the file
@ -429,19 +500,22 @@ end
[`file.open()`](#fileopen)
## file.write()
## file.obj:write()
Write a string to the open file.
#### Syntax
`file.write(string)`
`fd:write(string)`
#### Parameters
`string` content to be write to file
#### Returns
`true` if the write is ok, `nil` on error
#### Example
#### Example (basic model)
```lua
-- open 'init.lua' in 'a+' mode
if file.open("init.lua", "a+") then
@ -451,24 +525,38 @@ if file.open("init.lua", "a+") then
end
```
#### Example (object model)
```lua
-- open 'init.lua' in 'a+' mode
fd = file.open("init.lua", "a+")
if fd then
-- write 'foo bar' to the end of the file
fd:write('foo bar')
fd:close()
end
```
#### See also
- [`file.open()`](#fileopen)
- [`file.writeline()`](#filewriteline)
- [`file.writeline()` / `file.obj:writeline()`](#filewriteline)
## file.writeline()
## file.obj:writeline()
Write a string to the open file and append '\n' at the end.
#### Syntax
`file.writeline(string)`
`fd:writeline(string)`
#### Parameters
`string` content to be write to file
#### Returns
`true` if write ok, `nil` on error
#### Example
#### Example (basic model)
```lua
-- open 'init.lua' in 'a+' mode
if file.open("init.lua", "a+") then
@ -480,4 +568,4 @@ end
#### See also
- [`file.open()`](#fileopen)
- [`file.readline()`](#filereadline)
- [`file.readline()` / `file.obj:readline()`](#filereadline)

View File

@ -69,28 +69,29 @@ gpio.read(0)
## gpio.serout()
Serialize output based on a sequence of delay-times in µs. After each delay, the pin is toggled. After the last repeat and last delay the pin is not toggled.
Serialize output based on a sequence of delay-times in µs. After each delay, the pin is toggled. After the last cycle and last delay the pin is not toggled.
The function works in two modes:
* synchronous - for sub-50 µs resolution, restricted to max. overall duration,
* asynchrounous - synchronous operation with less granularity but virtually unrestricted duration.
Whether the asynchronous mode is chosen is defined by presence of the `callback` parameter. If present and is of function type the function goes asynchronous the callback function is invoked when sequence finishes. If the parameter is numeric the function still goes asynchronous but no callback is invoked when done.
Whether the asynchronous mode is chosen is defined by presence of the `callback` parameter. If present and is of function type the function goes asynchronous and the callback function is invoked when sequence finishes. If the parameter is numeric the function still goes asynchronous but no callback is invoked when done.
For asynchronous version minimum delay time should not be shorter than 50 μs and maximum delay time is 0x7fffff μs (~8.3 seconds).
In this mode the function does not block the stack and returns immediately before the output sequence is finalized. HW timer inf `FRC1_SOURCE` mode is used to change the states.
For the asynchronous version, the minimum delay time should not be shorter than 50 μs and maximum delay time is 0x7fffff μs (~8.3 seconds).
In this mode the function does not block the stack and returns immediately before the output sequence is finalized. HW timer `FRC1_SOURCE` mode is used to change the states. As there is only a single hardware timer, there
are restrictions on which modules can be used at the same time. An error will be raised if the timer is already in use.
Note that the synchronous variant (no or nil `callback` parameter) function blocks the stach and as such any use of it must adhere to the SDK guidelines (also explained [here](https://nodemcu.readthedocs.io/en/dev/en/extn-developer-faq/#extension-developer-faq)). Failure to do so may lead to WiFi issues or outright to crashes/reboots. Shortly it means that sum of all delay times multiplied by the number of repeats should not exceed 15 ms.
Note that the synchronous variant (no or nil `callback` parameter) function blocks the stack and as such any use of it must adhere to the SDK guidelines (also explained [here](../extn-developer-faq/#extension-developer-faq)). Failure to do so may lead to WiFi issues or outright to crashes/reboots. In short it means that the sum of all delay times multiplied by the number of cycles should not exceed 15 ms.
#### Syntax
`gpio.serout(pin, start_level, delay_times [, repeat_num[, callback]])`
`gpio.serout(pin, start_level, delay_times [, cycle_num[, callback]])`
#### Parameters
- `pin` pin to use, IO index
- `start_level` level to start on, either `gpio.HIGH` or `gpio.LOW`
- `delay_times` an array of delay times in µs between each toggle of the gpio pin.
- `repeat_num` an optional number of times to run through the sequence.
- `callback` an optional callback function or number, if present the function ruturns immediately and goes asynchronous.
- `cycle_num` an optional number of times to run through the sequence. (default is 1)
- `callback` an optional callback function or number, if present the function returns immediately and goes asynchronous.
#### Returns

View File

@ -135,15 +135,21 @@ Listen on port from IP address.
#### Example
```lua
-- 30s time out for a inactive client
sv = net.createServer(net.TCP, 30)
-- server listens on 80, if data received, print data to console and send "hello world" back to caller
sv:listen(80, function(c)
c:on("receive", function(c, pl)
print(pl)
-- 30s time out for a inactive client
sv = net.createServer(net.TCP, 30)
function receiver(sck, data)
print(data)
sck:close()
end
if sv then
sv:listen(80, function(conn)
conn:on("receive", receiver)
conn:send("hello world")
end)
c:send("hello world")
end)
end
```
#### See also
@ -303,32 +309,35 @@ Multiple consecutive `send()` calls aren't guaranteed to work (and often don't)
#### Example
```lua
srv = net.createServer(net.TCP)
srv:listen(80, function(conn)
conn:on("receive", function(sck, req)
local response = {}
-- if you're sending back HTML over HTTP you'll want something like this instead
-- local response = {"HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n"}
function receiver(sck, data)
local response = {}
response[#response + 1] = "lots of data"
response[#response + 1] = "even more data"
response[#response + 1] = "e.g. content read from a file"
-- sends and removes the first element from the 'response' table
local function send(sk)
if #response > 0
then sk:send(table.remove(response, 1))
else
sk:close()
response = nil
end
-- if you're sending back HTML over HTTP you'll want something like this instead
-- local response = {"HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n"}
response[#response + 1] = "lots of data"
response[#response + 1] = "even more data"
response[#response + 1] = "e.g. content read from a file"
-- sends and removes the first element from the 'response' table
local function send(localSocket)
if #response > 0
then localSocket:send(table.remove(response, 1))
else
localSocket:close()
response = nil
end
end
-- triggers the send() function again once the first chunk of data was sent
sck:on("sent", send)
send(sck)
end)
-- triggers the send() function again once the first chunk of data was sent
sck:on("sent", send)
send(sck)
end
srv:listen(80, function(conn)
conn:on("receive", receiver)
end)
```
If you do not or can not keep all the data you send back in memory at one time (remember that `response` is an aggregation) you may use explicit callbacks instead of building up a table like so:

View File

@ -84,7 +84,7 @@ Reads multi bytes.
#### Parameters
- `pin` 1~12, I/O index
- `size` number of bytes to be read from slave device
- `size` number of bytes to be read from slave device (up to 256)
#### Returns
`string` bytes read from slave device

View File

@ -55,7 +55,7 @@ rtctime.dsleep(5000000, 4)
For applications where it is necessary to take samples with high regularity, this function is useful. It provides an easy way to implement a "wake up on the next 5-minute boundary" scheme, without having to explicitly take into account how long the module has been active for etc before going back to sleep.
#### Syntax
`rtctime.dsleep(aligned_us, minsleep_us [, option])`
`rtctime.dsleep_aligned(aligned_us, minsleep_us [, option])`
#### Parameters
- `aligned_us` boundary interval in microseconds

View File

@ -12,7 +12,7 @@ When compiled together with the [rtctime](rtctime.md) module it also offers seam
Attempts to obtain time synchronization.
For best results you may want to to call this periodically in order to compensate for internal clock drift. As stated in the [rtctime](rtctime.md) module documentation it's advisable to sync time after deep sleep and it's necessary to sync after module reset (add it to [`init.lua`](upload.md#initlua) after WiFi initialization).
For best results you may want to to call this periodically in order to compensate for internal clock drift. As stated in the [rtctime](rtctime.md) module documentation it's advisable to sync time after deep sleep and it's necessary to sync after module reset (add it to [`init.lua`](../upload.md#initlua) after WiFi initialization).
#### Syntax
`sntp.sync([server_ip], [callback], [errcallback])`

45
docs/en/modules/somfy.md Normal file
View File

@ -0,0 +1,45 @@
# 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).

View File

@ -6,6 +6,10 @@
All transactions for sending and receiving are most-significant-bit first and least-significant last.
For technical details of the underlying hardware refer to [metalphreak's ESP8266 HSPI articles](http://d.av.id.au/blog/tag/hspi/).
!!! note
The ESP hardware provides two SPI busses, with IDs 0, and 1, which map to pins generally labelled SPI and HSPI. If you are using any kind of development board which provides flash, then bus ID 0 (SPI) is almost certainly used for communicating with the flash chip. You probably want to choose bus ID 1 (HSPI) for your communication, as you will have uncontended use of it.
## High Level Functions
The high level functions provide a send & receive API for half- and
full-duplex mode. Sent and received data items are restricted to 1 - 32 bit

View File

@ -8,7 +8,7 @@ U8glib is a graphics library developed at [olikraus/u8glib](https://github.com/o
I²C and SPI mode:
- sh1106_128x64
- ssd1306 - 128x64 and 64x48 variants
- ssd1306 - 128x32, 128x64, and 64x48 variants
- ssd1309_128x64
- ssd1327_96x96_gr
- uc1611 - dogm240 and dogxl240 variants
@ -107,6 +107,7 @@ Initialize a display via I²C.
The init sequence would insert delays to match the display specs. These can destabilize the overall system if wifi service is blocked for too long. It is therefore advisable to disable such delays unless the specific use case can exclude wifi traffic while initializing the display driver.
- `u8g.sh1106_128x64_i2c()`
- `u8g.ssd1306_128x32_i2c()`
- `u8g.ssd1306_128x64_i2c()`
- `u8g.ssd1306_64x48_i2c()`
- `u8g.ssd1309_128x64_i2c()`
@ -146,6 +147,7 @@ The init sequence would insert delays to match the display specs. These can dest
- `u8g.pcd8544_84x48_hw_spi()`
- `u8g.pcf8812_96x65_hw_spi()`
- `u8g.sh1106_128x64_hw_spi()`
- `u8g.ssd1306_128x32_hw_spi()`
- `u8g.ssd1306_128x64_hw_spi()`
- `u8g.ssd1306_64x48_hw_spi()`
- `u8g.ssd1309_128x64_hw_spi()`
@ -202,6 +204,30 @@ disp = u8g.ssd1306_128x64_hw_spi(cs, dc, res)
#### See also
[I²C Display Drivers](#i2c-display-drivers)
## u8g.fb_rle
Initialize a virtual display that provides run-length encoded framebuffer contents to a Lua callback.
The callback function can be used to process the framebuffer line by line. It's called with either `nil` as parameter to indicate the start of a new frame or with a string containing a line of the framebuffer with run-length encoding. First byte in the string specifies how many pairs of (x, len) follow, while each pair defines the start (leftmost x-coordinate) and length of a sequence of lit pixels. All other pixels in the line are dark.
```lua
n = struct.unpack("B", rle_line)
print(n.." pairs")
for i = 0,n-1 do
print(string.format(" x: %d len: %d", struct.unpack("BB", rle_line, 1+1 + i*2)))
end
```
#### Syntax
`u8g.fb_rle(cb_fn, width, height)`
#### Parameters
- `cb_fn([rle_line])` callback function. `rle_line` is a string containing a run-length encoded framebuffer line, or `nil` to indicate start of frame.
- `width` of display. Must be a multiple of 8, less than or equal to 248.
- `height` of display. Must be a multiple of 8, less than or equal to 248.
#### Returns
u8g display object
___
## Constants

View File

@ -7,10 +7,6 @@ A websocket *client* module that implements [RFC6455](https://tools.ietf.org/htm
The implementation supports fragmented messages, automatically respondes to ping requests and periodically pings if the server isn't communicating.
!!! note
Currently, it is **not** possible to change the request headers, most notably the user agent.
**SSL/TLS support**
Take note of constraints documented in the [net module](net.md).
@ -70,6 +66,27 @@ ws = nil -- fully dispose the client as lua will now gc it
```
## websocket.client:config(params)
Configures websocket client instance.
#### Syntax
`websocket:config(params)`
#### Parameters
- `params` table with configuration parameters. Following keys are recognized:
- `headers` table of extra request headers affecting every request
#### Returns
`nil`
#### Example
```lua
ws = websocket.createClient()
ws:config({headers={['User-Agent']='NodeMCU'}})
```
## websocket.client:connect()
Attempts to estabilish a websocket connection to the given URL.

View File

@ -24,6 +24,23 @@ Gets the current WiFi channel.
#### Returns
current WiFi channel
## wifi.getdefaultmode()
Gets default WiFi operation mode.
#### Syntax
`wifi.getdefaultmode()`
#### Parameters
`nil`
#### Returns
The WiFi mode, as one of the `wifi.STATION`, `wifi.SOFTAP`, `wifi.STATIONAP` or `wifi.NULLMODE` constants.
#### See also
[`wifi.getmode()`](#wifigetmode)
[`wifi.setmode()`](#wifisetmode)
## wifi.getmode()
Gets WiFi operation mode.
@ -38,6 +55,7 @@ Gets WiFi operation mode.
The WiFi mode, as one of the `wifi.STATION`, `wifi.SOFTAP`, `wifi.STATIONAP` or `wifi.NULLMODE` constants.
#### See also
[`wifi.getdefaultmode()`](#wifigetdefaultmode)
[`wifi.setmode()`](#wifisetmode)
## wifi.getphymode()
@ -67,18 +85,20 @@ Configures the WiFi mode to use. NodeMCU can run in one of four WiFi modes:
When using the combined Station + AP mode, the same channel will be used for both networks as the radio can only listen on a single channel.
NOTE: WiFi Mode configuration will be retained until changed even if device is turned off.
NOTE: WiFi Mode configuration will be retained until changed even if device is turned off.
#### Syntax
`wifi.setmode(mode)`
`wifi.setmode(mode[, save])`
#### Parameters
`mode` value should be one of
- `wifi.STATION` for when the device is connected to a WiFi router. This is often done to give the device access to the Internet.
- `wifi.SOFTAP` for when the device is acting *only* as an access point. This will allow you to see the device in the list of WiFi networks (unless you hide the SSID, of course). In this mode your computer can connect to the device, creating a local area network. Unless you change the value, the NodeMCU device will be given a local IP address of 192.168.4.1 and assign your computer the next available IP address, such as 192.168.4.2.
- `wifi.STATIONAP` is the combination of `wifi.STATION` and `wifi.SOFTAP`. It allows you to create a local WiFi connection *and* connect to another WiFi router.
- `wifi.NULLMODE` to switch off WiFi
- `mode` value should be one of
- `wifi.STATION` for when the device is connected to a WiFi router. This is often done to give the device access to the Internet.
- `wifi.SOFTAP` for when the device is acting *only* as an access point. This will allow you to see the device in the list of WiFi networks (unless you hide the SSID, of course). In this mode your computer can connect to the device, creating a local area network. Unless you change the value, the NodeMCU device will be given a local IP address of 192.168.4.1 and assign your computer the next available IP address, such as 192.168.4.2.
- `wifi.STATIONAP` is the combination of `wifi.STATION` and `wifi.SOFTAP`. It allows you to create a local WiFi connection *and* connect to another WiFi router.
- `wifi.NULLMODE` changing WiFi mode to NULL_MODE will put wifi into a low power state similar to MODEM_SLEEP, provided `wifi.nullmodesleep(false)` has not been called.
- `save` choose whether or not to save wifi mode to flash
- `true` WiFi mode configuration **will** be retained through power cycle. (Default)
- `false` WiFi mode configuration **will not** be retained through power cycle.
#### Returns
current mode after setup
@ -90,11 +110,12 @@ wifi.setmode(wifi.STATION)
#### See also
[`wifi.getmode()`](#wifigetmode)
[`wifi.getdefaultmode()`](#wifigetdefaultmode)
## wifi.setphymode()
Sets WiFi physical mode.
- `wifi.PHYMODE_B`
802.11b, more range, low Transfer rate, more current draw
- `wifi.PHYMODE_G`
@ -116,7 +137,7 @@ Information from the Espressif datasheet v4.3
`wifi.setphymode(mode)`
#### Parameters
`mode` one of the following
`mode` one of the following
- `wifi.PHYMODE_B`
- `wifi.PHYMODE_G`
@ -130,39 +151,23 @@ physical mode after setup
## wifi.nullmodesleep()
Configures whether or not WiFi automatically goes to sleep in NULL_MODE. Enabled by default.
Configures whether or not WiFi automatically goes to sleep in NULL_MODE. Enabled by default.
!!! note
This function **does not** store it's setting in flash, if auto sleep in NULL_MODE is not desired, `wifi.nullmodesleep(false)` must be called after powerup, restart, or wake from deep sleep.
#### Syntax
`wifi.nullmodesleep(enable)`
`wifi.nullmodesleep([enable])`
#### Parameters
- `enable`
- true: Enable WiFi auto sleep in NULL_MODE. (Default setting)
- false: Disable WiFi auto sleep in NULL_MODE.
- `enable`
- `true` Enable WiFi auto sleep in NULL_MODE. (Default setting)
- `false` Disable WiFi auto sleep in NULL_MODE.
#### Returns
Current/new NULL_MODE sleep setting.
## wifi.sleeptype()
Configures the WiFi modem sleep type.
#### Syntax
`wifi.sleeptype(type_wanted)`
#### Parameters
`type_wanted` one of the following:
- `wifi.NONE_SLEEP` to keep the modem on at all times
- `wifi.LIGHT_SLEEP` to allow the modem to power down under some circumstances
- `wifi.MODEM_SLEEP` to power down the modem as much as possible
#### Returns
The actual sleep mode set, as one of `wifi.NONE_SLEEP`, `wifi.LIGHT_SLEEP` or `wifi.MODEM_SLEEP`.
#### See also
- [`node.dsleep()`](node.md#nodedsleep)
- [`rtctime.dsleep()`](rtctime.md#rtctimedsleep)
- `sleep_enabled` Current/New NULL_MODE sleep setting
- If `wifi.nullmodesleep()` is called with no arguments, current setting is returned.
- If `wifi.nullmodesleep()` is called with `enable` argument, confirmation of new setting is returned.
## wifi.startsmart()
@ -241,61 +246,95 @@ wifi.sta.autoconnect(1)
- [`wifi.sta.connect()`](#wifistaconnect)
- [`wifi.sta.disconnect()`](#wifistadisconnect)
## wifi.sta.changeap()
Select Access Point from list returned by `wifi.sta.getapinfo()`
#### Syntax
`wifi.sta.changeap(ap_index)`
#### Parameters
`ap_index` Index of Access Point you would like to change to. (Range:1-5)
- Corresponds to index used by [`wifi.sta.getapinfo()`](#wifistagetapinfo) and [`wifi.sta.getapindex()`](#wifistagetapindex)
#### Returns
- `true` Success
- `false` Failure
#### Example
```lua
wifi.sta.changeap(4)
```
#### See also
- [`wifi.sta.getapinfo()`](#wifistagetapinfo)
- [`wifi.sta.getapindex()`](#wifistagetapindex)
## wifi.sta.config()
Sets the WiFi station configuration.
NOTE: Station configuration will be retained until changed even if device is turned off.
#### Syntax
`wifi.sta.config(ssid, password[, auto[, bssid]])`
`wifi.sta.config(station_config)`
#### Parameters
- `ssid` string which is less than 32 bytes.
- `password` string which is 8-64 or 0 bytes. Empty string indicates an open WiFi access point.
- `auto` defaults to 1
- 0 to disable auto connect and remain disconnected from access point
- 1 to enable auto connect and connect to access point, hence with `auto=1` there's no need to call [`wifi.sta.connect()`](#wifistaconnect) later
- `bssid` string that contains the MAC address of the access point (optional)
- `station_config` table containing configuration data for station
- `ssid` string which is less than 32 bytes.
- `pwd` string which is 8-64 or 0 bytes. Empty string indicates an open WiFi access point.
- `auto` defaults to true
- `true` to enable auto connect and connect to access point, hence with `auto=true` there's no need to call [`wifi.sta.connect()`](#wifistaconnect)
- `false` to disable auto connect and remain disconnected from access point
- `bssid` string that contains the MAC address of the access point (optional)
- You can set BSSID if you have multiple access points with the same SSID.
- Note: if you set BSSID for a specific SSID and would like to configure station to connect to the same SSID only without the BSSID requirement, you MUST first configure to station to a different SSID first, then connect to the desired SSID
- The following formats are valid:
- "DE-C1-A5-51-F1-ED"
- "DE:C1:A5:51:F1:ED"
- "AC-1D-1C-B1-0B-22"
- "DE AD BE EF 7A C0"
- `save` Save station configuration to flash.
- `true` configuration **will** be retained through power cycle.
- `false` configuration **will not** be retained through power cycle. (Default)
#### Returns
`nil`
- `true` Success
- `false` Failure
#### Example
```lua
-- Connect to access point automatically when in range, `auto` defaults to 1
wifi.sta.config("myssid", "password")
--connect to Access Point (DO NOT save config to flash)
station_cfg={}
station_cfg.ssid="NODE-AABBCC"
station_cfg.pwd="password"
wifi.sta.config(station_cfg)
-- Connect to Unsecured access point automatically when in range, `auto` defaults to 1
wifi.sta.config("myssid", "")
-- Connect to access point, User decides when to connect/disconnect to/from AP due to `auto=0`
wifi.sta.config("myssid", "mypassword", 0)
wifi.sta.connect()
-- ... do some WiFi stuff
wifi.sta.disconnect()
-- Connect to specific access point automatically when in range, `auto` defaults to 1
wifi.sta.config("myssid", "mypassword", "12:34:56:78:90:12")
--connect to Access Point (DO save config to flash)
station_cfg={}
station_cfg.ssid="NODE-AABBCC"
station_cfg.pwd="password"
station_cfg.save=true
wifi.sta.config(station_cfg)
--connect to Access Point with specific MAC address
station_cfg={}
station_cfg.ssid="NODE-AABBCC"
station_cfg.pwd="password"
station_cfg.bssid="AA:BB:CC:DD:EE:FF"
wifi.sta.config(station_cfg)
--configure station but don't connect to Access point
station_cfg={}
station_cfg.ssid="NODE-AABBCC"
station_cfg.pwd="password"
station_cfg.auto=false
wifi.sta.config(station_cfg)
-- Connect to specific access point, User decides when to connect/disconnect to/from AP due to `auto=0`
wifi.sta.config("myssid", "mypassword", 0, "12:34:56:78:90:12")
wifi.sta.connect()
-- ... do some WiFi stuff
wifi.sta.disconnect()
```
#### See also
- [`wifi.sta.connect()`](#wifistaconnect)
- [`wifi.sta.disconnect()`](#wifistadisconnect)
- [`wifi.sta.apinfo()`](#wifistaapinfo)
## wifi.sta.connect()
@ -318,6 +357,10 @@ none
Disconnects from AP in station mode.
!!! note
Please note that disconnecting from Access Point does not reduce power consumption.
If power saving is your goal, please refer to the description for `wifi.NULLMODE` in the function [`wifi.setmode()`](#wifisetmode) for more details.
#### Syntax
`wifi.sta.disconnect()`
@ -339,14 +382,14 @@ Registers callbacks for WiFi station status events.
- `wifi.sta.eventMonReg(wifi_status[, function([previous_state])])`
#### Parameters
- `wifi_status` WiFi status you would like to set a callback for:
- `wifi_status` WiFi status you would like to set a callback for:
- `wifi.STA_IDLE`
- `wifi.STA_CONNECTING`
- `wifi.STA_WRONGPWD`
- `wifi.STA_APNOTFOUND`
- `wifi.STA_FAIL`
- `wifi.STA_GOTIP`
- `function` callback function to perform when event occurs
- `function` callback function to perform when event occurs
- Note: leaving field blank unregisters callback.
- `previous_state` previous wifi_state(0 - 5)
@ -354,7 +397,7 @@ Registers callbacks for WiFi station status events.
`nil`
#### Example
```lua
```lua
--register callback
wifi.sta.eventMonReg(wifi.STA_IDLE, function() print("STATION_IDLE") end)
wifi.sta.eventMonReg(wifi.STA_CONNECTING, function() print("STATION_CONNECTING") end)
@ -362,16 +405,16 @@ wifi.sta.eventMonReg(wifi.STA_WRONGPWD, function() print("STATION_WRONG_PASSWORD
wifi.sta.eventMonReg(wifi.STA_APNOTFOUND, function() print("STATION_NO_AP_FOUND") end)
wifi.sta.eventMonReg(wifi.STA_FAIL, function() print("STATION_CONNECT_FAIL") end)
wifi.sta.eventMonReg(wifi.STA_GOTIP, function() print("STATION_GOT_IP") end)
--register callback: use previous state
wifi.sta.eventMonReg(wifi.STA_CONNECTING, function(previous_State)
if(previous_State==wifi.STA_GOTIP) then
if(previous_State==wifi.STA_GOTIP) then
print("Station lost connection with access point\n\tAttempting to reconnect...")
else
print("STATION_CONNECTING")
end
end)
--unregister callback
wifi.sta.eventMonReg(wifi.STA_IDLE)
```
@ -447,7 +490,7 @@ Scans AP list as a Lua table into callback function.
#### Parameters
- `cfg` table that contains scan configuration
- `ssid` SSID == nil, don't filter SSID
- `ssid` SSID == nil, don't filter SSID
- `bssid` BSSID == nil, don't filter BSSID
- `channel` channel == 0, scan all channels, otherwise scan set channel (default is 0)
- `show_hidden` show_hidden == 1, get info for router with hidden SSID (default is 0)
@ -479,7 +522,7 @@ function listap(t) -- (SSID : Authmode, RSSI, BSSID, Channel)
end
end
wifi.sta.getap(listap)
-- print AP list in new format
function listap(t)
for k,v in pairs(t) do
@ -507,8 +550,8 @@ function listap(t)
end
end
scan_cfg = {}
scan_cfg.ssid = "myssid"
scan_cfg.bssid = "AA:AA:AA:AA:AA:AA"
scan_cfg.ssid = "myssid"
scan_cfg.bssid = "AA:AA:AA:AA:AA:AA"
scan_cfg.channel = 0
scan_cfg.show_hidden = 1
wifi.sta.getap(scan_cfg, 1, listap)
@ -520,10 +563,10 @@ function listap(t)
print("CURRENT RSSI IS: "..rssi)
end
end
ssid, tmp, bssid_set, bssid=wifi.sta.getconfig()
ssid, tmp, bssid_set, bssid=wifi.sta.getconfig()
scan_cfg = {}
scan_cfg.ssid = ssid
scan_cfg.ssid = ssid
if bssid_set == 1 then scan_cfg.bssid = bssid else scan_cfg.bssid = nil end
scan_cfg.channel = wifi.getchannel()
scan_cfg.show_hidden = 0
@ -535,6 +578,88 @@ wifi.sta.getap(scan_cfg, 1, listap)
#### See also
[`wifi.sta.getip()`](#wifistagetip)
## wifi.sta.getapindex()
Get index of current Access Point stored in AP cache.
#### Syntax
`wifi.sta.getapindex()`
#### Parameters
none
#### Returns
`current_index` index of currently selected Access Point. (Range:1-5)
#### Example
```lua
print("the index of the currently selected AP is: "..wifi.sta.getapindex())
```
#### See also
- [`wifi.sta.getapindex()`](#wifistagetapindex)
- [`wifi.sta.apinfo()`](#wifistaapinfo)
- [`wifi.sta.apchange()`](#wifistaapchange)
## wifi.sta.getapinfo()
Get information of APs cached by ESP8266 station.
!!! Note
Any Access Points configured with save disabled `wifi.sta.config({save=false})` will populate this list (appearing to overwrite APs stored in flash) until restart.
#### Syntax
`wifi.sta.getapinfo()`
#### Parameters
`nil`
#### Returns
- `ap_info`
- `qty` quantity of APs returned
- `1-5` index of AP. (the index corresponds to index used by [`wifi.sta.changeap()`](#wifistachangeap) and [`wifi.sta.getapindex()`](#wifistagetapindex))
- `ssid` ssid of Access Point
- `pwd` Password for Access Point
- If no password was configured, the `pwd` field will be `nil`
- `bssid` MAC address of Access Point
- If no MAC address was configured, the `bssid` field will be `nil`
#### Example
```lua
--print stored access point info
do
for k,v in pairs(wifi.sta.getapinfo()) do
if (type(v)=="table") then
print(" "..k.." : "..type(v))
for k,v in pairs(v) do
print("\t\t"..k.." : "..v)
end
else
print(" "..k.." : "..v)
end
end
end
--print stored access point info(formatted)
do
local x=wifi.sta.getapinfo()
local y=wifi.sta.getapindex()
print("\n Number of APs stored in flash:", x.qty)
print(string.format(" %-6s %-32s %-64s %-18s", "index:", "SSID:", "Password:", "BSSID:"))
for i=1, (x.qty), 1 do
print(string.format(" %s%-6d %-32s %-64s %-18s",(i==y and ">" or " "), i, x[i].ssid, x[i].pwd and x[i].pwd or type(nil), x[i].bssid and x[i].bssid or type(nil)))
end
end
```
#### See also
- [`wifi.sta.getapindex()`](#wifistagetapindex)
- [`wifi.sta.setaplimit()`](#wifistasetaplimit)
- [`wifi.sta.changeap()`](#wifistachangeap)
- [`wifi.sta.config()`](#wifistaconfig)
## wifi.sta.getbroadcast()
Gets the broadcast address in station mode.
@ -546,7 +671,7 @@ Gets the broadcast address in station mode.
`nil`
#### Returns
broadcast address as string, for example "192.168.0.255",
broadcast address as string, for example "192.168.0.255",
returns `nil` if IP address = "0.0.0.0".
#### See also
@ -554,23 +679,39 @@ returns `nil` if IP address = "0.0.0.0".
## wifi.sta.getconfig()
Gets the WiFi station configuration.
Gets the WiFi station configuration.
#### Syntax
`wifi.sta.getconfig()`
#### Parameters
none
- `return_table`
- `true` returns data in a table
- `false` returns data in the old format (default)
#### Returns
ssid, password, bssid_set, bssid
If `return_table` is `true`:
- `config_table`
- `ssid` ssid of Access Point.
- `pwd` password to Access Point.
- If no password was configured, the `pwd` field will be `nil`
- `bssid` MAC address of Access Point
- If no MAC address was configured, the `bssid` field will be `nil`
Note: If bssid_set is equal to 0 then bssid is irrelevant
If `return_table` is `false`:
- ssid, password, bssid_set, bssid
- Note: If `bssid_set` is equal to `0` then `bssid` is irrelevant,
#### Example
```lua
--Get current Station configuration
--Get current Station configuration (NEW FORMAT)
do
local def_sta_config=wifi.sta.getconfig(true)
print(string.format("\tDefault station config\n\tssid:\"%s\"\tpassword:\"%s\"%s", def_sta_config.ssid, def_sta_config.pwd, (type(def_sta_config.bssid)=="string" and "\tbssid:\""..def_sta_config.bssid.."\"" or "")))
end
--Get current Station configuration (OLD FORMAT)
ssid, password, bssid_set, bssid=wifi.sta.getconfig()
print("\nCurrent Station configuration:\nSSID : "..ssid
.."\nPassword : "..password
@ -580,6 +721,55 @@ ssid, password, bssid_set, bssid=nil, nil, nil, nil
```
#### See also
- [`wifi.sta.getdefaultconfig()`](#wifistagetdefaultconfig)
- [`wifi.sta.connect()`](#wifistaconnect)
- [`wifi.sta.disconnect()`](#wifistadisconnect)
## wifi.sta.getdefaultconfig()
Gets the default WiFi station configuration stored in flash.
#### Syntax
`wifi.sta.getdefaultconfig(return_table)`
#### Parameters
- `return_table`
- `true` returns data in a table
- `false` returns data in the old format (default)
#### Returns
If `return_table` is `true`:
- `config_table`
- `ssid` ssid of Access Point.
- `pwd` password to Access Point.
- If no password was configured, the `pwd` field will be `nil`
- `bssid` MAC address of Access Point
- If no MAC address was configured, the `bssid` field will be `nil`
If `return_table` is `false`:
- ssid, password, bssid_set, bssid
- Note: If `bssid_set` is equal to `0` then `bssid` is irrelevant,
#### Example
```lua
--Get default Station configuration (NEW FORMAT)
do
local def_sta_config=wifi.sta.getdefaultconfig(true)
print(string.format("\tDefault station config\n\tssid:\"%s\"\tpassword:\"%s\"%s", def_sta_config.ssid, def_sta_config.pwd, (type(def_sta_config.bssid)=="string" and "\tbssid:\""..def_sta_config.bssid.."\"" or "")))
end
--Get default Station configuration (OLD FORMAT)
ssid, password, bssid_set, bssid=wifi.sta.getdefaultconfig()
print("\nCurrent Station configuration:\nSSID : "..ssid
.."\nPassword : "..password
.."\nBSSID_set : "..bssid_set
.."\nBSSID: "..bssid.."\n")
ssid, password, bssid_set, bssid=nil, nil, nil, nil
```
#### See also
- [`wifi.sta.getconfig()`](#wifistagetconfig)
- [`wifi.sta.connect()`](#wifistaconnect)
- [`wifi.sta.disconnect()`](#wifistadisconnect)
@ -667,6 +857,32 @@ RSSI=wifi.sta.getrssi()
print("RSSI is", RSSI)
```
## wifi.sta.setaplimit()
Set Maximum number of Access Points to store in flash.
- This value is written to flash
!!! Attention
If 5 Access Points are stored and AP limit is set to 4, the AP at index 5 will remain until [`node.restore()`](node.md#noderestore) is called or AP limit is set to 5 and AP is overwritten.
#### Syntax
`wifi.sta.setaplimit(qty)`
#### Parameters
`qty` Quantity of Access Points to store in flash. Range: 1-5 (Default: 5)
#### Returns
- `true` Success
- `false` Failure
#### Example
```lua
wifi.sta.setaplimit(true)
```
#### See also
- [`wifi.sta.getapinfo()`](#wifistagetapinfo)
## wifi.sta.sethostname()
Sets station hostname.
@ -732,6 +948,26 @@ print(wifi.sta.setmac("DE:AD:BE:EF:7A:C0"))
#### See also
[`wifi.sta.setip()`](#wifistasetip)
## wifi.sta.sleeptype()
Configures the WiFi modem sleep type to be used while station is connected to an Access Point.
!!! note
Does not apply to `wifi.SOFTAP`, `wifi.STATIONAP` or `wifi.NULLMODE`.
#### Syntax
`wifi.sta.sleeptype(type_wanted)`
#### Parameters
`type_wanted` one of the following:
- `wifi.NONE_SLEEP` to keep the modem on at all times
- `wifi.LIGHT_SLEEP` to allow the CPU to power down under some circumstances
- `wifi.MODEM_SLEEP` to power down the modem as much as possible
#### Returns
The actual sleep mode set, as one of `wifi.NONE_SLEEP`, `wifi.LIGHT_SLEEP` or `wifi.MODEM_SLEEP`.
## wifi.sta.status()
Gets the current status in station mode.
@ -758,22 +994,26 @@ number 0~5
Sets SSID and password in AP mode. Be sure to make the password at least 8 characters long! If you don't it will default to *no* password and not set the SSID! It will still work as an access point but use a default SSID like e.g. NODE-9997C3.
NOTE: SoftAP Configuration will be retained until changed even if device is turned off.
#### Syntax
`wifi.ap.config(cfg)`
#### Parameters
- `ssid` SSID chars 1-32
- `pwd` password chars 8-64
- `auth` authentication method, one of `wifi.OPEN` (default), `wifi.WPA_PSK`, `wifi.WPA2_PSK`, `wifi.WPA_WPA2_PSK`
- `channel` channel number 1-14 default = 6
- `hidden` 0 = not hidden, 1 = hidden, default 0
- `max` maximal number of connections 1-4 default=4
- `beacon` beacon interval time in range 100-60000, default = 100
- `cfg` table to hold configuration
- `ssid` SSID chars 1-32
- `pwd` password chars 8-64
- `auth` authentication method, one of `wifi.OPEN` (default), `wifi.WPA_PSK`, `wifi.WPA2_PSK`, `wifi.WPA_WPA2_PSK`
- `channel` channel number 1-14 default = 6
- `hidden` false = not hidden, true = hidden, default = false
- `max` maximum number of connections 1-4 default=4
- `beacon` beacon interval time in range 100-60000, default = 100
- `save` save configuration to flash.
- `true` configuration **will** be retained through power cycle. (Default)
- `false` configuration **will not** be retained through power cycle.
#### Returns
`nil`
- `true` Success
- `false` Failure
#### Example:
```lua
@ -785,7 +1025,7 @@ NOTE: SoftAP Configuration will be retained until changed even if device is turn
## wifi.ap.deauth()
Deauths (forcibly removes) a client from the ESP access point by sending a corresponding IEEE802.11 management packet (first) and removing the client from it's data structures (afterwards).
Deauths (forcibly removes) a client from the ESP access point by sending a corresponding IEEE802.11 management packet (first) and removing the client from it's data structures (afterwards).
The IEEE802.11 reason code used is 2 for "Previous authentication no longer valid"(AUTH_EXPIRE).
@ -803,11 +1043,11 @@ Returns true unless called while the ESP is in the STATION opmode
```lua
allowed_mac_list={"18:fe:34:00:00:00", "18:fe:34:00:00:01"}
wifi.eventmon.register(wifi.eventmon.AP_STACONNECTED, function(T)
wifi.eventmon.register(wifi.eventmon.AP_STACONNECTED, function(T)
print("\n\tAP - STATION CONNECTED".."\n\tMAC: "..T.MAC.."\n\tAID: "..T.AID)
if(allowed_mac_list~=nil) then
for _, v in pairs(allowed_mac_list) do
if(v == T.MAC) then return end
for _, v in pairs(allowed_mac_list) do
if(v == T.MAC) then return end
end
end
wifi.ap.deauth(T.MAC)
@ -831,7 +1071,7 @@ Gets broadcast address in AP mode.
none
#### Returns
broadcast address in string, for example "192.168.0.255",
broadcast address in string, for example "192.168.0.255",
returns `nil` if IP address = "0.0.0.0".
#### Example
@ -871,6 +1111,102 @@ for mac,ip in pairs(wifi.ap.getclient()) do
end
```
## wifi.ap.getconfig()
Gets the current SoftAP configuration.
#### Syntax
`wifi.ap.getconfig(return_table)`
#### Parameters
- `return_table`
- `true` returns data in a table
- `false` returns data in the old format (default)
#### Returns
If `return_table` is true:
- `config_table`
- `ssid` Network name
- `pwd` Password
- If no password was configured, the `pwd` field will be `nil`
- `auth` Authentication Method (`wifi.OPEN`, `wifi.WPA_PSK`, `wifi.WPA2_PSK` or `wifi.WPA_WPA2_PSK`)
- `channel` Channel number
- `hidden` `false` = not hidden, `true` = hidden
- `max` Maximum number of client connections
- `beacon` Beacon interval
If `return_table` is false:
ssid, password
Note: If bssid_set is equal to 0 then bssid is irrelevant
#### Example
```lua
--Get SoftAP configuration table (NEW FORMAT)
do
print("\n Current SoftAP configuration:")
for k,v in pairs(wifi.ap.getconfig(true)) do
print(" "..k.." :",v)
end
end
--Get current SoftAP configuration (OLD FORMAT)
do
local ssid, password=wifi.ap.getconfig()
print("\n Current SoftAP configuration:\n SSID : "..ssid..
"\n Password :",password)
ssid, password=nil, nil
end
```
## wifi.ap.getdefaultconfig()
Gets the default SoftAP configuration stored in flash.
#### Syntax
`wifi.ap.getdefaultconfig(return_table)`
#### Parameters
- `return_table`
- `true` returns data in a table
- `false` returns data in the old format (default)
#### Returns
If `return_table` is true:
- `config_table`
- `ssid` Network name
- `pwd` Password
- If no password was configured, the `pwd` field will be `nil`
- `auth` Authentication Method (`wifi.OPEN`, `wifi.WPA_PSK`, `wifi.WPA2_PSK` or `wifi.WPA_WPA2_PSK`)
- `channel` Channel number
- `hidden` `false` = not hidden, `true` = hidden
- `max` Maximum number of client connections
- `beacon` Beacon interval
If `return_table` is false:
ssid, password
Note: If bssid_set is equal to 0 then bssid is irrelevant
#### Example
```lua
--Get default SoftAP configuration table (NEW FORMAT)
do
print("\n Default SoftAP configuration:")
for k,v in pairs(wifi.ap.getdefaultconfig(true)) do
print(" "..k.." :",v)
end
end
--Get default SoftAP configuration (OLD FORMAT)
do
local ssid, password=wifi.ap.getdefaultconfig()
print("\n Default SoftAP configuration:\n SSID : "..ssid..
"\n Password :",password)
ssid, password=nil, nil
end
```
## wifi.ap.getip()
Gets IP address, netmask and gateway in AP mode.
@ -1077,39 +1413,39 @@ T: Table returned by event.
#### Example
```lua
wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T)
wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T)
print("\n\tSTA - CONNECTED".."\n\tSSID: "..T.SSID.."\n\tBSSID: "..
T.BSSID.."\n\tChannel: "..T.channel)
end)
wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T)
wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T)
print("\n\tSTA - DISCONNECTED".."\n\tSSID: "..T.SSID.."\n\tBSSID: "..
T.BSSID.."\n\treason: "..T.reason)
end)
wifi.eventmon.register(wifi.eventmon.STA_AUTHMODE_CHANGE, Function(T)
wifi.eventmon.register(wifi.eventmon.STA_AUTHMODE_CHANGE, Function(T)
print("\n\tSTA - AUTHMODE CHANGE".."\n\told_auth_mode: "..
T.old_auth_mode.."\n\tnew_auth_mode: "..T.new_auth_mode)
T.old_auth_mode.."\n\tnew_auth_mode: "..T.new_auth_mode)
end)
wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T)
wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T)
print("\n\tSTA - GOT IP".."\n\tStation IP: "..T.IP.."\n\tSubnet mask: "..
T.netmask.."\n\tGateway IP: "..T.gateway)
end)
wifi.eventmon.register(wifi.eventmon.STA_DHCP_TIMEOUT, function()
wifi.eventmon.register(wifi.eventmon.STA_DHCP_TIMEOUT, function()
print("\n\tSTA - DHCP TIMEOUT")
end)
wifi.eventmon.register(wifi.eventmon.AP_STACONNECTED, function(T)
wifi.eventmon.register(wifi.eventmon.AP_STACONNECTED, function(T)
print("\n\tAP - STATION CONNECTED".."\n\tMAC: "..T.MAC.."\n\tAID: "..T.AID)
end)
wifi.eventmon.register(wifi.eventmon.AP_STADISCONNECTED, function(T)
wifi.eventmon.register(wifi.eventmon.AP_STADISCONNECTED, function(T)
print("\n\tAP - STATION DISCONNECTED".."\n\tMAC: "..T.MAC.."\n\tAID: "..T.AID)
end)
wifi.eventmon.register(wifi.eventmon.AP_PROBEREQRECVED, function(T)
wifi.eventmon.register(wifi.eventmon.AP_PROBEREQRECVED, function(T)
print("\n\tAP - STATION DISCONNECTED".."\n\tMAC: ".. T.MAC.."\n\tRSSI: "..T.RSSI)
end)
```

View File

@ -71,7 +71,7 @@ ws2812.write(nil, string.char(0, 255, 0, 0, 255, 0)) -- turn the two first RGB l
For more advanced animations, it is useful to keep a "framebuffer" of the strip,
interact with it and flush it to the strip.
For this purpose, the ws2812 library offers a read/write buffer.
For this purpose, the ws2812 library offers a read/write buffer. This buffer has a `__tostring` method so that it can be printed. This is useful for debugging.
#### Example
Led chaser with a RGBW strip
@ -181,6 +181,92 @@ The number of given bytes must match the number of bytesPerLed of the buffer
buffer:fill(0, 0, 0) -- fill the buffer with black for a RGB strip
```
## ws2812.buffer:dump()
Returns the contents of the buffer (the pixel values) as a string. This can then be saved to a file or sent over a network.
#### Syntax
`buffer:dump()`
#### Returns
A string containing the pixel values.
#### Example
```lua
local s = buffer:dump()
```
## ws2812.buffer:replace()
Inserts a string (or a buffer) into another buffer with an offset.
The buffer must have the same number of colors per led or an error will be thrown.
#### Syntax
`buffer:replace(source[, offset])`
#### Parameters
- `source` the pixel values to be set into the buffer. This is either a string or a buffer.
- `offset` the offset where the source is to be placed in the buffer. Default is 1. Negative values can be used.
#### Returns
`nil`
#### Example
```lua
buffer:replace(anotherbuffer:dump()) -- copy one buffer into another via a string
buffer:replace(anotherbuffer) -- copy one buffer into another
newbuffer = buffer.sub(1) -- make a copy of a buffer into a new buffer
```
## ws2812.buffer:mix()
This is a general method that loads data into a buffer that is a linear combination of data from other buffers. It can be used to copy a buffer or,
more usefully, do a cross fade. The pixel values are computed as integers and then range limited to [0, 255]. This means that negative
factors work as expected, and that the order of combining buffers does not matter.
#### Syntax
`buffer:mix(factor1, buffer1, ...)`
#### Parameters
- `factor1` This is the factor that the contents of `buffer1` are multiplied by. This factor is scaled by a factor of 256. Thus `factor1` value of 256 is a factor of 1.0.
- `buffer1` This is the source buffer. It must be of the same shape as the destination buffer.
There can be any number of factor/buffer pairs.
#### Returns
`nil`
#### Example
```lua
-- loads buffer with a crossfade between buffer1 and buffer2
buffer:mix(256 - crossmix, buffer1, crossmix, buffer2)
-- multiplies all values in buffer by 0.75
-- This can be used in place of buffer:fade
buffer:mix(192, buffer)
```
## ws2812.buffer:power()
Computes the total energy requirement for the buffer. This is merely the total sum of all the pixel values (which assumes that each color in each
pixel consumes the same amount of power). A real WS2812 (or WS2811) has three constant current drivers of 20mA -- one for each of R, G and B. The
pulse width modulation will cause the *average* current to scale linearly with pixel value.
#### Syntax
`buffer:power()`
#### Returns
An integer which is the sum of all the pixel values.
#### Example
```lua
-- Dim the buffer to no more than the PSU can provide
local psu_current_ma = 1000
local led_current_ma = 20
local led_sum = psu_current_ma * 255 / led_current_ma
local p = buffer:power()
if p > led_sum then
buffer:mix(256 * led_sum / p, buffer) -- power is now limited
end
```
## ws2812.buffer:fade()
Fade in or out. Defaults to out. Multiply or divide each byte of each led with/by the given value. Useful for a fading effect.
@ -200,14 +286,17 @@ buffer:fade(2)
buffer:fade(2, ws2812.FADE_IN)
```
## ws2812.buffer:shift()
Shift the content of the buffer in positive or negative direction. This allows simple animation effects.
Shift the content of (a piece of) the buffer in positive or negative direction. This allows simple animation effects. A slice of the buffer can be specified by using the
standard start and end offset Lua notation. Negative values count backwards from the end of the buffer.
#### Syntax
`buffer:shift(value [, mode])`
`buffer:shift(value [, mode[, i[, j]]])`
#### Parameters
- `value` number of pixels by which to rotate the buffer. Positive values rotate forwards, negative values backwards.
- `mode` is the shift mode to use. Can be one of `ws2812.SHIFT_LOGICAL` or `ws2812.SHIFT_CIRCULAR`. In case of SHIFT\_LOGICAL, the freed pixels are set to 0 (off). In case of SHIFT\_CIRCULAR, the buffer is treated like a ring buffer, inserting the pixels falling out on one end again on the other end. Defaults to SHIFT\_LOGICAL.
- `i` is the first offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is 1.
- `j` is the last offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is -1.
#### Returns
`nil`
@ -216,3 +305,42 @@ Shift the content of the buffer in positive or negative direction. This allows s
```lua
buffer:shift(3)
```
## ws2812.buffer:sub()
This implements the extraction function like `string.sub`. The indexes are in leds and all the same rules apply.
#### Syntax
`buffer1:sub(i[, j])`
#### Parameters
- `i` This is the start of the extracted data. Negative values can be used.
- `j` this is the end of the extracted data. Negative values can be used. The default is -1.
#### Returns
A buffer containing the extracted piece.
#### Example
```
b = buffer:sub(1,10)
```
## ws2812.buffer:__concat()
This implements the `..` operator to concatenate two buffers. They must have the same number of colors per led.
#### Syntax
`buffer1 .. buffer2`
#### Parameters
- `buffer1` this is the start of the resulting buffer
- `buffer2` this is the end of the resulting buffer
#### Returns
The concatenated buffer.
#### Example
```
ws2812.write(buffer1 .. buffer2)
```

145
lua_examples/somfy.lua Normal file
View File

@ -0,0 +1,145 @@
-- Somfy module example (beside somfy module requires also CJSON module)
-- The rolling code number is stored in the file somfy.cfg. A cached write of the somfy.cfg file is implemented in order to reduce the number of write to the EEPROM memory. Together with the logic of the file module it should allow long lasting operation.
config_file = "somfy."
-- somfy.cfg looks like
-- {"window1":{"rc":1,"address":123},"window2":{"rc":1,"address":124}}
local tmr_cache = tmr.create()
local tmr_delay = tmr.create()
pin = 4
gpio.mode(pin, gpio.OUTPUT, gpio.PULLUP)
function deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key)] = deepcopy(orig_value)
end
setmetatable(copy, deepcopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
function readconfig()
local cfg, ok, ln
if file.exists(config_file.."cfg") then
print("Reading config from "..config_file.."cfg")
file.open(config_file.."cfg", "r+")
ln = file.readline()
file.close()
else
if file.exists(config_file.."bak") then
print("Reading config from "..config_file.."bak")
file.open(config_file.."bak", "r+")
ln = file.readline()
file.close()
end
end
if not ln then ln = "{}" end
print("Configuration: "..ln)
config = cjson.decode(ln)
config_saved = deepcopy(config)
end
function writeconfighard()
print("Saving config")
file.remove(config_file.."bak")
file.rename(config_file.."cfg", config_file.."bak")
file.open(config_file.."cfg", "w+")
local ok, cfg = pcall(cjson.encode, config)
if ok then
file.writeline(cfg)
else
print("Config not saved!")
end
file.close()
config_saved = deepcopy(config)
end
function writeconfig()
tmr.stop(tmr_cache)
local savenow = false
local savelater = false
--print("Config: "..cjson.encode(config))
--print("Config saved: "..cjson.encode(config))
local count = 0
for _ in pairs(config_saved) do count = count + 1 end
if count == 0 then
config_saved = readconfig()
end
for remote,cfg in pairs(config_saved) do
savelater = savelater or not config[remote] or config[remote].rc > cfg.rc
savenow = savenow or not config[remote] or config[remote].rc > cfg.rc + 10
end
savelater = savelater and not savenow
if savenow then
print("Saving config now!")
writeconfighard()
end
if savelater then
print("Saving config later")
tmr.alarm(tmr_cache, 65000, tmr.ALARM_SINGLE, writeconfighard)
end
end
--======================================================================================================--
function down(remote, cb, par)
par = par or {}
print("down: ".. remote)
config[remote].rc=config[remote].rc+1
somfy.sendcommand(pin, config[remote].address, somfy.DOWN, config[remote].rc, 16, function() wait(100, cb, par) end)
writeconfig()
end
function up(remote, cb, par)
par = par or {}
print("up: ".. remote)
config[remote].rc=config[remote].rc+1
somfy.sendcommand(pin, config[remote].address, somfy.UP, config[remote].rc, 16, function() wait(100, cb, par) end)
writeconfig()
end
function downStep(remote, cb, par)
par = par or {}
print("downStep: ".. remote)
config[remote].rc=config[remote].rc+1
somfy.sendcommand(pin, config[remote].address, somfy.DOWN, config[remote].rc, 2, function() wait(300, cb, par) end)
writeconfig()
end
function upStep(remote, cb, par)
par = par or {}
print("upStep: ".. remote)
config[remote].rc=config[remote].rc+1
somfy.sendcommand(pin, config[remote].address, somfy.UP, config[remote].rc, 2, function() wait(300, cb, par) end)
writeconfig()
end
function wait(ms, cb, par)
par = par or {}
print("wait: ".. ms)
if cb then tmr.alarm(tmr_delay, ms, tmr.ALARM_SINGLE, function () cb(unpack(par)) end) end
end
--======================================================================================================--
if not config then readconfig() end
if #config == 0 then -- somfy.cfg does not exist
config = cjson.decode([[{"window1":{"rc":1,"address":123},"window2":{"rc":1,"address":124}}]])
config_saved = deepcopy(config)
end
down('window1',
wait, {60000,
up, {'window1',
wait, {9000,
downStep, {'window1', downStep, {'window1', downStep, {'window1', downStep, {'window1', downStep, {'window1', downStep, {'window1', downStep, {'window1'
}}}}}}}}}})

View File

@ -6,5 +6,11 @@
bool wifi_softap_deauth(uint8 mac[6]);
uint8 get_fpm_auto_sleep_flag(void);
enum ext_flash_size_map {
FLASH_SIZE_32M_MAP_2048_2048 = 7,
FLASH_SIZE_64M_MAP = 8,
FLASH_SIZE_128M_MAP = 9
};
#endif /* SDK_OVERRIDES_INCLUDE_USER_INTERFACE_H_ */

View File

@ -21,7 +21,7 @@ cat user_config.h
cd "$TRAVIS_BUILD_DIR"/ld || exit
# increase irom0_0_seg size for all modules build
sed -E -i.bak 's@(.*irom0_0_seg *:.*len *=) *[^,]*(.*)@\1 0xC0000\2@' nodemcu.ld
sed -E -i.bak 's@(.*irom0_0_seg *:.*len *=) *[^,]*(.*)@\1 0xD0000\2@' nodemcu.ld
cat nodemcu.ld
# change to "root" directory no matter where the script was started from

View File

@ -2,7 +2,7 @@ SRCS=\
main.c \
../../app/spiffs/spiffs_cache.c ../../app/spiffs/spiffs_check.c ../../app/spiffs/spiffs_gc.c ../../app/spiffs/spiffs_hydrogen.c ../../app/spiffs/spiffs_nucleus.c
CFLAGS=-g -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -I. -I../../app/spiffs -I../../app/include -DNODEMCU_SPIFFS_NO_INCLUDE --include spiffs_typedefs.h
CFLAGS=-g -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -I. -I../../app/spiffs -I../../app/include -DNODEMCU_SPIFFS_NO_INCLUDE --include spiffs_typedefs.h -Ddbg_printf=printf
spiffsimg: $(SRCS)
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@