├── html ├── favicon.ico ├── jl-400x110.png- ├── wifi │ ├── icons.png │ ├── wifiAp.js │ ├── wifiSta.html │ └── wifiAp.html ├── head- ├── web-server.html ├── flash.js ├── flash.html ├── log.html ├── mqtt.js ├── services.js ├── mqtt.html ├── console.js ├── services.html └── console.html ├── tools ├── htmlcompressor-1.5.3.jar └── yuicompressor-2.4.8.jar ├── serial ├── slip.h ├── serled.h ├── console.h ├── uart.h ├── serbridge.h ├── serled.c ├── crc16.c ├── crc16.h └── slip.c ├── esp-link ├── cgitcp.h ├── status.h ├── cgipins.h ├── cgiwebserversetup.h ├── cgimqtt.h ├── cgioptiboot.h ├── cgiflash.h ├── task.h ├── cgiservices.h ├── mqtt_client.h ├── cgimega.h ├── log.h ├── cgiwifi.h ├── cgi.h ├── stk500.h ├── pgmshared.h ├── cgitcp.c ├── task.c ├── config.h ├── mqtt_client.c ├── status.c ├── cgipins.c ├── stk500v2.h ├── pgmshared.c └── log.c ├── httpd ├── base64.h ├── httpdespfs.h ├── auth.h ├── multipart.h ├── auth.c ├── httpd.h └── base64.c ├── user └── user_main.c ├── espmake.cmd ├── espfs ├── mkespfsimage │ ├── mman-win32 │ │ ├── config.mak │ │ ├── Makefile │ │ ├── mman.h │ │ ├── configure │ │ └── mman.c │ └── Makefile ├── espfsformat.h └── espfs.h ├── rest └── rest.h ├── .gitignore ├── mqtt ├── mqtt_cmd.h ├── pktbuf.h ├── pktbuf.c └── mqtt_msg.h ├── include ├── esp8266.h ├── user_config.h ├── c_types.h └── espmissingincludes.h ├── RESTMQTT.md ├── web-server └── web-server.h ├── esp-link.sln ├── socket └── socket.h ├── .travis.yml ├── Dockerfile ├── WINDOWS.md ├── TROUBLESHOOTING.md ├── CONTRIBUTING.md ├── avrflash ├── WEB-SERVER.md ├── megaflash ├── wiflash ├── flash2560.py ├── syslog └── syslog.h ├── FLASH.md ├── cmd ├── cmd.h └── cmd.c ├── WIFI-CONFIG.md └── LICENSE.txt /html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeelabs/esp-link/HEAD/html/favicon.ico -------------------------------------------------------------------------------- /html/jl-400x110.png-: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeelabs/esp-link/HEAD/html/jl-400x110.png- -------------------------------------------------------------------------------- /html/wifi/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeelabs/esp-link/HEAD/html/wifi/icons.png -------------------------------------------------------------------------------- /tools/htmlcompressor-1.5.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeelabs/esp-link/HEAD/tools/htmlcompressor-1.5.3.jar -------------------------------------------------------------------------------- /tools/yuicompressor-2.4.8.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeelabs/esp-link/HEAD/tools/yuicompressor-2.4.8.jar -------------------------------------------------------------------------------- /serial/slip.h: -------------------------------------------------------------------------------- 1 | #ifndef SLIP_H 2 | #define SLIP_H 3 | 4 | void slip_parse_buf(char *buf, short length); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /esp-link/cgitcp.h: -------------------------------------------------------------------------------- 1 | #ifndef CGITCP_H 2 | #define CGITCP_H 3 | 4 | #include "httpd.h" 5 | 6 | int cgiTcp(HttpdConnData *connData); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /httpd/base64.h: -------------------------------------------------------------------------------- 1 | #ifndef BASE64_H 2 | #define BASE64_H 3 | 4 | int base64_decode(size_t in_len, const char *in, size_t out_len, unsigned char *out); 5 | 6 | #endif -------------------------------------------------------------------------------- /user/user_main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // initialize the custom stuff that goes beyond esp-link 5 | void app_init() { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /serial/serled.h: -------------------------------------------------------------------------------- 1 | #ifndef SERLED_H 2 | #define SERLED_H 3 | 4 | void serledFlash(int duration); 5 | void serledInit(void); 6 | void makeGpio(uint8_t pin); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /esp-link/status.h: -------------------------------------------------------------------------------- 1 | #ifndef STATUS_H 2 | #define STATUS_H 3 | 4 | int mqttStatusMsg(char *buf); 5 | void statusWifiUpdate(uint8_t state); 6 | void statusInit(void); 7 | 8 | #endif 9 | 10 | -------------------------------------------------------------------------------- /esp-link/cgipins.h: -------------------------------------------------------------------------------- 1 | #ifndef CGIPINS_H 2 | #define CGIPINS_H 3 | 4 | #include "httpd.h" 5 | 6 | int cgiPins(HttpdConnData *connData); 7 | int8_t pin_reset, pin_isp, pin_conn, pin_ser; 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /esp-link/cgiwebserversetup.h: -------------------------------------------------------------------------------- 1 | #ifndef CGIWEBSERVER_H 2 | #define CGIWEBSERVER_H 3 | 4 | #include 5 | 6 | int ICACHE_FLASH_ATTR cgiWebServerSetupUpload(HttpdConnData *connData); 7 | 8 | #endif /* CGIWEBSERVER_H */ 9 | -------------------------------------------------------------------------------- /esp-link/cgimqtt.h: -------------------------------------------------------------------------------- 1 | char *mqttState(void); 2 | #ifdef MQTT 3 | #ifndef CGIMQTT_H 4 | #define CGIMQTT_H 5 | 6 | #include "httpd.h" 7 | int cgiMqtt(HttpdConnData *connData); 8 | 9 | #endif // CGIMQTT_H 10 | #endif // MQTT 11 | -------------------------------------------------------------------------------- /espmake.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM remove automatic created obj folder 4 | rd obj /S /Q >nul 2>&1 5 | 6 | PATH=%PATH%;C:\Espressif\xtensa-lx106-elf\bin;C:\MinGW\bin;C:\MinGW\msys\1.0\bin;C:\espressif\git-bin;C:\espressif\java-bin;C:\Python27 7 | make -f Makefile %1 %2 %3 %4 %5 -------------------------------------------------------------------------------- /espfs/mkespfsimage/mman-win32/config.mak: -------------------------------------------------------------------------------- 1 | # Automatically generated by configure 2 | PREFIX=/mingw 3 | libdir=/mingw/lib 4 | incdir=/mingw/include/sys 5 | AR=ar 6 | CC=gcc 7 | RANLIB=ranlib 8 | STRIP=strip 9 | BUILD_STATIC=yes 10 | BUILD_MSVC= 11 | LIBCMD=echo ignoring lib 12 | -------------------------------------------------------------------------------- /html/head-: -------------------------------------------------------------------------------- 1 | 2 | 3 | esp-link 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | -------------------------------------------------------------------------------- /esp-link/cgioptiboot.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 by Thorsten von Eicken, see LICENSE.txt in the esp-link repo 2 | 3 | #ifndef OPTIBOOT_H 4 | #define OPTIBOOT_H 5 | 6 | #include 7 | 8 | int ICACHE_FLASH_ATTR cgiOptibootSync(HttpdConnData *connData); 9 | int ICACHE_FLASH_ATTR cgiOptibootData(HttpdConnData *connData); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /rest/rest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * api.h 3 | * 4 | * Created on: Mar 4, 2015 5 | * Author: Minh 6 | */ 7 | 8 | #ifndef MODULES_API_H_ 9 | #define MODULES_API_H_ 10 | 11 | #include "cmd.h" 12 | 13 | void REST_Setup(CmdPacket *cmd); 14 | void REST_Request(CmdPacket *cmd); 15 | void REST_SetHeader(CmdPacket *cmd); 16 | 17 | #endif /* MODULES_INCLUDE_API_H_ */ 18 | -------------------------------------------------------------------------------- /esp-link/cgiflash.h: -------------------------------------------------------------------------------- 1 | #ifndef CGIFLASH_H 2 | #define CGIFLASH_H 3 | 4 | #include "httpd.h" 5 | 6 | int cgiReadFlash(HttpdConnData *connData); 7 | int cgiGetFirmwareNext(HttpdConnData *connData); 8 | int cgiUploadFirmware(HttpdConnData *connData); 9 | int cgiRebootFirmware(HttpdConnData *connData); 10 | int cgiReset(HttpdConnData *connData); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /httpd/httpdespfs.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPDESPFS_H 2 | #define HTTPDESPFS_H 3 | 4 | #include 5 | #include "espfs.h" 6 | #include "espfsformat.h" 7 | #include "cgi.h" 8 | #include "httpd.h" 9 | 10 | int cgiEspFsHook(HttpdConnData *connData); 11 | //int cgiEspFsTemplate(HttpdConnData *connData); 12 | //int ICACHE_FLASH_ATTR cgiEspFsHtml(HttpdConnData *connData); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /esp-link/task.h: -------------------------------------------------------------------------------- 1 | /* 2 | * task.h 3 | * 4 | * Copyright 2015 Susi's Strolch 5 | * 6 | * For license information see projects "License.txt" 7 | * 8 | * 9 | */ 10 | 11 | #ifndef USRTASK_H 12 | #define USRTASK_H 13 | 14 | #define _taskPrio 1 15 | #define _task_queueLen 8 16 | 17 | uint8_t register_usr_task (os_task_t event); 18 | bool post_usr_task(uint8_t task, os_param_t par); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | firmware/ 3 | espfs/mkespfsimage/*.o 4 | espfs/mkespfsimage/mkespfsimage 5 | webpages.espfs 6 | espfs/espfstest/*.o 7 | espfs/espfstest/espfstest 8 | *.DS_Store 9 | html_compressed/ 10 | esp-link.tgz 11 | tve-patch/ 12 | yui 13 | espfs/mkespfsimage/mman-win32/mman.o 14 | esp-link.opensdf 15 | esp-link.sdf 16 | espfs/mkespfsimage/mman-win32/libmman.a 17 | .localhistory/ 18 | local.conf 19 | *.tgz 20 | -------------------------------------------------------------------------------- /esp-link/cgiservices.h: -------------------------------------------------------------------------------- 1 | #ifndef CGISERVICES_H 2 | #define CGISERVICES_H 3 | 4 | #include "httpd.h" 5 | 6 | int cgiSystemSet(HttpdConnData *connData); 7 | int cgiSystemInfo(HttpdConnData *connData); 8 | 9 | void cgiServicesSNTPInit(); 10 | int cgiServicesInfo(HttpdConnData *connData); 11 | int cgiServicesSet(HttpdConnData *connData); 12 | 13 | extern char* rst_codes[7]; 14 | extern char* flash_maps[7]; 15 | 16 | #endif // CGISERVICES_H 17 | -------------------------------------------------------------------------------- /esp-link/mqtt_client.h: -------------------------------------------------------------------------------- 1 | #ifdef MQTT 2 | #ifndef MQTT_CLIENT_H 3 | #define MQTT_CLIENT_H 4 | #include "mqtt.h" 5 | 6 | extern MQTT_Client mqttClient; 7 | extern char* statusTopicStr; 8 | void mqtt_client_init(); 9 | void mqtt_client_on_connected(MqttCallback connectedCb); 10 | void mqtt_client_on_disconnected(MqttCallback disconnectedCb); 11 | void mqtt_client_on_published(MqttCallback publishedCb); 12 | void mqtt_client_on_data(MqttDataCallback dataCb); 13 | 14 | #endif //MQTT_CLIENT_H 15 | #endif // MQTT 16 | -------------------------------------------------------------------------------- /esp-link/cgimega.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016-2017 by Danny Backx, see LICENSE.txt in the esp-link repo 2 | 3 | #ifndef CGIMEGA_H 4 | #define CGIMEGA_H 5 | 6 | #include 7 | 8 | int ICACHE_FLASH_ATTR cgiMegaSync(HttpdConnData *connData); 9 | int ICACHE_FLASH_ATTR cgiMegaData(HttpdConnData *connData); 10 | int ICACHE_FLASH_ATTR cgiMegaRead(HttpdConnData *connData); 11 | int ICACHE_FLASH_ATTR cgiMegaFuse(HttpdConnData *connData); 12 | int ICACHE_FLASH_ATTR cgiMegaRebootMCU(HttpdConnData *connData); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /esp-link/log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_H 2 | #define LOG_H 3 | 4 | #include "httpd.h" 5 | 6 | #define LOG_MODE_AUTO 0 // start by logging to uart0, turn aff after we get an IP 7 | #define LOG_MODE_OFF 1 // always off 8 | #define LOG_MODE_ON0 2 // always log to uart0 9 | #define LOG_MODE_ON1 3 // always log to uart1 10 | 11 | void logInit(void); 12 | void log_uart(bool enable); 13 | int ajaxLog(HttpdConnData *connData); 14 | int ajaxLogDbg(HttpdConnData *connData); 15 | 16 | void dumpMem(void *addr, int len); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /serial/console.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSOLE_H 2 | #define CONSOLE_H 3 | 4 | #include "httpd.h" 5 | 6 | void consoleInit(void); 7 | void ICACHE_FLASH_ATTR console_write_char(char c); 8 | int ajaxConsole(HttpdConnData *connData); 9 | int ajaxConsoleReset(HttpdConnData *connData); 10 | int ajaxConsoleClear(HttpdConnData *connData); 11 | int ajaxConsoleBaud(HttpdConnData *connData); 12 | int ajaxConsoleFormat(HttpdConnData *connData); 13 | int ajaxConsoleSend(HttpdConnData *connData); 14 | int tplConsole(HttpdConnData *connData, char *token, void **arg); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /mqtt/mqtt_cmd.h: -------------------------------------------------------------------------------- 1 | #ifndef MODULES_MQTT_CMD_H_ 2 | #define MODULES_MQTT_CMD_H_ 3 | 4 | #include "cmd.h" 5 | 6 | typedef struct { 7 | uint32_t connectedCb; 8 | uint32_t disconnectedCb; 9 | uint32_t publishedCb; 10 | uint32_t dataCb; 11 | } MqttCmdCb; 12 | 13 | void MQTTCMD_Connect(CmdPacket *cmd); 14 | void MQTTCMD_Disconnect(CmdPacket *cmd); 15 | void MQTTCMD_Setup(CmdPacket *cmd); 16 | void MQTTCMD_Publish(CmdPacket *cmd); 17 | void MQTTCMD_Subscribe(CmdPacket *cmd); 18 | void MQTTCMD_Lwt(CmdPacket *cmd); 19 | 20 | void mqtt_block(); 21 | void mqtt_unblock(); 22 | 23 | #endif /* MODULES_MQTT_CMD_H_ */ 24 | -------------------------------------------------------------------------------- /httpd/auth.h: -------------------------------------------------------------------------------- 1 | #ifndef AUTH_H 2 | #define AUTH_H 3 | 4 | #include "httpd.h" 5 | 6 | #ifndef HTTP_AUTH_REALM 7 | #define HTTP_AUTH_REALM "Protected" 8 | #endif 9 | 10 | #define HTTPD_AUTH_SINGLE 0 11 | #define HTTPD_AUTH_CALLBACK 1 12 | 13 | #define AUTH_MAX_USER_LEN 32 14 | #define AUTH_MAX_PASS_LEN 32 15 | 16 | //Parameter given to authWhatever functions. This callback returns the usernames/passwords the device 17 | //has. 18 | typedef int (* AuthGetUserPw)(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen); 19 | 20 | int ICACHE_FLASH_ATTR authBasic(HttpdConnData *connData); 21 | 22 | #endif -------------------------------------------------------------------------------- /include/esp8266.h: -------------------------------------------------------------------------------- 1 | // Combined include file for esp8266 2 | #ifndef _ESP8266_H_ 3 | #define _ESP8266_H_ 4 | 5 | #undef MEMLEAK_DEBUG 6 | #define USE_OPTIMIZE_PRINTF 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | 24 | #include "espmissingincludes.h" 25 | #include "uart_hw.h" 26 | 27 | #ifdef __WIN32__ 28 | #include <_mingw.h> 29 | #endif 30 | 31 | #endif // _ESP8266_H_ 32 | -------------------------------------------------------------------------------- /html/web-server.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Web Server

4 |
5 | 6 |
7 |

User defined web pages can be uploaded to esp-link. This is useful if esp-link acts as a web server while MCU provides 8 | the measurement data.

