├── .gitignore ├── BOARDS.md ├── FLASH.md ├── LICENSE.txt ├── Makefile ├── README.adoc ├── README.md ├── WINDOWS.md ├── avrflash ├── cmd ├── cmd.c ├── cmd.h └── handlers.c ├── esp-link.sln ├── esp-link.vcxproj ├── esp-link ├── cgi.c ├── cgi.h ├── cgiflash.c ├── cgiflash.h ├── cgimqtt.c ├── cgimqtt.h ├── cgipins.c ├── cgipins.h ├── cgiservices.c ├── cgiservices.h ├── cgitcp.c ├── cgitcp.h ├── cgiwifi.c ├── cgiwifi.h ├── config.c ├── config.h ├── log.c ├── log.h ├── main.c ├── mqtt_client.c ├── mqtt_client.h ├── status.c ├── status.h ├── stk500.h ├── task.c └── task.h ├── espfs ├── espfs.c ├── espfs.h ├── espfsformat.h └── mkespfsimage │ ├── Makefile │ ├── main.c │ └── mman-win32 │ ├── Makefile │ ├── config.mak │ ├── configure │ ├── mman.c │ ├── mman.h │ └── test.c ├── espmake.cmd ├── flash_esp ├── html ├── console.html ├── console.js ├── favicon.ico ├── head- ├── home.html ├── jl-400x110.png- ├── log.html ├── mqtt.html ├── mqtt.js ├── pure.css ├── services.html ├── services.js ├── style.css ├── ui.js └── wifi │ ├── icons.png │ ├── wifiAp.html │ ├── wifiAp.js │ ├── wifiSta.html │ └── wifiSta.js ├── httpd ├── auth.c ├── auth.h ├── base64.c ├── base64.h ├── httpd.c ├── httpd.h ├── httpdespfs.c └── httpdespfs.h ├── include ├── esp8266.h ├── espmissingincludes.h ├── uart_hw.h └── user_config.h ├── mqtt ├── mqtt.c ├── mqtt.h ├── mqtt_cmd.c ├── mqtt_cmd.h ├── mqtt_msg.c ├── mqtt_msg.h ├── pktbuf.c └── pktbuf.h ├── rest ├── rest.c └── rest.h ├── serial ├── console.c ├── console.h ├── crc16.c ├── crc16.h ├── serbridge.c ├── serbridge.h ├── serled.c ├── serled.h ├── slip.c ├── slip.h ├── uart.c └── uart.h ├── syslog ├── syslog.c ├── syslog.h └── syslog.md ├── tlv ├── tlv.c └── tlv.h ├── user └── user_main.c ├── vnc ├── vncbridge.c └── vncbridge.h └── wiflash /.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 | tools/ 19 | local.conf 20 | *.tgz 21 | *.swp 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ESP8266 firmware for the Universal Serial aBuse project 2 | ======================================================= 3 | 4 | This repository contains the firmware for the ESP8266 chip on the USB board. 5 | 6 | Program it using the flash_esp wrapper, or generally using esptool. Note, this appears to require the modified esptool maintained by AprilBrother, available at https://github.com/AprilBrother/esptool 7 | 8 | It is forked from the excellent esp-link project (https://github.com/jeelabs/esp-link), and build instructions are identical to those for the original project. 9 | 10 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | extern const CmdList commands[]; 17 | 18 | //===== ESP -> Serial responses 19 | 20 | static void ICACHE_FLASH_ATTR 21 | cmdProtoWrite(uint8_t data) { 22 | switch(data){ 23 | case SLIP_END: 24 | uart0_write_char(SLIP_ESC); 25 | uart0_write_char(SLIP_ESC_END); 26 | break; 27 | case SLIP_ESC: 28 | uart0_write_char(SLIP_ESC); 29 | uart0_write_char(SLIP_ESC_ESC); 30 | break; 31 | default: 32 | uart0_write_char(data); 33 | } 34 | } 35 | 36 | static void ICACHE_FLASH_ATTR 37 | cmdProtoWriteBuf(const uint8_t *data, short len) { 38 | while (len--) cmdProtoWrite(*data++); 39 | } 40 | 41 | static uint16_t resp_crc; 42 | 43 | // Start a response, returns the partial CRC 44 | void ICACHE_FLASH_ATTR 45 | cmdResponseStart(uint16_t cmd, uint32_t value, uint16_t argc) { 46 | DBG("cmdResponse: cmd=%d val=%ld argc=%d\n", cmd, value, argc); 47 | 48 | uart0_write_char(SLIP_END); 49 | cmdProtoWriteBuf((uint8_t*)&cmd, 2); 50 | resp_crc = crc16_data((uint8_t*)&cmd, 2, 0); 51 | cmdProtoWriteBuf((uint8_t*)&argc, 2); 52 | resp_crc = crc16_data((uint8_t*)&argc, 2, resp_crc); 53 | cmdProtoWriteBuf((uint8_t*)&value, 4); 54 | resp_crc = crc16_data((uint8_t*)&value, 4, resp_crc); 55 | } 56 | 57 | // Adds data to a response, returns the partial CRC 58 | void ICACHE_FLASH_ATTR 59 | cmdResponseBody(const void *data, uint16_t len) { 60 | cmdProtoWriteBuf((uint8_t*)&len, 2); 61 | resp_crc = crc16_data((uint8_t*)&len, 2, resp_crc); 62 | 63 | cmdProtoWriteBuf(data, len); 64 | resp_crc = crc16_data(data, len, resp_crc); 65 | 66 | uint16_t pad = (4-(len&3))&3; // get to multiple of 4 67 | if (pad > 0) { 68 | uint32_t temp = 0; 69 | cmdProtoWriteBuf((uint8_t*)&temp, pad); 70 | resp_crc = crc16_data((uint8_t*)&temp, pad, resp_crc); 71 | } 72 | } 73 | 74 | // Ends a response 75 | void ICACHE_FLASH_ATTR 76 | cmdResponseEnd() { 77 | cmdProtoWriteBuf((uint8_t*)&resp_crc, 2); 78 | uart0_write_char(SLIP_END); 79 | } 80 | 81 | //===== serial -> ESP commands 82 | 83 | // Execute a parsed command 84 | static void ICACHE_FLASH_ATTR 85 | cmdExec(const CmdList *scp, CmdPacket *packet) { 86 | // Iterate through the command table and call the appropriate function 87 | while (scp->sc_function != NULL) { 88 | if(scp->sc_name == packet->cmd) { 89 | DBG("cmdExec: Dispatching cmd=%s\n", scp->sc_text); 90 | // call command function 91 | scp->sc_function(packet); 92 | return; 93 | } 94 | scp++; 95 | } 96 | DBG("cmdExec: cmd=%d not found\n", packet->cmd); 97 | } 98 | 99 | // Parse a packet and print info about it 100 | void ICACHE_FLASH_ATTR 101 | cmdParsePacket(uint8_t *buf, short len) { 102 | // minimum command length 103 | if (len < sizeof(CmdPacket)) return; 104 | 105 | // init pointers into buffer 106 | CmdPacket *packet = (CmdPacket*)buf; 107 | uint8_t *data_ptr = (uint8_t*)&packet->args; 108 | uint8_t *data_limit = data_ptr+len; 109 | 110 | DBG("cmdParsePacket: cmd=%d argc=%d value=%lu\n", 111 | packet->cmd, 112 | packet->argc, 113 | packet->value 114 | ); 115 | 116 | #if 0 117 | // print out arguments 118 | uint16_t argn = 0; 119 | uint16_t argc = packet->argc; 120 | while (data_ptr+2 < data_limit && argc--) { 121 | short l = *(uint16_t*)data_ptr; 122 | os_printf("cmdParsePacket: arg[%d] len=%d:", argn++, l); 123 | data_ptr += 2; 124 | while (data_ptr < data_limit && l--) { 125 | os_printf(" %02X", *data_ptr++); 126 | } 127 | os_printf("\n"); 128 | } 129 | #endif 130 | 131 | if (data_ptr <= data_limit) { 132 | cmdExec(commands, packet); 133 | } else { 134 | DBG("cmdParsePacket: packet length overrun, parsing arg %d\n", packet->argc); 135 | } 136 | } 137 | 138 | //===== Helpers to parse a command packet 139 | 140 | // Fill out a CmdRequest struct given a CmdPacket 141 | void ICACHE_FLASH_ATTR 142 | cmdRequest(CmdRequest *req, CmdPacket* cmd) { 143 | req->cmd = cmd; 144 | req->arg_num = 0; 145 | req->arg_ptr = (uint8_t*)&cmd->args; 146 | } 147 | 148 | // Return the number of arguments given a command struct 149 | uint32_t ICACHE_FLASH_ATTR 150 | cmdGetArgc(CmdRequest *req) { 151 | return req->cmd->argc; 152 | } 153 | 154 | // Copy the next argument from a command structure into the data pointer, returns 0 on success 155 | // -1 on error 156 | int32_t ICACHE_FLASH_ATTR 157 | cmdPopArg(CmdRequest *req, void *data, uint16_t len) { 158 | uint16_t length; 159 | 160 | if (req->arg_num >= req->cmd->argc) 161 | return -1; 162 | 163 | length = *(uint16_t*)req->arg_ptr; 164 | if (length != len) return -1; // safety check 165 | 166 | req->arg_ptr += 2; 167 | os_memcpy(data, req->arg_ptr, length); 168 | req->arg_ptr += (length+3)&~3; // round up to multiple of 4 169 | 170 | req->arg_num ++; 171 | return 0; 172 | } 173 | 174 | // Skip the next argument 175 | void ICACHE_FLASH_ATTR 176 | cmdSkipArg(CmdRequest *req) { 177 | uint16_t length; 178 | 179 | if (req->arg_num >= req->cmd->argc) return; 180 | 181 | length = *(uint16_t*)req->arg_ptr; 182 | 183 | req->arg_ptr += 2; 184 | req->arg_ptr += (length+3)&~3; 185 | req->arg_num ++; 186 | } 187 | 188 | // Return the length of the next argument 189 | uint16_t ICACHE_FLASH_ATTR 190 | cmdArgLen(CmdRequest *req) { 191 | return *(uint16_t*)req->arg_ptr; 192 | } 193 | -------------------------------------------------------------------------------- /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 | // Standard SLIP escape chars from RFC 10 | #define SLIP_END 0300 // indicates end of packet 11 | #define SLIP_ESC 0333 // indicates byte stuffing 12 | #define SLIP_ESC_END 0334 // ESC ESC_END means END data byte 13 | #define SLIP_ESC_ESC 0335 // ESC ESC_ESC means ESC data byte 14 | 15 | typedef struct __attribute__((__packed__)) { 16 | uint16_t len; // length of data 17 | uint8_t data[0]; // really data[len] 18 | } CmdArg; 19 | 20 | typedef struct __attribute__((__packed__)) { 21 | uint16_t cmd; // command to perform, from CmdName enum 22 | uint16_t argc; // number of arguments to command 23 | uint32_t value; // callback pointer for response or first argument 24 | CmdArg args[0]; // really args[argc] 25 | } CmdPacket; 26 | 27 | typedef struct { 28 | CmdPacket *cmd; // command packet header 29 | uint32_t arg_num; // number of args parsed 30 | uint8_t *arg_ptr; // pointer to ?? 31 | } CmdRequest; 32 | 33 | typedef enum { 34 | CMD_NULL = 0, 35 | CMD_SYNC, // synchronize and clear 36 | CMD_RESP_V, // response with a value 37 | CMD_RESP_CB, // response with a callback 38 | CMD_WIFI_STATUS, // get the current wifi status 39 | CMD_CB_ADD, 40 | CMD_CB_EVENTS, 41 | CMD_GET_TIME, // get current time in seconds since the unix epoch 42 | 43 | CMD_MQTT_SETUP = 10, // set-up callbacks 44 | CMD_MQTT_PUBLISH, // publish a message 45 | CMD_MQTT_SUBSCRIBE, // subscribe to a topic 46 | CMD_MQTT_LWT, // set the last-will-topic and messge 47 | 48 | CMD_REST_SETUP = 20, 49 | CMD_REST_REQUEST, 50 | CMD_REST_SETHEADER, 51 | } CmdName; 52 | 53 | typedef void (*cmdfunc_t)(CmdPacket *cmd); 54 | 55 | typedef struct { 56 | CmdName sc_name; // name as CmdName enum 57 | char *sc_text; // name as string 58 | cmdfunc_t sc_function; // pointer to function 59 | } CmdList; 60 | 61 | #define CMD_CBNLEN 16 62 | typedef struct { 63 | char name[CMD_CBNLEN]; 64 | uint32_t callback; 65 | } CmdCallback; 66 | 67 | // Used by slip protocol to cause parsing of a received packet 68 | void cmdParsePacket(uint8_t *buf, short len); 69 | 70 | // Return the info about a callback to the attached uC by name, these are callbacks that the 71 | // attached uC registers using the ADD_SENSOR command 72 | CmdCallback* cmdGetCbByName(char* name); 73 | 74 | // Add a callback 75 | uint32_t cmdAddCb(char *name, uint32_t callback); 76 | 77 | // Responses 78 | 79 | // Start a response 80 | void cmdResponseStart(uint16_t cmd, uint32_t value, uint16_t argc); 81 | // Adds data to a response 82 | void cmdResponseBody(const void* data, uint16_t len); 83 | // Ends a response 84 | void cmdResponseEnd(); 85 | 86 | //void cmdResponse(uint16_t cmd, uint32_t callback, uint32_t value, uint16_t argc, CmdArg* args[]); 87 | 88 | // Requests 89 | 90 | // Fill out a CmdRequest struct given a CmdPacket 91 | void cmdRequest(CmdRequest *req, CmdPacket* cmd); 92 | // Return the number of arguments given a request 93 | uint32_t cmdGetArgc(CmdRequest *req); 94 | // Return the length of the next argument 95 | uint16_t cmdArgLen(CmdRequest *req); 96 | // Copy next arg from request into the data pointer, returns 0 on success, -1 on error 97 | int32_t cmdPopArg(CmdRequest *req, void *data, uint16_t len); 98 | // Skip next arg 99 | void cmdSkipArg(CmdRequest *req); 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /cmd/handlers.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 8 | #ifdef MQTT 9 | #include 10 | #endif 11 | #ifdef REST 12 | #include 13 | #endif 14 | 15 | #ifdef CMD_DBG 16 | #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) 17 | #else 18 | #define DBG(format, ...) do { } while(0) 19 | #endif 20 | 21 | static void cmdNull(CmdPacket *cmd); 22 | static void cmdSync(CmdPacket *cmd); 23 | static void cmdWifiStatus(CmdPacket *cmd); 24 | static void cmdAddCallback(CmdPacket *cmd); 25 | 26 | // keep track of last status sent to uC so we can notify it when it changes 27 | static uint8_t lastWifiStatus = wifiIsDisconnected; 28 | static bool wifiCbAdded = false; 29 | 30 | // Command dispatch table for serial -> ESP commands 31 | const CmdList commands[] = { 32 | {CMD_NULL, "NULL", cmdNull}, // no-op 33 | {CMD_SYNC, "SYNC", cmdSync}, // synchronize 34 | {CMD_WIFI_STATUS, "WIFI_STATUS", cmdWifiStatus}, 35 | {CMD_CB_ADD, "ADD_CB", cmdAddCallback}, 36 | #ifdef MQTT 37 | {CMD_MQTT_SETUP, "MQTT_SETUP", MQTTCMD_Setup}, 38 | {CMD_MQTT_PUBLISH, "MQTT_PUB", MQTTCMD_Publish}, 39 | {CMD_MQTT_SUBSCRIBE , "MQTT_SUB", MQTTCMD_Subscribe}, 40 | {CMD_MQTT_LWT, "MQTT_LWT", MQTTCMD_Lwt}, 41 | #endif 42 | #ifdef REST 43 | {CMD_REST_SETUP, "REST_SETUP", REST_Setup}, 44 | {CMD_REST_REQUEST, "REST_REQ", REST_Request}, 45 | {CMD_REST_SETHEADER, "REST_SETHDR", REST_SetHeader}, 46 | #endif 47 | }; 48 | 49 | //===== List of registered callbacks (to uC) 50 | 51 | // WifiCb plus 10 for sensors 52 | #define MAX_CALLBACKS 12 53 | CmdCallback callbacks[MAX_CALLBACKS]; // cleared in cmdSync 54 | 55 | uint32_t ICACHE_FLASH_ATTR 56 | cmdAddCb(char* name, uint32_t cb) { 57 | for (uint8_t i = 0; i < MAX_CALLBACKS; i++) { 58 | //os_printf("cmdAddCb: index %d name=%s cb=%p\n", i, callbacks[i].name, 59 | // (void *)callbacks[i].callback); 60 | // find existing callback or add to the end 61 | if (os_strncmp(callbacks[i].name, name, CMD_CBNLEN) == 0 || callbacks[i].name[0] == '\0') { 62 | os_strncpy(callbacks[i].name, name, sizeof(callbacks[i].name)); 63 | callbacks[i].name[CMD_CBNLEN-1] = 0; // strncpy doesn't null terminate 64 | callbacks[i].callback = cb; 65 | DBG("cmdAddCb: '%s'->0x%lx added at %d\n", callbacks[i].name, cb, i); 66 | return 1; 67 | } 68 | } 69 | return 0; 70 | } 71 | 72 | CmdCallback* ICACHE_FLASH_ATTR 73 | cmdGetCbByName(char* name) { 74 | for (uint8_t i = 0; i < MAX_CALLBACKS; i++) { 75 | //os_printf("cmdGetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name, 76 | // (void *)callbacks[i].callback); 77 | // if callback doesn't exist or it's null 78 | if (os_strncmp(callbacks[i].name, name, CMD_CBNLEN) == 0) { 79 | DBG("cmdGetCbByName: cb %s found at index %d\n", name, i); 80 | return &callbacks[i]; 81 | } 82 | } 83 | os_printf("cmdGetCbByName: cb %s not found\n", name); 84 | return 0; 85 | } 86 | 87 | //===== Wifi callback 88 | 89 | // Callback from wifi subsystem to notify us of status changes 90 | static void ICACHE_FLASH_ATTR 91 | cmdWifiCb(uint8_t wifiStatus) { 92 | if (wifiStatus != lastWifiStatus){ 93 | DBG("cmdWifiCb: wifiStatus=%d\n", wifiStatus); 94 | lastWifiStatus = wifiStatus; 95 | CmdCallback *wifiCb = cmdGetCbByName("wifiCb"); 96 | if ((uint32_t)wifiCb->callback != -1) { 97 | uint8_t status = wifiStatus == wifiGotIP ? 5 : 1; 98 | cmdResponseStart(CMD_RESP_CB, (uint32_t)wifiCb->callback, 1); 99 | cmdResponseBody((uint8_t*)&status, 1); 100 | cmdResponseEnd(); 101 | } 102 | } 103 | } 104 | 105 | //===== Command handlers 106 | 107 | // Command handler for Null command 108 | static void ICACHE_FLASH_ATTR 109 | cmdNull(CmdPacket *cmd) { 110 | } 111 | 112 | // Command handler for sync command 113 | static void ICACHE_FLASH_ATTR 114 | cmdSync(CmdPacket *cmd) { 115 | CmdRequest req; 116 | cmdRequest(&req, cmd); 117 | if(cmd->argc != 0 || cmd->value == 0) { 118 | cmdResponseStart(CMD_RESP_V, 0, 0); 119 | cmdResponseEnd(); 120 | return; 121 | } 122 | 123 | // clear callbacks table 124 | os_memset(callbacks, 0, sizeof(callbacks)); 125 | 126 | // register our callback with wifi subsystem 127 | if (!wifiCbAdded) { 128 | wifiAddStateChangeCb(cmdWifiCb); 129 | wifiCbAdded = true; 130 | } 131 | 132 | // send OK response 133 | cmdResponseStart(CMD_RESP_V, cmd->value, 0); 134 | cmdResponseEnd(); 135 | 136 | // save the MCU's callback and trigger an initial callback 137 | cmdAddCb("wifiCb", cmd->value); 138 | lastWifiStatus = 0xff; // set to invalid value so we immediately send status cb in all cases 139 | cmdWifiCb(wifiState); 140 | 141 | return; 142 | } 143 | 144 | // Command handler for wifi status command 145 | static void ICACHE_FLASH_ATTR 146 | cmdWifiStatus(CmdPacket *cmd) { 147 | cmdResponseStart(CMD_RESP_V, wifiState, 0); 148 | cmdResponseEnd(); 149 | return; 150 | } 151 | 152 | 153 | // Command handler to add a callback to the named-callbacks list, this is for a callback to the uC 154 | static void ICACHE_FLASH_ATTR 155 | cmdAddCallback(CmdPacket *cmd) { 156 | CmdRequest req; 157 | cmdRequest(&req, cmd); 158 | if (cmd->argc != 1 || cmd->value == 0) return; 159 | 160 | char name[16]; 161 | uint16_t len; 162 | 163 | // get the callback name 164 | len = cmdArgLen(&req); 165 | if (len > 15) return; // max size of name is 15 characters 166 | if (cmdPopArg(&req, (uint8_t *)name, len)) return; 167 | name[len] = 0; 168 | DBG("cmdAddCallback: name=%s\n", name); 169 | 170 | cmdAddCb(name, cmd->value); // save the sensor callback 171 | } 172 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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, bool *config); 30 | 31 | int cgiMenu(HttpdConnData *connData); 32 | 33 | uint8_t UTILS_StrToIP(const char *str, void *ip); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /esp-link/cgimqtt.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt 2 | 3 | static char *mqtt_states[] = { 4 | "disconnected", "reconnecting", "connecting", "connected", "disabled" 5 | }; 6 | 7 | #if !defined(MQTT) 8 | char *mqttState(void) { 9 | return mqtt_states[4]; 10 | } 11 | #else 12 | #include 13 | #include "cgi.h" 14 | #include "config.h" 15 | #include "status.h" 16 | #include "mqtt_client.h" 17 | #include "cgimqtt.h" 18 | 19 | #ifdef CGIMQTT_DBG 20 | #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) 21 | #else 22 | #define DBG(format, ...) do { } while(0) 23 | #endif 24 | 25 | char *mqttState(void) { 26 | return mqtt_states[mqttClient.connState]; 27 | } 28 | 29 | // Cgi to return MQTT settings 30 | int ICACHE_FLASH_ATTR cgiMqttGet(HttpdConnData *connData) { 31 | char buff[1024]; 32 | int len; 33 | 34 | if (connData->conn==NULL) return HTTPD_CGI_DONE; 35 | 36 | // get the current status topic for display 37 | char status_buf1[128], *sb1=status_buf1; 38 | char status_buf2[128], *sb2=status_buf2; 39 | mqttStatusMsg(status_buf1); 40 | // quote all " for the json, sigh... 41 | for (int i=0; i<127 && *sb1; i++) { 42 | if (*sb1 == '"') { 43 | *sb2++ = '\\'; 44 | i++; 45 | } 46 | *sb2++ = *sb1++; 47 | } 48 | *sb2 = 0; 49 | 50 | len = os_sprintf(buff, "{ " 51 | "\"slip-enable\":%d, " 52 | "\"mqtt-enable\":%d, " 53 | "\"mqtt-state\":\"%s\", " 54 | "\"mqtt-status-enable\":%d, " 55 | "\"mqtt-clean-session\":%d, " 56 | "\"mqtt-port\":%d, " 57 | "\"mqtt-timeout\":%d, " 58 | "\"mqtt-keepalive\":%d, " 59 | "\"mqtt-host\":\"%s\", " 60 | "\"mqtt-client-id\":\"%s\", " 61 | "\"mqtt-username\":\"%s\", " 62 | "\"mqtt-password\":\"%s\", " 63 | "\"mqtt-status-topic\":\"%s\", " 64 | "\"mqtt-status-value\":\"%s\" }", 65 | flashConfig.slip_enable, flashConfig.mqtt_enable, 66 | mqtt_states[mqttClient.connState], flashConfig.mqtt_status_enable, 67 | flashConfig.mqtt_clean_session, flashConfig.mqtt_port, 68 | flashConfig.mqtt_timeout, flashConfig.mqtt_keepalive, 69 | flashConfig.mqtt_host, flashConfig.mqtt_clientid, 70 | flashConfig.mqtt_username, flashConfig.mqtt_password, 71 | flashConfig.mqtt_status_topic, status_buf2); 72 | 73 | jsonHeader(connData, 200); 74 | httpdSend(connData, buff, len); 75 | return HTTPD_CGI_DONE; 76 | } 77 | 78 | int ICACHE_FLASH_ATTR cgiMqttSet(HttpdConnData *connData) { 79 | if (connData->conn==NULL) return HTTPD_CGI_DONE; 80 | 81 | // handle MQTT server settings 82 | int8_t mqtt_server = 0; // accumulator for changes/errors 83 | mqtt_server |= getStringArg(connData, "mqtt-host", 84 | flashConfig.mqtt_host, sizeof(flashConfig.mqtt_host)); 85 | if (mqtt_server < 0) return HTTPD_CGI_DONE; 86 | mqtt_server |= getStringArg(connData, "mqtt-client-id", 87 | flashConfig.mqtt_clientid, sizeof(flashConfig.mqtt_clientid)); 88 | 89 | if (mqtt_server < 0) return HTTPD_CGI_DONE; 90 | mqtt_server |= getStringArg(connData, "mqtt-username", 91 | flashConfig.mqtt_username, sizeof(flashConfig.mqtt_username)); 92 | if (mqtt_server < 0) return HTTPD_CGI_DONE; 93 | mqtt_server |= getStringArg(connData, "mqtt-password", 94 | flashConfig.mqtt_password, sizeof(flashConfig.mqtt_password)); 95 | 96 | if (mqtt_server < 0) return HTTPD_CGI_DONE; 97 | mqtt_server |= getBoolArg(connData, "mqtt-clean-session", 98 | &flashConfig.mqtt_clean_session); 99 | 100 | if (mqtt_server < 0) return HTTPD_CGI_DONE; 101 | int8_t mqtt_en_chg = getBoolArg(connData, "mqtt-enable", 102 | &flashConfig.mqtt_enable); 103 | 104 | char buff[16]; 105 | 106 | // handle mqtt port 107 | if (httpdFindArg(connData->getArgs, "mqtt-port", buff, sizeof(buff)) > 0) { 108 | int32_t port = atoi(buff); 109 | if (port > 0 && port < 65536) { 110 | flashConfig.mqtt_port = port; 111 | mqtt_server |= 1; 112 | } else { 113 | errorResponse(connData, 400, "Invalid MQTT port"); 114 | return HTTPD_CGI_DONE; 115 | } 116 | } 117 | 118 | // handle mqtt timeout 119 | if (httpdFindArg(connData->getArgs, "mqtt-timeout", buff, sizeof(buff)) > 0) { 120 | int32_t timeout = atoi(buff); 121 | flashConfig.mqtt_timeout = timeout; 122 | } 123 | 124 | // handle mqtt keepalive 125 | if (httpdFindArg(connData->getArgs, "mqtt-keepalive", buff, sizeof(buff)) > 0) { 126 | int32_t keepalive = atoi(buff); 127 | flashConfig.mqtt_keepalive = keepalive; 128 | } 129 | 130 | // if server setting changed, we need to "make it so" 131 | if (mqtt_server) { 132 | DBG("MQTT server settings changed, enable=%d\n", flashConfig.mqtt_enable); 133 | MQTT_Free(&mqttClient); // safe even if not connected 134 | mqtt_client_init(); 135 | 136 | // if just enable changed we just need to bounce the client 137 | } 138 | else if (mqtt_en_chg > 0) { 139 | DBG("MQTT server enable=%d changed\n", flashConfig.mqtt_enable); 140 | if (flashConfig.mqtt_enable && strlen(flashConfig.mqtt_host) > 0) 141 | MQTT_Reconnect(&mqttClient); 142 | else 143 | MQTT_Disconnect(&mqttClient); 144 | } 145 | 146 | // no action required if mqtt status settings change, they just get picked up at the 147 | // next status tick 148 | if (getBoolArg(connData, "mqtt-status-enable", &flashConfig.mqtt_status_enable) < 0) 149 | return HTTPD_CGI_DONE; 150 | if (getStringArg(connData, "mqtt-status-topic", 151 | flashConfig.mqtt_status_topic, sizeof(flashConfig.mqtt_status_topic)) < 0) 152 | return HTTPD_CGI_DONE; 153 | 154 | // if SLIP-enable is toggled it gets picked-up immediately by the parser 155 | int slip_update = getBoolArg(connData, "slip-enable", &flashConfig.slip_enable); 156 | if (slip_update < 0) return HTTPD_CGI_DONE; 157 | if (slip_update > 0) 158 | DBG("SLIP-enable changed: %d\n", flashConfig.slip_enable); 159 | 160 | DBG("Saving config\n"); 161 | 162 | if (configSave()) { 163 | httpdStartResponse(connData, 200); 164 | httpdEndHeaders(connData); 165 | } else { 166 | httpdStartResponse(connData, 500); 167 | httpdEndHeaders(connData); 168 | httpdSend(connData, "Failed to save config", -1); 169 | } 170 | return HTTPD_CGI_DONE; 171 | } 172 | 173 | int ICACHE_FLASH_ATTR cgiMqtt(HttpdConnData *connData) { 174 | if (connData->requestType == HTTPD_METHOD_GET) { 175 | return cgiMqttGet(connData); 176 | } else if (connData->requestType == HTTPD_METHOD_POST) { 177 | return cgiMqttSet(connData); 178 | } else { 179 | jsonHeader(connData, 404); 180 | return HTTPD_CGI_DONE; 181 | } 182 | } 183 | #endif 184 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | bool 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 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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 | #endif 28 | -------------------------------------------------------------------------------- /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 | int8_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_host[32], 25 | mqtt_clientid[48], 26 | mqtt_username[32], 27 | mqtt_password[32], 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 | } FlashConfig; 41 | extern FlashConfig flashConfig; 42 | 43 | bool configSave(void); 44 | bool configRestore(void); 45 | void configWipe(void); 46 | const size_t getFlashSize(); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /esp-link/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ---------------------------------------------------------------------------- 3 | * "THE BEER-WARE LICENSE" (Revision 42): 4 | * Jeroen Domburg wrote this file. As long as you retain 5 | * this notice you can do whatever you want with this stuff. If we meet some day, 6 | * and you think this stuff is worth it, you can buy me a beer in return. 7 | * ---------------------------------------------------------------------------- 8 | * Heavily modified and enhanced by Thorsten von Eicken in 2015 9 | * ---------------------------------------------------------------------------- 10 | */ 11 | 12 | #include 13 | #include "httpd.h" 14 | #include "httpdespfs.h" 15 | #include "cgi.h" 16 | #include "cgiwifi.h" 17 | #include "cgipins.h" 18 | #include "cgitcp.h" 19 | #include "cgimqtt.h" 20 | #include "cgiflash.h" 21 | // #include "cgioptiboot.h" 22 | #include "auth.h" 23 | #include "espfs.h" 24 | #include "uart.h" 25 | #include "serbridge.h" 26 | #include "tlv.h" 27 | #include "vncbridge.h" 28 | #include "status.h" 29 | #include "serled.h" 30 | #include "console.h" 31 | #include "config.h" 32 | #include "log.h" 33 | #include "gpio.h" 34 | #ifdef SYSLOG 35 | #include "syslog.h" 36 | #else 37 | #define syslog(a,...) 38 | #define LOG_NOTICE(a,...) 39 | #endif 40 | #include "cgiservices.h" 41 | 42 | #define NOTICE(format, ...) do { \ 43 | LOG_NOTICE(format, ## __VA_ARGS__ ); \ 44 | os_printf(format "\n", ## __VA_ARGS__); \ 45 | } while ( 0 ) 46 | 47 | /* 48 | This is the main url->function dispatching data struct. 49 | In short, it's a struct with various URLs plus their handlers. The handlers can 50 | be 'standard' CGI functions you wrote, or 'special' CGIs requiring an argument. 51 | They can also be auth-functions. An asterisk will match any url starting with 52 | everything before the asterisks; "*" matches everything. The list will be 53 | handled top-down, so make sure to put more specific rules above the more 54 | general ones. Authorization things (like authBasic) act as a 'barrier' and 55 | should be placed above the URLs they protect. 56 | */ 57 | HttpdBuiltInUrl builtInUrls[] = { 58 | { "/", cgiRedirect, "/home.html" }, 59 | { "/menu", cgiMenu, NULL }, 60 | { "/flash/next", cgiGetFirmwareNext, NULL }, 61 | { "/flash/upload", cgiUploadFirmware, NULL }, 62 | { "/flash/reboot", cgiRebootFirmware, NULL }, 63 | // { "/pgm/sync", cgiOptibootSync, NULL }, 64 | // { "/pgm/upload", cgiOptibootData, NULL }, 65 | { "/log/text", ajaxLog, NULL }, 66 | { "/log/dbg", ajaxLogDbg, NULL }, 67 | { "/log/reset", cgiReset, NULL }, 68 | { "/console/reset", ajaxConsoleReset, NULL }, 69 | { "/console/baud", ajaxConsoleBaud, NULL }, 70 | { "/console/text", ajaxConsole, NULL }, 71 | { "/console/send", ajaxConsoleSend, NULL }, 72 | //Enable the line below to protect the WiFi configuration with an username/password combo. 73 | // {"/wifi/*", authBasic, myPassFn}, 74 | { "/wifi", cgiRedirect, "/wifi/wifi.html" }, 75 | { "/wifi/", cgiRedirect, "/wifi/wifi.html" }, 76 | { "/wifi/info", cgiWifiInfo, NULL }, 77 | { "/wifi/scan", cgiWiFiScan, NULL }, 78 | { "/wifi/connect", cgiWiFiConnect, NULL }, 79 | { "/wifi/connstatus", cgiWiFiConnStatus, NULL }, 80 | { "/wifi/setmode", cgiWiFiSetMode, NULL }, 81 | { "/wifi/special", cgiWiFiSpecial, NULL }, 82 | { "/wifi/apinfo", cgiApSettingsInfo, NULL }, 83 | { "/wifi/apchange", cgiApSettingsChange, NULL }, 84 | { "/system/info", cgiSystemInfo, NULL }, 85 | { "/system/update", cgiSystemSet, NULL }, 86 | { "/services/info", cgiServicesInfo, NULL }, 87 | { "/services/update", cgiServicesSet, NULL }, 88 | { "/pins", cgiPins, NULL }, 89 | #ifdef MQTT 90 | { "/mqtt", cgiMqtt, NULL }, 91 | #endif 92 | { "*", cgiEspFsHook, NULL }, //Catch-all cgi function for the filesystem 93 | { NULL, NULL, NULL } 94 | }; 95 | 96 | #ifdef SHOW_HEAP_USE 97 | static ETSTimer prHeapTimer; 98 | static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) { 99 | os_printf("Heap: %ld\n", (unsigned long)system_get_free_heap_size()); 100 | } 101 | #endif 102 | 103 | # define VERS_STR_STR(V) #V 104 | # define VERS_STR(V) VERS_STR_STR(V) 105 | char* esp_link_version = VERS_STR(VERSION); 106 | 107 | // address of espfs binary blob 108 | extern uint32_t _binary_espfs_img_start; 109 | 110 | extern void app_init(void); 111 | extern void mqtt_client_init(void); 112 | 113 | void user_rf_pre_init(void) { 114 | //default is enabled 115 | system_set_os_print(DEBUG_SDK); 116 | } 117 | 118 | // Main routine to initialize esp-link. 119 | void user_init(void) { 120 | // get the flash config so we know how to init things 121 | //configWipe(); // uncomment to reset the config for testing purposes 122 | bool restoreOk = configRestore(); 123 | // Init gpio pin registers 124 | gpio_init(); 125 | gpio_output_set(0, 0, 0, (1<<15)); // some people tie it to GND, gotta ensure it's disabled 126 | // init UART 127 | uart_init(flashConfig.baud_rate, 115200); 128 | logInit(); // must come after init of uart 129 | // Say hello (leave some time to cause break in TX after boot loader's msg 130 | os_delay_us(10000L); 131 | os_printf("\n\n** %s\n", esp_link_version); 132 | os_printf("Flash config restore %s\n", restoreOk ? "ok" : "*FAILED*"); 133 | // Status LEDs 134 | statusInit(); 135 | // serledInit(); 136 | // Wifi 137 | wifiInit(); 138 | // init the flash filesystem with the html stuff 139 | espFsInit(&_binary_espfs_img_start); 140 | //EspFsInitResult res = espFsInit(&_binary_espfs_img_start); 141 | //os_printf("espFsInit %s\n", res?"ERR":"ok"); 142 | // mount the http handlers 143 | httpdInit(builtInUrls, 80); 144 | // init the wifi-serial transparent bridge (port 23) 145 | serbridgeInit(23); 146 | vncbridgeInit(5900); 147 | // uart_add_recv_cb(&serbridgeUartCb); 148 | uart_add_recv_cb(&tlvUartCb); 149 | // uart_add_recv_cb(&vncbridgeUartCb); 150 | #ifdef SHOW_HEAP_USE 151 | os_timer_disarm(&prHeapTimer); 152 | os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL); 153 | os_timer_arm(&prHeapTimer, 10000, 1); 154 | #endif 155 | 156 | struct rst_info *rst_info = system_get_rst_info(); 157 | NOTICE("Reset cause: %d=%s", rst_info->reason, rst_codes[rst_info->reason]); 158 | NOTICE("exccause=%d epc1=0x%x epc2=0x%x epc3=0x%x excvaddr=0x%x depc=0x%x", 159 | rst_info->exccause, rst_info->epc1, rst_info->epc2, rst_info->epc3, 160 | rst_info->excvaddr, rst_info->depc); 161 | uint32_t fid = spi_flash_get_id(); 162 | NOTICE("Flash map %s, manuf 0x%02lX chip 0x%04lX", flash_maps[system_get_flash_size_map()], 163 | fid & 0xff, (fid&0xff00)|((fid>>16)&0xff)); 164 | NOTICE("** esp-link ready"); 165 | 166 | // Init SNTP service 167 | cgiServicesSNTPInit(); 168 | #ifdef MQTT 169 | NOTICE("initializing MQTT"); 170 | mqtt_client_init(); 171 | #endif 172 | NOTICE("initializing user application"); 173 | app_init(); 174 | NOTICE("Waiting for work to do..."); 175 | uart0_tx_buffer("d41d8cd98f00b204e9800998ecf8427e", 32); 176 | } 177 | -------------------------------------------------------------------------------- /esp-link/mqtt_client.c: -------------------------------------------------------------------------------- 1 | #ifdef MQTT 2 | #include 3 | #include "cgiwifi.h" 4 | #include "config.h" 5 | #include "mqtt.h" 6 | 7 | #ifdef MQTTCLIENT_DBG 8 | #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__) } while(0) 9 | #else 10 | #define DBG(format, ...) do { } while(0) 11 | #endif 12 | 13 | MQTT_Client mqttClient; // main mqtt client used by esp-link 14 | 15 | static MqttCallback connected_cb; 16 | static MqttCallback disconnected_cb; 17 | static MqttCallback published_cb; 18 | static MqttDataCallback data_cb; 19 | 20 | void ICACHE_FLASH_ATTR 21 | mqttConnectedCb(MQTT_Client* client) { 22 | DBG("MQTT Client: Connected\n"); 23 | //MQTT_Subscribe(client, "system/time", 0); // handy for testing 24 | if (connected_cb) 25 | connected_cb(client); 26 | } 27 | 28 | void ICACHE_FLASH_ATTR 29 | mqttDisconnectedCb(MQTT_Client* client) { 30 | DBG("MQTT Client: Disconnected\n"); 31 | if (disconnected_cb) 32 | disconnected_cb(client); 33 | } 34 | 35 | void ICACHE_FLASH_ATTR 36 | mqttPublishedCb(MQTT_Client* client) { 37 | DBG("MQTT Client: Published\n"); 38 | if (published_cb) 39 | published_cb(client); 40 | } 41 | 42 | void ICACHE_FLASH_ATTR 43 | mqttDataCb(MQTT_Client* client, const char* topic, uint32_t topic_len, 44 | const char *data, uint32_t data_len) 45 | { 46 | #ifdef MQTTCLIENT_DBG 47 | char *topicBuf = (char*)os_zalloc(topic_len + 1); 48 | char *dataBuf = (char*)os_zalloc(data_len + 1); 49 | 50 | os_memcpy(topicBuf, topic, topic_len); 51 | topicBuf[topic_len] = 0; 52 | 53 | os_memcpy(dataBuf, data, data_len); 54 | dataBuf[data_len] = 0; 55 | 56 | os_printf("MQTT Client: Received topic: %s, data: %s\n", topicBuf, dataBuf); 57 | os_free(topicBuf); 58 | os_free(dataBuf); 59 | #endif 60 | 61 | if (data_cb) 62 | data_cb(client, topic, topic_len, data, data_len); 63 | } 64 | 65 | void ICACHE_FLASH_ATTR 66 | wifiStateChangeCb(uint8_t status) 67 | { 68 | if (flashConfig.mqtt_enable) { 69 | if (status == wifiGotIP && mqttClient.connState != TCP_CONNECTING) { 70 | MQTT_Connect(&mqttClient); 71 | } 72 | else if (status == wifiIsDisconnected && mqttClient.connState == TCP_CONNECTING) { 73 | MQTT_Disconnect(&mqttClient); 74 | } 75 | } 76 | } 77 | 78 | void ICACHE_FLASH_ATTR 79 | mqtt_client_init() 80 | { 81 | MQTT_Init(&mqttClient, flashConfig.mqtt_host, flashConfig.mqtt_port, 0, flashConfig.mqtt_timeout, 82 | flashConfig.mqtt_clientid, flashConfig.mqtt_username, flashConfig.mqtt_password, 83 | flashConfig.mqtt_keepalive); 84 | 85 | MQTT_OnConnected(&mqttClient, mqttConnectedCb); 86 | MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); 87 | MQTT_OnPublished(&mqttClient, mqttPublishedCb); 88 | MQTT_OnData(&mqttClient, mqttDataCb); 89 | 90 | if (flashConfig.mqtt_enable && strlen(flashConfig.mqtt_host) > 0) 91 | MQTT_Connect(&mqttClient); 92 | 93 | wifiAddStateChangeCb(wifiStateChangeCb); 94 | } 95 | 96 | void ICACHE_FLASH_ATTR 97 | mqtt_client_on_connected(MqttCallback connectedCb) { 98 | connected_cb = connectedCb; 99 | } 100 | 101 | void ICACHE_FLASH_ATTR 102 | mqtt_client_on_disconnected(MqttCallback disconnectedCb) { 103 | disconnected_cb = disconnectedCb; 104 | } 105 | 106 | void ICACHE_FLASH_ATTR 107 | mqtt_client_on_published(MqttCallback publishedCb) { 108 | published_cb = publishedCb; 109 | } 110 | 111 | void ICACHE_FLASH_ATTR 112 | mqtt_client_on_data(MqttDataCallback dataCb) { 113 | data_cb = dataCb; 114 | } 115 | 116 | #endif // MQTT 117 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 64 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 | -------------------------------------------------------------------------------- /espfs/espfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | This is a simple read-only implementation of a file system. It uses a block of data coming from the 3 | mkespfsimg tool, and can use that block to do abstracted operations on the files that are in there. 4 | It's written for use with httpd, but doesn't need to be used as such. 5 | */ 6 | 7 | /* 8 | * ---------------------------------------------------------------------------- 9 | * "THE BEER-WARE LICENSE" (Revision 42): 10 | * Jeroen Domburg wrote this file. As long as you retain 11 | * this notice you can do whatever you want with this stuff. If we meet some day, 12 | * and you think this stuff is worth it, you can buy me a beer in return. 13 | * ---------------------------------------------------------------------------- 14 | */ 15 | 16 | 17 | //These routines can also be tested by comping them in with the espfstest tool. This 18 | //simplifies debugging, but needs some slightly different headers. The #ifdef takes 19 | //care of that. 20 | 21 | #ifdef __ets__ 22 | //esp build 23 | #include 24 | #else 25 | //Test build 26 | #include 27 | #include 28 | #include 29 | #include 30 | #define os_malloc malloc 31 | #define os_free free 32 | #define os_memcpy memcpy 33 | #define os_strncmp strncmp 34 | #define os_strcmp strcmp 35 | #define os_strcpy strcpy 36 | #define os_printf printf 37 | #define ICACHE_FLASH_ATTR 38 | #endif 39 | 40 | #include "espfsformat.h" 41 | #include "espfs.h" 42 | 43 | static char* espFsData = NULL; 44 | 45 | struct EspFsFile { 46 | EspFsHeader *header; 47 | char decompressor; 48 | int32_t posDecomp; 49 | char *posStart; 50 | char *posComp; 51 | void *decompData; 52 | }; 53 | 54 | /* 55 | Available locations, at least in my flash, with boundaries partially guessed. This 56 | is using 0.9.1/0.9.2 SDK on a not-too-new module. 57 | 0x00000 (0x10000): Code/data (RAM data?) 58 | 0x10000 (0x02000): Gets erased by something? 59 | 0x12000 (0x2E000): Free (filled with zeroes) (parts used by ESPCloud and maybe SSL) 60 | 0x40000 (0x20000): Code/data (ROM data?) 61 | 0x60000 (0x1C000): Free 62 | 0x7c000 (0x04000): Param store 63 | 0x80000 - end of flash 64 | 65 | Accessing the flash through the mem emulation at 0x40200000 is a bit hairy: All accesses 66 | *must* be aligned 32-bit accesses. Reading a short, byte or unaligned word will result in 67 | a memory exception, crashing the program. 68 | */ 69 | 70 | EspFsInitResult ICACHE_FLASH_ATTR espFsInit(void *flashAddress) { 71 | // base address must be aligned to 4 bytes 72 | if (((int)flashAddress & 3) != 0) { 73 | return ESPFS_INIT_RESULT_BAD_ALIGN; 74 | } 75 | 76 | // check if there is valid header at address 77 | EspFsHeader testHeader; 78 | os_memcpy(&testHeader, flashAddress, sizeof(EspFsHeader)); 79 | if (testHeader.magic != ESPFS_MAGIC) { 80 | return ESPFS_INIT_RESULT_NO_IMAGE; 81 | } 82 | 83 | espFsData = (char *)flashAddress; 84 | return ESPFS_INIT_RESULT_OK; 85 | } 86 | 87 | //Copies len bytes over from dst to src, but does it using *only* 88 | //aligned 32-bit reads. Yes, it's no too optimized but it's short and sweet and it works. 89 | 90 | //ToDo: perhaps os_memcpy also does unaligned accesses? 91 | #ifdef __ets__ 92 | void ICACHE_FLASH_ATTR memcpyAligned(char *dst, char *src, int len) { 93 | int x; 94 | int w, b; 95 | for (x=0; x>0); 99 | if (b==1) *dst=(w>>8); 100 | if (b==2) *dst=(w>>16); 101 | if (b==3) *dst=(w>>24); 102 | dst++; src++; 103 | } 104 | } 105 | #else 106 | #define memcpyAligned memcpy 107 | #endif 108 | 109 | // Returns flags of opened file. 110 | int ICACHE_FLASH_ATTR espFsFlags(EspFsFile *fh) { 111 | if (fh == NULL) { 112 | #ifdef ESPFS_DBG 113 | os_printf("File handle not ready\n"); 114 | #endif 115 | return -1; 116 | } 117 | 118 | int8_t flags; 119 | memcpyAligned((char*)&flags, (char*)&fh->header->flags, 1); 120 | return (int)flags; 121 | } 122 | 123 | //Open a file and return a pointer to the file desc struct. 124 | EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { 125 | if (espFsData == NULL) { 126 | #ifdef ESPFS_DBG 127 | os_printf("Call espFsInit first!\n"); 128 | #endif 129 | return NULL; 130 | } 131 | char *p=espFsData; 132 | char *hpos; 133 | char namebuf[256]; 134 | EspFsHeader h; 135 | EspFsFile *r; 136 | //Strip initial slashes 137 | while(fileName[0]=='/') fileName++; 138 | //Go find that file! 139 | while(1) { 140 | hpos=p; 141 | //Grab the next file header. 142 | os_memcpy(&h, p, sizeof(EspFsHeader)); 143 | if (h.magic!=ESPFS_MAGIC) { 144 | #ifdef ESPFS_DBG 145 | os_printf("Magic mismatch. EspFS image broken.\n"); 146 | #endif 147 | return NULL; 148 | } 149 | if (h.flags&FLAG_LASTFILE) { 150 | //os_printf("End of image.\n"); 151 | return NULL; 152 | } 153 | //Grab the name of the file. 154 | p+=sizeof(EspFsHeader); 155 | os_memcpy(namebuf, p, sizeof(namebuf)); 156 | // os_printf("Found file '%s'. Namelen=%x fileLenComp=%x, compr=%d flags=%d\n", 157 | // namebuf, (unsigned int)h.nameLen, (unsigned int)h.fileLenComp, h.compression, h.flags); 158 | if (os_strcmp(namebuf, fileName)==0) { 159 | //Yay, this is the file we need! 160 | p+=h.nameLen; //Skip to content. 161 | r=(EspFsFile *)os_malloc(sizeof(EspFsFile)); //Alloc file desc mem 162 | //os_printf("Alloc %p[%d]\n", r, sizeof(EspFsFile)); 163 | if (r==NULL) return NULL; 164 | r->header=(EspFsHeader *)hpos; 165 | r->decompressor=h.compression; 166 | r->posComp=p; 167 | r->posStart=p; 168 | r->posDecomp=0; 169 | if (h.compression==COMPRESS_NONE) { 170 | r->decompData=NULL; 171 | } else { 172 | #ifdef ESPFS_DBG 173 | os_printf("Invalid compression: %d\n", h.compression); 174 | #endif 175 | return NULL; 176 | } 177 | return r; 178 | } 179 | //We don't need this file. Skip name and file 180 | p+=h.nameLen+h.fileLenComp; 181 | if ((int)p&3) p+=4-((int)p&3); //align to next 32bit val 182 | } 183 | } 184 | 185 | //Read len bytes from the given file into buff. Returns the actual amount of bytes read. 186 | int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) { 187 | int flen, fdlen; 188 | if (fh==NULL) return 0; 189 | //Cache file length. 190 | memcpyAligned((char*)&flen, (char*)&fh->header->fileLenComp, 4); 191 | memcpyAligned((char*)&fdlen, (char*)&fh->header->fileLenDecomp, 4); 192 | //Do stuff depending on the way the file is compressed. 193 | if (fh->decompressor==COMPRESS_NONE) { 194 | int toRead; 195 | toRead=flen-(fh->posComp-fh->posStart); 196 | if (len>toRead) len=toRead; 197 | // os_printf("Reading %d bytes from %x\n", len, (unsigned int)fh->posComp); 198 | memcpyAligned(buff, fh->posComp, len); 199 | fh->posDecomp+=len; 200 | fh->posComp+=len; 201 | // os_printf("Done reading %d bytes, pos=%x\n", len, fh->posComp); 202 | return len; 203 | } 204 | return 0; 205 | } 206 | 207 | //Close the file. 208 | void ICACHE_FLASH_ATTR espFsClose(EspFsFile *fh) { 209 | if (fh==NULL) return; 210 | //os_printf("Freed %p\n", fh); 211 | os_free(fh); 212 | } 213 | 214 | 215 | 216 | -------------------------------------------------------------------------------- /espfs/espfs.h: -------------------------------------------------------------------------------- 1 | #ifndef ESPFS_H 2 | #define ESPFS_H 3 | 4 | typedef enum { 5 | ESPFS_INIT_RESULT_OK, 6 | ESPFS_INIT_RESULT_NO_IMAGE, 7 | ESPFS_INIT_RESULT_BAD_ALIGN, 8 | } EspFsInitResult; 9 | 10 | typedef struct EspFsFile EspFsFile; 11 | 12 | EspFsInitResult espFsInit(void *flashAddress); 13 | EspFsFile *espFsOpen(char *fileName); 14 | int espFsFlags(EspFsFile *fh); 15 | int espFsRead(EspFsFile *fh, char *buff, int len); 16 | void espFsClose(EspFsFile *fh); 17 | 18 | 19 | #endif -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 | CFLAGS=-I.. -std=gnu99 38 | ifeq ("$(GZIP_COMPRESSION)","yes") 39 | CFLAGS += -DESPFS_GZIP 40 | endif 41 | 42 | OBJS=main.o 43 | TARGET=mkespfsimage 44 | 45 | $(TARGET): $(OBJS) 46 | ifeq ("$(GZIP_COMPRESSION)","yes") 47 | $(CC) -o $@ $^ -lz 48 | else 49 | $(CC) -o $@ $^ 50 | endif 51 | 52 | clean: 53 | rm -f $(TARGET) $(OBJS) 54 | 55 | endif -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /espfs/mkespfsimage/mman-win32/test.c: -------------------------------------------------------------------------------- 1 | 2 | #include "mman.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifndef NULL 9 | #define NULL (void*)0 10 | #endif 11 | 12 | const char* map_file_name = "map_file.dat"; 13 | 14 | int test_anon_map_readwrite() 15 | { 16 | void* map = mmap(NULL, 1024, PROT_READ | PROT_WRITE, 17 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 18 | if (map == MAP_FAILED) 19 | { 20 | printf("mmap (MAP_ANONYMOUS, PROT_READ | PROT_WRITE) returned unexpected error: %d\n", errno); 21 | return -1; 22 | } 23 | 24 | *((unsigned char*)map) = 1; 25 | 26 | int result = munmap(map, 1024); 27 | 28 | if (result != 0) 29 | printf("munmap (MAP_ANONYMOUS, PROT_READ | PROT_WRITE) returned unexpected error: %d\n", errno); 30 | 31 | return result; 32 | } 33 | 34 | int test_anon_map_readonly() 35 | { 36 | void* map = mmap(NULL, 1024, PROT_READ, 37 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 38 | if (map == MAP_FAILED) 39 | { 40 | printf("mmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\n", errno); 41 | return -1; 42 | } 43 | 44 | *((unsigned char*)map) = 1; 45 | 46 | int result = munmap(map, 1024); 47 | 48 | if (result != 0) 49 | printf("munmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\n", errno); 50 | 51 | return result; 52 | } 53 | 54 | int test_anon_map_writeonly() 55 | { 56 | void* map = mmap(NULL, 1024, PROT_WRITE, 57 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 58 | if (map == MAP_FAILED) 59 | { 60 | printf("mmap (MAP_ANONYMOUS, PROT_WRITE) returned unexpected error: %d\n", errno); 61 | return -1; 62 | } 63 | 64 | *((unsigned char*)map) = 1; 65 | 66 | int result = munmap(map, 1024); 67 | 68 | if (result != 0) 69 | printf("munmap (MAP_ANONYMOUS, PROT_WRITE) returned unexpected error: %d\n", errno); 70 | 71 | return result; 72 | } 73 | 74 | int test_anon_map_readonly_nowrite() 75 | { 76 | void* map = mmap(NULL, 1024, PROT_READ, 77 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 78 | if (map == MAP_FAILED) 79 | { 80 | printf("mmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\n", errno); 81 | return -1; 82 | } 83 | 84 | if (*((unsigned char*)map) != 0) 85 | printf("test_anon_map_readonly_nowrite (MAP_ANONYMOUS, PROT_READ) returned unexpected value: %d\n", 86 | (int)*((unsigned char*)map)); 87 | 88 | int result = munmap(map, 1024); 89 | 90 | if (result != 0) 91 | printf("munmap (MAP_ANONYMOUS, PROT_READ) returned unexpected error: %d\n", errno); 92 | 93 | return result; 94 | } 95 | 96 | int test_file_map_readwrite() 97 | { 98 | mode_t mode = S_IRUSR | S_IWUSR; 99 | int o = open(map_file_name, O_TRUNC | O_BINARY | O_RDWR | O_CREAT, mode); 100 | 101 | void* map = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE, o, 0); 102 | if (map == MAP_FAILED) 103 | { 104 | printf("mmap returned unexpected error: %d\n", errno); 105 | return -1; 106 | } 107 | 108 | *((unsigned char*)map) = 1; 109 | 110 | int result = munmap(map, 1024); 111 | 112 | if (result != 0) 113 | printf("munmap returned unexpected error: %d\n", errno); 114 | 115 | close(o); 116 | 117 | /*TODO: get file info and content and compare it with the sources conditions */ 118 | unlink(map_file_name); 119 | 120 | return result; 121 | } 122 | 123 | int test_file_map_mlock_munlock() 124 | { 125 | const size_t map_size = 1024; 126 | 127 | int result = 0; 128 | mode_t mode = S_IRUSR | S_IWUSR; 129 | int o = open(map_file_name, O_TRUNC | O_BINARY | O_RDWR | O_CREAT, mode); 130 | if (o == -1) 131 | { 132 | printf("unable to create file %s: %d\n", map_file_name, errno); 133 | return -1; 134 | } 135 | 136 | void* map = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, o, 0); 137 | if (map == MAP_FAILED) 138 | { 139 | printf("mmap returned unexpected error: %d\n", errno); 140 | result = -1; 141 | goto done_close; 142 | } 143 | 144 | if (mlock(map, map_size) != 0) 145 | { 146 | printf("mlock returned unexpected error: %d\n", errno); 147 | result = -1; 148 | goto done_munmap; 149 | } 150 | 151 | *((unsigned char*)map) = 1; 152 | 153 | if (munlock(map, map_size) != 0) 154 | { 155 | printf("munlock returned unexpected error: %d\n", errno); 156 | result = -1; 157 | } 158 | 159 | done_munmap: 160 | result = munmap(map, map_size); 161 | 162 | if (result != 0) 163 | printf("munmap returned unexpected error: %d\n", errno); 164 | 165 | done_close: 166 | close(o); 167 | 168 | unlink(map_file_name); 169 | done: 170 | return result; 171 | } 172 | 173 | int test_file_map_msync() 174 | { 175 | const size_t map_size = 1024; 176 | 177 | int result = 0; 178 | mode_t mode = S_IRUSR | S_IWUSR; 179 | int o = open(map_file_name, O_TRUNC | O_BINARY | O_RDWR | O_CREAT, mode); 180 | if (o == -1) 181 | { 182 | printf("unable to create file %s: %d\n", map_file_name, errno); 183 | return -1; 184 | } 185 | 186 | void* map = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, o, 0); 187 | if (map == MAP_FAILED) 188 | { 189 | printf("mmap returned unexpected error: %d\n", errno); 190 | result = -1; 191 | goto done_close; 192 | } 193 | 194 | *((unsigned char*)map) = 1; 195 | 196 | if (msync(map, map_size, MS_SYNC) != 0) 197 | { 198 | printf("msync returned unexpected error: %d\n", errno); 199 | result = -1; 200 | } 201 | 202 | result = munmap(map, map_size); 203 | 204 | if (result != 0) 205 | printf("munmap returned unexpected error: %d\n", errno); 206 | 207 | done_close: 208 | close(o); 209 | 210 | unlink(map_file_name); 211 | done: 212 | return result; 213 | } 214 | 215 | #define EXEC_TEST(name) \ 216 | if (name() != 0) { result = -1; printf( #name ": fail\n"); } \ 217 | else { printf(#name ": pass\n"); } 218 | 219 | int main() 220 | { 221 | int result = 0; 222 | 223 | EXEC_TEST(test_anon_map_readwrite); 224 | //NOTE: this test must cause an access violation exception 225 | //EXEC_TEST(test_anon_map_readonly); 226 | EXEC_TEST(test_anon_map_readonly_nowrite); 227 | EXEC_TEST(test_anon_map_writeonly); 228 | 229 | EXEC_TEST(test_file_map_readwrite); 230 | EXEC_TEST(test_file_map_mlock_munlock); 231 | EXEC_TEST(test_file_map_msync); 232 | //TODO: EXEC_TEST(test_file_map_mprotect); 233 | 234 | return result; 235 | } 236 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /flash_esp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | ESPDIR=`dirname $0` 5 | ESPTOOL=${ESPDIR}/../esptool/esptool.py 6 | 7 | AVRDIR=${ESPDIR}/../avr 8 | AVRFLASH=${AVRDIR}/flash_avr 9 | ESP_PROG=${AVRDIR}/Program_ESP/Program_ESP.hex 10 | 11 | HEX="$1" 12 | AVRHEX="$2" 13 | 14 | # Flash the Program_ESP firmware to the avr, so we can then flash the ESP 15 | ${AVRFLASH} ${ESP_PROG} 16 | 17 | sleep 2 18 | 19 | # this device pattern works OK on Linux and OS X, but watch out 20 | # if you have any additional USB Serial devices connected 21 | if [ -z "${DEVICEPATTERN}" ] ; then 22 | DEVICEPATTERN="/dev/cu.usbmodem* /dev/ttyACM*" 23 | fi 24 | 25 | DEVICE=`ls ${DEVICEPATTERN} 2> /dev/null` 26 | 27 | if [ -z "$DEVICE" ] ; then 28 | printf "No device found, waiting for it to show up" 29 | while [ -z "$DEVICE" ] ; do 30 | sleep 1 31 | printf "." 32 | DEVICE=`ls ${DEVICEPATTERN} 2> /dev/null` 33 | done 34 | fi 35 | 36 | 37 | # this version only overwrites the app, but not the bootloader or configuration 38 | # ${ESPTOOL} --port $DEVICE --baud 230400 write_flash -fs 4m -ff 40m 0x00000 $1 39 | 40 | # This version creates a completely fresh device, with all relevant 41 | # parts of the flash initialised. 42 | # This ends up overwriting any saved configuration, so only do this 43 | # if you have the configuration embedded in the app already, or plan 44 | # to manually configure it afterwards 45 | ${ESPTOOL} --port $DEVICE --baud 230400 write_flash -fs 4m -ff 40m 0x00000 ${ESPDIR}/../esp_iot_sdk_v1.5.1/bin/boot_v1.5.bin 0x1000 ${HEX} 0x7E000 ${ESPDIR}/../esp_iot_sdk_v1.5.1/bin/blank.bin 46 | 47 | if [ -n "$AVRHEX" ] ; then 48 | printf "Flashing %s to the atmega32u4, press RESET twice quickly to begin\n" "${AVRHEX}" 49 | ${AVRFLASH} ${AVRHEX} 50 | fi 51 | 52 | -------------------------------------------------------------------------------- /html/console.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Microcontroller Console