9 | 10 |
11 | The custom web page to upload: 12 | 13 |
14 | 15 |
16 | 17 |
18 | 19 | 29 | 30 | -------------------------------------------------------------------------------- /mqtt/pktbuf.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt 2 | 3 | #ifndef PKTBUF_H 4 | #define PKTBUF_H 5 | 6 | typedef struct PktBuf { 7 | struct PktBuf *next; // next buffer in chain 8 | uint16_t filled; // number of bytes filled in buffer 9 | uint8_t data[0]; // data in buffer 10 | } PktBuf; 11 | 12 | // Allocate a new packet buffer of given length 13 | PktBuf *PktBuf_New(uint16_t length); 14 | 15 | // Append a buffer to the end of a packet buffer queue, returns new head 16 | PktBuf *PktBuf_Push(PktBuf *headBuf, PktBuf *buf); 17 | 18 | // Prepend a buffer to the beginning of a packet buffer queue, return new head 19 | PktBuf * PktBuf_Unshift(PktBuf *headBuf, PktBuf *buf); 20 | 21 | // Shift first buffer off queue, returns new head (not shifted buffer!) 22 | PktBuf *PktBuf_Shift(PktBuf *headBuf); 23 | 24 | // Shift first buffer off queue, free it, return new head 25 | PktBuf *PktBuf_ShiftFree(PktBuf *headBuf); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /RESTMQTT.md: -------------------------------------------------------------------------------- 1 | Esp-link: Outbound HTTP REST requests and MQTT client 2 | ------------------------------------------- 3 | 4 | The V2 versions of esp-link use the SLIP protocol over the serial link to support simple outbound 5 | HTTP REST requests as well as an MQTT client. The SLIP protocol consists of commands with 6 | binary arguments sent from the 7 | attached microcontroller to the esp8266, which then performs the command and responds back. 8 | The responses back use a callback address in the attached microcontroller code, i.e., the 9 | command sent by the uC contains a callback address and the response from the esp8266 starts 10 | with that callback address. This enables asynchronous communication where esp-link can notify the 11 | uC when requests complete or when other actions happen, such as wifi connectivity status changes. 12 | 13 | You can find REST and MQTT libraries as well as demo sketches in the 14 | [el-client](https://github.com/jeelabs/el-client) repository. 15 | -------------------------------------------------------------------------------- /web-server/web-server.h: -------------------------------------------------------------------------------- 1 | #ifndef WEB_SERVER_H 2 | #define WEB_SERVER_H 3 | 4 | #include 5 | 6 | #include "httpd.h" 7 | #include "cmd.h" 8 | 9 | typedef enum 10 | { 11 | LOAD=0, // loading web-page content at the first time 12 | REFRESH, // loading web-page subsequently 13 | BUTTON, // HTML button pressed 14 | SUBMIT, // HTML form is submitted 15 | 16 | INVALID=-1, 17 | } RequestReason; 18 | 19 | typedef enum 20 | { 21 | WEB_STRING=0, // the value is string 22 | WEB_NULL, // the value is NULL 23 | WEB_INTEGER, // the value is integer 24 | WEB_BOOLEAN, // the value is boolean 25 | WEB_FLOAT, // the value is float 26 | WEB_JSON // the value is JSON data 27 | } WebValueType; 28 | 29 | void WEB_Init(); 30 | 31 | char * WEB_UserPages(); 32 | 33 | int WEB_CgiJsonHook(HttpdConnData *connData); 34 | void WEB_Setup(CmdPacket *cmd); 35 | void WEB_Data(CmdPacket *cmd); 36 | 37 | #endif /* WEB_SERVER_H */ 38 | 39 | -------------------------------------------------------------------------------- /esp-link.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "esp-link", "esp-link.vcxproj", "{A92F0CAA-F89B-4F78-AD2A-A042429BD87F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|ARM = Debug|ARM 11 | Release|ARM = Release|ARM 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {A92F0CAA-F89B-4F78-AD2A-A042429BD87F}.Debug|ARM.ActiveCfg = Debug|ARM 15 | {A92F0CAA-F89B-4F78-AD2A-A042429BD87F}.Debug|ARM.Build.0 = Debug|ARM 16 | {A92F0CAA-F89B-4F78-AD2A-A042429BD87F}.Release|ARM.ActiveCfg = Release|ARM 17 | {A92F0CAA-F89B-4F78-AD2A-A042429BD87F}.Release|ARM.Build.0 = Release|ARM 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /espfs/mkespfsimage/Makefile: -------------------------------------------------------------------------------- 1 | GZIP_COMPRESSION ?= no 2 | 3 | ifeq ($(OS),Windows_NT) 4 | 5 | TARGET = mkespfsimage.exe 6 | 7 | CC = gcc 8 | LD = $(CC) 9 | CFLAGS=-c -I.. -Imman-win32 -std=gnu99 10 | LDFLAGS=-Lmman-win32 -lmman 11 | 12 | ifeq ("$(GZIP_COMPRESSION)","yes") 13 | CFLAGS += -DESPFS_GZIP 14 | LDFLAGS += -lz 15 | endif 16 | 17 | OBJECTS = main.o 18 | 19 | all: libmman $(TARGET) 20 | 21 | libmman: 22 | $(Q) make -C mman-win32 23 | 24 | $(TARGET): $(OBJECTS) 25 | $(LD) -o $@ $^ $(LDFLAGS) 26 | 27 | %.o: %.c 28 | $(CC) $(CFLAGS) -o $@ $^ 29 | 30 | clean: 31 | rm -f $(OBJECTS) $(TARGET) ./mman-win32/libmman.a ./mman-win32/mman.o 32 | 33 | .PHONY: all clean 34 | 35 | else 36 | 37 | CC=gcc 38 | CFLAGS=-I.. -std=gnu99 39 | ifeq ("$(GZIP_COMPRESSION)","yes") 40 | CFLAGS+= -DESPFS_GZIP 41 | endif 42 | 43 | OBJS=main.o 44 | TARGET=mkespfsimage 45 | 46 | $(TARGET): $(OBJS) 47 | ifeq ("$(GZIP_COMPRESSION)","yes") 48 | $(CC) -o $@ $^ -lz 49 | else 50 | $(CC) -o $@ $^ 51 | endif 52 | 53 | clean: 54 | rm -f $(TARGET) $(OBJS) 55 | 56 | endif 57 | -------------------------------------------------------------------------------- /include/user_config.h: -------------------------------------------------------------------------------- 1 | #ifndef _USER_CONFIG_H_ 2 | #define _USER_CONFIG_H_ 3 | #include 4 | #ifdef __WIN32__ 5 | #include <_mingw.h> 6 | #endif 7 | 8 | #undef SHOW_HEAP_USE 9 | #define DEBUGIP 10 | #define SDK_DBG 11 | 12 | #define CMD_DBG 13 | #undef ESPFS_DBG 14 | #undef CGI_DBG 15 | #define CGIFLASH_DBG 16 | #define CGIMQTT_DBG 17 | #define CGIPINS_DBG 18 | #define CGIWIFI_DBG 19 | #define CONFIG_DBG 20 | #define LOG_DBG 21 | #define STATUS_DBG 22 | #define HTTPD_DBG 23 | #define MQTT_DBG 24 | #define MQTTCMD_DBG 25 | #define MQTTCLIENT_DBG 26 | #undef PKTBUF_DBG 27 | #define REST_DBG 28 | #define RESTCMD_DBG 29 | #define SERBR_DBG 30 | #define SERLED_DBG 31 | #define SLIP_DBG 32 | #define UART_DBG 33 | #define MDNS_DBG 34 | #define OPTIBOOT_DBG 35 | #undef SYSLOG_DBG 36 | #define CGISERVICES_DBG 37 | 38 | // If defined, the default hostname for DHCP will include the chip ID to make it unique 39 | #undef CHIP_IN_HOSTNAME 40 | 41 | extern char* esp_link_version; 42 | extern uint8_t UTILS_StrToIP(const char* str, void *ip); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /esp-link/cgiwifi.h: -------------------------------------------------------------------------------- 1 | #ifndef CGIWIFI_H 2 | #define CGIWIFI_H 3 | 4 | #include "httpd.h" 5 | 6 | enum { wifiIsDisconnected, wifiIsConnected, wifiGotIP }; 7 | typedef void(*WifiStateChangeCb)(uint8_t wifiStatus); 8 | 9 | int cgiWiFiScan(HttpdConnData *connData); 10 | int cgiWifiInfo(HttpdConnData *connData); 11 | int cgiWiFi(HttpdConnData *connData); 12 | int cgiWiFiConnect(HttpdConnData *connData); 13 | int cgiWiFiSetMode(HttpdConnData *connData); 14 | int cgiWiFiConnStatus(HttpdConnData *connData); 15 | int cgiWiFiSpecial(HttpdConnData *connData); 16 | int cgiApSettingsChange(HttpdConnData *connData); 17 | int cgiApSettingsInfo(HttpdConnData *connData); 18 | void configWifiIP(); 19 | void wifiInit(void); 20 | void wifiAddStateChangeCb(WifiStateChangeCb cb); 21 | void wifiStartMDNS(struct ip_addr); 22 | int checkString(char *str); 23 | 24 | extern uint8_t wifiState; 25 | extern bool mdns_started; 26 | 27 | int wifiGetApCount(); 28 | void wifiGetApName(int, char *); 29 | int wifiSignalStrength(int); 30 | void connectToNetwork(char *, char *); 31 | void wifiStartScan(); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /espfs/espfsformat.h: -------------------------------------------------------------------------------- 1 | #ifndef ESPROFSFORMAT_H 2 | #define ESPROFSFORMAT_H 3 | 4 | /* 5 | Stupid cpio-like tool to make read-only 'filesystems' that live on the flash SPI chip of the module. 6 | Can (will) use lzf compression (when I come around to it) to make shit quicker. Aligns names, files, 7 | headers on 4-byte boundaries so the SPI abstraction hardware in the ESP8266 doesn't crap on itself 8 | when trying to do a <4byte or unaligned read. 9 | */ 10 | 11 | /* 12 | The idea 'borrows' from cpio: it's basically a concatenation of {header, filename, file} data. 13 | Header, filename and file data is 32-bit aligned. The last file is indicated by data-less header 14 | with the FLAG_LASTFILE flag set. 15 | */ 16 | 17 | 18 | #define FLAG_LASTFILE (1<<0) 19 | #define FLAG_GZIP (1<<1) 20 | #define COMPRESS_NONE 0 21 | #define COMPRESS_HEATSHRINK 1 22 | #define ESPFS_MAGIC 0x73665345 23 | 24 | typedef struct { 25 | int32_t magic; 26 | int8_t flags; 27 | int8_t compression; 28 | int16_t nameLen; 29 | int32_t fileLenComp; 30 | int32_t fileLenDecomp; 31 | } __attribute__((packed)) EspFsHeader; 32 | 33 | #endif -------------------------------------------------------------------------------- /socket/socket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * socket.h 3 | * 4 | * Created on: Sep 16th 2016 5 | * Author: BeeGee 6 | */ 7 | 8 | #ifndef MODULES_SOCKET_H_ 9 | #define MODULES_SOCKET_H_ 10 | 11 | #include "cmd.h" 12 | 13 | void SOCKET_Setup(CmdPacket *cmd); 14 | void SOCKET_Send(CmdPacket *cmd); 15 | 16 | // Socket mode 17 | typedef enum { 18 | SOCKET_TCP_CLIENT = 0, /**< TCP socket client for sending only, doesn't wait for response from server */ 19 | SOCKET_TCP_CLIENT_LISTEN, /**< TCP socket client, waits for response from server after sending */ 20 | SOCKET_TCP_SERVER, /**< TCP socket server */ 21 | SOCKET_UDP, /**< UDP socket for sending and receiving UDP packets */ 22 | } socketMode; 23 | 24 | // Callback type 25 | typedef enum { 26 | USERCB_SENT = 0, /**< Data send finished */ 27 | USERCB_RECV, /**< Data received */ 28 | USERCB_RECO, /**< Connection error */ 29 | USERCB_CONN, /**< Connection event */ 30 | } cbType; 31 | 32 | // Connection status 33 | typedef enum { 34 | CONNSTAT_DIS = 0, // Disconnected 35 | CONNSTAT_CON, // Connected 36 | } connStat; 37 | 38 | #endif /* MODULES_SOCKET_H_ */ 39 | -------------------------------------------------------------------------------- /espfs/mkespfsimage/mman-win32/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # mman-win32 (mingw32) Makefile 3 | # 4 | include config.mak 5 | 6 | ifeq ($(BUILD_STATIC),yes) 7 | TARGETS+=libmman.a 8 | INSTALL+=static-install 9 | endif 10 | ifeq ($(BUILD_MSVC),yes) 11 | SHFLAGS+=-Wl,--output-def,libmman.def 12 | INSTALL+=lib-install 13 | endif 14 | 15 | all: $(TARGETS) 16 | 17 | mman.o: mman.c mman.h 18 | $(CC) -o mman.o -c mman.c -Wall -O3 -fomit-frame-pointer 19 | 20 | libmman.a: mman.o 21 | $(AR) cru libmman.a mman.o 22 | $(RANLIB) libmman.a 23 | 24 | static-install: 25 | mkdir -p $(DESTDIR)$(libdir) 26 | cp libmman.a $(DESTDIR)$(libdir) 27 | mkdir -p $(DESTDIR)$(incdir) 28 | cp mman.h $(DESTDIR)$(incdir) 29 | 30 | lib-install: 31 | mkdir -p $(DESTDIR)$(libdir) 32 | cp libmman.lib $(DESTDIR)$(libdir) 33 | 34 | install: $(INSTALL) 35 | 36 | test.exe: test.c mman.c mman.h 37 | $(CC) -o test.exe test.c -L. -lmman 38 | 39 | test: $(TARGETS) test.exe 40 | test.exe 41 | 42 | clean:: 43 | rm -f mman.o libmman.a libmman.def libmman.lib test.exe *.dat 44 | 45 | distclean: clean 46 | rm -f config.mak 47 | 48 | .PHONY: clean distclean install test 49 | -------------------------------------------------------------------------------- /html/flash.js: -------------------------------------------------------------------------------- 1 | //===== FLASH cards 2 | 3 | function flashFirmware(e) { 4 | e.preventDefault(); 5 | var fw_data = document.getElementById('fw-file').files[0]; 6 | 7 | $("#fw-form").setAttribute("hidden", ""); 8 | $("#fw-spinner").removeAttribute("hidden"); 9 | showNotification("Firmware is being updated ..."); 10 | 11 | ajaxReq("POST", "/flash/upload", function (resp) { 12 | ajaxReq("GET", "/flash/reboot", function (resp) { 13 | showNotification("Firmware has been successfully updated!"); 14 | setTimeout(function(){ window.location.reload()}, 4000); 15 | 16 | $("#fw-spinner").setAttribute("hidden", ""); 17 | $("#fw-form").removeAttribute("hidden"); 18 | }); 19 | }, null, fw_data) 20 | } 21 | 22 | function fetchFlash() { 23 | ajaxReq("GET", "/flash/next", function (resp) { 24 | $("#fw-slot").innerHTML = resp; 25 | $("#fw-spinner").setAttribute("hidden", ""); 26 | $("#fw-form").removeAttribute("hidden"); 27 | }); 28 | ajaxJson("GET", "/menu", function(data) { 29 | var v = $("#current-fw"); 30 | if (v != null) { v.innerHTML = data.version; } 31 | } 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis-CI file for Esp-Link 2 | 3 | language: c 4 | 5 | git: 6 | depth: 1000 7 | 8 | before_install: 9 | - curl -Ls http://s3.voneicken.com/xtensa-lx106-elf-20160330.tgx | tar Jxf - 10 | - curl -Ls http://s3.voneicken.com/esp_iot_sdk_v2.1.0.tgx | tar -C .. -Jxf - 11 | 12 | after_script: 13 | # upload to an S3 bucket, requires S3_BUCKET, AWS_ACCESS_KEY_ID and AWS_SECRET_KEY to be set 14 | # in environment using travis' repository settings 15 | - "if [[ -n \"$S3_BUCKET\" && -n \"$AWS_ACCESS_KEY_ID\" ]]; then 16 | echo Uploading *.tgz to $S3_BUCKET; 17 | curl -Ls https://github.com/rlmcpherson/s3gof3r/releases/download/v0.5.0/gof3r_0.5.0_linux_amd64.tar.gz | tar zxf - gof3r_0.5.0_linux_amd64/gof3r; 18 | mv gof3r*/gof3r .; 19 | ls *.tgz | xargs -I {} ./gof3r put -b $S3_BUCKET -k esp-link/{} --acl public-read -p {}; 20 | ls *.tgz | xargs -I {} echo \"URL: http://$S3_BUCKET/esp-link/{}\"; 21 | fi" 22 | 23 | compiler: gcc 24 | 25 | env: 26 | 27 | script: 28 | - export XTENSA_TOOLS_ROOT=$PWD/xtensa-lx106-elf/bin/ 29 | - export BRANCH=$TRAVIS_BRANCH 30 | #- export SDK_BASE=$PWD/esp_iot_sdk_v2.0.0.p1 31 | - git tag -n1 32 | - git describe --tags --match 'v*.0' --long --debug 33 | - make release 34 | 35 | notifications: 36 | email: false 37 | -------------------------------------------------------------------------------- /serial/uart.h: -------------------------------------------------------------------------------- 1 | #ifndef __UART_H__ 2 | #define __UART_H__ 3 | 4 | #include "uart_hw.h" 5 | 6 | // Receive callback function signature 7 | typedef void (*UartRecv_cb)(char *buf, short len); 8 | 9 | // Initialize UARTs to the provided baud rates (115200 recommended). This also makes the os_printf 10 | // calls use uart1 for output (for debugging purposes) 11 | void uart_init(uint32 conf0, UartBautRate uart0_br, UartBautRate uart1_br); 12 | 13 | // Transmit a buffer of characters on UART0 14 | void uart0_tx_buffer(char *buf, uint16 len); 15 | 16 | void uart0_write_char(char c); 17 | STATUS uart_tx_one_char(uint8 uart, uint8 c); 18 | 19 | void uart1_write_char(char c); 20 | 21 | // Add a receive callback function, this is called on the uart receive task each time a chunk 22 | // of bytes are received. A small number of callbacks can be added and they are all called 23 | // with all new characters. 24 | void uart_add_recv_cb(UartRecv_cb cb); 25 | 26 | // Turn UART interrupts off and poll for nchars or until timeout hits 27 | uint16_t uart0_rx_poll(char *buff, uint16_t nchars, uint32_t timeout_us); 28 | 29 | void uart0_baud(int rate); 30 | void uart0_config(uint8_t data_bits, uint8_t parity, uint8_t stop_bits); 31 | void uart_config(uint8 uart_no, UartBautRate baudrate, uint32 conf0); 32 | 33 | #endif /* __UART_H__ */ 34 | -------------------------------------------------------------------------------- /httpd/multipart.h: -------------------------------------------------------------------------------- 1 | #ifndef MULTIPART_H 2 | #define MULTIPART_H 3 | 4 | #include 5 | 6 | typedef enum { 7 | FILE_UPLOAD_START, // multipart: uploading files started 8 | FILE_START, // multipart: the start of a new file (can be more) 9 | FILE_DATA, // multipart: file data 10 | FILE_DONE, // multipart: file end 11 | FILE_UPLOAD_DONE, // multipart: finished for all files 12 | } MultipartCmd; 13 | 14 | // multipart callback 15 | // -> FILE_START : data+dataLen contains the filename, position is 0 16 | // -> FILE_DATA : data+dataLen contains file data, position is the file position 17 | // -> FILE_DONE : data+dataLen is 0, position is the complete file size 18 | 19 | typedef int (* MultipartCallback)(MultipartCmd cmd, char *data, int dataLen, int position); 20 | 21 | struct _MultipartCtx; // the context for multipart listening 22 | 23 | typedef struct _MultipartCtx MultipartCtx; 24 | 25 | // use this for creating a multipart context 26 | MultipartCtx * ICACHE_FLASH_ATTR multipartCreateContext(MultipartCallback callback); 27 | 28 | // for destroying multipart context 29 | void ICACHE_FLASH_ATTR multipartDestroyContext(MultipartCtx * context); 30 | 31 | // use this function for processing HTML multipart updates 32 | int ICACHE_FLASH_ATTR multipartProcess(MultipartCtx * context, HttpdConnData * post ); 33 | 34 | #endif /* MULTIPART_H */ 35 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for github.com/jeelabs/esp-link 2 | # 3 | # This dockerfile is intended to be used to compile esp-link as it's checked out on 4 | # your desktop/laptop. You can git clone esp-link, and then compile it using 5 | # a commandline of `docker run -v $PWD:/esp-link jeelabs/esp-link`. The -v mounts 6 | # your esp-link source directory onto /esp-link in the container and the default command is 7 | # to run make. 8 | # If you would like to create your own container image, use `docker build -t esp-link .` 9 | FROM ubuntu:16.04 10 | 11 | RUN apt-get update \ 12 | && apt-get install -y software-properties-common build-essential python curl git \ 13 | zlib1g-dev openjdk-8-jre-headless 14 | 15 | RUN curl -Ls http://s3.voneicken.com/xtensa-lx106-elf-20160330.tgx | tar Jxf - 16 | RUN curl -Ls http://s3.voneicken.com/esp_iot_sdk_v2.1.0.tgx | tar -Jxf - 17 | 18 | ENV XTENSA_TOOLS_ROOT /xtensa-lx106-elf/bin/ 19 | 20 | # This could be used to create an image with esp-link in it from github: 21 | #RUN git clone https://github.com/jeelabs/esp-link 22 | 23 | # This could be used to create an image with esp-link in it from the local dir: 24 | #COPY . esp-link/ 25 | 26 | # Expect the esp-link source/home dir to be mounted here: 27 | VOLUME /esp-link 28 | WORKDIR /esp-link 29 | 30 | # Default command is to run a build, can be overridden on the docker commandline: 31 | CMD make 32 | -------------------------------------------------------------------------------- /html/flash.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Upgrade Firmware

4 |
5 | 6 |
7 |
8 |
9 |
10 |

Upgrade Firmware 11 |
12 |

13 | 29 |
30 |
31 |
32 |
33 |
34 |
35 | 36 | 37 | 43 | 44 | -------------------------------------------------------------------------------- /espfs/espfs.h: -------------------------------------------------------------------------------- 1 | #ifndef ESPFS_H 2 | #define ESPFS_H 3 | 4 | #include "espfsformat.h" 5 | 6 | typedef enum { 7 | ESPFS_INIT_RESULT_OK, 8 | ESPFS_INIT_RESULT_NO_IMAGE, 9 | ESPFS_INIT_RESULT_BAD_ALIGN, 10 | } EspFsInitResult; 11 | 12 | // Only 1 MByte of the flash can be directly accessed with ESP8266 13 | // If flash size is >1 Mbyte, SDK API is required to retrieve flash content 14 | typedef enum { 15 | ESPFS_MEMORY, // read data directly from memory (fast, max 1 MByte) 16 | ESPFS_FLASH, // read data from flash using SDK API (no limit for the size) 17 | } EspFsSource; 18 | 19 | typedef struct EspFsFile EspFsFile; 20 | typedef struct EspFsContext EspFsContext; 21 | 22 | typedef struct { 23 | EspFsHeader header; // the header of the current file 24 | EspFsContext *ctx; // pointer to espfs context 25 | char name[256]; // the name of the current file 26 | char *position; // position of the iterator (pointer on the file system) 27 | } EspFsIterator; 28 | 29 | extern EspFsContext * espLinkCtx; 30 | extern EspFsContext * userPageCtx; 31 | 32 | EspFsInitResult espFsInit(EspFsContext *ctx, void *flashAddress, EspFsSource source); 33 | EspFsFile *espFsOpen(EspFsContext *ctx, char *fileName); 34 | int espFsIsValid(EspFsContext *ctx); 35 | int espFsFlags(EspFsFile *fh); 36 | int espFsRead(EspFsFile *fh, char *buff, int len); 37 | void espFsClose(EspFsFile *fh); 38 | 39 | void espFsIteratorInit(EspFsContext *ctx, EspFsIterator *iterator); 40 | int espFsIteratorNext(EspFsIterator *iterator); 41 | 42 | #endif -------------------------------------------------------------------------------- /espfs/mkespfsimage/mman-win32/mman.h: -------------------------------------------------------------------------------- 1 | /* 2 | * sys/mman.h 3 | * mman-win32 4 | */ 5 | 6 | #ifndef _SYS_MMAN_H_ 7 | #define _SYS_MMAN_H_ 8 | 9 | #ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. 10 | #define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. 11 | #endif 12 | 13 | /* All the headers include this file. */ 14 | #ifndef _MSC_VER 15 | #include <_mingw.h> 16 | #endif 17 | 18 | #include 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #define PROT_NONE 0 25 | #define PROT_READ 1 26 | #define PROT_WRITE 2 27 | #define PROT_EXEC 4 28 | 29 | #define MAP_FILE 0 30 | #define MAP_SHARED 1 31 | #define MAP_PRIVATE 2 32 | #define MAP_TYPE 0xf 33 | #define MAP_FIXED 0x10 34 | #define MAP_ANONYMOUS 0x20 35 | #define MAP_ANON MAP_ANONYMOUS 36 | 37 | #define MAP_FAILED ((void *)-1) 38 | 39 | /* Flags for msync. */ 40 | #define MS_ASYNC 1 41 | #define MS_SYNC 2 42 | #define MS_INVALIDATE 4 43 | 44 | void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off); 45 | int munmap(void *addr, size_t len); 46 | int mprotect(void *addr, size_t len, int prot); 47 | int msync(void *addr, size_t len, int flags); 48 | int mlock(const void *addr, size_t len); 49 | int munlock(const void *addr, size_t len); 50 | 51 | #ifdef __cplusplus 52 | }; 53 | #endif 54 | 55 | #endif /* _SYS_MMAN_H_ */ 56 | -------------------------------------------------------------------------------- /esp-link/cgi.h: -------------------------------------------------------------------------------- 1 | #ifndef CGI_H 2 | #define CGI_H 3 | 4 | #include 5 | #include "httpd.h" 6 | 7 | void noCacheHeaders(HttpdConnData *connData, int code); 8 | void jsonHeader(HttpdConnData *connData, int code); 9 | void errorResponse(HttpdConnData *connData, int code, char *message); 10 | 11 | // Get the HTTP query-string param 'name' and store it at 'config' with max length 12 | // 'max_len' (incl terminating zero), returns -1 on error, 0 if not found, 1 if found 13 | int8_t getStringArg(HttpdConnData *connData, char *name, char *config, int max_len); 14 | 15 | // Get the HTTP query-string param 'name' and store it as a int8_t value at 'config', 16 | // supports signed and unsigned, returns -1 on error, 0 if not found, 1 if found 17 | int8_t getInt8Arg(HttpdConnData *connData, char *name, int8_t *config); 18 | 19 | // Get the HTTP query-string param 'name' and store it as a uint8_t value at 'config', 20 | // supports signed and unsigned, returns -1 on error, 0 if not found, 1 if found 21 | int8_t getUInt8Arg(HttpdConnData *connData, char *name, uint8_t *config); 22 | 23 | // Get the HTTP query-string param 'name' and store it as a uint16_t value at 'config', 24 | // supports signed and unsigned, returns -1 on error, 0 if not found, 1 if found 25 | int8_t getUInt16Arg(HttpdConnData *connData, char *name, uint16_t *config); 26 | 27 | // Get the HTTP query-string param 'name' and store it boolean value at 'config', 28 | // supports 1/true and 0/false, returns -1 on error, 0 if not found, 1 if found 29 | int8_t getBoolArg(HttpdConnData *connData, char *name, uint8_t *config); 30 | 31 | int cgiMenu(HttpdConnData *connData); 32 | 33 | uint8_t UTILS_StrToIP(const char *str, void *ip); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /serial/serbridge.h: -------------------------------------------------------------------------------- 1 | #ifndef __SER_BRIDGE_H__ 2 | #define __SER_BRIDGE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define MAX_CONN 4 9 | #define SER_BRIDGE_TIMEOUT 300 // 300 seconds = 5 minutes 10 | 11 | // Send buffer size 12 | #define MAX_TXBUFFER (2*1460) 13 | 14 | enum connModes { 15 | cmInit = 0, // initialization mode: nothing received yet 16 | cmPGMInit, // initialization mode for programming 17 | cmTransparent, // transparent mode 18 | cmPGM, // Arduino/AVR/ARM programming mode 19 | cmTelnet, // use telnet escape sequences for programming mode 20 | }; 21 | 22 | typedef struct serbridgeConnData { 23 | struct espconn *conn; 24 | enum connModes conn_mode; // connection mode 25 | uint8_t telnet_state; 26 | uint16 txbufferlen; // length of data in txbuffer 27 | char *txbuffer; // buffer for the data to send 28 | char *sentbuffer; // buffer sent, awaiting callback to get freed 29 | uint32_t txoverflow_at; // when the transmitter started to overflow 30 | bool readytosend; // true, if txbuffer can be sent by espconn_sent 31 | } serbridgeConnData; 32 | 33 | // port1 is transparent&programming, second port is programming only 34 | void ICACHE_FLASH_ATTR serbridgeInit(int port1, int port2); 35 | void ICACHE_FLASH_ATTR serbridgeInitPins(void); 36 | void ICACHE_FLASH_ATTR serbridgeUartCb(char *buf, short len); 37 | void ICACHE_FLASH_ATTR serbridgeReset(); 38 | 39 | int ICACHE_FLASH_ATTR serbridgeInMCUFlashing(); 40 | 41 | // callback when receiving UART chars when in programming mode 42 | extern void (*programmingCB)(char *buffer, short length); 43 | 44 | #endif /* __SER_BRIDGE_H__ */ 45 | -------------------------------------------------------------------------------- /WINDOWS.md: -------------------------------------------------------------------------------- 1 | * Install [SourceTree](https://www.sourcetreeapp.com) and check CLI git or other git distribution to obtain git from CLI 2 | * Install the latest Java JRE 3 | * Install Python 2.7 to C:\Python27 4 | * Install link shell extension from [here](http://schinagl.priv.at/nt/hardlinkshellext/linkshellextension.html) 5 | * Download and install the [Windows Unofficial Development Kit for Espressif ESP8266](http://programs74.ru/get.php?file=EspressifESP8266DevKit) to c:\espressif 6 | * Create a symbolic link for java/bin and git/bin directories under C:\espressif\git-bin and C:\espressif\java-bin. You must do this because "make" doesn't work properly with paths like "program files(x86)". You can see all the expected paths in the [espmake.cmd](https://github.com/jeelabs/esp-link/blob/master/espmake.cmd) 7 | * [Download](http://sourceforge.net/projects/mingw/files/Installer/) and install MinGW. Run mingw-get-setup.exe. During the installation process select without GUI. (uncheck "... also install support for the graphical user interface") 8 | * [Download](http://programs74.ru/get.php?file=EspressifESP8266DevKitAddon) the scripts to automate the installation of additional modules for MinGW. 9 | * Run install-mingw-package.bat. This will install the basic modules required for MinGW to build esp8266. 10 | * Checkout esp-link from git to C:\espressif\esp-link 11 | * When you're done open a command prompt and run: espmake.cmd "make all wiflash" 12 | * For a new flash over serial use: espmake.cmd "make all flash" 13 | * If you want to program with serial but not loose your config each time use: espmake.cmd "make all baseflash" 14 | * You can open the esp-link.sln file in Visual Studio 2013. "Build Solution" will issue "make all wiflash". "Clean Solution" will issue "make clean". "Rebuild Solution" will issue "make clean all". This can be changed under solution properties -> Configuration Properties -> NMake -------------------------------------------------------------------------------- /esp-link/stk500.h: -------------------------------------------------------------------------------- 1 | /* STK500 constants list, from AVRDUDE 2 | * 3 | * Trivial set of constants derived from Atmel App Note AVR061 4 | * Not copyrighted. Released to the public domain. 5 | */ 6 | 7 | #define STK_OK 0x10 8 | #define STK_FAILED 0x11 // Not used 9 | #define STK_UNKNOWN 0x12 // Not used 10 | #define STK_NODEVICE 0x13 // Not used 11 | #define STK_INSYNC 0x14 // ' ' 12 | #define STK_NOSYNC 0x15 // Not used 13 | #define ADC_CHANNEL_ERROR 0x16 // Not used 14 | #define ADC_MEASURE_OK 0x17 // Not used 15 | #define PWM_CHANNEL_ERROR 0x18 // Not used 16 | #define PWM_ADJUST_OK 0x19 // Not used 17 | #define CRC_EOP 0x20 // 'SPACE' 18 | #define STK_GET_SYNC 0x30 // '0' 19 | #define STK_GET_SIGN_ON 0x31 // '1' 20 | #define STK_SET_PARAMETER 0x40 // '@' 21 | #define STK_GET_PARAMETER 0x41 // 'A' 22 | #define STK_SET_DEVICE 0x42 // 'B' 23 | #define STK_SET_DEVICE_EXT 0x45 // 'E' 24 | #define STK_ENTER_PROGMODE 0x50 // 'P' 25 | #define STK_LEAVE_PROGMODE 0x51 // 'Q' 26 | #define STK_CHIP_ERASE 0x52 // 'R' 27 | #define STK_CHECK_AUTOINC 0x53 // 'S' 28 | #define STK_LOAD_ADDRESS 0x55 // 'U' 29 | #define STK_UNIVERSAL 0x56 // 'V' 30 | #define STK_PROG_FLASH 0x60 // '`' 31 | #define STK_PROG_DATA 0x61 // 'a' 32 | #define STK_PROG_FUSE 0x62 // 'b' 33 | #define STK_PROG_LOCK 0x63 // 'c' 34 | #define STK_PROG_PAGE 0x64 // 'd' 35 | #define STK_PROG_FUSE_EXT 0x65 // 'e' 36 | #define STK_READ_FLASH 0x70 // 'p' 37 | #define STK_READ_DATA 0x71 // 'q' 38 | #define STK_READ_FUSE 0x72 // 'r' 39 | #define STK_READ_LOCK 0x73 // 's' 40 | #define STK_READ_PAGE 0x74 // 't' 41 | #define STK_READ_SIGN 0x75 // 'u' 42 | #define STK_READ_OSCCAL 0x76 // 'v' 43 | #define STK_READ_FUSE_EXT 0x77 // 'w' 44 | #define STK_READ_OSCCAL_EXT 0x78 // 'x' 45 | -------------------------------------------------------------------------------- /mqtt/pktbuf.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt 2 | 3 | #include 4 | #include "pktbuf.h" 5 | 6 | #ifdef PKTBUF_DBG 7 | //static void ICACHE_FLASH_ATTR 8 | //PktBuf_Print(PktBuf *buf) { 9 | // os_printf("PktBuf:"); 10 | // for (int i=-16; i<0; i++) 11 | // os_printf(" %02X", ((uint8_t*)buf)[i]); 12 | // os_printf(" %p", buf); 13 | // for (int i=0; i<16; i++) 14 | // os_printf(" %02X", ((uint8_t*)buf)[i]); 15 | // os_printf("\n"); 16 | // os_printf("PktBuf: next=%p len=0x%04x\n", 17 | // ((void**)buf)[-4], ((uint16_t*)buf)[-6]); 18 | //} 19 | #endif 20 | 21 | 22 | PktBuf * ICACHE_FLASH_ATTR 23 | PktBuf_New(uint16_t length) { 24 | PktBuf *buf = os_zalloc(length+sizeof(PktBuf)); 25 | buf->next = NULL; 26 | buf->filled = 0; 27 | //os_printf("PktBuf_New: %p l=%d->%d d=%p\n", 28 | // buf, length, length+sizeof(PktBuf), buf->data); 29 | return buf; 30 | } 31 | 32 | PktBuf * ICACHE_FLASH_ATTR 33 | PktBuf_Push(PktBuf *headBuf, PktBuf *buf) { 34 | if (headBuf == NULL) { 35 | //os_printf("PktBuf_Push: %p\n", buf); 36 | return buf; 37 | } 38 | PktBuf *h = headBuf; 39 | while (h->next != NULL) h = h->next; 40 | h->next = buf; 41 | //os_printf("PktBuf_Push: %p->..->%p\n", headBuf, buf); 42 | return headBuf; 43 | } 44 | 45 | PktBuf * ICACHE_FLASH_ATTR 46 | PktBuf_Unshift(PktBuf *headBuf, PktBuf *buf) { 47 | buf->next = headBuf; 48 | //os_printf("PktBuf_Unshift: %p->%p\n", buf, buf->next); 49 | return buf; 50 | } 51 | 52 | PktBuf * ICACHE_FLASH_ATTR 53 | PktBuf_Shift(PktBuf *headBuf) { 54 | PktBuf *buf = headBuf->next; 55 | headBuf->next = NULL; 56 | //os_printf("PktBuf_Shift: (%p)->%p\n", headBuf, buf); 57 | return buf; 58 | } 59 | 60 | PktBuf * ICACHE_FLASH_ATTR 61 | PktBuf_ShiftFree(PktBuf *headBuf) { 62 | PktBuf *buf = headBuf->next; 63 | //os_printf("PktBuf_ShiftFree: (%p)->%p\n", headBuf, buf); 64 | os_free(headBuf); 65 | return buf; 66 | } 67 | -------------------------------------------------------------------------------- /esp-link/pgmshared.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 by Thorsten von Eicken, see LICENSE.txt in the esp-link repo 2 | // Copyright (c) 2017 by Danny Backx 3 | 4 | #ifndef _PGM_SHARED_H_ 5 | #define _PGM_SHARED_H_ 6 | 7 | #include "user_config.h" 8 | 9 | #define RESP_SZ 64 10 | #define ERR_MAX 128 11 | 12 | extern char responseBuf[RESP_SZ]; 13 | extern short responseLen; 14 | extern char errMessage[ERR_MAX]; 15 | 16 | // structure used to remember request details from one callback to the next 17 | // allocated dynamically so we don't burn so much static RAM 18 | extern struct optibootData { 19 | char *saved; // buffer for saved incomplete hex records 20 | char *pageBuf; // buffer for received data to be sent to AVR 21 | uint16_t pageLen; // number of bytes in pageBuf 22 | uint16_t pgmSz; // size of flash page to be programmed at a time 23 | uint32_t pgmDone; // number of bytes programmed 24 | uint32_t address; // address to write next page to 25 | uint32_t segment; // for extended segment addressing, added to the address field 26 | uint32_t startTime; // time of program POST request 27 | HttpdConnData *conn; // request doing the programming, so we can cancel it 28 | bool eof; // got EOF record 29 | 30 | // Whether to use the Mega (STK500v2) protocol 31 | bool mega; 32 | 33 | // STK500v2 variables 34 | int hardwareVersion, 35 | firmwareVersionMajor, 36 | firmwareVersionMinor, 37 | vTarget; 38 | uint8_t signature[3]; 39 | uint8_t lfuse, hfuse, efuse; 40 | } *optibootData; 41 | 42 | bool ICACHE_FLASH_ATTR checkHex(char *buf, short len); 43 | uint32_t ICACHE_FLASH_ATTR getHexValue(char *buf, short len); 44 | bool ICACHE_FLASH_ATTR processRecord(char *buf, short len); 45 | bool megaProgramPage(void); 46 | bool optibootProgramPage(void); 47 | 48 | #ifdef OPTIBOOT_DBG 49 | #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) 50 | #else 51 | #define DBG(format, ...) do { } while(0) 52 | #endif 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /serial/serled.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static ETSTimer serledTimer; 8 | 9 | static void ICACHE_FLASH_ATTR setSerled(int on) { 10 | int8_t pin = flashConfig.ser_led_pin; 11 | if (pin < 0) return; // disabled 12 | // LED is active-low 13 | if (on) { 14 | gpio_output_set(0, (1<= 0) { 34 | makeGpio(pin); 35 | gpio_output_set(0, 0, (1< wrote this file. As long as you retain 9 | * this notice you can do whatever you want with this stuff. If we meet some day, 10 | * and you think this stuff is worth it, you can buy me a beer in return. 11 | * ---------------------------------------------------------------------------- 12 | */ 13 | 14 | 15 | #include 16 | #include "auth.h" 17 | #include "base64.h" 18 | 19 | int ICACHE_FLASH_ATTR authBasic(HttpdConnData *connData) { 20 | const char *forbidden="401 Forbidden."; 21 | int no=0; 22 | int r; 23 | char hdr[(AUTH_MAX_USER_LEN+AUTH_MAX_PASS_LEN+2)*10]; 24 | char userpass[AUTH_MAX_USER_LEN+AUTH_MAX_PASS_LEN+2]; 25 | char user[AUTH_MAX_USER_LEN]; 26 | char pass[AUTH_MAX_PASS_LEN]; 27 | if (connData->conn==NULL) { 28 | //Connection aborted. Clean up. 29 | return HTTPD_CGI_DONE; 30 | } 31 | 32 | r=httpdGetHeader(connData, "Authorization", hdr, sizeof(hdr)); 33 | if (r && strncmp(hdr, "Basic", 5)==0) { 34 | r=base64_decode(strlen(hdr)-6, hdr+6, sizeof(userpass), (unsigned char *)userpass); 35 | if (r<0) r=0; //just clean out string on decode error 36 | userpass[r]=0; //zero-terminate user:pass string 37 | // os_printf("Auth: %s\n", userpass); 38 | while (((AuthGetUserPw)(connData->cgiArg))(connData, no, 39 | user, AUTH_MAX_USER_LEN, pass, AUTH_MAX_PASS_LEN)) { 40 | //Check user/pass against auth header 41 | if (strlen(userpass)==strlen(user)+strlen(pass)+1 && 42 | os_strncmp(userpass, user, strlen(user))==0 && 43 | userpass[strlen(user)]==':' && 44 | os_strcmp(userpass+strlen(user)+1, pass)==0) { 45 | //Authenticated. Yay! 46 | return HTTPD_CGI_AUTHENTICATED; 47 | } 48 | no++; //Not authenticated with this user/pass. Check next user/pass combo. 49 | } 50 | } 51 | 52 | //Not authenticated. Go bug user with login screen. 53 | httpdStartResponse(connData, 401); 54 | httpdHeader(connData, "Content-Type", "text/plain"); 55 | httpdHeader(connData, "WWW-Authenticate", "Basic realm=\""HTTP_AUTH_REALM"\""); 56 | httpdEndHeaders(connData); 57 | httpdSend(connData, forbidden, -1); 58 | //Okay, all done. 59 | return HTTPD_CGI_DONE; 60 | } 61 | 62 | -------------------------------------------------------------------------------- /esp-link/cgitcp.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt 2 | // // TCP Client settings 3 | 4 | #include 5 | #include "cgi.h" 6 | #include "config.h" 7 | #include "cgitcp.h" 8 | 9 | // Cgi to return TCP client settings 10 | int ICACHE_FLASH_ATTR cgiTcpGet(HttpdConnData *connData) { 11 | char buff[1024]; 12 | int len; 13 | 14 | if (connData->conn==NULL) return HTTPD_CGI_DONE; 15 | 16 | len = os_sprintf(buff, "{ \"tcp_enable\":%d, \"rssi_enable\": %d, \"api_key\":\"%s\" }", 17 | flashConfig.tcp_enable, flashConfig.rssi_enable, flashConfig.api_key); 18 | 19 | jsonHeader(connData, 200); 20 | httpdSend(connData, buff, len); 21 | return HTTPD_CGI_DONE; 22 | } 23 | 24 | // Cgi to change choice of pin assignments 25 | int ICACHE_FLASH_ATTR cgiTcpSet(HttpdConnData *connData) { 26 | if (connData->conn==NULL) return HTTPD_CGI_DONE; 27 | 28 | // Handle tcp_enable flag 29 | char buff[128]; 30 | int len = httpdFindArg(connData->getArgs, "tcp_enable", buff, sizeof(buff)); 31 | if (len <= 0) { 32 | jsonHeader(connData, 400); 33 | return HTTPD_CGI_DONE; 34 | } 35 | flashConfig.tcp_enable = os_strcmp(buff, "true") == 0; 36 | 37 | // Handle rssi_enable flag 38 | len = httpdFindArg(connData->getArgs, "rssi_enable", buff, sizeof(buff)); 39 | if (len <= 0) { 40 | jsonHeader(connData, 400); 41 | return HTTPD_CGI_DONE; 42 | } 43 | flashConfig.rssi_enable = os_strcmp(buff, "true") == 0; 44 | 45 | // Handle api_key flag 46 | len = httpdFindArg(connData->getArgs, "api_key", buff, sizeof(buff)); 47 | if (len < 0) { 48 | jsonHeader(connData, 400); 49 | return HTTPD_CGI_DONE; 50 | } 51 | buff[sizeof(flashConfig.api_key)-1] = 0; // ensure we don't get an overrun 52 | os_strcpy(flashConfig.api_key, buff); 53 | 54 | if (configSave()) { 55 | httpdStartResponse(connData, 200); 56 | httpdEndHeaders(connData); 57 | } else { 58 | httpdStartResponse(connData, 500); 59 | httpdEndHeaders(connData); 60 | httpdSend(connData, "Failed to save config", -1); 61 | } 62 | return HTTPD_CGI_DONE; 63 | } 64 | 65 | int ICACHE_FLASH_ATTR cgiTcp(HttpdConnData *connData) { 66 | if (connData->requestType == HTTPD_METHOD_GET) { 67 | return cgiTcpGet(connData); 68 | } else if (connData->requestType == HTTPD_METHOD_POST) { 69 | return cgiTcpSet(connData); 70 | } else { 71 | jsonHeader(connData, 404); 72 | return HTTPD_CGI_DONE; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /esp-link/task.c: -------------------------------------------------------------------------------- 1 | /* 2 | * task.c 3 | * 4 | * Copyright 2015 Susi's Strolch 5 | * 6 | * For license information see projects "License.txt" 7 | * 8 | * Not sure if it's save to use ICACHE_FLASH_ATTR, so we're running from RAM 9 | */ 10 | 11 | #undef USRTASK_DBG 12 | 13 | #include "esp8266.h" 14 | #include 15 | 16 | #define MAXUSRTASKS 8 17 | 18 | #ifdef USRTASK_DBG 19 | #define DBG_USRTASK(format, ...) os_printf(format, ## __VA_ARGS__) 20 | #else 21 | #define DBG_USRTASK(format, ...) do { } while(0) 22 | #endif 23 | 24 | LOCAL os_event_t *_task_queue = NULL; // system_os_task queue 25 | LOCAL os_task_t *usr_task_queue = NULL; // user task queue 26 | 27 | // it seems save to run the usr_event_handler from RAM, so no ICACHE_FLASH_ATTR here... 28 | 29 | LOCAL void usr_event_handler(os_event_t *e) 30 | { 31 | DBG_USRTASK("usr_event_handler: event %p (sig=%d, par=%p)\n", e, (int)e->sig, (void *)e->par); 32 | if (usr_task_queue[e->sig] == NULL || e->sig < 0 || e->sig >= MAXUSRTASKS) { 33 | os_printf("usr_event_handler: task %d %s\n", (int)e->sig, 34 | usr_task_queue[e->sig] == NULL ? "not registered" : "out of range"); 35 | return; 36 | } 37 | (usr_task_queue[e->sig])(e); 38 | } 39 | 40 | LOCAL void init_usr_task() { 41 | if (_task_queue == NULL) 42 | _task_queue = (os_event_t *)os_zalloc(sizeof(os_event_t) * _task_queueLen); 43 | 44 | if (usr_task_queue == NULL) 45 | usr_task_queue = (os_task_t *)os_zalloc(sizeof(os_task_t) * MAXUSRTASKS); 46 | 47 | system_os_task(usr_event_handler, _taskPrio, _task_queue, _task_queueLen); 48 | } 49 | 50 | // public functions 51 | bool post_usr_task(uint8_t task, os_param_t par) 52 | { 53 | return system_os_post(_taskPrio, task, par); 54 | } 55 | 56 | uint8_t register_usr_task (os_task_t event) 57 | { 58 | int task; 59 | 60 | DBG_USRTASK("register_usr_task: %p\n", event); 61 | if (_task_queue == NULL || usr_task_queue == NULL) 62 | init_usr_task(); 63 | 64 | for (task = 0; task < MAXUSRTASKS; task++) { 65 | if (usr_task_queue[task] == event) 66 | return task; // task already registered - bail out... 67 | } 68 | 69 | for (task = 0; task < MAXUSRTASKS; task++) { 70 | if (usr_task_queue[task] == NULL) { 71 | DBG_USRTASK("register_usr_task: assign task #%d\n", task); 72 | usr_task_queue[task] = event; 73 | break; 74 | } 75 | } 76 | return task; 77 | } 78 | 79 | -------------------------------------------------------------------------------- /html/log.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Debug Log