4 |
5 | 6 |
7 |

8 | Reset µC 9 |   Clear Log 10 |   Baud: 11 | 22 |   Fmt: 8N1 23 |

24 |
25 |
Console
26 |
27 |
28 |
--- No Content ---
29 |
30 |
31 |
Console entry
32 |
33 | (ENTER to submit, ESC to clear) 34 |
35 |
36 | Add: 37 | 38 | 39 | 40 |
41 |
42 |
43 |
44 | 45 | 46 |
47 |
48 |
49 |
History buffer
50 |
(UP/DOWN arrows to select)
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | 60 | 61 | 62 | 63 | 105 | 106 | -------------------------------------------------------------------------------- /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= sendHistory.children.length) { 76 | idx = 0; 77 | } 78 | loadHistory(idx); 79 | } 80 | 81 | sendHistory.addEventListener("change", function(e) { 82 | inputText.value = sendHistory.value; 83 | }); 84 | 85 | function pushHistory(text) { 86 | var idx = findHistory(text); 87 | if (idx !== null) { 88 | loadHistory(idx); 89 | return false; 90 | } 91 | var newOption = m(''); 98 | newOption.value = text; 99 | sendHistory.appendChild(newOption); 100 | sendHistory.value = text; 101 | for (; sendHistory.children.length > 15; ) { 102 | sendHistory.removeChild(sendHistory.children[0]); 103 | } 104 | return true; 105 | } 106 | 107 | inputText.addEventListener("keydown", function(e) { 108 | switch (e.keyCode) { 109 | case 38: /* the up arrow key pressed */ 110 | e.preventDefault(); 111 | navHistory(-1); 112 | break; 113 | case 40: /* the down arrow key pressed */ 114 | e.preventDefault(); 115 | navHistory(+1); 116 | break; 117 | case 27: /* the escape key pressed */ 118 | e.preventDefault(); 119 | inputText.value = ""; 120 | sendHistory.value = ""; 121 | break; 122 | case 13: /* the enter key pressed */ 123 | e.preventDefault(); 124 | var text = inputText.value; 125 | if (inputAddCr.checked) text += '\r'; 126 | if (inputAddLf.checked) text += '\n'; 127 | pushHistory(inputText.value); 128 | inputText.value = ""; 129 | ajaxSpin('POST', "/console/send?text=" + encodeURIComponent(text), 130 | function(resp) { showNotification("Text sent"); }, 131 | function(s, st) { showWarning("Error sending text"); } 132 | ); 133 | break; 134 | } 135 | }); 136 | } 137 | 138 | //===== Log page 139 | 140 | function showDbgMode(mode) { 141 | var btns = $('.dbg-btn'); 142 | for (var i=0; i < btns.length; i++) { 143 | if (btns[i].id === "dbg-"+mode) 144 | addClass(btns[i], "button-selected"); 145 | else 146 | removeClass(btns[i], "button-selected"); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensepost/esp-vnc/132ae445d3dbe06411f6d8bc749962a84fdc6eac/html/favicon.ico -------------------------------------------------------------------------------- /html/head-: -------------------------------------------------------------------------------- 1 | 2 | 3 | esp-link 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | -------------------------------------------------------------------------------- /html/jl-400x110.png-: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensepost/esp-vnc/132ae445d3dbe06411f6d8bc749962a84fdc6eac/html/jl-400x110.png- -------------------------------------------------------------------------------- /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.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/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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | $("#Syslog-form").removeAttribute("hidden"); 55 | $("#SNTP-form").removeAttribute("hidden"); 56 | $("#mDNS-form").removeAttribute("hidden"); 57 | 58 | var i, inputs = $("input"); 59 | for (i = 0; i < inputs.length; i++) { 60 | if (inputs[i].name == "mdns_enable") inputs[i].onclick = function () { setMDNS(this.checked) }; 61 | } 62 | } 63 | 64 | function setMDNS(v) { 65 | ajaxSpin("POST", "/services/update?mdns_enable=" + (v ? 1 : 0), function () { 66 | showNotification("mDNS is now " + (v ? "enabled" : "disabled")); 67 | }, function () { 68 | showWarning("Enable/disable failed"); 69 | window.setTimeout(fetchServices, 100); 70 | }); 71 | } 72 | 73 | function fetchServices() { 74 | ajaxJson("GET", "/services/info", displayServices, function () { 75 | window.setTimeout(fetchServices, 1000); 76 | }); 77 | } 78 | -------------------------------------------------------------------------------- /html/wifi/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensepost/esp-vnc/132ae445d3dbe06411f6d8bc749962a84fdc6eac/html/wifi/icons.png -------------------------------------------------------------------------------- /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 | 101 |
102 |
103 |
104 |
105 |
106 | 107 | 108 | 110 | 111 | 124 | 125 | -------------------------------------------------------------------------------- /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 | 7 | function changeWifiMode(m) { 8 | blockScan = 1; 9 | hideWarning(); 10 | ajaxSpin("POST", "setmode?mode=" + m, function(resp) { 11 | showNotification("Mode changed"); 12 | window.setTimeout(getWifiInfo, 100); 13 | blockScan = 0; 14 | }, function(s, st) { 15 | showWarning("Error changing mode: " + st); 16 | window.setTimeout(getWifiInfo, 100); 17 | blockScan = 0; 18 | }); 19 | } 20 | 21 | function changeApSettings(e) { 22 | e.preventDefault(); 23 | var url = "/wifi/apchange?100=1"; 24 | var i, inputs = document.querySelectorAll("#" + e.target.id + " input,select"); 25 | for (i = 0; i < inputs.length; i++) { 26 | if (inputs[i].type == "checkbox") { 27 | var val = (inputs[i].checked) ? 1 : 0; 28 | url += "&" + inputs[i].name + "=" + val; 29 | } 30 | else{ 31 | var clean = inputs[i].value.replace(/[^\w]/gi, ""); 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 | 88 | function doApAdvanced() { 89 | $('#AP_Settings-on').removeAttribute('hidden'); 90 | $("#AP_Settings-off").setAttribute("hidden", ""); 91 | $("#AP_Settings-roff").removeAttribute("checked"); 92 | } 93 | 94 | function undoApAdvanced(){ 95 | $("#AP_Settings-on").setAttribute("hidden", ""); 96 | $("#AP_Settings-off").removeAttribute("hidden"); 97 | $("#AP_Settings-roff").setAttribute("checked", ""); 98 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /httpd/auth.c: -------------------------------------------------------------------------------- 1 | /* 2 | HTTP auth implementation. Only does basic authentication for now. 3 | */ 4 | 5 | /* 6 | * ---------------------------------------------------------------------------- 7 | * "THE BEER-WARE LICENSE" (Revision 42): 8 | * Jeroen Domburg 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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 | HttpdPriv *priv; 34 | cgiSendCallback cgi; 35 | HttpdPostData *post; 36 | }; 37 | 38 | //A struct describing the POST data sent inside the http connection. This is used by the CGI functions 39 | struct HttpdPostData { 40 | int len; // POST Content-Length 41 | int buffSize; // The maximum length of the post buffer 42 | int buffLen; // The amount of bytes in the current post buffer 43 | int received; // The total amount of bytes received so far 44 | char *buff; // Actual POST data buffer 45 | char *multipartBoundary; 46 | }; 47 | 48 | //A struct describing an url. This is the main struct that's used to send different URL requests to 49 | //different routines. 50 | typedef struct { 51 | const char *url; 52 | cgiSendCallback cgiCb; 53 | const void *cgiArg; 54 | } HttpdBuiltInUrl; 55 | 56 | int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData); 57 | void ICACHE_FLASH_ATTR httpdRedirect(HttpdConnData *conn, char *newUrl); 58 | int httpdUrlDecode(char *val, int valLen, char *ret, int retLen); 59 | int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLen); 60 | void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port); 61 | const char *httpdGetMimetype(char *url); 62 | void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code); 63 | void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const char *val); 64 | void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn); 65 | int ICACHE_FLASH_ATTR httpdGetHeader(HttpdConnData *conn, char *header, char *ret, int retLen); 66 | int ICACHE_FLASH_ATTR httpdSend(HttpdConnData *conn, const char *data, int len); 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /include/esp8266.h: -------------------------------------------------------------------------------- 1 | // Combined include file for esp8266 2 | #ifndef _ESP8266_H_ 3 | #define _ESP8266_H_ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "espmissingincludes.h" 21 | #include "uart_hw.h" 22 | 23 | #ifdef __WIN32__ 24 | #include <_mingw.h> 25 | #endif 26 | 27 | #endif // _ESP8266_H_ -------------------------------------------------------------------------------- /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, char * file, int line); 66 | void *pvPortMalloc(size_t xWantedSize, char * file, int line); 67 | void *pvPortZalloc(size_t, 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< 4 | #ifdef __WIN32__ 5 | #include <_mingw.h> 6 | #endif 7 | 8 | #define 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 | #undef HTTPD_DBG 23 | #define MQTT_DBG 24 | #define MQTTCMD_DBG 25 | #undef PKTBUF_DBG 26 | #define REST_DBG 27 | #define RESTCMD_DBG 28 | #define SERBR_DBG 29 | #define SERLED_DBG 30 | #define SLIP_DBG 31 | #define UART_DBG 32 | #define MDNS_DBG 33 | #define OPTIBOOT_DBG 34 | #undef SYSLOG_DBG 35 | #undef CGISERVICES_DBG 36 | 37 | // If defined, the default hostname for DHCP will include the chip ID to make it unique 38 | #define CHIP_IN_HOSTNAME 39 | 40 | extern char* esp_link_version; 41 | extern uint8_t UTILS_StrToIP(const char* str, void *ip); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /mqtt/mqtt.h: -------------------------------------------------------------------------------- 1 | /* mqtt.h 2 | * 3 | * Copyright (c) 2014-2015, Tuan PM 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * 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 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * 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 OWNER 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 | #ifndef MQTT_H_ 31 | #define MQTT_H_ 32 | 33 | #include "mqtt_msg.h" 34 | #include "pktbuf.h" 35 | 36 | // in rest.c 37 | uint8_t UTILS_StrToIP(const char* str, void *ip); 38 | 39 | // State of MQTT connection 40 | typedef enum { 41 | MQTT_DISCONNECTED, // we're in disconnected state 42 | TCP_RECONNECT_REQ, // connect failed, needs reconnecting 43 | TCP_CONNECTING, // in TCP connection process 44 | MQTT_CONNECTED, // conneted (or connecting) 45 | } tConnState; 46 | 47 | typedef struct MQTT_Client MQTT_Client; // forward definition 48 | 49 | // Simple notification callback 50 | typedef void (*MqttCallback)(MQTT_Client *client); 51 | // Callback with data messge 52 | typedef void (*MqttDataCallback)(MQTT_Client *client, const char* topic, uint32_t topic_len, 53 | const char* data, uint32_t data_len); 54 | 55 | // MQTTY client data structure 56 | struct MQTT_Client { 57 | struct espconn* pCon; // socket 58 | // connection information 59 | char* host; // MQTT server 60 | uint16_t port; 61 | uint8_t security; // 0=tcp, 1=ssl 62 | ip_addr_t ip; // MQTT server IP address 63 | mqtt_connect_info_t connect_info; // info to connect/reconnect 64 | // protocol state and message assembly 65 | tConnState connState; // connection state 66 | bool sending; // espconn_send is pending 67 | mqtt_connection_t mqtt_connection; // message assembly descriptor 68 | PktBuf* msgQueue; // queued outbound messages 69 | // TCP input buffer 70 | uint8_t* in_buffer; 71 | int in_buffer_size; // length allocated 72 | int in_buffer_filled; // number of bytes held 73 | // outstanding message when we expect an ACK 74 | PktBuf* pending_buffer; // buffer sent and awaiting ACK 75 | PktBuf* sending_buffer; // buffer sent not awaiting ACK 76 | // timer and associated timeout counters 77 | ETSTimer mqttTimer; // timer for this connection 78 | uint8_t keepAliveTick; // seconds 'til keep-alive is required (0=no k-a) 79 | uint8_t keepAliveAckTick; // seconds 'til keep-alive ack is overdue (0=no k-a) 80 | uint8_t timeoutTick; // seconds 'til other timeout 81 | uint8_t sendTimeout; // value of send timeout setting 82 | uint8_t reconTimeout; // timeout to reconnect (back-off) 83 | // callbacks 84 | MqttCallback connectedCb; 85 | MqttCallback cmdConnectedCb; 86 | MqttCallback disconnectedCb; 87 | MqttCallback cmdDisconnectedCb; 88 | MqttCallback publishedCb; 89 | MqttCallback cmdPublishedCb; 90 | MqttDataCallback dataCb; 91 | MqttDataCallback cmdDataCb; 92 | // misc 93 | void* user_data; 94 | }; 95 | 96 | // Initialize client data structure 97 | void MQTT_Init(MQTT_Client* mqttClient, char* host, uint32 port, 98 | uint8_t security, uint8_t sendTimeout, 99 | char* client_id, char* client_user, char* client_pass, 100 | uint8_t keepAliveTime); 101 | 102 | // Completely free buffers associated with client data structure 103 | // This does not free the mqttClient struct itself, it just readies the struct so 104 | // it can be freed or MQTT_Init can be called on it again 105 | void MQTT_Free(MQTT_Client* mqttClient); 106 | 107 | // Set Last Will Topic on client, must be called before MQTT_InitConnection 108 | void MQTT_InitLWT(MQTT_Client* mqttClient, char* will_topic, char* will_msg, 109 | uint8_t will_qos, uint8_t will_retain); 110 | 111 | // Disconnect and reconnect in order to change params (such as LWT) 112 | void MQTT_Reconnect(MQTT_Client* mqttClient); 113 | 114 | // Kick of a persistent connection to the broker, will reconnect anytime conn breaks 115 | void MQTT_Connect(MQTT_Client* mqttClient); 116 | 117 | // Kill persistent connection 118 | void MQTT_Disconnect(MQTT_Client* mqttClient); 119 | 120 | // Subscribe to a topic 121 | bool MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos); 122 | 123 | // Publish a message 124 | bool MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint16_t data_len, 125 | uint8_t qos, uint8_t retain); 126 | 127 | // Callback when connected 128 | void MQTT_OnConnected(MQTT_Client* mqttClient, MqttCallback connectedCb); 129 | // Callback when disconnected 130 | void MQTT_OnDisconnected(MQTT_Client* mqttClient, MqttCallback disconnectedCb); 131 | // Callback when publish succeeded 132 | void MQTT_OnPublished(MQTT_Client* mqttClient, MqttCallback publishedCb); 133 | // Callback when data arrives for subscription 134 | void MQTT_OnData(MQTT_Client* mqttClient, MqttDataCallback dataCb); 135 | 136 | #endif /* USER_AT_MQTT_H_ */ 137 | -------------------------------------------------------------------------------- /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 | #endif /* MODULES_MQTT_CMD_H_ */ 21 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /serial/console.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt 2 | 3 | #include 4 | #include "uart.h" 5 | #include "cgi.h" 6 | #include "uart.h" 7 | #include "serbridge.h" 8 | #include "config.h" 9 | #include "console.h" 10 | #include "tlv.h" 11 | 12 | // Microcontroller console capturing the last 1024 characters received on the uart so 13 | // they can be shown on a web page 14 | 15 | // Buffer to hold concole contents. 16 | // Invariants: 17 | // - console_rd==console_wr <=> buffer empty 18 | // - *console_rd == next char to read 19 | // - *console_wr == next char to write 20 | // - 0 <= console_xx < BUF_MAX 21 | // - (console_wr+1)%BUF_MAX) == console_rd <=> buffer full 22 | #define BUF_MAX (1024) 23 | static char console_buf[BUF_MAX]; 24 | static int console_wr, console_rd; 25 | static int console_pos; // offset since reset of buffer 26 | 27 | static void ICACHE_FLASH_ATTR 28 | console_write(char c) { 29 | console_buf[console_wr] = c; 30 | console_wr = (console_wr+1) % BUF_MAX; 31 | if (console_wr == console_rd) { 32 | // full, we write anyway and loose the oldest char 33 | console_rd = (console_rd+1) % BUF_MAX; // full, eat first char 34 | console_pos++; 35 | } 36 | } 37 | 38 | #if 0 39 | // return previous character in console, 0 if at start 40 | static char ICACHE_FLASH_ATTR 41 | console_prev(void) { 42 | if (console_wr == console_rd) return 0; 43 | return console_buf[(console_wr-1+BUF_MAX)%BUF_MAX]; 44 | } 45 | #endif 46 | 47 | void ICACHE_FLASH_ATTR 48 | console_write_char(char c) { 49 | //if (c == '\n' && console_prev() != '\r') console_write('\r'); // does more harm than good 50 | console_write(c); 51 | } 52 | 53 | int ICACHE_FLASH_ATTR 54 | ajaxConsoleReset(HttpdConnData *connData) { 55 | if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. 56 | jsonHeader(connData, 200); 57 | console_rd = console_wr = console_pos = 0; 58 | // serbridgeReset(); 59 | return HTTPD_CGI_DONE; 60 | } 61 | 62 | int ICACHE_FLASH_ATTR 63 | ajaxConsoleBaud(HttpdConnData *connData) { 64 | if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. 65 | char buff[512]; 66 | int len, status = 400; 67 | len = httpdFindArg(connData->getArgs, "rate", buff, sizeof(buff)); 68 | if (len > 0) { 69 | int rate = atoi(buff); 70 | if (rate >= 9600 && rate <= 1000000) { 71 | uart0_baud(rate); 72 | flashConfig.baud_rate = rate; 73 | status = configSave() ? 200 : 400; 74 | } 75 | } else if (connData->requestType == HTTPD_METHOD_GET) { 76 | status = 200; 77 | } 78 | 79 | jsonHeader(connData, status); 80 | os_sprintf(buff, "{\"rate\": %ld}", flashConfig.baud_rate); 81 | httpdSend(connData, buff, -1); 82 | return HTTPD_CGI_DONE; 83 | } 84 | 85 | int ICACHE_FLASH_ATTR 86 | ajaxConsoleSend(HttpdConnData *connData) { 87 | if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. 88 | char buff[2048]; 89 | int len, status = 400; 90 | 91 | // figure out where to start in buffer based on URI param 92 | len = httpdFindArg(connData->getArgs, "text", buff, sizeof(buff)); 93 | if (len > 0 && len < TLV_MAX_PACKET) { 94 | tlv_send(TLV_PIPE, buff, len); 95 | status = 200; 96 | } 97 | 98 | jsonHeader(connData, status); 99 | return HTTPD_CGI_DONE; 100 | } 101 | 102 | int ICACHE_FLASH_ATTR 103 | ajaxConsole(HttpdConnData *connData) { 104 | if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. 105 | char buff[2048]; 106 | int len; // length of text in buff 107 | int console_len = (console_wr+BUF_MAX-console_rd) % BUF_MAX; // num chars in console_buf 108 | int start = 0; // offset onto console_wr to start sending out chars 109 | 110 | jsonHeader(connData, 200); 111 | 112 | // figure out where to start in buffer based on URI param 113 | len = httpdFindArg(connData->getArgs, "start", buff, sizeof(buff)); 114 | if (len > 0) { 115 | start = atoi(buff); 116 | if (start < console_pos) { 117 | start = 0; 118 | } else if (start >= console_pos+console_len) { 119 | start = console_len; 120 | } else { 121 | start = start - console_pos; 122 | } 123 | } 124 | 125 | // start outputting 126 | len = os_sprintf(buff, "{\"len\":%d, \"start\":%d, \"text\": \"", 127 | console_len-start, console_pos+start); 128 | 129 | int rd = (console_rd+start) % BUF_MAX; 130 | while (len < 2040 && rd != console_wr) { 131 | uint8_t c = console_buf[rd]; 132 | if (c == '\\' || c == '"') { 133 | buff[len++] = '\\'; 134 | buff[len++] = c; 135 | } else if (c == '\r') { 136 | // this is crummy, but browsers display a newline for \r\n sequences 137 | } else if (c < ' ') { 138 | len += os_sprintf(buff+len, "\\u%04x", c); 139 | } else { 140 | buff[len++] = c; 141 | } 142 | rd = (rd + 1) % BUF_MAX; 143 | } 144 | os_strcpy(buff+len, "\"}"); len+=2; 145 | httpdSend(connData, buff, len); 146 | return HTTPD_CGI_DONE; 147 | } 148 | 149 | void ICACHE_FLASH_ATTR consoleInit() { 150 | console_wr = 0; 151 | console_rd = 0; 152 | } 153 | -------------------------------------------------------------------------------- /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 ajaxConsoleBaud(HttpdConnData *connData); 11 | int ajaxConsoleSend(HttpdConnData *connData); 12 | int tplConsole(HttpdConnData *connData, char *token, void **arg); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /serial/crc16.h: -------------------------------------------------------------------------------- 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 | /** 34 | * \file 35 | * Header file for the CRC16 calculcation 36 | * \author 37 | * Adam Dunkels 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 | -------------------------------------------------------------------------------- /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 1 9 | #define SER_BRIDGE_TIMEOUT 300 // 300 seconds = 5 minutes 10 | 11 | // Send buffer size 12 | #define MAX_TXBUFFER (2*1460) 13 | #define MAX_RXBUFFER (8*1460) 14 | 15 | typedef struct serbridgeConnData { 16 | uint16 rxbufferlen; // length of data in rxbuffer 17 | char *rxbuffer; // buffer for received data 18 | struct espconn *conn; 19 | uint8_t telnet_state; 20 | uint16 txbufferlen; // length of data in txbuffer 21 | char *txbuffer; // buffer for the data to send 22 | char *sentbuffer; // buffer sent, awaiting callback to get freed 23 | uint32_t txoverflow_at; // when the transmitter started to overflow 24 | bool readytosend; // true, if txbuffer can be sent by espconn_sent 25 | } serbridgeConnData; 26 | 27 | // port1 is transparent 28 | void ICACHE_FLASH_ATTR serbridgeInit(int port1); 29 | void ICACHE_FLASH_ATTR serbridgeUartCb(char *buf, short len); 30 | void ICACHE_FLASH_ATTR serbridgeReset(); 31 | 32 | #endif /* __SER_BRIDGE_H__ */ 33 | -------------------------------------------------------------------------------- /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) { 35 | makeGpio(pin); 36 | gpio_output_set(0, 0, (1< 2) { 41 | // proper SLIP packet, invoke command processor after checking CRC 42 | //os_printf("SLIP: rcv %d\n", slip_len); 43 | uint16_t crc = crc16_data((uint8_t*)slip_buf, slip_len-2, 0); 44 | uint16_t rcv = ((uint16_t)slip_buf[slip_len-2]) | ((uint16_t)slip_buf[slip_len-1] << 8); 45 | if (crc == rcv) { 46 | cmdParsePacket((uint8_t*)slip_buf, slip_len-2); 47 | } else { 48 | os_printf("SLIP: bad CRC, crc=%04x rcv=%04x len=%d\n", crc, rcv, slip_len); 49 | 50 | for (short i=0; i= ' ' && slip_buf[i] <= '~') { 52 | DBG("%c", slip_buf[i]); 53 | } else { 54 | DBG("\\%02X", slip_buf[i]); 55 | } 56 | } 57 | DBG("\n"); 58 | } 59 | } 60 | } 61 | 62 | // determine whether a character is printable or not (or \r \n) 63 | static bool ICACHE_FLASH_ATTR 64 | slip_printable(char c) { 65 | return (c >= ' ' && c <= '~') || c == '\n' || c == '\r'; 66 | } 67 | 68 | static void ICACHE_FLASH_ATTR 69 | slip_reset() { 70 | //os_printf("SLIP: reset\n"); 71 | slip_inpkt = true; 72 | slip_escaped = false; 73 | slip_len = 0; 74 | } 75 | 76 | // SLIP parse a single character 77 | static void ICACHE_FLASH_ATTR 78 | slip_parse_char(char c) { 79 | if (c == SLIP_END) { 80 | // either start or end of packet, process whatever we may have accumulated 81 | DBG("SLIP: start or end len=%d inpkt=%d\n", slip_len, slip_inpkt); 82 | if (slip_len > 0) { 83 | if (slip_len > 2 && slip_inpkt) slip_process(); 84 | else console_process(slip_buf, slip_len); 85 | } 86 | slip_reset(); 87 | } else if (slip_escaped) { 88 | // prev char was SLIP_ESC 89 | if (c == SLIP_ESC_END) c = SLIP_END; 90 | if (c == SLIP_ESC_ESC) c = SLIP_ESC; 91 | if (slip_len < SLIP_MAX) slip_buf[slip_len++] = c; 92 | slip_escaped = false; 93 | } else if (slip_inpkt && c == SLIP_ESC) { 94 | slip_escaped = true; 95 | } else { 96 | if (slip_len == 1 && slip_printable(slip_buf[0]) && slip_printable(c)) { 97 | // start of packet and it's a printable character, we're gonna assume that this is console text 98 | slip_inpkt = false; 99 | } 100 | if (slip_len < SLIP_MAX) slip_buf[slip_len++] = c; 101 | } 102 | } 103 | 104 | // callback with a buffer of characters that have arrived on the uart 105 | void ICACHE_FLASH_ATTR 106 | slip_parse_buf(char *buf, short length) { 107 | // do SLIP parsing 108 | for (short i=0; i 0) { 113 | console_process(slip_buf, slip_len); 114 | slip_len = 0; 115 | } 116 | } 117 | 118 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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(UartBautRate uart0_br, UartBautRate uart1_br); 12 | 13 | // returns the number of characters pending in the UART0 tx fifo 14 | uint8_t ICACHE_FLASH_ATTR 15 | uart0_tx_fifo_length(void); 16 | 17 | // Transmit a buffer of characters on UART0 18 | void uart0_tx_buffer(char *buf, uint16 len); 19 | 20 | void uart0_write_char(char c); 21 | STATUS uart_tx_one_char(uint8 uart, uint8 c); 22 | 23 | void uart1_write_char(char c); 24 | 25 | // Add a receive callback function, this is called on the uart receive task each time a chunk 26 | // of bytes are received. A small number of callbacks can be added and they are all called 27 | // with all new characters. 28 | void uart_add_recv_cb(UartRecv_cb cb); 29 | 30 | // Turn UART interrupts off and poll for nchars or until timeout hits 31 | uint16_t uart0_rx_poll(char *buff, uint16_t nchars, uint32_t timeout_us); 32 | 33 | void uart0_baud(int rate); 34 | 35 | #endif /* __UART_H__ */ 36 | -------------------------------------------------------------------------------- /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 | extern uint32_t realtime_stamp; // 1sec NTP ticker 74 | 75 | typedef struct syslog_host_t syslog_host_t; 76 | struct syslog_host_t { 77 | uint32_t min_heap_size; // minimum allowed heap size when buffering 78 | ip_addr_t addr; 79 | uint16_t port; 80 | }; 81 | 82 | // buffered syslog event - f.e. if network stack isn't up and running 83 | typedef struct syslog_entry_t syslog_entry_t; 84 | struct syslog_entry_t { 85 | syslog_entry_t *next; 86 | uint32_t msgid; 87 | uint32_t tick; 88 | uint16_t datagram_len; 89 | char datagram[]; 90 | }; 91 | 92 | syslog_host_t syslogserver; 93 | 94 | void ICACHE_FLASH_ATTR syslog_init(char *syslog_host); 95 | void ICACHE_FLASH_ATTR syslog(uint8_t facility, uint8_t severity, const char tag[], const char message[], ...); 96 | 97 | // some convenience macros 98 | #ifdef SYSLOG 99 | // extern char *esp_link_version; // in user_main.c 100 | #define LOG_DEBUG(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_DEBUG, "esp_link", format, ## __VA_ARGS__ ) 101 | #define LOG_NOTICE(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_NOTICE, "esp_link", format, ## __VA_ARGS__ ) 102 | #define LOG_INFO(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_INFO, "esp_link", format, ## __VA_ARGS__ ) 103 | #define LOG_WARN(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_WARNING, "esp_link", format, ## __VA_ARGS__ ) 104 | #define LOG_ERR(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_ERR, "esp_link", format, ## __VA_ARGS__ ) 105 | #else 106 | #define LOG_DEBUG(format, ...) do { } while(0) 107 | #define LOG_NOTICE(format, ...) do { } while(0) 108 | #define LOG_WARN(format, ...) do { } while(0) 109 | #define LOG_INFO(format, ...) do { } while(0) 110 | #define LOG_ERR(format, ...) do { } while(0) 111 | #endif 112 | 113 | #ifdef __cplusplus 114 | } 115 | #endif 116 | 117 | #endif /* _SYSLOG_H */ 118 | -------------------------------------------------------------------------------- /tlv/tlv.c: -------------------------------------------------------------------------------- 1 | #include "tlv.h" 2 | #include "config.h" 3 | #ifdef SYSLOG 4 | #include "syslog.h" 5 | #else 6 | #define syslog(a, ...) do {} while (0); 7 | #endif 8 | #include 9 | 10 | #define TLV_DBG 11 | #ifdef TLV_DBG 12 | #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) 13 | #define DBG_RATE(timeout, format, ...) do { static uint32_t t = 0; if (system_get_time() - t > (timeout)) { DBG(format, ## __VA_ARGS__); t = system_get_time(); } } while (0); 14 | #else 15 | #define DBG(format, ...) do { } while(0) 16 | #define DBG_RATE(format, ...) 17 | #endif 18 | 19 | volatile static bool tlv_send_flow_paused = false; 20 | 21 | void tlv_poll_uart(void); 22 | 23 | static uint32_t lastUart = 0; 24 | 25 | bool ICACHE_FLASH_ATTR 26 | tlv_is_send_paused() { 27 | int gpio0 = GPIO_INPUT_GET(0); 28 | if (gpio0 != 0) DBG("gpio0 is %d\n", gpio0); 29 | return tlv_send_flow_paused; 30 | } 31 | 32 | void ICACHE_FLASH_ATTR tlv_send_fc(bool enabled) { 33 | static char buf[] = { 0, 2, 0, 0 }; 34 | 35 | buf[3] = enabled ? 1 : 0; 36 | uart0_tx_buffer(buf, 4); 37 | } 38 | 39 | int8_t ICACHE_FLASH_ATTR tlv_send(uint8_t channel, char *buf, uint8_t len) 40 | { 41 | if (tlv_send_flow_paused) { 42 | DBG_RATE(10*1000*1000, "Flow control active while sending\n"); 43 | if (system_get_time() - lastUart > 50*1000) { // 0.05s 44 | tlv_poll_uart(); 45 | } 46 | return -1; 47 | } 48 | DBG_RATE(10*1000*1000, "Sending packet, channel %d, length %d\n", channel, len); 49 | 50 | uart0_write_char((char) channel); 51 | uart0_write_char((char) len); 52 | uart0_tx_buffer(buf, len); 53 | tlv_send_flow_paused = true; 54 | return 0; 55 | } 56 | 57 | static tlv_data_t tlv_data; 58 | static uint8_t tlv_data_read = 0; 59 | 60 | static enum { 61 | CHANNEL = 0, LENGTH = 1, DATA = 2 62 | } tlv_read_state = CHANNEL; 63 | 64 | 65 | static tlv_receive_cb tlv_cb[TLV_MAX_HANDLERS]; 66 | 67 | // callback with a buffer of characters that have arrived on the uart 68 | void ICACHE_FLASH_ATTR 69 | tlvUartCb(char *buf, short length) { 70 | lastUart = system_get_time(); 71 | short pos = 0; 72 | uint8_t read; 73 | 74 | if (length < 4) { DBG("tlvUartCb: %d bytes\n", length); } 75 | while (pos < length) { 76 | switch (tlv_read_state) { 77 | case CHANNEL: 78 | tlv_data.channel = (uint8_t) buf[pos++]; 79 | tlv_read_state = LENGTH; 80 | // DBG("New packet, channel %d\n", tlv_data.channel); 81 | break; 82 | case LENGTH: 83 | tlv_data.length = (uint8_t) buf[pos++]; 84 | // DBG("New packet, channel %d, length %d\n", tlv_data.channel, tlv_data.length); 85 | tlv_data_read = 0; 86 | tlv_read_state = DATA; 87 | break; 88 | case DATA: 89 | #define MIN(X, Y) (((X)<(Y))?(X):(Y)) 90 | read = MIN(tlv_data.length - tlv_data_read, length - pos); 91 | #undef MIN 92 | 93 | memcpy(tlv_data.data + tlv_data_read, buf + pos, read); 94 | pos += read; 95 | tlv_data_read += read; 96 | 97 | // DBG("Packet for channel %d, read %d of %d\n", tlv_data.channel, tlv_data_read, tlv_data.length); 98 | 99 | if (tlv_data_read == tlv_data.length) { 100 | // DBG("Complete packet read, channel %d, length %d at %d of %d\n", tlv_data.channel, tlv_data.length, pos, length); 101 | 102 | if (tlv_data.channel == TLV_CONTROL) { 103 | if (tlv_data.data[0] == TLV_CONTROL_FLOW && tlv_data.length == 2) { 104 | // DBG("TLV Flow control message: %d\n", tlv_data.data[1]); 105 | tlv_send_flow_paused = (tlv_data.data[1] != 0); 106 | } 107 | } 108 | tlv_receive_cb cb; 109 | if (tlv_data.channel < TLV_MAX_HANDLERS) { 110 | cb = tlv_cb[tlv_data.channel]; 111 | } else { 112 | cb = tlv_cb[0]; 113 | } 114 | if (cb != NULL) { 115 | cb(&tlv_data); 116 | } 117 | tlv_read_state = CHANNEL; 118 | } 119 | break; 120 | } 121 | } 122 | } 123 | 124 | void ICACHE_FLASH_ATTR tlv_register_channel_handler(uint8_t channel, tlv_receive_cb cb) { 125 | if (channel < TLV_MAX_HANDLERS) { 126 | tlv_cb[channel] = cb; 127 | } 128 | } 129 | 130 | void ICACHE_FLASH_ATTR tlv_poll_uart() { 131 | char recv[1]; 132 | 133 | uint16_t got; 134 | while ((got = uart0_rx_poll(recv, 1, 100)) == 1) { 135 | // feed the watchdog 136 | system_soft_wdt_feed(); 137 | DBG("poll\n"); 138 | tlvUartCb(recv, got); 139 | } 140 | } 141 | 142 | void ICACHE_FLASH_ATTR tlv_init() { 143 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0); 144 | PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO0_U); 145 | } -------------------------------------------------------------------------------- /tlv/tlv.h: -------------------------------------------------------------------------------- 1 | #ifndef __TLV_H__ 2 | #define __TLV_H__ 3 | 4 | #include 5 | 6 | #define TLV_MAX_HANDLERS 4 7 | #define TLV_MAX_PACKET 64 8 | 9 | #define TLV_CONTROL 0 10 | #define TLV_HID 1 11 | #define TLV_PIPE 2 12 | #define TLV_DEBUG 3 13 | 14 | #define TLV_CONTROL_FLOW 0 15 | #define TLV_CONTROL_CONNECT 1 16 | 17 | typedef struct { 18 | uint8_t channel; 19 | uint8_t length; 20 | uint8_t data[TLV_MAX_PACKET]; 21 | } tlv_data_t; 22 | 23 | void ICACHE_FLASH_ATTR 24 | tlvUartCb(char *buf, short length); 25 | 26 | /** Callback used when registering the handler for incoming data 27 | * The channel number is a 1-based channel sequence 28 | * Channel 0 is the default handler for any channels greater than TLV_MAX_CHANNELS 29 | * It should return a true or false to indicate whether the message could be consumed or not 30 | * this is intended to be used as a sort of flow control mechanism, but needs to be properly thought through! 31 | */ 32 | typedef int8_t (*tlv_receive_cb)(tlv_data_t *tlv_data); 33 | 34 | /** 35 | * Returns 0 if successful, non-zero if not 36 | * -1 indicates that the uart has been blocked 37 | * -2 indicates that the channel has been blocked 38 | * 39 | * In the event of a blocked transmission, action should be taken to 40 | * send the tlv at a later time, typically by posting a task using post_user_task 41 | **/ 42 | int8_t ICACHE_FLASH_ATTR tlv_send(uint8_t channel, char *buf, uint8_t len); 43 | void ICACHE_FLASH_ATTR tlv_register_channel_handler(uint8_t channel, tlv_receive_cb cb); 44 | 45 | bool tlv_is_send_paused(void); 46 | 47 | #endif /* __TLV_H__ */ 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /vnc/vncbridge.h: -------------------------------------------------------------------------------- 1 | #ifndef __VNC_BRIDGE_H__ 2 | #define __VNC_BRIDGE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define VNC_MAX_CONN 1 9 | #define VNC_BRIDGE_TIMEOUT 300 // 300 seconds = 5 minutes 10 | 11 | typedef enum { 12 | CLIENT_HELLO, // client has connected, we expect a hello 13 | CLIENT_AUTH, // challenge sent, await auth 14 | CLIENT_INIT, // expect client initialisation messages 15 | RFB_MESSAGE, // expect RFB messages 16 | CUT_TEXT // Received CUT_TEXT message, now read the data 17 | } VncState; 18 | 19 | typedef enum { 20 | SetPixelFormat = 0, 21 | FixColourMapEntries = 1, 22 | SetEncodings = 2, 23 | FrameBufferUpdateRequest = 3, 24 | KeyEvent = 4, 25 | PointerEvent = 5, 26 | ClientCutText = 6 27 | } rfb; 28 | 29 | typedef struct vncbridgeConnData { 30 | struct espconn *conn; 31 | uint16 rxbufferlen; // length of data in rxbuffer 32 | char *rxbuffer; // buffer for received data 33 | bool recv_hold; // is the connection on hold 34 | VncState state; // the next message to be processed 35 | uint32 cut_text; // how much data to be read in cut_text state 36 | uint16 txbufferlen; // length of data in txbuffer 37 | char *txbuffer; // buffer for the data to send 38 | char *sentbuffer; // buffer sent, awaiting callback to get freed 39 | uint32_t txoverflow_at; // when the transmitter started to overflow 40 | bool readytosend; // true, if txbuffer can be sent by espconn_sent 41 | } vncbridgeConnData; 42 | 43 | void ICACHE_FLASH_ATTR vncbridgeInit(int port); 44 | void ICACHE_FLASH_ATTR vncbridgeInitPins(void); 45 | void ICACHE_FLASH_ATTR vncbridgeUartCb(char *buf, short len); 46 | void ICACHE_FLASH_ATTR vncbridgeReset(); 47 | // int8 ICACHE_FLASH_ATTR vnc_proto_handler(vncbridgeConnData *conn, char *data, unsigned short len); 48 | 49 | #endif /* __VNC_BRIDGE_H__ */ 50 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------