4 |
5 | 6 |
7 |

The debug log shows the most recent characters printed by the esp-link software itself to 8 | its own debug log.

9 |
10 |

11 | Refresh 12 |  Reset esp-link 13 |

14 |

15 | UART debug log: 16 | auto 17 | off 18 | on uart0 19 | on uart1 20 |

21 |
22 |

23 |     
24 |
25 | 26 | 27 | 28 | 29 | 60 | 61 | -------------------------------------------------------------------------------- /html/mqtt.js: -------------------------------------------------------------------------------- 1 | //===== MQTT cards 2 | 3 | function changeMqtt(e) { 4 | e.preventDefault(); 5 | var url = "mqtt?1=1"; 6 | var i, inputs = document.querySelectorAll('#mqtt-form input'); 7 | for (i = 0; i < inputs.length; i++) { 8 | if (inputs[i].type != "checkbox") 9 | url += "&" + inputs[i].name + "=" + inputs[i].value; 10 | }; 11 | 12 | hideWarning(); 13 | var cb = $("#mqtt-button"); 14 | addClass(cb, 'pure-button-disabled'); 15 | ajaxSpin("POST", url, function (resp) { 16 | showNotification("MQTT updated"); 17 | removeClass(cb, 'pure-button-disabled'); 18 | }, function (s, st) { 19 | showWarning("Error: " + st); 20 | removeClass(cb, 'pure-button-disabled'); 21 | window.setTimeout(fetchMqtt, 100); 22 | }); 23 | } 24 | 25 | function displayMqtt(data) { 26 | Object.keys(data).forEach(function (v) { 27 | el = $("#" + v); 28 | if (el != null) { 29 | if (el.nodeName === "INPUT") el.value = data[v]; 30 | else el.innerHTML = data[v]; 31 | return; 32 | } 33 | el = document.querySelector('input[name="' + v + '"]'); 34 | if (el != null) { 35 | if (el.type == "checkbox") el.checked = data[v] > 0; 36 | else el.value = data[v]; 37 | } 38 | }); 39 | $("#mqtt-spinner").setAttribute("hidden", ""); 40 | $("#mqtt-status-spinner").setAttribute("hidden", ""); 41 | $("#mqtt-form").removeAttribute("hidden"); 42 | $("#mqtt-status-form").removeAttribute("hidden"); 43 | 44 | var i, inputs = $("input"); 45 | for (i = 0; i < inputs.length; i++) { 46 | if (inputs[i].type == "checkbox") 47 | inputs[i].onclick = function () { setMqtt(this.name, this.checked) }; 48 | } 49 | } 50 | 51 | function fetchMqtt() { 52 | ajaxJson("GET", "/mqtt", displayMqtt, function () { 53 | window.setTimeout(fetchMqtt, 1000); 54 | }); 55 | } 56 | 57 | function changeMqttStatus(e) { 58 | e.preventDefault(); 59 | var v = document.querySelector('input[name="mqtt-status-topic"]').value; 60 | ajaxSpin("POST", "/mqtt?mqtt-status-topic=" + v, function () { 61 | showNotification("MQTT status settings updated"); 62 | }, function (s, st) { 63 | showWarning("Error: " + st); 64 | window.setTimeout(fetchMqtt, 100); 65 | }); 66 | } 67 | 68 | function setMqtt(name, v) { 69 | ajaxSpin("POST", "/mqtt?" + name + "=" + (v ? 1 : 0), function () { 70 | var n = name.replace("-enable", ""); 71 | showNotification(n + " is now " + (v ? "enabled" : "disabled")); 72 | }, function () { 73 | showWarning("Enable/disable failed"); 74 | window.setTimeout(fetchMqtt, 100); 75 | }); 76 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to Esp-Link 2 | ======================== 3 | 4 | Esp-link is not the work of a single individual, rather many people have contributed directly or indirectly. 5 | Your contribution is very much appreciated, but please follow these guidelines to make the task easier on 6 | everyone. 7 | 8 | - Contributions do not have to be in the form of code: often documentation, how-tos are very valuable and answering questions 9 | in github issues as well as gitter is also very valuable and welcome! 10 | - Before you make a change or submit a change via a pull request, **open an issue and discuss your proposed change**. Gitter 11 | is a good alternative to a github issue. This ensures that you don't spend time doing work that ultimately won't be accepted. 12 | There's nothing more frustrating than receiving a pull-request that has lots of goodies but doesn't fit because it wasn't 13 | discussed and agreed upon up-front. 14 | - Keep your pull request as small as practical, if you have 3 things you want to change, please create 3 pull requests, 15 | or at the very least, make sure your 3 changes are in different commits. This makes the review and testing easier 16 | and ensures that if one feature is good to go it can be merged even if another feature needs more tweaking. 17 | - The esp-link codebase is not uniform, it comes from a variety of sources, in particular esphttpd. A result of this is 18 | that there is more than one coding style in use. If you make changes to existing files, please respect the file's 19 | coding style (yes, sometimes that's not even totally uniform). Your overall goal should be for your code or changes to 20 | look as if the original author had made them, not how you would like them to look. 21 | - Changes that reformat or reorganize code will generally not be accepted, please do not mix them with other functionality 22 | changes you are making and certainly discuss them first. Accept the fact that some people prefer bastards over pure-breads ;-). 23 | - Esp-link has a mission stated in the readme.md, changes that deviate from that mission will generally be rejected. The reason 24 | is that at the end of the day focusing on doing one thing well has a higher chance of succeeding than doing many things. 25 | In that sense, esp-link is not a swiss-army knife firmware. (This being said, many people have used esp-link as a basis to add 26 | their own functionality, which is very cool.) 27 | 28 | I believe the above guidelines are pretty standard across a very large number of open source projects and not unique to esp-link, 29 | so please do not get discouraged. Thank you for taking a look at esp-link! 30 | -------------------------------------------------------------------------------- /html/services.js: -------------------------------------------------------------------------------- 1 | function changeServices(e) { 2 | e.preventDefault(); 3 | var url = "services/update?1=1"; 4 | var i, inputs = document.querySelectorAll("#" + e.target.id + " input,select"); 5 | for (i = 0; i < inputs.length; i++) { 6 | if (inputs[i].type == "checkbox") { 7 | if (inputs[i].name.slice(-6) == "enable") 8 | continue; 9 | var val = (inputs[i].checked) ? 1 : 0; 10 | url += "&" + inputs[i].name + "=" + val; 11 | } 12 | else 13 | url += "&" + inputs[i].name + "=" + inputs[i].value; 14 | }; 15 | 16 | hideWarning(); 17 | var n = e.target.id.replace("-form", ""); 18 | var cb = $("#" + n + "-button"); 19 | addClass(cb, "pure-button-disabled"); 20 | ajaxSpin("POST", url, function (resp) { 21 | showNotification(n + " updated"); 22 | removeClass(cb, "pure-button-disabled"); 23 | }, function (s, st) { 24 | showWarning("Error: " + st); 25 | removeClass(cb, "pure-button-disabled"); 26 | window.setTimeout(fetchServices, 100); 27 | }); 28 | } 29 | 30 | function displayServices(data) { 31 | Object.keys(data).forEach(function (v) { 32 | el = $("#" + v); 33 | if (el != null) { 34 | if (el.nodeName === "INPUT") el.value = data[v]; 35 | else el.innerHTML = data[v]; 36 | return; 37 | } 38 | 39 | el = document.querySelector('input[name="' + v + '"]'); 40 | if (el == null) 41 | el = document.querySelector('select[name="' + v + '"]'); 42 | 43 | if (el != null) { 44 | if (el.type == "checkbox") { 45 | el.checked = data[v] == "enabled"; 46 | } else el.value = data[v]; 47 | } 48 | }); 49 | 50 | $("#syslog-spinner").setAttribute("hidden", ""); 51 | $("#sntp-spinner").setAttribute("hidden", ""); 52 | $("#mdns-spinner").setAttribute("hidden", ""); 53 | 54 | if (data.syslog_host !== undefined) { 55 | $("#Syslog-form").removeAttribute("hidden"); 56 | } else { 57 | // syslog disabled... 58 | $("#Syslog-form").parentNode.setAttribute("hidden", ""); 59 | } 60 | $("#SNTP-form").removeAttribute("hidden"); 61 | $("#mDNS-form").removeAttribute("hidden"); 62 | 63 | var i, inputs = $("input"); 64 | for (i = 0; i < inputs.length; i++) { 65 | if (inputs[i].name == "mdns_enable") inputs[i].onclick = function () { setMDNS(this.checked) }; 66 | } 67 | } 68 | 69 | function setMDNS(v) { 70 | ajaxSpin("POST", "/services/update?mdns_enable=" + (v ? 1 : 0), function () { 71 | showNotification("mDNS is now " + (v ? "enabled" : "disabled")); 72 | }, function () { 73 | showWarning("Enable/disable failed"); 74 | window.setTimeout(fetchServices, 100); 75 | }); 76 | } 77 | 78 | function fetchServices() { 79 | ajaxJson("GET", "/services/info", displayServices, function () { 80 | window.setTimeout(fetchServices, 1000); 81 | }); 82 | } 83 | -------------------------------------------------------------------------------- /esp-link/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | // Flash configuration settings. When adding new items always add them at the end and formulate 5 | // them such that a value of zero is an appropriate default or backwards compatible. Existing 6 | // modules that are upgraded will have zero in the new fields. This ensures that an upgrade does 7 | // not wipe out the old settings. 8 | typedef struct { 9 | uint32_t seq; // flash write sequence number 10 | uint16_t magic, crc; 11 | int8_t reset_pin, isp_pin, conn_led_pin, ser_led_pin; 12 | int32_t baud_rate; 13 | char hostname[32]; // if using DHCP 14 | uint32_t staticip, netmask, gateway; // using DHCP if staticip==0 15 | uint8_t log_mode; // UART log debug mode 16 | uint8_t swap_uart; // swap uart0 to gpio 13&15 17 | uint8_t tcp_enable, rssi_enable; // TCP client settings 18 | char api_key[48]; // RSSI submission API key (Grovestreams for now) 19 | uint8_t slip_enable, mqtt_enable, // SLIP protocol, MQTT client 20 | mqtt_status_enable, // MQTT status reporting 21 | mqtt_timeout, // MQTT send timeout 22 | mqtt_clean_session; // MQTT clean session 23 | uint16_t mqtt_port, mqtt_keepalive; // MQTT Host port, MQTT Keepalive timer 24 | char mqtt_old_host[32], // replaced by 64-char mqtt_host below 25 | mqtt_clientid[48], 26 | mqtt_old_username[32], 27 | mqtt_old_password[32], // replaced by 70-char mqtt_password below 28 | mqtt_status_topic[32]; 29 | char sys_descr[129]; // system description 30 | int8_t rx_pullup; // internal pull-up on RX pin 31 | char sntp_server[32]; 32 | char syslog_host[32]; 33 | uint16_t syslog_minheap; // min. heap to allow queuing 34 | uint8_t syslog_filter, // min. severity 35 | syslog_showtick, // show system tick (µs) 36 | syslog_showdate; // populate SYSLOG date field 37 | uint8_t mdns_enable; 38 | char mdns_servername[32]; 39 | int8_t timezone_offset; 40 | char mqtt_host[64]; // MQTT host we connect to, was 32-char mqtt_old_host 41 | int8_t data_bits; 42 | int8_t parity; 43 | int8_t stop_bits; 44 | char mqtt_password[70]; // MQTT password, was 32-char mqtt_old_password 45 | char mqtt_username[70]; // MQTT username, was 32-char mqtt_old_username 46 | } FlashConfig; 47 | extern FlashConfig flashConfig; 48 | 49 | bool configSave(void); 50 | bool configRestore(void); 51 | void configWipe(void); 52 | const size_t getFlashSize(); 53 | 54 | const uint32_t getUserPageSectionStart(); 55 | const uint32_t getUserPageSectionEnd(); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /html/wifi/wifiAp.js: -------------------------------------------------------------------------------- 1 | var specials = []; 2 | specials["ap_ssid"] = "SSID name"; 3 | specials["ap_password"] = "PASSWORD"; 4 | specials["ap_maxconn"] = "Max Connections number"; 5 | specials["ap_beacon"] = "Beacon Interval"; 6 | specials["ap_channel"] = "Channel"; 7 | 8 | function changeWifiMode(m) { 9 | blockScan = 1; 10 | hideWarning(); 11 | ajaxSpin("POST", "setmode?mode=" + m, function(resp) { 12 | showNotification("Mode changed"); 13 | window.setTimeout(getWifiInfo, 100); 14 | blockScan = 0; 15 | }, function(s, st) { 16 | showWarning("Error changing mode: " + st); 17 | window.setTimeout(getWifiInfo, 100); 18 | blockScan = 0; 19 | }); 20 | } 21 | 22 | function changeApSettings(e) { 23 | e.preventDefault(); 24 | var url = "/wifi/apchange?100=1"; 25 | var i, inputs = document.querySelectorAll("#" + e.target.id + " input,select"); 26 | for (i = 0; i < inputs.length; i++) { 27 | if (inputs[i].type == "checkbox") { 28 | var val = (inputs[i].checked) ? 1 : 0; 29 | url += "&" + inputs[i].name + "=" + val; 30 | } else { 31 | var clean = inputs[i].value.replace(/[^!-~]/g, ""); 32 | var comp = clean.localeCompare(inputs[i].value); 33 | if ( comp != 0 ){ 34 | showWarning("Invalid characters in " + specials[inputs[i].name]); 35 | return; 36 | } 37 | url += "&" + inputs[i].name + "=" + clean; 38 | } 39 | }; 40 | 41 | hideWarning(); 42 | var n = e.target.id.replace("-form", ""); 43 | var cb = $("#" + n + "-button"); 44 | addClass(cb, "pure-button-disabled"); 45 | ajaxSpin("POST", url, function (resp) { 46 | showNotification(n + " updated"); 47 | removeClass(cb, "pure-button-disabled"); 48 | window.setTimeout(getWifiInfo, 100); 49 | }, function (s, st) { 50 | showWarning(st); 51 | removeClass(cb, "pure-button-disabled"); 52 | window.setTimeout(fetchApSettings, 2000); 53 | }); 54 | } 55 | 56 | function displayApSettings(data) { 57 | Object.keys(data).forEach(function (v) { 58 | el = $("#" + v); 59 | if (el != null) { 60 | if (el.nodeName === "INPUT") el.value = data[v]; 61 | else el.innerHTML = data[v]; 62 | return; 63 | } 64 | 65 | el = document.querySelector('input[name="' + v + '"]'); 66 | if (el == null) 67 | el = document.querySelector('select[name="' + v + '"]'); 68 | 69 | if (el != null) { 70 | if (el.type == "checkbox") { 71 | el.checked = data[v] == "enabled"; 72 | } else el.value = data[v]; 73 | } 74 | }); 75 | 76 | $("#AP_Settings-spinner").setAttribute("hidden", ""); 77 | $("#AP_Settings-form").removeAttribute("hidden"); 78 | showWarning("Don't modify SOFTAP parameters with active connections"); 79 | window.setTimeout(hideWarning(), 2000); 80 | } 81 | 82 | function fetchApSettings() { 83 | ajaxJson("GET", "/wifi/apinfo", displayApSettings, function () { 84 | window.setTimeout(fetchApSettings, 1000); 85 | }); 86 | } 87 | -------------------------------------------------------------------------------- /httpd/httpd.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPD_H 2 | #define HTTPD_H 3 | 4 | #define HTTPDVER "0.3" 5 | 6 | #define HTTPD_CGI_MORE 0 7 | #define HTTPD_CGI_DONE 1 8 | #define HTTPD_CGI_NOTFOUND 2 9 | #define HTTPD_CGI_AUTHENTICATED 3 10 | 11 | #define HTTPD_METHOD_GET 1 12 | #define HTTPD_METHOD_POST 2 13 | 14 | 15 | typedef struct HttpdPriv HttpdPriv; 16 | typedef struct HttpdConnData HttpdConnData; 17 | typedef struct HttpdPostData HttpdPostData; 18 | 19 | typedef int (* cgiSendCallback)(HttpdConnData *connData); 20 | 21 | //A struct describing a http connection. This gets passed to cgi functions. 22 | struct HttpdConnData { 23 | struct espconn *conn; 24 | //int remote_port; 25 | //uint8 remote_ip[4]; 26 | uint32 startTime; 27 | char requestType; // HTTP_METHOD_GET | HTTPD_METHOD_POST 28 | char *url; 29 | char *getArgs; 30 | const void *cgiArg; 31 | void *cgiData; 32 | void *cgiPrivData; // Used for streaming handlers storing state between requests 33 | void *cgiResponse; // used for forwarding response to the CGI handler 34 | HttpdPriv *priv; 35 | cgiSendCallback cgi; 36 | HttpdPostData *post; 37 | }; 38 | 39 | //A struct describing the POST data sent inside the http connection. This is used by the CGI functions 40 | struct HttpdPostData { 41 | int len; // POST Content-Length 42 | int buffSize; // The maximum length of the post buffer 43 | int buffLen; // The amount of bytes in the current post buffer 44 | int received; // The total amount of bytes received so far 45 | char *buff; // Actual POST data buffer 46 | char *multipartBoundary; 47 | }; 48 | 49 | //A struct describing an url. This is the main struct that's used to send different URL requests to 50 | //different routines. 51 | typedef struct { 52 | const char *url; 53 | cgiSendCallback cgiCb; 54 | const void *cgiArg; 55 | } HttpdBuiltInUrl; 56 | 57 | int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData); 58 | void ICACHE_FLASH_ATTR httpdRedirect(HttpdConnData *conn, char *newUrl); 59 | int httpdUrlDecode(char *val, int valLen, char *ret, int retLen); 60 | int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLen); 61 | void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port); 62 | const char *httpdGetMimetype(char *url); 63 | void ICACHE_FLASH_ATTR httpdSetOutputBuffer(HttpdConnData *conn, char *buff, short max); 64 | void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code); 65 | void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const char *val); 66 | void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn); 67 | int ICACHE_FLASH_ATTR httpdGetHeader(HttpdConnData *conn, char *header, char *ret, int retLen); 68 | int ICACHE_FLASH_ATTR httpdSend(HttpdConnData *conn, const char *data, int len); 69 | void ICACHE_FLASH_ATTR httpdFlush(HttpdConnData *conn); 70 | HttpdConnData * ICACHE_FLASH_ATTR httpdLookUpConn(uint8_t * ip, int port); 71 | int ICACHE_FLASH_ATTR httpdSetCGIResponse(HttpdConnData * conn, void *response); 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /serial/crc16.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005, Swedish Institute of Computer Science 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the Institute nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * This file is part of the Contiki operating system. 30 | * 31 | */ 32 | 33 | /** \addtogroup crc16 34 | * @{ */ 35 | 36 | /** 37 | * \file 38 | * Implementation of the CRC16 calculcation 39 | * \author 40 | * Adam Dunkels 41 | * 42 | */ 43 | 44 | /* CITT CRC16 polynomial ^16 + ^12 + ^5 + 1 */ 45 | /*---------------------------------------------------------------------------*/ 46 | unsigned short 47 | crc16_add(unsigned char b, unsigned short acc) 48 | { 49 | /* 50 | acc = (unsigned char)(acc >> 8) | (acc << 8); 51 | acc ^= b; 52 | acc ^= (unsigned char)(acc & 0xff) >> 4; 53 | acc ^= (acc << 8) << 4; 54 | acc ^= ((acc & 0xff) << 4) << 1; 55 | */ 56 | 57 | acc ^= b; 58 | acc = (acc >> 8) | (acc << 8); 59 | acc ^= (acc & 0xff00) << 4; 60 | acc ^= (acc >> 8) >> 4; 61 | acc ^= (acc & 0xff00) >> 5; 62 | return acc; 63 | } 64 | /*---------------------------------------------------------------------------*/ 65 | unsigned short 66 | crc16_data(const unsigned char *data, int len, unsigned short acc) 67 | { 68 | int i; 69 | 70 | for(i = 0; i < len; ++i) { 71 | acc = crc16_add(*data, acc); 72 | ++data; 73 | } 74 | return acc; 75 | } 76 | /*---------------------------------------------------------------------------*/ 77 | 78 | /** @} */ 79 | -------------------------------------------------------------------------------- /avrflash: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Flash an AVR with optiboot using the esp-link built-in programmer 4 | # Basically we first reset the AVR and get in sync, and then send the hex file 5 | # 6 | # ---------------------------------------------------------------------------- 7 | # "THE BEER-WARE LICENSE" (Revision 42): 8 | # Thorsten von Eicken wrote this file. As long as you retain 9 | # this notice you can do whatever you want with this stuff. If we meet some day, 10 | # and you think this stuff is worth it, you can buy me a beer in return. 11 | # ---------------------------------------------------------------------------- 12 | 13 | show_help() { 14 | cat </dev/null; then 26 | echo "ERROR: Cannot find curl: it is required for this script." >&2 27 | exit 1 28 | fi 29 | 30 | start=`date +%s` 31 | 32 | # ===== Parse arguments 33 | 34 | verbose= 35 | 36 | while getopts "hvx:" opt; do 37 | case "$opt" in 38 | h) show_help; exit 0 ;; 39 | v) verbose=1 ;; 40 | x) foo="$OPTARG" ;; 41 | '?') show_help >&2; exit 1 ;; 42 | esac 43 | done 44 | 45 | # Shift off the options and optional --. 46 | shift "$((OPTIND-1))" 47 | 48 | # Get the fixed arguments 49 | if [[ $# != 2 ]]; then 50 | show_help >&2 51 | exit 1 52 | fi 53 | hostname=$1 54 | hex=$2 55 | 56 | re='[-A-Za-z0-9.]+' 57 | if [[ ! "$hostname" =~ $re ]]; then 58 | echo "ERROR: hostname ${hostname} is not a valid hostname or ip address" >&2 59 | exit 1 60 | fi 61 | 62 | if [[ ! -r "$hex" ]]; then 63 | echo "ERROR: cannot read hex file ($hex)" >&2 64 | exit 1 65 | fi 66 | 67 | # ===== Get AVR in sync 68 | 69 | [[ -n "$verbose" ]] && echo "Resetting AVR with http://$hostname/pgm/sync" >&2 70 | v=; [[ -n "$verbose" ]] && v=-v 71 | sync=`curl -m 10 $v -s -w '%{http_code}' -XPOST "http://$hostname/pgm/sync"` 72 | if [[ $? != 0 || "$sync" != 204 ]]; then 73 | echo "Error resetting AVR" >&2 74 | exit 1 75 | fi 76 | 77 | while true; do 78 | sync=`curl -m 10 $v -s "http://$hostname/pgm/sync"` 79 | if [[ $? != 0 ]]; then 80 | echo "Error checking sync" >&2 81 | exit 1 82 | fi 83 | case "$sync" in 84 | SYNC*) 85 | echo "AVR in $sync" >&2 86 | break;; 87 | "NOT READY"*) 88 | [[ -n "$verbose" ]] && echo " Waiting for sync..." >&2 89 | ;; 90 | *) 91 | echo "Error checking sync: $sync" >&2 92 | exit 1 93 | ;; 94 | esac 95 | sleep 0.1 96 | done 97 | 98 | # ===== Send HEX file 99 | 100 | [[ -n "$verbose" ]] && echo "Sending HEX file for programming" >&2 101 | sync=`curl -m 10 $v -s -g -d "@$hex" "http://$hostname/pgm/upload"` 102 | echo $sync 103 | if [[ $? != 0 || ! "$sync" =~ ^Success ]]; then 104 | echo "Error programming AVR" >&2 105 | exit 1 106 | fi 107 | 108 | sec=$(( `date +%s` - $start )) 109 | echo "Success, took $sec seconds" >&2 110 | exit 0 111 | -------------------------------------------------------------------------------- /WEB-SERVER.md: -------------------------------------------------------------------------------- 1 | ESP-LINK web-server tutorial 2 | ============================ 3 | 4 | Video 5 | -------------------- 6 | 7 | https://www.youtube.com/watch?v=vBESCO0UhYI 8 | 9 | 10 | Installing el-client Arduino library 11 | -------------------- 12 | 13 | Download and install ELClient library. 14 | 15 | https://github.com/jeelabs/el-client 16 | 17 | 18 | LED flashing sample 19 | -------------------- 20 | 21 | Circuit: 22 | 23 | - 1: connect a Nodemcu (ESP8266) board and an Arduino Nano / UNO: 24 | (RX - levelshifter - TX, TX - levelshifter - RX) 25 | - 2: optionally connect RESET-s with a level shifter 26 | 27 | 28 | Installation steps: 29 | 30 | - 1: open webserver_led ELClient sample file in Arduino 31 | - 2: upload the code onto an Arduino Nano/Uno 32 | - 3: open the Web Server page on esp-link UI 33 | - 4: upload LED.html from webserver_led ( ELCient/examples/webserver_led/LED.html ) 34 | - 5: choose LED page on esp-link UI 35 | - 6: turn on/off the LED 36 | 37 | 38 | HTML controls sample 39 | -------------------------- 40 | 41 | Circuit: 42 | 43 | - 1: connect a Nodemcu (ESP8266) board and an Arduino Nano / UNO: 44 | (RX - levelshifter - TX, TX - levelshifter - RX) 45 | - 2: optionally connect RESET-s with a level shifter 46 | - 3: add a trimmer to A0 for voltage measurement 47 | 48 | Installation steps: 49 | 50 | - 1: open webserver_controls ELClient sample file in Arduino 51 | - 2: upload the code onto an Arduino Nano/Uno 52 | - 3: open the Web Server page on esp-link UI 53 | - 4: upload the 3 HTML files from webserver_controls ( select multiple htmls from ELCient/examples/webserver_controls/ ) 54 | - 5: jump to LED/User/Voltage pages 55 | - 6: try out different settings 56 | 57 | 58 | Supported HTML controls 59 | -------------------- 60 | 61 | HTML control       | Value Type | Description | Form Submission | 62 | ------------------------ | ----- | ------------ | ------- | 63 | <p id="id"/>
<div id="id"/>
<tr id="id"/>
<th id="id"/>
<td id="id"/>
<textarea id="id"/> | String (HTML) | MCU can replace the inner HTML part of the control at LOAD/REFRESH queries. The string (sent by MCU) is handled as HTML, so <img...> will be displayed as an image on the page | NO | 64 | <button id="id"/> | String | When button is pressed, a message is transmitted to MCU containing the id (BUTTON_PRESS) | NO | 65 | <input name="id"/> | String
Integer
Float
Boolean | MCU can replace the value or checked properties of the HTML control in the form (LOAD/REFRESH). At form submission, the content of value will be transmitted to MCU (SET_FIELD). | YES | 66 | <select name="id"/> | String | MCU can choose a value from the drop down (LOAD/REFRESH). At form submission the currently selected value will be transmitted to MCU (SET_FIELD) | YES | 67 | <ul id="id"/>
<ol id="id"/> | JSON list
["1","2","3"] | MCU can send a JSON list which is transformed to an HTML list ( <li/> ) (LOAD/REFRESH) | NO | 68 | <table id="id"/> | JSON table
[["1","2"],
["3","4"]] | MCU sends a JSON table which is transformed to an HTML table (LOAD/REFRESH) | NO | 69 | 70 | -------------------------------------------------------------------------------- /include/c_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 - 2011 Espressif System 3 | * 4 | */ 5 | 6 | // Modified for esp-link to confiorm with c99 using the patch included with 7 | // esp-open-sdk https://github.com/pfalcon/esp-open-sdk/blob/master/c_types-c99.patch 8 | // This is included here because otherwise there is a discrepancy between users that use 9 | // the original Espressif SDK vs ones who want to use the SDK included with esp-open-sdk. 10 | // This is a mess, if only Espressif fixed their crap! 11 | 12 | #ifndef _C_TYPES_H_ 13 | #define _C_TYPES_H_ 14 | 15 | #include 16 | #include 17 | 18 | //typedef unsigned char uint8_t; 19 | typedef signed char sint8_t; 20 | //typedef signed char int8_t; 21 | //typedef unsigned short uint16_t; 22 | typedef signed short sint16_t; 23 | //typedef signed short int16_t; 24 | //typedef unsigned long uint32_t; 25 | typedef signed long sint32_t; 26 | //typedef signed long int32_t; 27 | typedef signed long long sint64_t; 28 | //typedef unsigned long long uint64_t; 29 | typedef unsigned long long u_int64_t; 30 | typedef float real32_t; 31 | typedef double real64_t; 32 | 33 | typedef unsigned char uint8; 34 | typedef unsigned char u8; 35 | typedef signed char sint8; 36 | typedef signed char int8; 37 | typedef signed char s8; 38 | typedef unsigned short uint16; 39 | typedef unsigned short u16; 40 | typedef signed short sint16; 41 | typedef signed short s16; 42 | typedef unsigned int uint32; 43 | typedef unsigned int u_int; 44 | typedef unsigned int u32; 45 | typedef signed int sint32; 46 | typedef signed int s32; 47 | typedef int int32; 48 | typedef signed long long sint64; 49 | typedef unsigned long long uint64; 50 | typedef unsigned long long u64; 51 | typedef float real32; 52 | typedef double real64; 53 | 54 | #define __le16 u16 55 | 56 | typedef unsigned int size_t; 57 | 58 | #define __packed __attribute__((packed)) 59 | 60 | #define LOCAL static 61 | 62 | #ifndef NULL 63 | #define NULL (void *)0 64 | #endif /* NULL */ 65 | 66 | /* probably should not put STATUS here */ 67 | typedef enum { 68 | OK = 0, 69 | FAIL, 70 | PENDING, 71 | BUSY, 72 | CANCEL, 73 | } STATUS; 74 | 75 | #define BIT(nr) (1UL << (nr)) 76 | 77 | #define REG_SET_BIT(_r, _b) (*(volatile uint32_t*)(_r) |= (_b)) 78 | #define REG_CLR_BIT(_r, _b) (*(volatile uint32_t*)(_r) &= ~(_b)) 79 | 80 | #define DMEM_ATTR __attribute__((section(".bss"))) 81 | #define SHMEM_ATTR 82 | 83 | #ifdef ICACHE_FLASH 84 | #define ICACHE_FLASH_ATTR __attribute__((section(".irom0.text"))) 85 | #define ICACHE_RODATA_ATTR __attribute__((section(".irom.text"))) 86 | #else 87 | #define ICACHE_FLASH_ATTR 88 | #define ICACHE_RODATA_ATTR 89 | #endif /* ICACHE_FLASH */ 90 | 91 | #define STORE_ATTR __attribute__((aligned(4))) 92 | 93 | #ifndef __cplusplus 94 | //typedef unsigned char bool; 95 | #define BOOL bool 96 | //#define true (1) 97 | //#define false (0) 98 | #define TRUE true 99 | #define FALSE false 100 | 101 | 102 | #endif /* !__cplusplus */ 103 | 104 | #endif /* _C_TYPES_H_ */ 105 | -------------------------------------------------------------------------------- /megaflash: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Flash an Arduino Mega with STK500v2 using the esp-link built-in programmer 4 | # Basically we first reset the AVR and get in sync, and then send the hex file 5 | # 6 | # ---------------------------------------------------------------------------- 7 | # "THE BEER-WARE LICENSE" (Revision 42): 8 | # Thorsten von Eicken wrote this file. As long as you retain 9 | # this notice you can do whatever you want with this stuff. If we meet some day, 10 | # and you think this stuff is worth it, you can buy me a beer in return. 11 | # 12 | # Danny Backx wrote the changes for Arduino Mega. 13 | # ---------------------------------------------------------------------------- 14 | 15 | show_help() { 16 | cat </dev/null; then 29 | echo "ERROR: Cannot find curl: it is required for this script." >&2 30 | exit 1 31 | fi 32 | 33 | start=`date +%s` 34 | 35 | # ===== Parse arguments 36 | 37 | verbose= 38 | 39 | while getopts "hvx:" opt; do 40 | case "$opt" in 41 | h) show_help; exit 0 ;; 42 | v) verbose=1 ;; 43 | x) foo="$OPTARG" ;; 44 | '?') show_help >&2; exit 1 ;; 45 | esac 46 | done 47 | 48 | # Shift off the options and optional --. 49 | shift "$((OPTIND-1))" 50 | 51 | # Get the fixed arguments 52 | if [[ $# != 2 ]]; then 53 | show_help >&2 54 | exit 1 55 | fi 56 | hostname=$1 57 | hex=$2 58 | 59 | re='[-A-Za-z0-9.]+' 60 | if [[ ! "$hostname" =~ $re ]]; then 61 | echo "ERROR: hostname ${hostname} is not a valid hostname or ip address" >&2 62 | exit 1 63 | fi 64 | 65 | if [[ ! -r "$hex" ]]; then 66 | echo "ERROR: cannot read hex file ($hex)" >&2 67 | exit 1 68 | fi 69 | 70 | # ===== Get AVR in sync 71 | 72 | [[ -n "$verbose" ]] && echo "Resetting AVR with http://$hostname/pgmmega/sync" >&2 73 | v=; [[ -n "$verbose" ]] && v=-v 74 | sync=`curl -m 10 $v -s -w '%{http_code}' -XPOST "http://$hostname/pgmmega/sync"` 75 | if [[ $? != 0 || "$sync" != 204 ]]; then 76 | echo "Error resetting AVR" >&2 77 | exit 1 78 | fi 79 | 80 | while true; do 81 | # sync=`curl -m 10 $v -s "http://$hostname/pgmmega/sync"` 82 | sync=`curl $v -s "http://$hostname/pgmmega/sync"` 83 | if [[ $? != 0 ]]; then 84 | echo "Error checking sync" >&2 85 | exit 1 86 | fi 87 | case "$sync" in 88 | SYNC*) 89 | echo "AVR in $sync" >&2 90 | break;; 91 | "NOT READY"*) 92 | [[ -n "$verbose" ]] && echo " Waiting for sync..." >&2 93 | ;; 94 | *) 95 | echo "Error checking sync: $sync" >&2 96 | exit 1 97 | ;; 98 | esac 99 | sleep 0.1 100 | done 101 | 102 | # ===== Send HEX file 103 | 104 | [[ -n "$verbose" ]] && echo "Sending HEX file for programming" >&2 105 | sync=`curl -m 20 $v -s -g -d "@$hex" "http://$hostname/pgmmega/upload"` 106 | echo $sync 107 | if [[ $? != 0 || ! "$sync" =~ ^Success ]]; then 108 | echo "Error programming AVR" >&2 109 | exit 1 110 | fi 111 | 112 | sec=$(( `date +%s` - $start )) 113 | echo "Success, took $sec seconds" >&2 114 | exit 0 115 | -------------------------------------------------------------------------------- /esp-link/mqtt_client.c: -------------------------------------------------------------------------------- 1 | #ifdef MQTT 2 | #include 3 | #include "cgiwifi.h" 4 | #include "config.h" 5 | #include "mqtt.h" 6 | 7 | 8 | #ifdef MQTTCLIENT_DBG 9 | #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) 10 | #else 11 | #define DBG(format, ...) do { } while(0) 12 | #endif 13 | 14 | MQTT_Client mqttClient; // main mqtt client used by esp-link 15 | 16 | static MqttCallback connected_cb; 17 | static MqttCallback disconnected_cb; 18 | static MqttCallback published_cb; 19 | static MqttDataCallback data_cb; 20 | 21 | void ICACHE_FLASH_ATTR 22 | mqttConnectedCb(MQTT_Client* client) { 23 | DBG("MQTT Client: Connected\n"); 24 | //MQTT_Subscribe(client, "system/time", 0); // handy for testing 25 | if (connected_cb) 26 | connected_cb(client); 27 | } 28 | 29 | void ICACHE_FLASH_ATTR 30 | mqttDisconnectedCb(MQTT_Client* client) { 31 | DBG("MQTT Client: Disconnected\n"); 32 | if (disconnected_cb) 33 | disconnected_cb(client); 34 | } 35 | 36 | void ICACHE_FLASH_ATTR 37 | mqttPublishedCb(MQTT_Client* client) { 38 | DBG("MQTT Client: Published\n"); 39 | if (published_cb) 40 | published_cb(client); 41 | } 42 | 43 | void ICACHE_FLASH_ATTR 44 | mqttDataCb(MQTT_Client* client, const char* topic, uint32_t topic_len, 45 | const char *data, uint32_t data_len) 46 | { 47 | #ifdef MQTTCLIENT_DBG 48 | char *topicBuf = (char*)os_zalloc(topic_len + 1); 49 | char *dataBuf = (char*)os_zalloc(data_len + 1); 50 | 51 | os_memcpy(topicBuf, topic, topic_len); 52 | topicBuf[topic_len] = 0; 53 | 54 | os_memcpy(dataBuf, data, data_len); 55 | dataBuf[data_len] = 0; 56 | 57 | os_printf("MQTT Client: Received topic: %s, data: %s\n", topicBuf, dataBuf); 58 | os_free(topicBuf); 59 | os_free(dataBuf); 60 | #endif 61 | 62 | if (data_cb) 63 | data_cb(client, topic, topic_len, data, data_len); 64 | } 65 | 66 | void ICACHE_FLASH_ATTR 67 | wifiStateChangeCb(uint8_t status) 68 | { 69 | if (flashConfig.mqtt_enable) { 70 | if (status == wifiGotIP && mqttClient.connState < TCP_CONNECTING) { 71 | MQTT_Connect(&mqttClient); 72 | } 73 | else if (status == wifiIsDisconnected && mqttClient.connState == TCP_CONNECTING) { 74 | MQTT_Disconnect(&mqttClient); 75 | } 76 | } 77 | } 78 | 79 | void ICACHE_FLASH_ATTR 80 | mqtt_client_init() 81 | { 82 | MQTT_Init(&mqttClient, flashConfig.mqtt_host, flashConfig.mqtt_port, 0, flashConfig.mqtt_timeout, 83 | flashConfig.mqtt_clientid, flashConfig.mqtt_username, flashConfig.mqtt_password, 84 | flashConfig.mqtt_keepalive); 85 | 86 | MQTT_OnConnected(&mqttClient, mqttConnectedCb); 87 | MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); 88 | MQTT_OnPublished(&mqttClient, mqttPublishedCb); 89 | MQTT_OnData(&mqttClient, mqttDataCb); 90 | 91 | // Don't connect now, wait for a wifi status change callback 92 | //if (flashConfig.mqtt_enable && strlen(flashConfig.mqtt_host) > 0) 93 | // MQTT_Connect(&mqttClient); 94 | 95 | wifiAddStateChangeCb(wifiStateChangeCb); 96 | } 97 | 98 | void ICACHE_FLASH_ATTR 99 | mqtt_client_on_connected(MqttCallback connectedCb) { 100 | connected_cb = connectedCb; 101 | } 102 | 103 | void ICACHE_FLASH_ATTR 104 | mqtt_client_on_disconnected(MqttCallback disconnectedCb) { 105 | disconnected_cb = disconnectedCb; 106 | } 107 | 108 | void ICACHE_FLASH_ATTR 109 | mqtt_client_on_published(MqttCallback publishedCb) { 110 | published_cb = publishedCb; 111 | } 112 | 113 | void ICACHE_FLASH_ATTR 114 | mqtt_client_on_data(MqttDataCallback dataCb) { 115 | data_cb = dataCb; 116 | } 117 | #endif // MQTT 118 | -------------------------------------------------------------------------------- /esp-link/status.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt 2 | 3 | #include 4 | #include "config.h" 5 | #include "serled.h" 6 | #include "cgiwifi.h" 7 | 8 | #ifdef MQTT 9 | #include "mqtt.h" 10 | #include "mqtt_client.h" 11 | extern MQTT_Client mqttClient; 12 | 13 | //===== MQTT Status update 14 | 15 | // Every minute... 16 | #define MQTT_STATUS_INTERVAL (60*1000) 17 | 18 | static ETSTimer mqttStatusTimer; 19 | 20 | int ICACHE_FLASH_ATTR 21 | mqttStatusMsg(char *buf) { 22 | sint8 rssi = wifi_station_get_rssi(); 23 | if (rssi > 0) rssi = 0; // not connected or other error 24 | //os_printf("timer rssi=%d\n", rssi); 25 | 26 | // compose MQTT message 27 | return os_sprintf(buf, 28 | "{\"rssi\":%d, \"heap_free\":%ld}", 29 | rssi, (unsigned long)system_get_free_heap_size()); 30 | } 31 | 32 | // Timer callback to send an RSSI update to a monitoring system 33 | static void ICACHE_FLASH_ATTR mqttStatusCb(void *v) { 34 | if (!flashConfig.mqtt_status_enable || os_strlen(flashConfig.mqtt_status_topic) == 0 || 35 | mqttClient.connState != MQTT_CONNECTED) 36 | return; 37 | 38 | char buf[128]; 39 | mqttStatusMsg(buf); 40 | MQTT_Publish(&mqttClient, flashConfig.mqtt_status_topic, buf, os_strlen(buf), 1, 0); 41 | } 42 | 43 | 44 | 45 | #endif // MQTT 46 | 47 | //===== "CONN" LED status indication 48 | 49 | static ETSTimer ledTimer; 50 | 51 | // Set the LED on or off, respecting the defined polarity 52 | static void ICACHE_FLASH_ATTR setLed(int on) { 53 | int8_t pin = flashConfig.conn_led_pin; 54 | if (pin < 0) return; // disabled 55 | // LED is active-low 56 | if (on) { 57 | gpio_output_set(0, (1<= 0) { 111 | makeGpio(flashConfig.conn_led_pin); 112 | setLed(1); 113 | } 114 | #ifdef STATUS_DBG 115 | os_printf("CONN led=%d\n", flashConfig.conn_led_pin); 116 | #endif 117 | 118 | os_timer_disarm(&ledTimer); 119 | os_timer_setfn(&ledTimer, ledTimerCb, NULL); 120 | os_timer_arm(&ledTimer, 2000, 0); 121 | 122 | #ifdef MQTT 123 | os_timer_disarm(&mqttStatusTimer); 124 | os_timer_setfn(&mqttStatusTimer, mqttStatusCb, NULL); 125 | os_timer_arm(&mqttStatusTimer, MQTT_STATUS_INTERVAL, 1); // recurring timer 126 | #endif // MQTT 127 | } 128 | 129 | 130 | -------------------------------------------------------------------------------- /wiflash: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # Flash an esp8266 over wifi. This communicates with the esphttpd's /flash handlers 4 | # and POSTS the correct binary depending on the parittion that needs to be flashed 5 | # next. 6 | # 7 | # ---------------------------------------------------------------------------- 8 | # "THE BEER-WARE LICENSE" (Revision 42): 9 | # Thorsten von Eicken wrote this file. As long as you retain 10 | # this notice you can do whatever you want with this stuff. If we meet some day, 11 | # and you think this stuff is worth it, you can buy me a beer in return. 12 | # ---------------------------------------------------------------------------- 13 | 14 | show_help() { 15 | cat < with either or 18 | depending on its current state. Reboot the esp8266 after flashing and wait for it to come 19 | up again. 20 | -v Be verbose 21 | -h show this help 22 | 23 | Example: ${0##*/} -v esp8266 firmware/user1.bin firmware/user2.bin 24 | ${0##*/} 192.168.4.1 firmware/user1.bin firmware/user2.bin 25 | EOT 26 | } 27 | 28 | if ! which curl >/dev/null; then 29 | echo "ERROR: Cannot find curl: it is required for this script." >&2 30 | exit 1 31 | fi 32 | 33 | start=`date +%s` 34 | 35 | # ===== Parse arguments 36 | 37 | verbose= 38 | 39 | while getopts "hvx:" opt; do 40 | case "$opt" in 41 | h) show_help; exit 0 ;; 42 | v) verbose=1 ;; 43 | x) foo="$OPTARG" ;; 44 | '?') show_help >&2; exit 1 ;; 45 | esac 46 | done 47 | 48 | # Shift off the options and optional --. 49 | shift "$((OPTIND-1))" 50 | 51 | # Get the fixed arguments 52 | if [[ $# != 3 ]]; then 53 | show_help >&2 54 | exit 1 55 | fi 56 | hostname=$1 57 | user1=$2 58 | user2=$3 59 | 60 | re='[-A-Za-z0-9.]+' 61 | if [[ ! "$hostname" =~ $re ]]; then 62 | echo "ERROR: hostname ${hostname} is not a valid hostname or ip address" >&2 63 | exit 1 64 | fi 65 | 66 | if [[ ! -r "$user1" ]]; then 67 | echo "ERROR: cannot read user1 firmware file ($user1)" >&2 68 | exit 1 69 | fi 70 | 71 | if [[ ! -r "$user2" ]]; then 72 | echo "ERROR: cannot read user2 firmware file ($user2)" >&2 73 | exit 1 74 | fi 75 | 76 | # ===== Retrieve the 'next' firmware required 77 | 78 | fw= 79 | while true; do 80 | [[ -n "$verbose" ]] && echo "Fetching http://$hostname/flash/next" >&2 81 | v=; [[ -n "$verbose" ]] && v=-v 82 | next=`curl -m 10 $v -s "http://$hostname/flash/next"` 83 | if [[ $? != 0 ]]; then 84 | echo "Error retrieving http://$hostname/flash/next" >&2 85 | exit 1 86 | fi 87 | case "$next" in 88 | user1.bin) 89 | echo "Flashing user1.bin" >&2 90 | fw="$user1" 91 | break;; 92 | user2.bin) 93 | echo "Flashing user2.bin" >&2 94 | fw="$user2" 95 | break;; 96 | *) 97 | echo "Error retrieving or parsing http://$hostname/flash/next" >&2 98 | exit 1 99 | ;; 100 | esac 101 | done 102 | 103 | #silent=-s 104 | [[ -n "$verbose" ]] && silent= 105 | res=`curl $silent -XPOST --data-binary "@$fw" "http://$hostname/flash/upload"` 106 | if [[ $? != 0 ]]; then 107 | echo "Error flashing $fw" >&2 108 | exit 1 109 | fi 110 | 111 | sleep 2 112 | echo "Rebooting into new firmware" >&2 113 | curl -m 10 -s "http://$hostname/flash/reboot" 114 | 115 | sleep 2 116 | echo "Waiting for ESP8266 to come back" 117 | while true; do 118 | [[ -n "$verbose" ]] && echo "Fetching http://$hostname/flash/next" >&2 119 | next2=`curl -m 10 $v -s "http://$hostname/flash/next"` 120 | [[ -n "$verbose" ]] && echo "got: $next2" 121 | re='user[12]\.bin' 122 | if [[ "$next2" =~ $re ]]; then 123 | if [[ "$next2" != "$next" ]]; then 124 | sec=$(( `date +%s` - $start )) 125 | echo "Success, took $sec seconds" >&2 126 | exit 0 127 | else 128 | echo "Flashing seems to have failed and it reverted to the old firmware?" >&2 129 | exit 1 130 | fi 131 | fi 132 | sleep 1 133 | done 134 | -------------------------------------------------------------------------------- /httpd/base64.c: -------------------------------------------------------------------------------- 1 | /* base64.c : base-64 / MIME encode/decode */ 2 | /* PUBLIC DOMAIN - Jon Mayo - November 13, 2003 */ 3 | #include 4 | #include "base64.h" 5 | 6 | static const uint8_t base64dec_tab[256]= { 7 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 8 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 9 | 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, 10 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255, 11 | 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 12 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, 13 | 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 14 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255, 15 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 16 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 17 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 18 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 19 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 20 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 21 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 22 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 23 | }; 24 | 25 | #if 0 26 | static int ICACHE_FLASH_ATTR base64decode(const char in[4], char out[3]) { 27 | uint8_t v[4]; 28 | 29 | v[0]=base64dec_tab[(unsigned)in[0]]; 30 | v[1]=base64dec_tab[(unsigned)in[1]]; 31 | v[2]=base64dec_tab[(unsigned)in[2]]; 32 | v[3]=base64dec_tab[(unsigned)in[3]]; 33 | 34 | out[0]=(v[0]<<2)|(v[1]>>4); 35 | out[1]=(v[1]<<4)|(v[2]>>2); 36 | out[2]=(v[2]<<6)|(v[3]); 37 | return (v[0]|v[1]|v[2]|v[3])!=255 ? in[3]=='=' ? in[2]=='=' ? 1 : 2 : 3 : 0; 38 | } 39 | #endif 40 | 41 | /* decode a base64 string in one shot */ 42 | int ICACHE_FLASH_ATTR base64_decode(size_t in_len, const char *in, size_t out_len, unsigned char *out) { 43 | unsigned int ii, io; 44 | uint32_t v; 45 | unsigned int rem; 46 | 47 | for(io=0,ii=0,v=0,rem=0;ii=8) { 56 | rem-=8; 57 | if(io>=out_len) return -1; /* truncation is failure */ 58 | out[io++]=(v>>rem)&255; 59 | } 60 | } 61 | if(rem>=8) { 62 | rem-=8; 63 | if(io>=out_len) return -1; /* truncation is failure */ 64 | out[io++]=(v>>rem)&255; 65 | } 66 | return io; 67 | } 68 | 69 | //Only need decode functions for now. 70 | #if 0 71 | 72 | static const uint8_t base64enc_tab[64]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 73 | 74 | void base64encode(const unsigned char in[3], unsigned char out[4], int count) { 75 | out[0]=base64enc_tab[(in[0]>>2)]; 76 | out[1]=base64enc_tab[((in[0]&3)<<4)|(in[1]>>4)]; 77 | out[2]=count<2 ? '=' : base64enc_tab[((in[1]&15)<<2)|(in[2]>>6)]; 78 | out[3]=count<3 ? '=' : base64enc_tab[(in[2]&63)]; 79 | } 80 | 81 | 82 | int base64_encode(size_t in_len, const unsigned char *in, size_t out_len, char *out) { 83 | unsigned ii, io; 84 | uint_least32_t v; 85 | unsigned rem; 86 | 87 | for(io=0,ii=0,v=0,rem=0;ii=6) { 93 | rem-=6; 94 | if(io>=out_len) return -1; /* truncation is failure */ 95 | out[io++]=base64enc_tab[(v>>rem)&63]; 96 | } 97 | } 98 | if(rem) { 99 | v<<=(6-rem); 100 | if(io>=out_len) return -1; /* truncation is failure */ 101 | out[io++]=base64enc_tab[v&63]; 102 | } 103 | while(io&3) { 104 | if(io>=out_len) return -1; /* truncation is failure */ 105 | out[io++]='='; 106 | } 107 | if(io>=out_len) return -1; /* no room for null terminator */ 108 | out[io]=0; 109 | return io; 110 | } 111 | 112 | #endif -------------------------------------------------------------------------------- /flash2560.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # ---------------------------------------------------------------------------- 4 | # "THE BEER-WARE LICENSE" (Revision 42): 5 | # Fabio Manz wrote this file. As long as you retain 6 | # this notice you can do whatever you want with this stuff. If we meet some day, 7 | # and you think this stuff is worth it, you can buy me a beer in return. 8 | # ---------------------------------------------------------------------------- 9 | 10 | import sys 11 | import re 12 | import requests 13 | import platform # For getting the operating system name 14 | import subprocess # For executing a shell command 15 | import os 16 | import time 17 | 18 | ip_regex = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$" 19 | 20 | 21 | def main(argv): 22 | print("--- flash2560 - Created by Fabio Manz ---") 23 | 24 | hostname = "192.168.4.1" 25 | input_file = "none" 26 | 27 | # Handle the command line arguments 28 | for index, arg in enumerate(argv): 29 | if arg == "-h" or arg == "--help": 30 | print_help() 31 | sys.exit(0) 32 | elif arg == "-H" or arg == "--hostname": 33 | if index + 1 < len(argv) and re.search(ip_regex, argv[index + 1]): 34 | hostname = argv[index + 1] 35 | if not ping(hostname): 36 | print("IP is not reachable:") 37 | sys.exit(2) 38 | else: 39 | print("IP address is not right") 40 | print_help() 41 | sys.exit(1) 42 | elif arg == "-f" or arg == "--file": 43 | if index + 1 < len(argv) and os.path.isfile(argv[index + 1]): 44 | input_file = argv[index + 1] 45 | else: 46 | print("Can't open file") 47 | print_help() 48 | sys.exit(3) 49 | 50 | if input_file == "none": 51 | print("No input file") 52 | print_help() 53 | sys.exit(4) 54 | 55 | response = requests.post('http://' + hostname + '/pgmmega/sync') 56 | 57 | # ------------ GET AVR in SYNC ---------------------------------------- 58 | 59 | if response.status_code != 204: 60 | print("Failed to reset the AVR (HTML ERROR: " + response.status_code + ")") 61 | sys.exit(5) 62 | 63 | while True: 64 | response = requests.get('http://' + hostname + '/pgmmega/sync') 65 | 66 | if "SYNC" in response.content.decode('ASCII'): 67 | print(response.content) 68 | break 69 | elif "NOT READY" not in response.content.decode('ASCII'): 70 | print("Could not get in Sync with AVR") 71 | sys.exit(7) 72 | 73 | time.sleep(0.1) 74 | 75 | # -------------- Upload HEX file ----------------------------------------- 76 | 77 | hex_file = open(input_file).read() 78 | 79 | response = requests.post('http://' + hostname + '/pgmmega/upload', data=hex_file, timeout=20.0) 80 | 81 | if "Success" in response.content.decode('ASCII'): 82 | print("+++++ Success :) ++++++") 83 | else: 84 | print("Failed :(") 85 | sys.exit(8) 86 | 87 | # Reset the avr to solve a bug in the bootloader that the program dows not start immediately 88 | time.sleep(0.1) 89 | requests.post('http://' + hostname + '/console/reset') 90 | 91 | sys.exit(0) 92 | 93 | 94 | def print_help(): 95 | print('\n') 96 | print("Usage: ") 97 | print("flash2560.py -H -f ") 98 | print("\nExample:") 99 | print("flash2560.py -H 192.168.4.1 -f Sketch.hex") 100 | 101 | 102 | def ping(host): 103 | param = '-n' if platform.system().lower() == 'windows' else '-c' 104 | 105 | command = ['ping', param, '1', host] 106 | output = open(os.devnull, 'w') 107 | return subprocess.call(command, stdout=output) == 0 108 | 109 | 110 | if __name__ == "__main__": 111 | main(sys.argv[1:]) 112 | -------------------------------------------------------------------------------- /espfs/mkespfsimage/mman-win32/configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # mmap-win32 configure script 3 | # 4 | # Parts copied from FFmpeg's configure 5 | # 6 | 7 | set_all(){ 8 | value=$1 9 | shift 10 | for var in $*; do 11 | eval $var=$value 12 | done 13 | } 14 | 15 | enable(){ 16 | set_all yes $* 17 | } 18 | 19 | disable(){ 20 | set_all no $* 21 | } 22 | 23 | enabled(){ 24 | eval test "x\$$1" = "xyes" 25 | } 26 | 27 | disabled(){ 28 | eval test "x\$$1" = "xno" 29 | } 30 | 31 | show_help(){ 32 | echo "Usage: configure [options]" 33 | echo "Options: [defaults in brackets after descriptions]" 34 | echo "All \"enable\" options have \"disable\" counterparts" 35 | echo 36 | echo " --help print this message" 37 | echo " --prefix=PREFIX install in PREFIX [$PREFIX]" 38 | echo " --libdir=DIR install libs in DIR [$PREFIX/lib]" 39 | echo " --incdir=DIR install includes in DIR [$PREFIX/include]" 40 | echo " --enable-static build static libraries [yes]" 41 | echo " --enable-msvc create msvc-compatible import lib [auto]" 42 | echo 43 | echo " --cc=CC use C compiler CC [$cc_default]" 44 | echo " --cross-prefix=PREFIX use PREFIX for compilation tools [$cross_prefix]" 45 | exit 1 46 | } 47 | 48 | die_unknown(){ 49 | echo "Unknown option \"$1\"." 50 | echo "See $0 --help for available options." 51 | exit 1 52 | } 53 | 54 | PREFIX="/mingw" 55 | libdir="${PREFIX}/lib" 56 | incdir="${PREFIX}/include/sys" 57 | ar="ar" 58 | cc_default="gcc" 59 | ranlib="ranlib" 60 | strip="strip" 61 | 62 | DEFAULT="msvc 63 | " 64 | 65 | DEFAULT_YES="static 66 | stripping 67 | " 68 | 69 | CMDLINE_SELECT="$DEFAULT 70 | $DEFAULT_NO 71 | $DEFAULT_YES 72 | " 73 | 74 | enable $DEFAULT_YES 75 | disable $DEFAULT_NO 76 | 77 | for opt do 78 | optval="${opt#*=}" 79 | case "$opt" in 80 | --help) 81 | show_help 82 | ;; 83 | --prefix=*) 84 | PREFIX="$optval" 85 | ;; 86 | --libdir=*) 87 | libdir="$optval" 88 | ;; 89 | --incdir=*) 90 | incdir="$optval" 91 | ;; 92 | --cc=*) 93 | cc="$optval" 94 | ;; 95 | --cross-prefix=*) 96 | cross_prefix="$optval" 97 | ;; 98 | --enable-?*|--disable-?*) 99 | eval `echo "$opt" | sed 's/--/action=/;s/-/ option=/;s/-/_/g'` 100 | echo "$CMDLINE_SELECT" | grep -q "^ *$option\$" || die_unknown $opt 101 | $action $option 102 | ;; 103 | *) 104 | die_unknown $opt 105 | ;; 106 | esac 107 | done 108 | 109 | ar="${cross_prefix}${ar}" 110 | cc_default="${cross_prefix}${cc_default}" 111 | ranlib="${cross_prefix}${ranlib}" 112 | strip="${cross_prefix}${strip}" 113 | 114 | if ! test -z $cc; then 115 | cc_default="${cc}" 116 | fi 117 | cc="${cc_default}" 118 | 119 | disabled static && { 120 | echo "At least one library type must be set."; 121 | exit 1; 122 | } 123 | 124 | if enabled msvc; then 125 | lib /? > /dev/null 2>&1 /dev/null || { 126 | echo "MSVC's lib command not found." 127 | echo "Make sure MSVC is installed and its bin folder is in your \$PATH." 128 | exit 1 129 | } 130 | fi 131 | 132 | if ! enabled stripping; then 133 | strip="echo ignoring strip" 134 | fi 135 | 136 | enabled msvc && libcmd="lib" || libcmd="echo ignoring lib" 137 | 138 | echo "# Automatically generated by configure" > config.mak 139 | echo "PREFIX=$PREFIX" >> config.mak 140 | echo "libdir=$libdir" >> config.mak 141 | echo "incdir=$incdir" >> config.mak 142 | echo "AR=$ar" >> config.mak 143 | echo "CC=$cc" >> config.mak 144 | echo "RANLIB=$ranlib" >> config.mak 145 | echo "STRIP=$strip" >> config.mak 146 | echo "BUILD_STATIC=$static" >> config.mak 147 | echo "BUILD_MSVC=$msvc" >> config.mak 148 | echo "LIBCMD=$libcmd" >> config.mak 149 | 150 | echo "prefix: $PREFIX" 151 | echo "libdir: $libdir" 152 | echo "incdir: $incdir" 153 | echo "ar: $ar" 154 | echo "cc: $cc" 155 | echo "ranlib: $ranlib" 156 | echo "strip: $strip" 157 | echo "static: $static" 158 | -------------------------------------------------------------------------------- /include/espmissingincludes.h: -------------------------------------------------------------------------------- 1 | #ifndef ESPMISSINGINCLUDES_H 2 | #define ESPMISSINGINCLUDES_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | //Missing function prototypes in include folders. Gcc will warn on these if we don't define 'em anywhere. 12 | //MOST OF THESE ARE GUESSED! but they seem to work and shut up the compiler. 13 | typedef struct espconn espconn; 14 | 15 | bool wifi_station_set_hostname(char *); 16 | char *wifi_station_get_hostname(void); 17 | 18 | int atoi(const char *nptr); 19 | 20 | //void ets_install_putc1(void *routine); // necessary for #define os_xxx -> ets_xxx 21 | //void ets_isr_attach(int intr, void *handler, void *arg); 22 | void ets_isr_mask(unsigned intr); 23 | void ets_isr_unmask(unsigned intr); 24 | 25 | int ets_memcmp(const void *s1, const void *s2, size_t n); 26 | void *ets_memcpy(void *dest, const void *src, size_t n); 27 | void *ets_memmove(void *dest, const void *src, size_t n); 28 | void *ets_memset(void *s, int c, size_t n); 29 | int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format (printf, 2, 3))); 30 | int ets_str2macaddr(void *, void *); 31 | int ets_strcmp(const char *s1, const char *s2); 32 | char *ets_strcpy(char *dest, const char *src); 33 | //size_t ets_strlen(const char *s); 34 | //int ets_strncmp(const char *s1, const char *s2, int len); 35 | char *ets_strncpy(char *dest, const char *src, size_t n); 36 | char *ets_strstr(const char *haystack, const char *needle); 37 | 38 | //void ets_timer_arm_new(ETSTimer *a, int b, int c, int isMstimer); 39 | void ets_timer_disarm(ETSTimer *a); 40 | void ets_timer_setfn(ETSTimer *t, ETSTimerFunc *fn, void *parg); 41 | 42 | void ets_update_cpu_frequency(int freqmhz); 43 | 44 | #ifdef SDK_DBG 45 | #define DEBUG_SDK true 46 | #else 47 | #define DEBUG_SDK false 48 | #endif 49 | 50 | int ets_vsprintf(char *str, const char *format, va_list argptr); 51 | int ets_vsnprintf(char *buffer, size_t sizeOfBuffer, const char *format, va_list argptr); 52 | int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__((format(printf, 3, 4))); 53 | int os_printf_plus(const char *format, ...) __attribute__((format(printf, 1, 2))); 54 | 55 | #undef os_printf 56 | #define os_printf(format, ...) do { \ 57 | system_set_os_print(true); \ 58 | os_printf_plus(format, ## __VA_ARGS__); \ 59 | system_set_os_print(DEBUG_SDK); \ 60 | } while (0) 61 | 62 | 63 | // memory allocation functions are "different" due to memory debugging functionality 64 | // added in SDK 1.4.0 65 | //void vPortFree(void *ptr, const char * file, int line); 66 | //void *pvPortMalloc(size_t xWantedSize, const char * file, int line); 67 | //void *pvPortZalloc(size_t, const char * file, int line); 68 | void *vPortMalloc(size_t xWantedSize); 69 | void pvPortFree(void *ptr); 70 | 71 | //void uart_div_modify(int no, unsigned int freq); 72 | uint32 system_get_time(); 73 | int rand(void); 74 | void ets_bzero(void *s, size_t n); 75 | //void ets_delay_us(int ms); 76 | 77 | // disappeared in SDK 1.1.0: 78 | #define os_timer_done ets_timer_done 79 | #define os_timer_handler_isr ets_timer_handler_isr 80 | #define os_timer_init ets_timer_init 81 | 82 | // This is not missing in SDK 1.1.0 but causes a parens error 83 | #undef PIN_FUNC_SELECT 84 | #define PIN_FUNC_SELECT(PIN_NAME, FUNC) do { \ 85 | WRITE_PERI_REG(PIN_NAME, \ 86 | (READ_PERI_REG(PIN_NAME) & ~(PERIPHS_IO_MUX_FUNC< 38 | * 39 | */ 40 | 41 | /** \addtogroup lib 42 | * @{ */ 43 | 44 | /** 45 | * \defgroup crc16 Cyclic Redundancy Check 16 (CRC16) calculation 46 | * 47 | * The Cyclic Redundancy Check 16 is a hash function that produces a 48 | * checksum that is used to detect errors in transmissions. The CRC16 49 | * calculation module is an iterative CRC calculator that can be used 50 | * to cumulatively update a CRC checksum for every incoming byte. 51 | * 52 | * @{ 53 | */ 54 | 55 | #ifndef CRC16_H_ 56 | #define CRC16_H_ 57 | #ifdef __cplusplus 58 | extern "C" { 59 | #endif 60 | /** 61 | * \brief Update an accumulated CRC16 checksum with one byte. 62 | * \param b The byte to be added to the checksum 63 | * \param crc The accumulated CRC that is to be updated. 64 | * \return The updated CRC checksum. 65 | * 66 | * This function updates an accumulated CRC16 checksum 67 | * with one byte. It can be used as a running checksum, or 68 | * to checksum an entire data block. 69 | * 70 | * \note The algorithm used in this implementation is 71 | * tailored for a running checksum and does not perform as 72 | * well as a table-driven algorithm when checksumming an 73 | * entire data block. 74 | * 75 | */ 76 | unsigned short crc16_add(unsigned char b, unsigned short crc); 77 | 78 | /** 79 | * \brief Calculate the CRC16 over a data area 80 | * \param data Pointer to the data 81 | * \param datalen The length of the data 82 | * \param acc The accumulated CRC that is to be updated (or zero). 83 | * \return The CRC16 checksum. 84 | * 85 | * This function calculates the CRC16 checksum of a data area. 86 | * 87 | * \note The algorithm used in this implementation is 88 | * tailored for a running checksum and does not perform as 89 | * well as a table-driven algorithm when checksumming an 90 | * entire data block. 91 | */ 92 | unsigned short crc16_data(const unsigned char *data, int datalen, 93 | unsigned short acc); 94 | #ifdef __cplusplus 95 | } 96 | #endif 97 | #endif /* CRC16_H_ */ 98 | 99 | /** @} */ 100 | /** @} */ 101 | -------------------------------------------------------------------------------- /html/wifi/wifiSta.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

WiFi Station Configuration

4 |
5 | 6 |
7 |
8 |
9 |

WiFi State

10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 |
25 |
26 |

WiFi Association

27 | 28 |
29 | To connect to a WiFi network, please select one of the detected networks, 30 | enter the password, and hit the connect button... 31 | 32 |
Scanning...
33 | 37 | 38 | 39 | 40 |
41 |
42 |
43 |
44 |
45 |

Special Settings

46 |
47 | Special settings, use with care! 48 |
49 | 52 | 55 |
56 |
57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 |
65 | 66 |
67 |
68 |
69 |
70 |
71 | 72 | 73 | 75 | 76 | 86 | 87 | -------------------------------------------------------------------------------- /syslog/syslog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * syslog.h 3 | * 4 | * 5 | * Copyright 2015 Susi's Strolch 6 | * 7 | * For license information see projects "License.txt" 8 | * 9 | * part of syslog.c - client library 10 | * 11 | */ 12 | 13 | 14 | #ifndef _SYSLOG_H 15 | #define _SYSLOG_H 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | enum syslog_state { 22 | SYSLOG_NONE, // not initialized 23 | SYSLOG_WAIT, // waiting for Wifi 24 | SYSLOG_INIT, // WIFI avail, must initialize 25 | SYSLOG_INITDONE, 26 | SYSLOG_DNSWAIT, // WIFI avail, init done, waiting for DNS resolve 27 | SYSLOG_READY, // Wifi established, ready to send 28 | SYSLOG_SENDING, // UDP package on the air 29 | SYSLOG_SEND, 30 | SYSLOG_SENT, 31 | SYSLOG_HALTED, // heap full, discard message 32 | SYSLOG_ERROR, 33 | }; 34 | 35 | enum syslog_priority { 36 | SYSLOG_PRIO_EMERG, /* system is unusable */ 37 | SYSLOG_PRIO_ALERT, /* action must be taken immediately */ 38 | SYSLOG_PRIO_CRIT, /* critical conditions */ 39 | SYSLOG_PRIO_ERR, /* error conditions */ 40 | SYSLOG_PRIO_WARNING, /* warning conditions */ 41 | SYSLOG_PRIO_NOTICE, /* normal but significant condition */ 42 | SYSLOG_PRIO_INFO, /* informational */ 43 | SYSLOG_PRIO_DEBUG, /* debug-level messages */ 44 | }; 45 | 46 | enum syslog_facility { 47 | SYSLOG_FAC_KERN, /* kernel messages */ 48 | SYSLOG_FAC_USER, /* random user-level messages */ 49 | SYSLOG_FAC_MAIL, /* mail system */ 50 | SYSLOG_FAC_DAEMON, /* system daemons */ 51 | SYSLOG_FAC_AUTH, /* security/authorization messages */ 52 | SYSLOG_FAC_SYSLOG, /* messages generated internally by syslogd */ 53 | SYSLOG_FAC_LPR, /* line printer subsystem */ 54 | SYSLOG_FAC_NEWS, /* network news subsystem */ 55 | SYSLOG_FAC_UUCP, /* UUCP subsystem */ 56 | SYSLOG_FAC_CRON, /* clock daemon */ 57 | SYSLOG_FAC_AUTHPRIV,/* security/authorization messages (private) */ 58 | SYSLOG_FAC_FTP, /* ftp daemon */ 59 | SYSLOG_FAC_LOCAL0, /* reserved for local use */ 60 | SYSLOG_FAC_LOCAL1, /* reserved for local use */ 61 | SYSLOG_FAC_LOCAL2, /* reserved for local use */ 62 | SYSLOG_FAC_LOCAL3, /* reserved for local use */ 63 | SYSLOG_FAC_LOCAL4, /* reserved for local use */ 64 | SYSLOG_FAC_LOCAL5, /* reserved for local use */ 65 | SYSLOG_FAC_LOCAL6, /* reserved for local use */ 66 | SYSLOG_FAC_LOCAL7, /* reserved for local use */ 67 | }; 68 | 69 | #define MINIMUM_HEAP_SIZE 8192 70 | #define REG_READ(_r) (*(volatile uint32 *)(_r)) 71 | #define WDEV_NOW() REG_READ(0x3ff20c00) 72 | 73 | // This variable disappeared from lwip in SDK 2.0... 74 | // extern uint32_t realtime_stamp; // 1sec NTP ticker 75 | 76 | typedef struct syslog_host_t syslog_host_t; 77 | struct syslog_host_t { 78 | uint32_t min_heap_size; // minimum allowed heap size when buffering 79 | ip_addr_t addr; 80 | uint16_t port; 81 | }; 82 | 83 | // buffered syslog event - f.e. if network stack isn't up and running 84 | typedef struct syslog_entry_t syslog_entry_t; 85 | struct syslog_entry_t { 86 | syslog_entry_t *next; 87 | uint32_t msgid; 88 | uint32_t tick; 89 | uint16_t datagram_len; 90 | char datagram[]; 91 | }; 92 | 93 | syslog_host_t syslogserver; 94 | 95 | void ICACHE_FLASH_ATTR syslog_init(char *syslog_host); 96 | void ICACHE_FLASH_ATTR syslog(uint8_t facility, uint8_t severity, const char tag[], const char message[], ...); 97 | 98 | // some convenience macros 99 | #ifdef SYSLOG 100 | // extern char *esp_link_version; // in user_main.c 101 | #define LOG_DEBUG(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_DEBUG, "esp_link", format, ## __VA_ARGS__ ) 102 | #define LOG_NOTICE(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_NOTICE, "esp_link", format, ## __VA_ARGS__ ) 103 | #define LOG_INFO(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_INFO, "esp_link", format, ## __VA_ARGS__ ) 104 | #define LOG_WARN(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_WARNING, "esp_link", format, ## __VA_ARGS__ ) 105 | #define LOG_ERR(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_ERR, "esp_link", format, ## __VA_ARGS__ ) 106 | #else 107 | #define LOG_DEBUG(format, ...) do { } while(0) 108 | #define LOG_NOTICE(format, ...) do { } while(0) 109 | #define LOG_WARN(format, ...) do { } while(0) 110 | #define LOG_INFO(format, ...) do { } while(0) 111 | #define LOG_ERR(format, ...) do { } while(0) 112 | #endif 113 | 114 | #ifdef __cplusplus 115 | } 116 | #endif 117 | 118 | #endif /* _SYSLOG_H */ 119 | -------------------------------------------------------------------------------- /serial/slip.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt 2 | 3 | #include "esp8266.h" 4 | #include "uart.h" 5 | #include "crc16.h" 6 | #include "serbridge.h" 7 | #include "console.h" 8 | #include "cmd.h" 9 | 10 | #ifdef SLIP_DBG 11 | #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) 12 | #else 13 | #define DBG(format, ...) do { } while(0) 14 | #endif 15 | 16 | extern void ICACHE_FLASH_ATTR console_process(char *buf, short len); 17 | 18 | // This SLIP parser tries to conform to RFC 1055 https://tools.ietf.org/html/rfc1055. 19 | // It accumulates each packet into a static buffer and calls cmd_parse() when the end 20 | // of a packet is reached. It expects cmd_parse() to copy anything it needs from the 21 | // buffer elsewhere as the buffer is immediately reused. 22 | // One special feature is that if the first two characters of a packet are both printable or 23 | // \n or \r then the parser assumes it's dealing with console debug output and calls 24 | // slip_console(c) for each character and does not accumulate chars in the buffer until the 25 | // next SLIP_END marker is seen. This allows random console debug output to come in between 26 | // packets as long as each packet starts *and* ends with SLIP_END (which is an official 27 | // variation on the SLIP protocol). 28 | 29 | static bool slip_escaped; // true when prev char received is escape 30 | static bool slip_inpkt; // true when we're after SLIP_START and before SLIP_END 31 | #define SLIP_MAX 1024 // max length of SLIP packet 32 | static char slip_buf[SLIP_MAX]; // buffer for current SLIP packet 33 | static short slip_len; // accumulated length in slip_buf 34 | 35 | // SLIP process a packet or a bunch of debug console chars 36 | static void ICACHE_FLASH_ATTR 37 | slip_process() { 38 | if (slip_len > 2) { 39 | // proper SLIP packet, invoke command processor after checking CRC 40 | //os_printf("SLIP: rcv %d\n", slip_len); 41 | uint16_t crc = crc16_data((uint8_t*)slip_buf, slip_len-2, 0); 42 | uint16_t rcv = ((uint16_t)slip_buf[slip_len-2]) | ((uint16_t)slip_buf[slip_len-1] << 8); 43 | if (crc == rcv) { 44 | cmdParsePacket((uint8_t*)slip_buf, slip_len-2); 45 | } else { 46 | os_printf("SLIP: bad CRC, crc=%04x rcv=%04x len=%d\n", crc, rcv, slip_len); 47 | 48 | for (short i=0; i= ' ' && slip_buf[i] <= '~') { 50 | DBG("%c", slip_buf[i]); 51 | } else { 52 | DBG("\\%02X", slip_buf[i]); 53 | } 54 | } 55 | DBG("\n"); 56 | } 57 | } 58 | } 59 | 60 | // determine whether a character is printable or not (or \r \n) 61 | static bool ICACHE_FLASH_ATTR 62 | slip_printable(char c) { 63 | return (c >= ' ' && c <= '~') || c == '\n' || c == '\r'; 64 | } 65 | 66 | static void ICACHE_FLASH_ATTR 67 | slip_reset() { 68 | //os_printf("SLIP: reset\n"); 69 | slip_inpkt = true; 70 | slip_escaped = false; 71 | slip_len = 0; 72 | } 73 | 74 | // SLIP parse a single character 75 | static void ICACHE_FLASH_ATTR 76 | slip_parse_char(char c) { 77 | if (c == SLIP_END) { 78 | // either start or end of packet, process whatever we may have accumulated 79 | DBG("SLIP: start or end len=%d inpkt=%d\n", slip_len, slip_inpkt); 80 | if (slip_len > 0) { 81 | if (slip_len > 2 && slip_inpkt) slip_process(); 82 | else console_process(slip_buf, slip_len); 83 | } 84 | slip_reset(); 85 | } else if (slip_escaped) { 86 | // prev char was SLIP_ESC 87 | if (c == SLIP_ESC_END) c = SLIP_END; 88 | if (c == SLIP_ESC_ESC) c = SLIP_ESC; 89 | if (slip_len < SLIP_MAX) slip_buf[slip_len++] = c; 90 | slip_escaped = false; 91 | } else if (slip_inpkt && c == SLIP_ESC) { 92 | slip_escaped = true; 93 | } else { 94 | if (slip_len == 1 && slip_printable(slip_buf[0]) && slip_printable(c)) { 95 | // start of packet and it's a printable character, we're gonna assume that this is console text 96 | slip_inpkt = false; 97 | } 98 | if (slip_len < SLIP_MAX) slip_buf[slip_len++] = c; 99 | } 100 | } 101 | 102 | // callback with a buffer of characters that have arrived on the uart 103 | void ICACHE_FLASH_ATTR 104 | slip_parse_buf(char *buf, short length) { 105 | // do SLIP parsing 106 | for (short i=0; i 0) { 111 | console_process(slip_buf, slip_len); 112 | slip_len = 0; 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /FLASH.md: -------------------------------------------------------------------------------- 1 | ESP-LINK OTA Flash Layout 2 | ========================= 3 | 4 | The flash layout dictated by the bootloader is the following (all this assumes a 512KB flash chip 5 | and is documented in Espressif's `99C-ESP8266__OTA_Upgrade__EN_v1.5.pdf`): 6 | - @0x00000 4KB bootloader 7 | - @0x01000 236KB partition1 8 | - @0x3E000 16KB esp-link parameters 9 | - @0x40000 4KB unused 10 | - @0x41000 236KB partition2 11 | - @0x7E000 16KB system wifi parameters 12 | 13 | What this means is that we can flash just about anything into partition1 or partition2 as long 14 | as it doesn't take more than 236KB and has the right format that the boot loader understands. 15 | We can't mess with the first 4KB nor the last 16KB of the flash. 16 | 17 | Now how does a code partition break down? that is reflected in the following definition found in 18 | the loader scripts: 19 | ``` 20 | dram0_0_seg : org = 0x3FFE8000, len = 0x14000 21 | iram1_0_seg : org = 0x40100000, len = 0x8000 22 | irom0_0_seg : org = 0x40201010, len = 0x2B000 23 | ``` 24 | This means that 80KB (0x14000) are reserved for "dram0_0", 32KB (0x8000) for "iram1_0" and 25 | 172KB (0x2B000) are reserved for irom0_0. The segments are used as follows: 26 | - dram0_0 is the data RAM and some of that gets initialized at boot time from flash (static variable initialization) 27 | - iram1_0 is the instruction RAM and all of that gets loaded at boot time from flash 28 | - irom0_0 is the instruction cache which gets loaded on-demand from flash (all functions 29 | with the `ICACHE_FLASH_ATTR` attribute go there) 30 | 31 | You might notice that 80KB+32KB+172KB is more than 236KB and that's because not the entire dram0_0 32 | segment needs to be loaded from flash, only the portion with statically initialized data. 33 | You might also notice that while iram1_0 is as large as the chip's instruction RAM (at least 34 | according to the info I've seen) the size of the irom0_0 segment is smaller than it could be, 35 | since it's really not bounded by any limitation of the processor (it simply backs the cache). 36 | 37 | When putting the OTA flash process together I ran into loader issues, namely, while I was having 38 | relatively little initialized data and also not 32KB of iram1_0 instructions I was overflowing 39 | the allotted 172KB of irom0_0. To fix the problem the build process modifies the loader scripts 40 | (see the `build/eagle.esphttpd1.v6.ld` target in the Makefile) to increase the irom0_0 segment 41 | to 224KB (a somewhat arbitrary value). This doesn't mean that there will be 224KB of irom0_0 42 | in flash, it just means that that's the maximum the linker will put there without giving an error. 43 | In the end what has to fit into the magic 236KB is the sum of the actual initialized data, 44 | the actually used iram1_0 segment, and the irom0_0 segment. 45 | In addition, the dram0_0 and iram1_0 segments can't exceed what's specified 46 | in the loader script 'cause those are the limitations of the processor. 47 | 48 | Now that you hopefully understand the above you can understand the line printed by the Makefile 49 | when linking the firmware, which looks something like: 50 | ``` 51 | ** user1.bin uses 218592 bytes of 241664 available 52 | ``` 53 | Here 241664 is 236KB and 218592 is the size of what's getting flashed, so you can tell that you have 54 | another 22KB to spend (modulo some 4KB flash segment rounding). 55 | (Note that user2.bin has exactly the same size, so the Makefile doesn't print its info.) 56 | The Makefile also prints a few more details: 57 | ``` 58 | ls -ls eagle*bin 59 | 4 -rwxrwxr-x 1 tve tve 2652 May 24 10:12 eagle.app.v6.data.bin 60 | 176 -rwxrwxr-x 1 tve tve 179732 May 24 10:12 eagle.app.v6.irom0text.bin 61 | 8 -rwxrwxr-x 1 tve tve 5732 May 24 10:12 eagle.app.v6.rodata.bin 62 | 32 -rwxrwxr-x 1 tve tve 30402 May 24 10:12 eagle.app.v6.text.bin 63 | ``` 64 | This says that we have 179732 bytes of irom0_0, we have 5732+2652 bytes of dram0_0 (read-only data 65 | plus initialized read-write data), and we have 30402 bytes of iram1_0. 66 | 67 | There's an additional twist to all this for the espfs "file system" that esphttpd uses. 68 | The data for this is loaded at the end of irom0_0 and is called espfs. 69 | The Makefile modifies the loader script to place the espfs at the start of irom0_0 and 70 | ensure that it's 32-bit aligned. The size of the espfs is shown here: 71 | ``` 72 | 4026be14 g .irom0.text 00000000 _binary_espfs_img_end 73 | 40269e98 g .irom0.text 00000000 _binary_espfs_img_start 74 | 00001f7c g *ABS* 00000000 _binary_espfs_img_size 75 | ``` 76 | Namely, 0x1f7c = 8060 bytes. 77 | 78 | 79 | -------------------------------------------------------------------------------- /esp-link/cgipins.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "cgi.h" 4 | #include "espfs.h" 5 | #include "config.h" 6 | #include "serled.h" 7 | #include "status.h" 8 | #include "serbridge.h" 9 | 10 | #if 0 11 | static char *map_names[] = { 12 | "esp-bridge", "jn-esp-v2", "esp-01(AVR)", "esp-01(ARM)", "esp-br-rev", "wifi-link-12", 13 | }; 14 | static char* map_func[] = { "reset", "isp", "conn_led", "ser_led", "swap_uart" }; 15 | static int8_t map_asn[][5] = { 16 | { 12, 13, 0, 14, 0 }, // esp-bridge 17 | { 12, 13, 0, 2, 0 }, // jn-esp-v2 18 | { 0, -1, 2, -1, 0 }, // esp-01(AVR) 19 | { 0, 2, -1, -1, 0 }, // esp-01(ARM) 20 | { 13, 12, 14, 0, 0 }, // esp-br-rev -- for test purposes 21 | { 1, 3, 0, 2, 1 }, // esp-link-12 22 | }; 23 | static const int num_map_names = sizeof(map_names)/sizeof(char*); 24 | static const int num_map_func = sizeof(map_func)/sizeof(char*); 25 | #endif 26 | 27 | // Cgi to return choice of pin assignments 28 | int ICACHE_FLASH_ATTR cgiPinsGet(HttpdConnData *connData) { 29 | if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted 30 | 31 | char buff[1024]; 32 | int len; 33 | 34 | len = os_sprintf(buff, 35 | "{ \"reset\":%d, \"isp\":%d, \"conn\":%d, \"ser\":%d, \"swap\":%d, \"rxpup\":%d }", 36 | flashConfig.reset_pin, flashConfig.isp_pin, flashConfig.conn_led_pin, 37 | flashConfig.ser_led_pin, !!flashConfig.swap_uart, !!flashConfig.rx_pullup); 38 | 39 | jsonHeader(connData, 200); 40 | httpdSend(connData, buff, len); 41 | return HTTPD_CGI_DONE; 42 | } 43 | 44 | // Cgi to change choice of pin assignments 45 | int ICACHE_FLASH_ATTR cgiPinsSet(HttpdConnData *connData) { 46 | if (connData->conn==NULL) { 47 | return HTTPD_CGI_DONE; // Connection aborted 48 | } 49 | 50 | int8_t ok = 0; 51 | int8_t reset, isp, conn, ser; 52 | uint8_t swap, rxpup; 53 | ok |= getInt8Arg(connData, "reset", &reset); 54 | ok |= getInt8Arg(connData, "isp", &isp); 55 | ok |= getInt8Arg(connData, "conn", &conn); 56 | ok |= getInt8Arg(connData, "ser", &ser); 57 | ok |= getBoolArg(connData, "swap", &swap); 58 | ok |= getBoolArg(connData, "rxpup", &rxpup); 59 | if (ok < 0) return HTTPD_CGI_DONE; 60 | 61 | char *coll; 62 | if (ok > 0) { 63 | // check whether two pins collide 64 | uint16_t pins = 0; 65 | if (reset >= 0) pins = 1 << reset; 66 | if (isp >= 0) { 67 | if (pins & (1<= 0) { 71 | if (pins & (1<= 0) { 75 | if (pins & (1<conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. 123 | if (connData->requestType == HTTPD_METHOD_GET) { 124 | return cgiPinsGet(connData); 125 | } else if (connData->requestType == HTTPD_METHOD_POST) { 126 | return cgiPinsSet(connData); 127 | } else { 128 | jsonHeader(connData, 404); 129 | return HTTPD_CGI_DONE; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /cmd/cmd.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt 2 | // 3 | // Adapted from: github.com/tuanpmt/esp_bridge, Created on: Jan 9, 2015, Author: Minh 4 | 5 | #ifndef CMD_H 6 | #define CMD_H 7 | #include 8 | 9 | // keep track of whether we received a sync command from uC 10 | extern bool cmdInSync; 11 | 12 | // Standard SLIP escape chars from RFC 13 | #define SLIP_END 0300 // indicates end of packet 14 | #define SLIP_ESC 0333 // indicates byte stuffing 15 | #define SLIP_ESC_END 0334 // ESC ESC_END means END data byte 16 | #define SLIP_ESC_ESC 0335 // ESC ESC_ESC means ESC data byte 17 | 18 | typedef struct __attribute__((__packed__)) { 19 | uint16_t len; // length of data 20 | uint8_t data[0]; // really data[len] 21 | } CmdArg; 22 | 23 | typedef struct __attribute__((__packed__)) { 24 | uint16_t cmd; // command to perform, from CmdName enum 25 | uint16_t argc; // number of arguments to command 26 | uint32_t value; // callback pointer for response or first argument 27 | CmdArg args[0]; // really args[argc] 28 | } CmdPacket; 29 | 30 | typedef struct { 31 | CmdPacket *cmd; // command packet header 32 | uint32_t arg_num; // number of args parsed 33 | uint8_t *arg_ptr; // pointer to ?? 34 | } CmdRequest; 35 | 36 | typedef enum { 37 | CMD_NULL = 0, 38 | CMD_SYNC, // synchronize and clear 39 | CMD_RESP_V, // response with a value 40 | CMD_RESP_CB, // response with a callback 41 | CMD_WIFI_STATUS, // get the current wifi status 42 | CMD_CB_ADD, 43 | CMD_CB_EVENTS, 44 | CMD_GET_TIME, // get current time in seconds since the unix epoch 45 | CMD_GET_WIFI_INFO, // query ip address info 46 | CMD_SET_WIFI_INFO, // set ip address info 47 | 48 | CMD_MQTT_SETUP = 10, // set-up callbacks 49 | CMD_MQTT_PUBLISH, // publish a message 50 | CMD_MQTT_SUBSCRIBE, // subscribe to a topic 51 | CMD_MQTT_LWT, // set the last-will-topic and messge 52 | CMD_MQTT_GET_CLIENTID, 53 | 54 | CMD_REST_SETUP = 20, // set-up callbacks 55 | CMD_REST_REQUEST, // do REST request 56 | CMD_REST_SETHEADER, // define header 57 | 58 | CMD_WEB_SETUP = 30, // set-up WEB callback 59 | CMD_WEB_DATA, // WEB data from MCU 60 | 61 | CMD_SOCKET_SETUP = 40, // set-up callbacks 62 | CMD_SOCKET_SEND, // send data over UDP socket 63 | 64 | CMD_WIFI_GET_APCOUNT = 50, // Query the number of networks / Access Points known 65 | CMD_WIFI_GET_APNAME, // Query the name (SSID) of an Access Point (AP) 66 | CMD_WIFI_SELECT_SSID, // Connect to a specific network 67 | CMD_WIFI_SIGNAL_STRENGTH, // Query RSSI 68 | CMD_WIFI_GET_SSID, // Query SSID currently connected to 69 | CMD_WIFI_START_SCAN, // Trigger a scan (takes a long time) 70 | 71 | } CmdName; 72 | 73 | typedef void (*cmdfunc_t)(CmdPacket *cmd); 74 | 75 | typedef struct { 76 | CmdName sc_name; // name as CmdName enum 77 | char *sc_text; // name as string 78 | cmdfunc_t sc_function; // pointer to function 79 | } CmdList; 80 | 81 | // command dispatch table 82 | extern const CmdList commands[]; 83 | 84 | #define CMD_CBNLEN 16 85 | typedef struct { 86 | char name[CMD_CBNLEN]; 87 | uint32_t callback; 88 | } CmdCallback; 89 | 90 | // Used by slip protocol to cause parsing of a received packet 91 | void cmdParsePacket(uint8_t *buf, short len); 92 | 93 | // Return the info about a callback to the attached uC by name, these are callbacks that the 94 | // attached uC registers using the ADD_SENSOR command 95 | CmdCallback* cmdGetCbByName(char* name); 96 | 97 | // Add a callback 98 | uint32_t cmdAddCb(char *name, uint32_t callback); 99 | 100 | // Responses 101 | 102 | // Start a response 103 | void cmdResponseStart(uint16_t cmd, uint32_t value, uint16_t argc); 104 | // Adds data to a response 105 | void cmdResponseBody(const void* data, uint16_t len); 106 | // Ends a response 107 | void cmdResponseEnd(); 108 | 109 | //void cmdResponse(uint16_t cmd, uint32_t callback, uint32_t value, uint16_t argc, CmdArg* args[]); 110 | 111 | // Requests 112 | 113 | // Fill out a CmdRequest struct given a CmdPacket 114 | void cmdRequest(CmdRequest *req, CmdPacket* cmd); 115 | // Return the number of arguments given a request 116 | uint32_t cmdGetArgc(CmdRequest *req); 117 | // Return the length of the next argument 118 | uint16_t cmdArgLen(CmdRequest *req); 119 | // Copy next arg from request into the data pointer, returns 0 on success, -1 on error 120 | int32_t cmdPopArg(CmdRequest *req, void *data, uint16_t len); 121 | // Skip next arg 122 | void cmdSkipArg(CmdRequest *req); 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /html/mqtt.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

REST & MQTT

4 |
5 | 6 |
7 |
8 |
9 |

The REST & MQTT support uses the SLIP protocol over the serial port to enable 10 | the attached microcontroller to initiate outbound connections. 11 | The REST support lets the uC initiate simple HTTP requests while the MQTT support 12 | lets it communicate with an MQTT server bidirectionally at QoS 0 thru 2.

13 |

The MQTT support is in the form of a built-in client that connects to a server 14 | using parameters set below and stored in esp-link's flash settings. This allows 15 | esp-link to take care of connection parameters and disconnect/reconnect operations.

16 |

The MQTT client also supports sending periodic status messages about esp-link itself, 17 | including WiFi RSSI, and free heap memory.

18 |
19 | 20 | 21 |
22 |
23 |
24 |
25 |
26 |
27 |

MQTT 28 |
29 |

30 | 61 |
62 |
63 |
64 |
65 |

Status reporting 66 |
67 |

68 | 84 |
85 |
86 |

REST

87 |

REST requests are enabled as soon as SLIP is enabled. 88 | There are no REST-specific settings.

89 |
90 |
91 |
92 |
93 |
94 | 95 | 96 | 97 | 104 | 105 | -------------------------------------------------------------------------------- /html/wifi/wifiAp.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

WiFi Soft-AP Configuration

4 |
5 | 6 |
7 |
8 |
9 |
10 |

Soft-AP State

11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 | 29 |
30 |

Soft-AP Settings

31 |
32 | 33 | 88 |
89 |
90 |
91 |
92 |
93 | 94 | 95 | 97 | 98 | 108 | 109 | -------------------------------------------------------------------------------- /html/console.js: -------------------------------------------------------------------------------- 1 | //===== Fetching console text 2 | 3 | function fetchText(delay, repeat) { 4 | var el = $("#console"); 5 | if (el.textEnd == undefined) { 6 | el.textEnd = 0; 7 | el.innerHTML = ""; 8 | } 9 | window.setTimeout(function() { 10 | ajaxJson('GET', console_url + "?start=" + el.textEnd, 11 | function(resp) { 12 | var dly = updateText(resp); 13 | if (repeat) fetchText(dly, repeat); 14 | }, 15 | function() { retryLoad(repeat); }); 16 | }, delay); 17 | } 18 | 19 | function updateText(resp) { 20 | var el = $("#console"); 21 | 22 | var delay = 3000; 23 | if (resp != null && resp.len > 0) { 24 | // console.log("updateText got", resp.len, "chars at", resp.start); 25 | var isScrolledToBottom = el.scrollHeight - el.clientHeight <= el.scrollTop + 1; 26 | //console.log("isScrolledToBottom="+isScrolledToBottom, "scrollHeight="+el.scrollHeight, 27 | // "clientHeight="+el.clientHeight, "scrollTop="+el.scrollTop, 28 | // "" + (el.scrollHeight - el.clientHeight) + "<=" + (el.scrollTop + 1)); 29 | 30 | // append the text 31 | if (resp.start > el.textEnd) { 32 | el.innerHTML = el.innerHTML.concat("\r\n/g, '>') 38 | .replace(/"/g, '"')); 39 | el.textEnd = resp.start + resp.len; 40 | delay = 500; 41 | 42 | // scroll to bottom 43 | if(isScrolledToBottom) el.scrollTop = el.scrollHeight - el.clientHeight; 44 | } 45 | return delay; 46 | } 47 | 48 | function retryLoad(repeat) { 49 | fetchText(1000, repeat); 50 | } 51 | 52 | //===== Text entry 53 | 54 | function consoleSendInit() { 55 | var sendHistory = $("#send-history"); 56 | var inputText = $("#input-text"); 57 | var inputAddCr = $("#input-add-cr"); 58 | var inputAddLf = $("#input-add-lf"); 59 | 60 | function findHistory(text) { 61 | for (var i = 0; i < sendHistory.children.length; i++) { 62 | if (text == sendHistory.children[i].value) { 63 | return i; 64 | } 65 | } 66 | return null; 67 | } 68 | 69 | function loadHistory(idx) { 70 | sendHistory.value = sendHistory.children[idx].value; 71 | inputText.value = sendHistory.children[idx].value; 72 | } 73 | 74 | function navHistory(rel) { 75 | var idx = findHistory(sendHistory.value) + rel; 76 | if (idx < 0) { 77 | idx = sendHistory.children.length - 1; 78 | } 79 | if (idx >= sendHistory.children.length) { 80 | idx = 0; 81 | } 82 | loadHistory(idx); 83 | } 84 | 85 | sendHistory.addEventListener("change", function(e) { 86 | inputText.value = sendHistory.value; 87 | }); 88 | 89 | function pushHistory(text) { 90 | var idx = findHistory(text); 91 | if (idx !== null) { 92 | loadHistory(idx); 93 | return false; 94 | } 95 | var newOption = m(''); 102 | newOption.value = text; 103 | sendHistory.appendChild(newOption); 104 | sendHistory.value = text; 105 | for (; sendHistory.children.length > 15; ) { 106 | sendHistory.removeChild(sendHistory.children[0]); 107 | } 108 | return true; 109 | } 110 | 111 | inputText.addEventListener("keydown", function(e) { 112 | switch (e.keyCode) { 113 | case 38: /* the up arrow key pressed */ 114 | e.preventDefault(); 115 | navHistory(-1); 116 | break; 117 | case 40: /* the down arrow key pressed */ 118 | e.preventDefault(); 119 | navHistory(+1); 120 | break; 121 | case 27: /* the escape key pressed */ 122 | e.preventDefault(); 123 | inputText.value = ""; 124 | sendHistory.value = ""; 125 | break; 126 | case 13: /* the enter key pressed */ 127 | e.preventDefault(); 128 | var text = inputText.value; 129 | if (inputAddCr.checked) text += '\r'; 130 | if (inputAddLf.checked) text += '\n'; 131 | pushHistory(inputText.value); 132 | inputText.value = ""; 133 | ajaxSpin('POST', "/console/send?text=" + encodeURIComponent(text), 134 | function(resp) { showNotification("Text sent"); }, 135 | function(s, st) { showWarning("Error sending text"); } 136 | ); 137 | break; 138 | } 139 | }); 140 | } 141 | 142 | //===== Log page 143 | 144 | function showDbgMode(mode) { 145 | var btns = $('.dbg-btn'); 146 | for (var i=0; i < btns.length; i++) { 147 | if (btns[i].id === "dbg-"+mode) 148 | addClass(btns[i], "button-selected"); 149 | else 150 | removeClass(btns[i], "button-selected"); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /espfs/mkespfsimage/mman-win32/mman.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "mman.h" 7 | 8 | #ifndef FILE_MAP_EXECUTE 9 | #define FILE_MAP_EXECUTE 0x0020 10 | #endif /* FILE_MAP_EXECUTE */ 11 | 12 | static int __map_mman_error(const DWORD err, const int deferr) 13 | { 14 | if (err == 0) 15 | return 0; 16 | //TODO: implement 17 | return err; 18 | } 19 | 20 | static DWORD __map_mmap_prot_page(const int prot) 21 | { 22 | DWORD protect = 0; 23 | 24 | if (prot == PROT_NONE) 25 | return protect; 26 | 27 | if ((prot & PROT_EXEC) != 0) 28 | { 29 | protect = ((prot & PROT_WRITE) != 0) ? 30 | PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; 31 | } 32 | else 33 | { 34 | protect = ((prot & PROT_WRITE) != 0) ? 35 | PAGE_READWRITE : PAGE_READONLY; 36 | } 37 | 38 | return protect; 39 | } 40 | 41 | static DWORD __map_mmap_prot_file(const int prot) 42 | { 43 | DWORD desiredAccess = 0; 44 | 45 | if (prot == PROT_NONE) 46 | return desiredAccess; 47 | 48 | if ((prot & PROT_READ) != 0) 49 | desiredAccess |= FILE_MAP_READ; 50 | if ((prot & PROT_WRITE) != 0) 51 | desiredAccess |= FILE_MAP_WRITE; 52 | if ((prot & PROT_EXEC) != 0) 53 | desiredAccess |= FILE_MAP_EXECUTE; 54 | 55 | return desiredAccess; 56 | } 57 | 58 | void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off) 59 | { 60 | HANDLE fm, h; 61 | 62 | void * map = MAP_FAILED; 63 | 64 | #ifdef _MSC_VER 65 | #pragma warning(push) 66 | #pragma warning(disable: 4293) 67 | #endif 68 | 69 | const DWORD dwFileOffsetLow = (sizeof(off_t) <= sizeof(DWORD)) ? 70 | (DWORD)off : (DWORD)(off & 0xFFFFFFFFL); 71 | const DWORD dwFileOffsetHigh = (sizeof(off_t) <= sizeof(DWORD)) ? 72 | (DWORD)0 : (DWORD)((off >> 32) & 0xFFFFFFFFL); 73 | const DWORD protect = __map_mmap_prot_page(prot); 74 | const DWORD desiredAccess = __map_mmap_prot_file(prot); 75 | 76 | const off_t maxSize = off + (off_t)len; 77 | 78 | const DWORD dwMaxSizeLow = (sizeof(off_t) <= sizeof(DWORD)) ? 79 | (DWORD)maxSize : (DWORD)(maxSize & 0xFFFFFFFFL); 80 | const DWORD dwMaxSizeHigh = (sizeof(off_t) <= sizeof(DWORD)) ? 81 | (DWORD)0 : (DWORD)((maxSize >> 32) & 0xFFFFFFFFL); 82 | 83 | #ifdef _MSC_VER 84 | #pragma warning(pop) 85 | #endif 86 | 87 | errno = 0; 88 | 89 | if (len == 0 90 | /* Unsupported flag combinations */ 91 | || (flags & MAP_FIXED) != 0 92 | /* Usupported protection combinations */ 93 | || prot == PROT_EXEC) 94 | { 95 | errno = EINVAL; 96 | return MAP_FAILED; 97 | } 98 | 99 | h = ((flags & MAP_ANONYMOUS) == 0) ? 100 | (HANDLE)_get_osfhandle(fildes) : INVALID_HANDLE_VALUE; 101 | 102 | if ((flags & MAP_ANONYMOUS) == 0 && h == INVALID_HANDLE_VALUE) 103 | { 104 | errno = EBADF; 105 | return MAP_FAILED; 106 | } 107 | 108 | fm = CreateFileMapping(h, NULL, protect, dwMaxSizeHigh, dwMaxSizeLow, NULL); 109 | 110 | if (fm == NULL) 111 | { 112 | errno = __map_mman_error(GetLastError(), EPERM); 113 | return MAP_FAILED; 114 | } 115 | 116 | map = MapViewOfFile(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len); 117 | 118 | CloseHandle(fm); 119 | 120 | if (map == NULL) 121 | { 122 | errno = __map_mman_error(GetLastError(), EPERM); 123 | return MAP_FAILED; 124 | } 125 | 126 | return map; 127 | } 128 | 129 | int munmap(void *addr, size_t len) 130 | { 131 | if (UnmapViewOfFile(addr)) 132 | return 0; 133 | 134 | errno = __map_mman_error(GetLastError(), EPERM); 135 | 136 | return -1; 137 | } 138 | 139 | int mprotect(void *addr, size_t len, int prot) 140 | { 141 | DWORD newProtect = __map_mmap_prot_page(prot); 142 | DWORD oldProtect = 0; 143 | 144 | if (VirtualProtect(addr, len, newProtect, &oldProtect)) 145 | return 0; 146 | 147 | errno = __map_mman_error(GetLastError(), EPERM); 148 | 149 | return -1; 150 | } 151 | 152 | int msync(void *addr, size_t len, int flags) 153 | { 154 | if (FlushViewOfFile(addr, len)) 155 | return 0; 156 | 157 | errno = __map_mman_error(GetLastError(), EPERM); 158 | 159 | return -1; 160 | } 161 | 162 | int mlock(const void *addr, size_t len) 163 | { 164 | if (VirtualLock((LPVOID)addr, len)) 165 | return 0; 166 | 167 | errno = __map_mman_error(GetLastError(), EPERM); 168 | 169 | return -1; 170 | } 171 | 172 | int munlock(const void *addr, size_t len) 173 | { 174 | if (VirtualUnlock((LPVOID)addr, len)) 175 | return 0; 176 | 177 | errno = __map_mman_error(GetLastError(), EPERM); 178 | 179 | return -1; 180 | } 181 | -------------------------------------------------------------------------------- /esp-link/stk500v2.h: -------------------------------------------------------------------------------- 1 | //**** ATMEL AVR - A P P L I C A T I O N N O T E ************************ 2 | //* 3 | //* Title: AVR068 - STK500 Communication Protocol 4 | //* Filename: command.h 5 | //* Version: 1.0 6 | //* Last updated: 31.01.2005 7 | //* 8 | //* Support E-mail: avr@atmel.com 9 | //* 10 | //************************************************************************** 11 | 12 | // *****************[ STK message constants ]*************************** 13 | 14 | #define MESSAGE_START 0x1B //= ESC = 27 decimal 15 | #define TOKEN 0x0E 16 | 17 | // *****************[ STK general command constants ]************************** 18 | 19 | #define CMD_SIGN_ON 0x01 20 | #define CMD_SET_PARAMETER 0x02 21 | #define CMD_GET_PARAMETER 0x03 22 | #define CMD_SET_DEVICE_PARAMETERS 0x04 23 | #define CMD_OSCCAL 0x05 24 | #define CMD_LOAD_ADDRESS 0x06 25 | #define CMD_FIRMWARE_UPGRADE 0x07 26 | 27 | 28 | // *****************[ STK ISP command constants ]****************************** 29 | 30 | #define CMD_ENTER_PROGMODE_ISP 0x10 31 | #define CMD_LEAVE_PROGMODE_ISP 0x11 32 | #define CMD_CHIP_ERASE_ISP 0x12 33 | #define CMD_PROGRAM_FLASH_ISP 0x13 34 | #define CMD_READ_FLASH_ISP 0x14 35 | #define CMD_PROGRAM_EEPROM_ISP 0x15 36 | #define CMD_READ_EEPROM_ISP 0x16 37 | #define CMD_PROGRAM_FUSE_ISP 0x17 38 | #define CMD_READ_FUSE_ISP 0x18 39 | #define CMD_PROGRAM_LOCK_ISP 0x19 40 | #define CMD_READ_LOCK_ISP 0x1A 41 | #define CMD_READ_SIGNATURE_ISP 0x1B 42 | #define CMD_READ_OSCCAL_ISP 0x1C 43 | #define CMD_SPI_MULTI 0x1D 44 | 45 | // *****************[ STK PP command constants ]******************************* 46 | 47 | #define CMD_ENTER_PROGMODE_PP 0x20 48 | #define CMD_LEAVE_PROGMODE_PP 0x21 49 | #define CMD_CHIP_ERASE_PP 0x22 50 | #define CMD_PROGRAM_FLASH_PP 0x23 51 | #define CMD_READ_FLASH_PP 0x24 52 | #define CMD_PROGRAM_EEPROM_PP 0x25 53 | #define CMD_READ_EEPROM_PP 0x26 54 | #define CMD_PROGRAM_FUSE_PP 0x27 55 | #define CMD_READ_FUSE_PP 0x28 56 | #define CMD_PROGRAM_LOCK_PP 0x29 57 | #define CMD_READ_LOCK_PP 0x2A 58 | #define CMD_READ_SIGNATURE_PP 0x2B 59 | #define CMD_READ_OSCCAL_PP 0x2C 60 | 61 | #define CMD_SET_CONTROL_STACK 0x2D 62 | 63 | // *****************[ STK HVSP command constants ]***************************** 64 | 65 | #define CMD_ENTER_PROGMODE_HVSP 0x30 66 | #define CMD_LEAVE_PROGMODE_HVSP 0x31 67 | #define CMD_CHIP_ERASE_HVSP 0x32 68 | #define CMD_PROGRAM_FLASH_HVSP 0x33 69 | #define CMD_READ_FLASH_HVSP 0x34 70 | #define CMD_PROGRAM_EEPROM_HVSP 0x35 71 | #define CMD_READ_EEPROM_HVSP 0x36 72 | #define CMD_PROGRAM_FUSE_HVSP 0x37 73 | #define CMD_READ_FUSE_HVSP 0x38 74 | #define CMD_PROGRAM_LOCK_HVSP 0x39 75 | #define CMD_READ_LOCK_HVSP 0x3A 76 | #define CMD_READ_SIGNATURE_HVSP 0x3B 77 | #define CMD_READ_OSCCAL_HVSP 0x3C 78 | 79 | // *****************[ STK status constants ]*************************** 80 | 81 | // Success 82 | #define STATUS_CMD_OK 0x00 83 | 84 | // Warnings 85 | #define STATUS_CMD_TOUT 0x80 86 | #define STATUS_RDY_BSY_TOUT 0x81 87 | #define STATUS_SET_PARAM_MISSING 0x82 88 | 89 | // Errors 90 | #define STATUS_CMD_FAILED 0xC0 91 | #define STATUS_CKSUM_ERROR 0xC1 92 | #define STATUS_CMD_UNKNOWN 0xC9 93 | 94 | // *****************[ STK parameter constants ]*************************** 95 | #define PARAM_BUILD_NUMBER_LOW 0x80 96 | #define PARAM_BUILD_NUMBER_HIGH 0x81 97 | #define PARAM_HW_VER 0x90 98 | #define PARAM_SW_MAJOR 0x91 99 | #define PARAM_SW_MINOR 0x92 100 | #define PARAM_VTARGET 0x94 101 | #define PARAM_VADJUST 0x95 102 | #define PARAM_OSC_PSCALE 0x96 103 | #define PARAM_OSC_CMATCH 0x97 104 | #define PARAM_SCK_DURATION 0x98 105 | #define PARAM_TOPCARD_DETECT 0x9A 106 | #define PARAM_STATUS 0x9C 107 | #define PARAM_DATA 0x9D 108 | #define PARAM_RESET_POLARITY 0x9E 109 | #define PARAM_CONTROLLER_INIT 0x9F 110 | 111 | // *****************[ STK answer constants ]*************************** 112 | 113 | #define ANSWER_CKSUM_ERROR 0xB0 114 | 115 | -------------------------------------------------------------------------------- /html/services.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Services

4 |
5 | 6 |
7 |
8 |
9 |
10 |

11 | Syslog 12 |
13 |

14 | 55 |
56 |
57 |

58 | mDNS 59 |
60 |

61 | 78 |
79 |
80 |
81 |
82 |

83 | SNTP 84 |
85 |

86 | 104 |
105 |
106 |
107 |
108 |
109 | 110 | 111 | 112 | 120 | 121 | -------------------------------------------------------------------------------- /WIFI-CONFIG.md: -------------------------------------------------------------------------------- 1 | Esp-link Wifi configuration 2 | =========================== 3 | 4 | For proper operation the end state that esp-link needs to arrive at is to have it 5 | join your pre-existing wifi network as a pure station. 6 | However, in order to get there esp-link will start out as an access point and you'll have 7 | to join its network to configure it. The short version is: 8 | 9 | 1. esp-link creates a wifi access point with an SSID of the form `ESP_012ABC` (some modules 10 | use a different SSID form, such as `ai-thinker-012ABC`) 11 | 2. you join your laptop or phone to esp-link's network as a station and you configure 12 | esp-link wifi with your network info by pointing your browser at `http://192.168.4.1/` 13 | 3. you set a hostname for esp-link on the "home" page, or leave the default ("esp-link") 14 | 4. esp-link starts to connect to your network while continuing to also be an access point 15 | ("AP+STA"), the esp-link may show up with a `${hostname}.local` hostname 16 | (depends on your DHCP/DNS config) 17 | 4. esp-link succeeds in connecting and shuts down its own access point after 15 seconds, 18 | you reconnect your laptop/phone to your normal network and access esp-link via its hostname 19 | or IP address 20 | 21 | ### Notes on using AP (access point) mode 22 | 23 | Esp-link does not support STA+AP mode, however it does support STA mode and AP mode. What happens 24 | is that STA+AP mode is used at boot and when making STA changes to allow for recovery: the AP 25 | mode stays on for a while so you can connect to it and fix the STA mode. Once STA has connected, 26 | esp-link switches to STA-only mode. There is no setting to stay in STA+AP mode. So... if you want 27 | to use AP ensure you set esp-link to AP-only mode. If you want STA+AP mode you're gonna have to 28 | modify the source for yourself. (This stuff is painful to test and rather tricky, so don't expect 29 | the way it works to change.) 30 | 31 | Configuration details 32 | --------------------- 33 | 34 | ### Wifi 35 | 36 | After you have serially flashed the module it will create a wifi access point (AP) with an 37 | SSID of the form `ESP_012ABC` where 012ABC is a piece of the module's MAC address. 38 | Using a laptop, phone, or tablet connect to this SSID and then open a browser pointed at 39 | http://192.168.4.1/, you should then see the esp-link web site. 40 | 41 | Now configure the wifi. The desired configuration is for the esp-link to be a 42 | station on your local wifi network so you can communicate with it from all your computers. 43 | 44 | To make this happen, navigate to the wifi page and you should see the esp-link scan 45 | for available networks. You should then see a list of detected networks on the web page and you 46 | can select yours. 47 | Enter a password if your network is secure (highly recommended...) and hit the connect button. 48 | 49 | You should now see that the esp-link has connected to your network and it should show you 50 | its IP address. _Write it down_. You will then have to switch your laptop, phone, or tablet 51 | back to your network and then you can connect to the esp-link's IP address or, depending on your 52 | network's DHCP/DNS config you may be able to go to http://esp-link.local 53 | 54 | At this point the esp-link will have switched to STA mode and be just a station on your 55 | wifi network. These settings are stored in flash and thereby remembered through resets and 56 | power cycles. They are also remembered when you flash new firmware. Only flashing `blank.bin` 57 | via the serial port as indicated above will reset the wifi settings. 58 | 59 | There is a fail-safe, which is that after a reset or a configuration change, if the esp-link 60 | cannot connect to your network it will revert back to AP+STA mode after 15 seconds and thus 61 | both present its `ESP_012ABC`-style network and continue trying to reconnect to the requested network. 62 | You can then connect to the esp-link's AP and reconfigure the station part. 63 | 64 | One open issue (#28) is that esp-link cannot always display the IP address it is getting to the browser 65 | used to configure the ssid/password info. The problem is that the initial STA+AP mode may use 66 | channel 1 and you configure it to connect to an AP on channel 6. This requires the ESP8266's AP 67 | to also switch to channel 6 disconnecting you in the meantime. 68 | 69 | ### Hostname, description, DHCP, mDNS 70 | 71 | You can set a hostname on the "home" page, this should be just the hostname and not a domain 72 | name, i.e., something like "test-module-1" and not "test-module-1.mydomain.com". 73 | This has a number of effects: 74 | 75 | - you will see the first 12 chars of the hostname in the menu bar (top left of the page) so 76 | if you have multiple modules you can distinguish them visually 77 | - esp-link will use the hostname in its DHCP request, which allows you to identify the module's 78 | MAC and IP addresses in your DHCP server (typ. your wifi router). In addition, some DHCP 79 | servers will inject these names into the local DNS cache so you can use URLs like 80 | `hostname.local`. 81 | - someday, esp-link will inject the hostname into mDNS (multicast DNS, bonjour, etc...) so 82 | URLs of the form `hostname.local` work for everyone (as of v2.1.beta5 mDNS is disabled due 83 | to reliability issues with it) 84 | 85 | You can also enter a description of up to 128 characters on the home page (bottom right). This 86 | allows you to leave a memo for yourself, such as "installed in basement to control the heating 87 | system". This descritpion is not used anywhere else. 88 | -------------------------------------------------------------------------------- /html/console.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Microcontroller Console

4 |
5 | 6 |
7 |

8 | Reset µC 9 |   Clear Log 10 |   Baud: 11 | 26 |   Fmt: 27 | 37 |

38 |
39 |
Console
40 |
41 |
42 |
--- No Content ---
43 |
44 |
45 |
Console entry
46 |
47 | (ENTER to submit, ESC to clear) 48 |
49 |
50 | Add: 51 | 52 | 53 | 54 |
55 |
56 |
57 |
58 | 59 | 60 |
61 |
62 |
63 |
History buffer
64 |
(UP/DOWN arrows to select)
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | 74 | 75 | 76 | 77 | 136 | 137 | -------------------------------------------------------------------------------- /mqtt/mqtt_msg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Stephen Robinson 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the copyright holder nor the names of its 15 | * contributors may be used to endorse or promote products derived 16 | * from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | * 30 | */ 31 | 32 | #ifndef MQTT_MSG_H 33 | #define MQTT_MSG_H 34 | 35 | #define PROTOCOL_NAMEv311 36 | 37 | enum mqtt_message_type { 38 | MQTT_MSG_TYPE_CONNECT = 1, 39 | MQTT_MSG_TYPE_CONNACK = 2, 40 | MQTT_MSG_TYPE_PUBLISH = 3, 41 | MQTT_MSG_TYPE_PUBACK = 4, 42 | MQTT_MSG_TYPE_PUBREC = 5, 43 | MQTT_MSG_TYPE_PUBREL = 6, 44 | MQTT_MSG_TYPE_PUBCOMP = 7, 45 | MQTT_MSG_TYPE_SUBSCRIBE = 8, 46 | MQTT_MSG_TYPE_SUBACK = 9, 47 | MQTT_MSG_TYPE_UNSUBSCRIBE = 10, 48 | MQTT_MSG_TYPE_UNSUBACK = 11, 49 | MQTT_MSG_TYPE_PINGREQ = 12, 50 | MQTT_MSG_TYPE_PINGRESP = 13, 51 | MQTT_MSG_TYPE_DISCONNECT = 14 52 | }; 53 | 54 | // Descriptor for a serialized MQTT message, this is returned by functions that compose a message 55 | // (It's really an MQTT packet in v3.1.1 terminology) 56 | typedef struct mqtt_message { 57 | uint8_t* data; 58 | uint16_t length; 59 | } mqtt_message_t; 60 | 61 | // Descriptor for a connection with message assembly storage 62 | typedef struct mqtt_connection { 63 | mqtt_message_t message; // resulting message 64 | uint16_t message_id; // id of assembled message and memo to calculate next message id 65 | uint8_t* buffer; // buffer for assembling messages 66 | uint16_t buffer_length; // buffer length 67 | } mqtt_connection_t; 68 | 69 | // Descriptor for a connect request 70 | typedef struct mqtt_connect_info { 71 | char* client_id; 72 | char* username; 73 | char* password; 74 | char* will_topic; 75 | char* will_message; 76 | uint8_t keepalive; 77 | uint8_t will_qos; 78 | uint8_t will_retain; 79 | uint8_t clean_session; 80 | } mqtt_connect_info_t; 81 | 82 | static inline int ICACHE_FLASH_ATTR mqtt_get_type(const uint8_t* buffer) { 83 | return (buffer[0] & 0xf0) >> 4; 84 | } 85 | 86 | static inline int ICACHE_FLASH_ATTR mqtt_get_dup(const uint8_t* buffer) { 87 | return (buffer[0] & 0x08) >> 3; 88 | } 89 | 90 | static inline int ICACHE_FLASH_ATTR mqtt_get_qos(const uint8_t* buffer) { 91 | return (buffer[0] & 0x06) >> 1; 92 | } 93 | 94 | static inline int ICACHE_FLASH_ATTR mqtt_get_retain(const uint8_t* buffer) { 95 | return (buffer[0] & 0x01); 96 | } 97 | 98 | // Init a connection descriptor 99 | void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); 100 | 101 | // Returns the total length of a message including MQTT fixed header 102 | int mqtt_get_total_length(const uint8_t* buffer, uint16_t length); 103 | 104 | // Return pointer to topic, length in in/out param: in=length of buffer, out=length of topic 105 | const char* mqtt_get_publish_topic(const uint8_t* buffer, uint16_t* length); 106 | 107 | // Return pointer to data, length in in/out param: in=length of buffer, out=length of data 108 | const char* mqtt_get_publish_data(const uint8_t* buffer, uint16_t* length); 109 | 110 | // Return message id 111 | uint16_t mqtt_get_id(const uint8_t* buffer, uint16_t length); 112 | 113 | // The following functions construct an outgoing message 114 | mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); 115 | mqtt_message_t* mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id); 116 | mqtt_message_t* mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); 117 | mqtt_message_t* mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id); 118 | mqtt_message_t* mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id); 119 | mqtt_message_t* mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id); 120 | mqtt_message_t* mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id); 121 | mqtt_message_t* mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id); 122 | mqtt_message_t* mqtt_msg_pingreq(mqtt_connection_t* connection); 123 | mqtt_message_t* mqtt_msg_pingresp(mqtt_connection_t* connection); 124 | mqtt_message_t* mqtt_msg_disconnect(mqtt_connection_t* connection); 125 | 126 | #endif // MQTT_MSG_H 127 | 128 | -------------------------------------------------------------------------------- /esp-link/pgmshared.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 by Thorsten von Eicken, see LICENSE.txt in the esp-link repo 2 | // Copyright (c) 2016-2017 by Danny Backx 3 | 4 | #include 5 | #include 6 | #include "cgi.h" 7 | #include "config.h" 8 | #include "uart.h" 9 | #include "stk500v2.h" 10 | #include "serbridge.h" 11 | #include "serled.h" 12 | 13 | #include "pgmshared.h" 14 | 15 | struct optibootData *optibootData; 16 | 17 | char responseBuf[RESP_SZ]; // buffer to accumulate responses from optiboot 18 | short responseLen = 0; // amount accumulated so far 19 | char errMessage[ERR_MAX]; // error message 20 | 21 | // verify that N chars are hex characters 22 | bool ICACHE_FLASH_ATTR checkHex(char *buf, short len) { 23 | while (len--) { 24 | char c = *buf++; 25 | if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) 26 | continue; 27 | DBG("OB non-hex\n"); 28 | os_sprintf(errMessage, "Non hex char in POST record: '%c'/0x%02x", c, c); 29 | return false; 30 | } 31 | return true; 32 | } 33 | 34 | // get hex value of some hex characters 35 | uint32_t ICACHE_FLASH_ATTR getHexValue(char *buf, short len) { 36 | uint32_t v = 0; 37 | while (len--) { 38 | v = (v<<4) | (uint32_t)(*buf & 0xf); 39 | if (*buf > '9') v += 9; 40 | buf++; 41 | } 42 | return v; 43 | } 44 | 45 | // verify checksum 46 | static bool ICACHE_FLASH_ATTR verifyChecksum(char *buf, short len) { 47 | uint8_t sum = 0; 48 | while (len >= 2) { 49 | sum += (uint8_t)getHexValue(buf, 2); 50 | buf += 2; 51 | len -= 2; 52 | } 53 | return sum == 0; 54 | } 55 | 56 | // We've not built one function that works on both, but kept the different functions. 57 | // This calls one or the other 58 | static bool ICACHE_FLASH_ATTR programPage() { 59 | if (optibootData->mega) 60 | return megaProgramPage(); 61 | return optibootProgramPage(); 62 | } 63 | 64 | // Process a hex record -- assumes that the records starts with ':' & hex length 65 | bool ICACHE_FLASH_ATTR processRecord(char *buf, short len) { 66 | buf++; len--; // skip leading ':' 67 | // check we have all hex chars 68 | if (!checkHex(buf, len)) return false; 69 | // verify checksum 70 | if (!verifyChecksum(buf, len)) { 71 | buf[len] = 0; 72 | os_sprintf(errMessage, "Invalid checksum for record %s", buf); 73 | return false; 74 | } 75 | // dispatch based on record type 76 | uint8_t type = getHexValue(buf+6, 2); 77 | switch (type) { 78 | case 0x00: { // Intel HEX data record 79 | //DBG("OB REC data %ld pglen=%d\n", getHexValue(buf, 2), optibootData->pageLen); 80 | uint32_t addr = getHexValue(buf+2, 4); 81 | // check whether we need to program previous record(s) 82 | if (optibootData->pageLen > 0 && 83 | addr != ((optibootData->address+optibootData->pageLen)&0xffff)) { 84 | //DBG("OB addr chg\n"); 85 | //DBG("processRecord addr chg, len %d, addr 0x%04x\n", optibootData->pageLen, addr); 86 | if (!programPage()) return false; 87 | } 88 | // set address, unless we're adding to the end (programPage may have changed pageLen) 89 | if (optibootData->pageLen == 0) { 90 | optibootData->address = (optibootData->address & 0xffff0000) | addr; 91 | //DBG("OB set-addr 0x%lx\n", optibootData->address); 92 | } 93 | // append record 94 | uint16_t recLen = getHexValue(buf, 2); 95 | for (uint16_t i=0; ipageBuf[optibootData->pageLen++] = getHexValue(buf+8+2*i, 2); 97 | // program page, if we have a full page 98 | if (optibootData->pageLen >= optibootData->pgmSz) { 99 | //DBG("OB full\n"); 100 | DBG("processRecord %d, call programPage() %08x\n", optibootData->pgmSz, optibootData->address + optibootData->segment); 101 | if (!programPage()) return false; 102 | } 103 | break; } 104 | case 0x01: // Intel HEX EOF record 105 | DBG("OB EOF\n"); 106 | // program any remaining partial page 107 | #if 1 108 | if (optibootData->pageLen > 0) { 109 | // DBG("processRecord remaining partial page, len %d, addr 0x%04x\n", optibootData->pageLen, optibootData->address + optibootData->segment); 110 | if (!programPage()) return false; 111 | } 112 | optibootData->eof = true; 113 | #else 114 | if (optibootData->pageLen > 0) { 115 | // HACK : fill up with 0xFF 116 | while (optibootData->pageLen < 256) { 117 | optibootData->pageBuf[optibootData->pageLen++] = 0xFF; 118 | } 119 | if (!programPage()) return false; 120 | } 121 | optibootData->eof = true; 122 | #endif 123 | break; 124 | case 0x04: // Intel HEX address record 125 | DBG("OB address 0x%x\n", getHexValue(buf+8, 4) << 16); 126 | // program any remaining partial page 127 | if (optibootData->pageLen > 0) { 128 | DBG("processRecord 0x04 remaining partial page, len %d, addr 0x%04x\n", 129 | optibootData->pageLen, optibootData->address + optibootData->segment); 130 | if (!programPage()) return false; 131 | } 132 | optibootData->address = getHexValue(buf+8, 4) << 16; 133 | break; 134 | case 0x05: // Intel HEX start address (MDK-ARM only) 135 | // ignore, there's no way to tell optiboot that... 136 | break; 137 | case 0x02: // Intel HEX extended segment address record 138 | // Depending on the case, just ignoring this record could solve the problem 139 | // optibootData->segment = getHexValue(buf+8, 4) << 4; 140 | DBG("OB segment 0x%08X\n", optibootData->segment); 141 | return true; 142 | default: 143 | // DBG("OB bad record type\n"); 144 | DBG(errMessage, "Invalid/unknown record type: 0x%02x, packet %s", type, buf); 145 | return false; 146 | } 147 | return true; 148 | } 149 | 150 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) Thorsten von Eicken, 2015 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 24 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | --------------------------------------------------------------------- 28 | 29 | Portions of this software are derived from esphttpd and have the following 30 | license: 31 | 32 | "THE BEER-WARE LICENSE" (Revision 42): 33 | Jeroen Domburg wrote this file. As long as you retain 34 | this notice you can do whatever you want with this stuff. If we meet some day, 35 | and you think this stuff is worth it, you can buy me a beer in return. 36 | 37 | --------------------------------------------------------------------- 38 | 39 | Portions of this software are derived from Espressif's SDK and carry the 40 | following copyright notice: 41 | 42 | This file is part of Espressif's AT+ command set program. 43 | Copyright (C) 2013 - 2016, Espressif Systems 44 | 45 | This program is free software: you can redistribute it and/or modify 46 | it under the terms of version 3 of the GNU General Public License as 47 | published by the Free Software Foundation. 48 | 49 | This program is distributed in the hope that it will be useful, 50 | but WITHOUT ANY WARRANTY; without even the implied warranty of 51 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 52 | GNU General Public License for more details. 53 | 54 | You should have received a copy of the GNU General Public License along 55 | with this program. If not, see . 56 | 57 | --------------------------------------------------------------------- 58 | 59 | The Pure CSS portions of this software carry the following license: 60 | 61 | Copyright 2014 Yahoo! Inc. All rights reserved. 62 | 63 | Redistribution and use in source and binary forms, with or without 64 | modification, are permitted provided that the following conditions 65 | are met: 66 | 67 | * Redistributions of source code must retain the above copyright 68 | notice, this list of conditions and the following disclaimer. 69 | 70 | * Redistributions in binary form must reproduce the above copyright 71 | notice, this list of conditions and the following disclaimer in the 72 | documentation and/or other materials provided with the distribution. 73 | 74 | * Neither the name of the Yahoo! Inc. nor the 75 | names of its contributors may be used to endorse or promote products 76 | derived from this software without specific prior written permission. 77 | 78 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 79 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 80 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 81 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL YAHOO! INC. BE LIABLE FOR 82 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 83 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 84 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 85 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 86 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 87 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 88 | POSSIBILITY OF SUCH DAMAGE. 89 | 90 | --------------------------------------------------------------------- 91 | 92 | Normalize.css used in this firmware carries the following license: 93 | 94 | Copyright (c) Nicolas Gallagher and Jonathan Neal 95 | 96 | Permission is hereby granted, free of charge, to any person obtaining a copy of 97 | this software and associated documentation files (the "Software"), to deal in 98 | the Software without restriction, including without limitation the rights to 99 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 100 | the Software, and to permit persons to whom the Software is furnished to do so, 101 | subject to the following conditions: 102 | 103 | The above copyright notice and this permission notice shall be included in all 104 | copies or substantial portions of the Software. 105 | 106 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 107 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 108 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 109 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 110 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 111 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 112 | 113 | -------------------------------------------------------------------------------- /cmd/cmd.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt 2 | // 3 | // Adapted from: github.com/tuanpmt/esp_bridge, Created on: Jan 9, 2015, Author: Minh 4 | 5 | #include "esp8266.h" 6 | #include "cmd.h" 7 | #include "crc16.h" 8 | #include "uart.h" 9 | 10 | #ifdef CMD_DBG 11 | #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) 12 | #else 13 | #define DBG(format, ...) do { } while(0) 14 | #endif 15 | 16 | //===== ESP -> Serial responses 17 | 18 | static void ICACHE_FLASH_ATTR 19 | cmdProtoWrite(uint8_t data) { 20 | switch(data){ 21 | case SLIP_END: 22 | uart0_write_char(SLIP_ESC); 23 | uart0_write_char(SLIP_ESC_END); 24 | break; 25 | case SLIP_ESC: 26 | uart0_write_char(SLIP_ESC); 27 | uart0_write_char(SLIP_ESC_ESC); 28 | break; 29 | default: 30 | uart0_write_char(data); 31 | } 32 | } 33 | 34 | static void ICACHE_FLASH_ATTR 35 | cmdProtoWriteBuf(const uint8_t *data, short len) { 36 | while (len--) cmdProtoWrite(*data++); 37 | } 38 | 39 | static uint16_t resp_crc; 40 | 41 | // Start a response, returns the partial CRC 42 | void ICACHE_FLASH_ATTR 43 | cmdResponseStart(uint16_t cmd, uint32_t value, uint16_t argc) { 44 | DBG("cmdResponse: cmd=%d val=%d argc=%d\n", cmd, value, argc); 45 | 46 | uart0_write_char(SLIP_END); 47 | cmdProtoWriteBuf((uint8_t*)&cmd, 2); 48 | resp_crc = crc16_data((uint8_t*)&cmd, 2, 0); 49 | cmdProtoWriteBuf((uint8_t*)&argc, 2); 50 | resp_crc = crc16_data((uint8_t*)&argc, 2, resp_crc); 51 | cmdProtoWriteBuf((uint8_t*)&value, 4); 52 | resp_crc = crc16_data((uint8_t*)&value, 4, resp_crc); 53 | } 54 | 55 | // Adds data to a response, returns the partial CRC 56 | void ICACHE_FLASH_ATTR 57 | cmdResponseBody(const void *data, uint16_t len) { 58 | cmdProtoWriteBuf((uint8_t*)&len, 2); 59 | resp_crc = crc16_data((uint8_t*)&len, 2, resp_crc); 60 | 61 | cmdProtoWriteBuf(data, len); 62 | resp_crc = crc16_data(data, len, resp_crc); 63 | 64 | uint16_t pad = (4-((len+2)&3))&3; // get to multiple of 4 65 | if (pad > 0) { 66 | uint32_t temp = 0; 67 | cmdProtoWriteBuf((uint8_t*)&temp, pad); 68 | resp_crc = crc16_data((uint8_t*)&temp, pad, resp_crc); 69 | } 70 | } 71 | 72 | // Ends a response 73 | void ICACHE_FLASH_ATTR 74 | cmdResponseEnd() { 75 | cmdProtoWriteBuf((uint8_t*)&resp_crc, 2); 76 | uart0_write_char(SLIP_END); 77 | } 78 | 79 | //===== serial -> ESP commands 80 | 81 | // Execute a parsed command 82 | static void ICACHE_FLASH_ATTR 83 | cmdExec(const CmdList *scp, CmdPacket *packet) { 84 | // Iterate through the command table and call the appropriate function 85 | while (scp->sc_function != NULL) { 86 | if(scp->sc_name == packet->cmd) { 87 | DBG("cmdExec: Dispatching cmd=%s\n", scp->sc_text); 88 | // call command function 89 | scp->sc_function(packet); 90 | return; 91 | } 92 | scp++; 93 | } 94 | DBG("cmdExec: cmd=%d not found\n", packet->cmd); 95 | } 96 | 97 | // Parse a packet and print info about it 98 | void ICACHE_FLASH_ATTR 99 | cmdParsePacket(uint8_t *buf, short len) { 100 | // minimum command length 101 | if (len < sizeof(CmdPacket)) return; 102 | 103 | // init pointers into buffer 104 | CmdPacket *packet = (CmdPacket*)buf; 105 | uint8_t *data_ptr = (uint8_t*)&packet->args; 106 | uint8_t *data_limit = data_ptr+len; 107 | 108 | DBG("cmdParsePacket: cmd=%d argc=%d value=%u\n", 109 | packet->cmd, 110 | packet->argc, 111 | packet->value 112 | ); 113 | 114 | #if 0 115 | // print out arguments 116 | uint16_t argn = 0; 117 | uint16_t argc = packet->argc; 118 | while (data_ptr+2 < data_limit && argc--) { 119 | short l = *(uint16_t*)data_ptr; 120 | os_printf("cmdParsePacket: arg[%d] len=%d:", argn++, l); 121 | data_ptr += 2; 122 | while (data_ptr < data_limit && l--) { 123 | os_printf(" %02X", *data_ptr++); 124 | } 125 | os_printf("\n"); 126 | } 127 | #endif 128 | 129 | if (!cmdInSync && packet->cmd != CMD_SYNC) { 130 | // we have not received a sync, perhaps we reset? Tell MCU to do a sync 131 | cmdResponseStart(CMD_SYNC, 0, 0); 132 | cmdResponseEnd(); 133 | } else if (data_ptr <= data_limit) { 134 | cmdExec(commands, packet); 135 | } else { 136 | DBG("cmdParsePacket: packet length overrun, parsing arg %d\n", packet->argc); 137 | } 138 | } 139 | 140 | //===== Helpers to parse a command packet 141 | 142 | // Fill out a CmdRequest struct given a CmdPacket 143 | void ICACHE_FLASH_ATTR 144 | cmdRequest(CmdRequest *req, CmdPacket* cmd) { 145 | req->cmd = cmd; 146 | req->arg_num = 0; 147 | req->arg_ptr = (uint8_t*)&cmd->args; 148 | } 149 | 150 | // Return the number of arguments given a command struct 151 | uint32_t ICACHE_FLASH_ATTR 152 | cmdGetArgc(CmdRequest *req) { 153 | return req->cmd->argc; 154 | } 155 | 156 | // Copy the next argument from a command structure into the data pointer, returns 0 on success 157 | // -1 on error 158 | int32_t ICACHE_FLASH_ATTR 159 | cmdPopArg(CmdRequest *req, void *data, uint16_t len) { 160 | uint16_t length; 161 | 162 | if (req->arg_num >= req->cmd->argc) 163 | return -1; 164 | 165 | length = *(uint16_t*)req->arg_ptr; 166 | if (length != len) return -1; // safety check 167 | 168 | req->arg_ptr += 2; 169 | os_memcpy(data, req->arg_ptr, length); 170 | req->arg_ptr += (length+3)&~3; // round up to multiple of 4 171 | 172 | req->arg_num ++; 173 | return 0; 174 | } 175 | 176 | // Skip the next argument 177 | void ICACHE_FLASH_ATTR 178 | cmdSkipArg(CmdRequest *req) { 179 | uint16_t length; 180 | 181 | if (req->arg_num >= req->cmd->argc) return; 182 | 183 | length = *(uint16_t*)req->arg_ptr; 184 | 185 | req->arg_ptr += 2; 186 | req->arg_ptr += (length+3)&~3; 187 | req->arg_num ++; 188 | } 189 | 190 | // Return the length of the next argument 191 | uint16_t ICACHE_FLASH_ATTR 192 | cmdArgLen(CmdRequest *req) { 193 | return *(uint16_t*)req->arg_ptr; 194 | } 195 | -------------------------------------------------------------------------------- /esp-link/log.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt 2 | 3 | #include 4 | #include "uart.h" 5 | #include "cgi.h" 6 | #include "config.h" 7 | #include "log.h" 8 | 9 | #ifdef LOG_DBG 10 | #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) 11 | #else 12 | #define DBG(format, ...) do { } while(0) 13 | #endif 14 | 15 | // Web log for the esp8266 to replace outputting to uart1. 16 | // The web log has a 1KB circular in-memory buffer which os_printf prints into and 17 | // the HTTP handler simply displays the buffer content on a web page. 18 | 19 | // see console.c for invariants (same here) 20 | #define BUF_MAX (1400) 21 | static char log_buf[BUF_MAX]; 22 | static int log_wr, log_rd; 23 | static int log_pos; 24 | static bool log_no_uart; // start out printing to uart 25 | static bool log_newline; // at start of a new line 26 | 27 | // write to the uart designated for logging 28 | static void uart_write_char(char c) { 29 | if (flashConfig.log_mode == LOG_MODE_ON1) 30 | uart1_write_char(c); 31 | else 32 | uart0_write_char(c); 33 | } 34 | 35 | // called from wifi reset timer to turn UART on when we loose wifi and back off 36 | // when we connect to wifi AP. Here this is gated by the flash setting 37 | void ICACHE_FLASH_ATTR 38 | log_uart(bool enable) { 39 | if (!enable && !log_no_uart && flashConfig.log_mode < LOG_MODE_ON0) { 40 | // we're asked to turn uart off, and uart is on, and the flash setting isn't always-on 41 | DBG("Turning OFF uart log\n"); 42 | os_delay_us(4*1000L); // time for uart to flush 43 | log_no_uart = !enable; 44 | } else if (enable && log_no_uart && flashConfig.log_mode != LOG_MODE_OFF) { 45 | // we're asked to turn uart on, and uart is off, and the flash setting isn't always-off 46 | log_no_uart = !enable; 47 | DBG("Turning ON uart log\n"); 48 | } 49 | } 50 | 51 | // write a character into the log buffer 52 | static void ICACHE_FLASH_ATTR 53 | log_write(char c) { 54 | log_buf[log_wr] = c; 55 | log_wr = (log_wr+1) % BUF_MAX; 56 | if (log_wr == log_rd) { 57 | log_rd = (log_rd+1) % BUF_MAX; // full, eat first char 58 | log_pos++; 59 | } 60 | } 61 | 62 | // write a character to the log buffer and the uart, and handle newlines specially 63 | static void ICACHE_FLASH_ATTR 64 | log_write_char(char c) { 65 | // log timestamp 66 | if (log_newline) { 67 | char buff[16]; 68 | int l = os_sprintf(buff, "%6d> ", (system_get_time()/1000)%1000000); 69 | if (!log_no_uart) 70 | for (int i=0; iconn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. 94 | jsonHeader(connData, 200); 95 | 96 | // figure out where to start in buffer based on URI param 97 | len = httpdFindArg(connData->getArgs, "start", buff, sizeof(buff)); 98 | if (len > 0) { 99 | start = atoi(buff); 100 | if (start < log_pos) { 101 | start = 0; 102 | } else if (start >= log_pos+log_len) { 103 | start = log_len; 104 | } else { 105 | start = start - log_pos; 106 | } 107 | } 108 | 109 | // start outputting 110 | len = os_sprintf(buff, "{\"len\":%d, \"start\":%d, \"text\": \"", 111 | log_len-start, log_pos+start); 112 | 113 | int rd = (log_rd+start) % BUF_MAX; 114 | while (len < 2040 && rd != log_wr) { 115 | uint8_t c = log_buf[rd]; 116 | if (c == '\\' || c == '"') { 117 | buff[len++] = '\\'; 118 | buff[len++] = c; 119 | } else if (c < ' ') { 120 | len += os_sprintf(buff+len, "\\u%04x", c); 121 | } else { 122 | buff[len++] = c; 123 | } 124 | rd = (rd + 1) % BUF_MAX; 125 | } 126 | os_strcpy(buff+len, "\"}"); len+=2; 127 | httpdSend(connData, buff, len); 128 | return HTTPD_CGI_DONE; 129 | } 130 | 131 | static char *dbg_mode[] = { "auto", "off", "on0", "on1" }; 132 | 133 | int ICACHE_FLASH_ATTR 134 | ajaxLogDbg(HttpdConnData *connData) { 135 | if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. 136 | char buff[512]; 137 | int len, status = 400; 138 | len = httpdFindArg(connData->getArgs, "mode", buff, sizeof(buff)); 139 | if (len > 0) { 140 | int8_t mode = -1; 141 | if (os_strcmp(buff, "auto") == 0) mode = LOG_MODE_AUTO; 142 | if (os_strcmp(buff, "off") == 0) mode = LOG_MODE_OFF; 143 | if (os_strcmp(buff, "on0") == 0) mode = LOG_MODE_ON0; 144 | if (os_strcmp(buff, "on1") == 0) mode = LOG_MODE_ON1; 145 | if (mode >= 0) { 146 | flashConfig.log_mode = mode; 147 | if (mode != LOG_MODE_AUTO) log_uart(mode >= LOG_MODE_ON0); 148 | status = configSave() ? 200 : 400; 149 | } 150 | } else if (connData->requestType == HTTPD_METHOD_GET) { 151 | status = 200; 152 | } 153 | 154 | jsonHeader(connData, status); 155 | os_sprintf(buff, "{\"mode\": \"%s\"}", dbg_mode[flashConfig.log_mode]); 156 | httpdSend(connData, buff, -1); 157 | return HTTPD_CGI_DONE; 158 | } 159 | 160 | void ICACHE_FLASH_ATTR dumpMem(void *addr, int len) { 161 | uint8_t *a = addr; 162 | int off = 0; 163 | while (off < len) { 164 | os_printf("%p ", a); 165 | for (int i = 0; i < 16 && off + i < len; i++) { 166 | os_printf(" %02x", a[i]); 167 | } 168 | os_printf(" "); 169 | for (int i=0; i<16 && off 0x20 && *a < 0x3f ? *a : '.'); 171 | os_printf("\n"); 172 | } 173 | } 174 | 175 | void ICACHE_FLASH_ATTR logInit() { 176 | log_no_uart = flashConfig.log_mode == LOG_MODE_OFF; // ON unless set to always-off 177 | log_wr = 0; 178 | log_rd = 0; 179 | os_install_putc1((void *)log_write_char); 180 | } 181 | 182 | 183 | --------------------------------------------------------------------------------