├── .gitignore ├── .gitmodules ├── Makefile ├── README ├── README.md ├── espfstest ├── Makefile ├── espfs.c ├── heatshrink_decoder.c └── main.c ├── html ├── dht22.tpl ├── index.tpl ├── led.tpl ├── style.css └── wifi │ ├── 140medley.min.js │ ├── connecting.html │ └── wifi.tpl ├── include ├── espmissingincludes.h ├── httpdconfig.h ├── stdint.h ├── uart_hw.h └── user_config.h ├── mkespfsimage ├── Makefile ├── espfsformat.h ├── heatshrink_encoder.c └── main.c └── user ├── cgi.c ├── cgi.h ├── cgiwifi.c ├── cgiwifi.h ├── dht.c ├── dht.h ├── ds18b20.c ├── ds18b20.h ├── espfs.c ├── espfs.h ├── heatshrink_config_httpd.h ├── heatshrink_decoder.c ├── httpd.c ├── httpd.h ├── httpdespfs.c ├── httpdespfs.h ├── io.c ├── io.h ├── stdout.c ├── stdout.h ├── tempd.c ├── tempd.h ├── user_main.c ├── wifi.c └── wifi.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | firmware/ 3 | mkespfsimage/*.o 4 | mkespfsimage/mkespfsimage 5 | webpages.espfs 6 | espfstest/*.o 7 | espfstest/espfstest 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/heatshrink"] 2 | path = lib/heatshrink 3 | url = https://github.com/atomicobject/heatshrink.git 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # tnx to mamalala 2 | # Changelog 3 | # Changed the variables to include the header file directory 4 | # Added global var for the XTENSA tool root 5 | # 6 | # This make file still needs some work. 7 | # 8 | # 9 | # Output directors to store intermediate compiled files 10 | # relative to the project directory 11 | BUILD_BASE = build 12 | FW_BASE = firmware 13 | 14 | # Base directory for the compiler 15 | XTENSA_TOOLS_ROOT ?= /opt/xtensa-lx106-elf/bin 16 | 17 | #Extra Tensilica includes from the ESS VM 18 | SDK_EXTRA_INCLUDES ?= /opt/Espressif/include 19 | 20 | # base directory of the ESP8266 SDK package, absolute 21 | SDK_BASE ?=/home/esp8266/Share/esp_iot_sdk 22 | 23 | #Esptool.py path and port 24 | ESPTOOL ?= /home/esp8266/Share/esp_iot_sdk/esptool.py 25 | ESPPORT ?= /dev/ttyUSB0 26 | 27 | # name for the target project 28 | TARGET = httpd 29 | 30 | # which modules (subdirectories) of the project to include in compiling 31 | MODULES = driver user 32 | EXTRA_INCDIR = include \ 33 | . \ 34 | lib/heatshrink/ \ 35 | $(SDK_EXTRA_INCLUDES) 36 | 37 | # libraries used in this project, mainly provided by the SDK 38 | LIBS = c gcc hal phy net80211 lwip wpa main 39 | 40 | # compiler flags using during compilation of source files 41 | CFLAGS = -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH 42 | 43 | # linker flags used to generate the main object file 44 | LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static 45 | 46 | # linker script used for the above linkier step 47 | LD_SCRIPT = eagle.app.v6.ld 48 | 49 | # various paths from the SDK used in this project 50 | SDK_LIBDIR = lib 51 | SDK_LDDIR = ld 52 | SDK_INCDIR = include include/json 53 | 54 | # we create two different files for uploading into the flash 55 | # these are the names and options to generate them 56 | FW_FILE_1 = 0x00000 57 | FW_FILE_1_ARGS = -bo $@ -bs .text -bs .data -bs .rodata -bc -ec 58 | FW_FILE_2 = 0x40000 59 | FW_FILE_2_ARGS = -es .irom0.text $@ -ec 60 | FW_FILE_3 = webpages.espfs 61 | 62 | # select which tools to use as compiler, librarian and linker 63 | CC := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 64 | AR := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-ar 65 | LD := $(XTENSA_TOOLS_ROOT)/xtensa-lx106-elf-gcc 66 | 67 | 68 | 69 | #### 70 | #### no user configurable options below here 71 | #### 72 | FW_TOOL ?= /usr/bin/esptool 73 | SRC_DIR := $(MODULES) 74 | BUILD_DIR := $(addprefix $(BUILD_BASE)/,$(MODULES)) 75 | 76 | SDK_LIBDIR := $(addprefix $(SDK_BASE)/,$(SDK_LIBDIR)) 77 | SDK_INCDIR := $(addprefix -I$(SDK_BASE)/,$(SDK_INCDIR)) 78 | 79 | SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c)) 80 | OBJ := $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC)) 81 | LIBS := $(addprefix -l,$(LIBS)) 82 | APP_AR := $(addprefix $(BUILD_BASE)/,$(TARGET)_app.a) 83 | TARGET_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).out) 84 | 85 | LD_SCRIPT := $(addprefix -T$(SDK_BASE)/$(SDK_LDDIR)/,$(LD_SCRIPT)) 86 | 87 | INCDIR := $(addprefix -I,$(SRC_DIR)) 88 | EXTRA_INCDIR := $(addprefix -I,$(EXTRA_INCDIR)) 89 | MODULE_INCDIR := $(addsuffix /include,$(INCDIR)) 90 | 91 | FW_FILE_1 := $(addprefix $(FW_BASE)/,$(FW_FILE_1).bin) 92 | FW_FILE_2 := $(addprefix $(FW_BASE)/,$(FW_FILE_2).bin) 93 | 94 | V ?= $(VERBOSE) 95 | ifeq ("$(V)","1") 96 | Q := 97 | vecho := @true 98 | else 99 | Q := @ 100 | vecho := @echo 101 | endif 102 | 103 | vpath %.c $(SRC_DIR) 104 | 105 | define compile-objects 106 | $1/%.o: %.c 107 | $(vecho) "CC $$<" 108 | $(Q) $(CC) $(INCDIR) $(MODULE_INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) $(CFLAGS) -c $$< -o $$@ 109 | endef 110 | 111 | .PHONY: all checkdirs clean 112 | 113 | all: checkdirs $(TARGET_OUT) $(FW_FILE_1) $(FW_FILE_2) 114 | 115 | $(FW_FILE_1): $(TARGET_OUT) firmware 116 | $(vecho) "FW $@" 117 | $(Q) $(FW_TOOL) -eo $(TARGET_OUT) $(FW_FILE_1_ARGS) 118 | 119 | $(FW_FILE_2): $(TARGET_OUT) firmware 120 | $(vecho) "FW $@" 121 | $(Q) $(FW_TOOL) -eo $(TARGET_OUT) $(FW_FILE_2_ARGS) 122 | 123 | $(TARGET_OUT): $(APP_AR) 124 | $(vecho) "LD $@" 125 | $(Q) $(LD) -L$(SDK_LIBDIR) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@ 126 | 127 | $(APP_AR): $(OBJ) 128 | $(vecho) "AR $@" 129 | $(Q) $(AR) cru $@ $^ 130 | 131 | checkdirs: $(BUILD_DIR) $(FW_BASE) 132 | 133 | $(BUILD_DIR): 134 | $(Q) mkdir -p $@ 135 | 136 | firmware: 137 | $(Q) mkdir -p $@ 138 | 139 | flash: $(FW_FILE_1) $(FW_FILE_2) 140 | -$(ESPTOOL) --port $(ESPPORT) write_flash 0x00000 firmware/0x00000.bin 141 | sleep 10 142 | -$(ESPTOOL) --port $(ESPPORT) write_flash 0x40000 firmware/0x40000.bin 143 | 144 | webpages.espfs: html/ mkespfsimage/mkespfsimage 145 | cd html; find | ../mkespfsimage/mkespfsimage > ../webpages.espfs; cd .. 146 | 147 | mkespfsimage/mkespfsimage: mkespfsimage/ 148 | make -C mkespfsimage 149 | 150 | htmlflash: webpages.espfs 151 | if [ $$(stat -c '%s' webpages.espfs) -gt $$(( 0x2E000 )) ]; then echo "webpages.espfs too big!"; false; fi 152 | -$(ESPTOOL) --port $(ESPPORT) write_flash 0x12000 webpages.espfs 153 | 154 | clean: 155 | $(Q) rm -f $(APP_AR) 156 | $(Q) rm -f $(TARGET_OUT) 157 | $(Q) find $(BUILD_BASE) -type f | xargs rm -f 158 | 159 | 160 | $(Q) rm -f $(FW_FILE_1) 161 | $(Q) rm -f $(FW_FILE_2) 162 | $(Q) rm -f $(FW_FILE_3) 163 | $(Q) rm -rf $(FW_BASE) 164 | 165 | $(foreach bdir,$(BUILD_DIR),$(eval $(call compile-objects,$(bdir)))) 166 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | esp-httpd README 2 | 3 | This is a small but powerful webserver for ESP8266(EX) chips. Included is an example of how 4 | to make a module that can have the AP it connects to configured over a webbrowser. 5 | 6 | ABOUT THE WEBSERVER 7 | 8 | The Good (aka: what's awesome) 9 | - Supports multiple connections, for eg simultaneous html/css/js/images downloading 10 | - Static files stored in flash, in an (optionally compressed) RO filesystem 11 | - Pluggable using external cgi routines 12 | - Simple template engine for mixed c and html things 13 | 14 | The Bad (aka: what can be improved) 15 | - Not built for speediness, although it's reasonable fast. 16 | - Built according to what I remember of the HTTP protocol, not according to the 17 | RFCs. Should work with most modern browsers, though. 18 | - No support for authentication or https. 19 | 20 | The Ugly (aka: bugs, misbehaviour) 21 | - Possible buffer overflows (usually not remotely exploitable) due to no os_snprintf 22 | This can be theoretically remedied by either Espressif including an os_snprintf in 23 | their libs or by using some alternate printf lib, like elm-chans xprintf 24 | 25 | ABOUT THE EXAMPLE 26 | 27 | When you flash the example into an ESP8266(EX) module, you get a small webserver with a few example 28 | pages. If you've already connected your module to your WLAN before, it'll keep those settings. When 29 | you haven't or the settings are wrong, keep GPIO0 for >5 seconds. The module will reboot into 30 | its STA+AP mode. Connect a computer to the newly formed access point and browse to 31 | http://192.168.4.1/wifi in order to connect the module to your WiFi network. The example also 32 | allows you to control a LED that's connected to GPIO2. 33 | 34 | BUILDING EVERYTHING 35 | 36 | For this, you need an environment that can compile ESP8266 firmware. Environments for this still 37 | are in flux at the moment, but I'm using a crosstool-ng gcc setup combined with the libs & includes 38 | from the ESP SDK and ESP VM. You probably also need an UNIX-slike system; I'm working on 39 | Debian Linux myself. 40 | 41 | To manage the paths to all this, you can source a small shell fragment into your current session. For 42 | example, I source a file with these contents: 43 | export PATH=${PWD}/crosstool-NG/builds/xtensa-lx106-elf/bin:$PATH 44 | export XTENSA_TOOLS_ROOT=${PWD}/crosstool-NG/builds/xtensa-lx106-elf/bin 45 | export SDK_BASE=${PWD}/esp_iot_sdk_v0.9.2/ 46 | export SDK_EXTRA_INCLUDES=${PWD}/esp_iot_sdk_novm_unpacked/usr/xtensa/XtDevTools/install/builds/RC-2010.1-win32/lx106/xtensa-elf/include/ 47 | export ESPTOOL=${PWD}/esptool/esptool.py 48 | export ESPPORT=/dev/ttyUSB0 49 | Actual setup of the SDK and toolchain is out of the scope of this document, so I hope this helps you 50 | enough to set up your own if you haven't already. 51 | 52 | If you have that, you can clone out the source code: 53 | git clone http://git.spritesserver.nl/esphttpd.git/ 54 | 55 | This project makes use of heatshrink, which is a git submodule. To fetch the code: 56 | cd esphttpd 57 | git submodule init 58 | git submodule update 59 | 60 | 61 | Now, build the code: 62 | make 63 | 64 | Flash the code happens in 2 steps. First the code itself gets flashed. Reset the module into bootloader 65 | mode and enter 'make flash'. You may want to reset and re-enter the bootloader halfway (at 'sleep 3') for 66 | the 2nd part of this flash to work. 67 | 68 | The 2nd step is to pack the static files the webserver will serve and flash that. Reset the module into 69 | bootloader mode again and enter 'make htmlflash'. 70 | 71 | You should have a working webserver now. 72 | 73 | WRITING CODE FOR THE WEBSERVER 74 | 75 | ...errm... to be done. For now, look at the examples. Hey, you probably managed to find out how 76 | the SDK works, this shouldn't be too hard :P 77 | 78 | 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP8266-based DHT11 (and 22) logger 2 | 3 | This application builds on [Martin's DHT22 webserver](http://harizanov.com/2014/11/esp8266-powered-web-server-led-control-dht22-temperaturehumidity-sensor-reading/) implementation, which uses [sprite_tm's ESP8266 httpd](http://www.esp8266.com/viewtopic.php?f=6&t=376). 4 | 5 | This version adds a check to make sure a DHT sensor is connected, and prevents the sensor being polled past the manufacturer's maximum frequency. It also logs sensor readings to a remote machine over the network as JSON, sent over UDP. 6 | 7 | In addition to a DHT sensor, a DS18B20 1wire temperature sensor can be attached to port 0. If there's one attached, readings will be sent to the logging server and shown in the dht22.tpl page on the webserver. 8 | 9 | If enabled (default is off), the code will try to sleep the ESP chip rather than leave it running. This requires a physical connection between the RST and GPIO16 pin, otherwise the chip won't wake up after going to sleep. The cycle time in "sleep mode" is about one minute. See [Issue #3](https://github.com/mathew-hall/esp8266-dht/issues/3) for the needed fix to allow the board to wake back up. 10 | 11 | # Configuration 12 | 13 | There are a few parameters that can be changed: 14 | * user/user_main.c: sensor type (DHT11 or DHT22) and poll rate 15 | * tempd.c: hostname and UDP port to send readings, poll rate for readings, fallback IP (needs to be set in `dnsLookupCb`), and sleep mode setting (default off) 16 | 17 | # Building 18 | 19 | Make sure the IoT SDK and toolchain are set up according to the instructions on the [ESP8266 wiki](https://github.com/esp8266/esp8266-wiki/wiki/Toolchain). The makefile in this project relies on some environment variables that need to be set. It should be enough to add these to your `.profile`: 20 | 21 | PATH="/opt/Espressif/crosstool-NG/builds/xtensa-lx106-elf/bin/:$PATH" 22 | export SDK_BASE="/opt/Espressif/ESP8266_SDK" 23 | export XTENSA_TOOLS_ROOT="/opt/Espressif/crosstool-NG/builds/xtensa-lx106-elf/bin/" 24 | export ESPTOOL="/opt/Espressif/esptool-py/esptool.py" 25 | 26 | From the root of this repository, run `make`. This will build the two firmware images. To update the webserver's HTML files, run `make webpages.espfs`. 27 | 28 | # Hardware 29 | 30 | The whole circuit runs on 3.3V power, which can be obtained using a cheap AMS1117 3.3V module or regulator circuit. 31 | 32 | | Pin | Connection | 33 | | --- | ------------------------------------------------------- | 34 | | VCC and CH_PD | +3.3V | 35 | | GPIO0 | DS18B20 DQ pin, pulled to 3.3V through a 4.7k resistor| 36 | | GPIO2 | DHT11 pin 2, pulled to 3.3V through a 10k resistor | 37 | | GND | ground | 38 | | TX | UART adapter RX | 39 | | RX | UART adapter TX | 40 | | RST | N/C [or solder to GPIO16 on the module for sleep mode]| 41 | 42 | The sensors both need their VCC and GND pins connecting to power and ground respectively. 43 | 44 | ## Sleep mode 45 | 46 | The sleep mode requires the RST pin to be tied to GPIO16 to allow the chip to wake itself back up. If this change isn't made (and sleep mode is turned on in `tempd.c`) then the board will post a single update and then not do anything until manually reset. 47 | 48 | You'll need to solder a wire from RST to GPIO16. See [Issue #3](https://github.com/mathew-hall/esp8266-dht/issues/3) for the needed bodge. The pitch on the chip is very small, so use a very thin wire. I used an individual strand from an IDE cable. 49 | 50 | This might not apply for newer ESP modules. 51 | 52 | # Licenses 53 | 54 | This code is entirely based on [Sprite_tm](http://www.esp8266.com/viewtopic.php?f=6&t=376)'s httpd implementation for the ESP8266, licensed under the Beer-ware license. See the license headers in the `user` directory for the full text of the license. 55 | 56 | The webserver uses Scott Vokes's [heatshrink](https://github.com/atomicobject/heatshrink) library. See the LICENSE file in `lib/heatshrink` for the full text of the license. 57 | 58 | The DHT code is based on [Martin's DHT22 cgi script](http://harizanov.com/2014/11/esp8266-powered-web-server-led-control-dht22-temperaturehumidity-sensor-reading/). The revised DHT driver itself is based on the implementation from Adafruit Industries. See the comment in dht.c for the full text of the license. Code for the CRC calculation is taken from [Maxim AN162](http://www.maximintegrated.com/en/app-notes/index.mvp/id/162). 59 | 60 | The ds18b20 driver includes code based on [Grant Forest's Energia](http://forum.43oh.com/topic/3314-energia-library-onewire-ds18b20-430-stellaris/) code and licensed under the GPL. See the file for the full license text. 61 | 62 | The temperature logger is licensed under a modified Beer-wrare license. See the header for `tempd.c` for details. 63 | -------------------------------------------------------------------------------- /espfstest/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-I../lib/heatshrink -I../user -I../include -std=gnu99 2 | 3 | espfstest: main.o espfs.o heatshrink_decoder.o 4 | $(CC) -o $@ $^ 5 | 6 | espfs.o: espfs.c ../user/espfs.c 7 | 8 | 9 | clean: 10 | rm -f *.o espfstest 11 | -------------------------------------------------------------------------------- /espfstest/espfs.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "../user/espfs.c" 4 | -------------------------------------------------------------------------------- /espfstest/heatshrink_decoder.c: -------------------------------------------------------------------------------- 1 | #include "httpdconfig.h" 2 | #ifdef EFS_HEATSHRINK 3 | //Stupid wrapper so we don't have to move c-files around 4 | //Also loads httpd-specific config. 5 | 6 | #include "../lib/heatshrink/heatshrink_decoder.c" 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /espfstest/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Simple and stupid file decompressor for an espfs image. Mostly used as a testbed for espfs.c and 3 | the decompressors: code compiled natively is way easier to debug using gdb et all :) 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | #include "espfs.h" 16 | 17 | char *espFsData; 18 | 19 | int main(int argc, char **argv) { 20 | int f, out; 21 | int len; 22 | char buff[128]; 23 | EspFsFile *ef; 24 | off_t size; 25 | 26 | if (argc!=3) { 27 | printf("Usage: %s espfs-image file\nExpands file from the espfs-image archive.\n", argv[0]); 28 | exit(0); 29 | } 30 | 31 | f=open(argv[1], O_RDONLY); 32 | if (f<=0) { 33 | perror(argv[1]); 34 | exit(1); 35 | } 36 | size=lseek(f, 0, SEEK_END); 37 | espFsData=mmap(NULL, size, PROT_READ, MAP_SHARED, f, 0); 38 | if (espFsData==MAP_FAILED) { 39 | perror("mmap"); 40 | exit(1); 41 | } 42 | 43 | ef=espFsOpen(argv[2]); 44 | if (ef==NULL) { 45 | printf("Couldn't find %s in image.\n", argv[2]); 46 | exit(1); 47 | } 48 | 49 | out=open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0644); 50 | if (out<=0) { 51 | perror(argv[2]); 52 | exit(1); 53 | } 54 | 55 | while ((len=espFsRead(ef, buff, 128))!=0) { 56 | write(out, buff, len); 57 | } 58 | espFsClose(ef); 59 | //munmap, close, ... I can't be bothered. 60 | } 61 | -------------------------------------------------------------------------------- /html/dht22.tpl: -------------------------------------------------------------------------------- 1 | DHT 2 | 3 | 4 | 5 |
6 |

DHT 22 temperature/humidity sensor

7 |

8 | It looks like there %sensor_present% a sensor hooked up to GPIO2 9 |

10 |

11 | If there's one connected, its temperature reading is %temperature%*C, humidity is %humidity%. 12 |

13 |
14 |

15 | If there's a DS18B20 connected on GPIO port 0, its reading is %ds_temperature%. 16 |

17 | 18 |
19 | 20 | -------------------------------------------------------------------------------- /html/index.tpl: -------------------------------------------------------------------------------- 1 | 2 | Esp8266 web server 3 | 4 | 5 | 6 |
7 |

Main

8 |

9 |

    10 |
  • If you haven't connected this device to your WLAN network now, you can do so.
  • 11 |
  • You can also control the LED.
  • 12 |
  • ..or view readings of DHT22 sensor.
  • 13 |
  • You can download the raw contents of the SPI flash rom
  • 14 |
15 |

16 |

Page has been loaded %counter% times. 17 |

18 | 19 | -------------------------------------------------------------------------------- /html/led.tpl: -------------------------------------------------------------------------------- 1 | LED 2 | 3 | 4 | 5 |
6 |

The LED

7 |

8 | If there's a LED connected to GPIO13, it's now %ledstate%. You can change that using the buttons below. 9 |

10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 | -------------------------------------------------------------------------------- /html/style.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | background-color: #404040; 4 | font-family: sans-serif; 5 | } 6 | 7 | #main { 8 | background-color: #d0d0FF; 9 | -moz-border-radius: 5px; 10 | -webkit-border-radius: 5px; 11 | border-radius: 5px; 12 | border: 2px solid #000000; 13 | width: 80%; 14 | margin: 0 auto; 15 | padding: 20px 16 | } 17 | -------------------------------------------------------------------------------- /html/wifi/140medley.min.js: -------------------------------------------------------------------------------- 1 | var t=function(a,b){return function(c,d){return a.replace(/#{([^}]*)}/g,function(a,f){return Function("x","with(x)return "+f).call(c,d||b||{})})}},s=function(a,b){return b?{get:function(c){return a[c]&&b.parse(a[c])},set:function(c,d){a[c]=b.stringify(d)}}:{}}(this.localStorage||{},JSON),p=function(a,b,c,d){c=c||document;d=c[b="on"+b];a=c[b]=function(e){d=d&&d(e=e||c.event);return(a=a&&b(e))?b:d};c=this},m=function(a,b,c){b=document;c=b.createElement("p");c.innerHTML=a;for(a=b.createDocumentFragment();b= 2 | c.firstChild;)a.appendChild(b);return a},$=function(a,b){a=a.match(/^(\W)?(.*)/);return(b||document)["getElement"+(a[1]?a[1]=="#"?"ById":"sByClassName":"sByTagName")](a[2])},j=function(a){for(a=0;a<4;a++)try{return a?new ActiveXObject([,"Msxml2","Msxml3","Microsoft"][a]+".XMLHTTP"):new XMLHttpRequest}catch(b){}}; 3 | -------------------------------------------------------------------------------- /html/wifi/connecting.html: -------------------------------------------------------------------------------- 1 | 2 | Connecting 3 | 4 | 5 | Connecting to AP now... 6 | 7 | 8 | -------------------------------------------------------------------------------- /html/wifi/wifi.tpl: -------------------------------------------------------------------------------- 1 | WiFi connection 2 | 3 | 4 | 62 | 63 | 64 |
65 |

66 | Current WiFi mode: %WiFiMode% 67 |

68 |
69 |

70 | To connect to a WiFi network, please select one of the detected networks...
71 |

Scanning...
72 |
73 | WiFi password, if applicable:
74 |
75 | 76 |

77 |
78 | 79 | 80 | -------------------------------------------------------------------------------- /include/espmissingincludes.h: -------------------------------------------------------------------------------- 1 | #ifndef ESPMISSINGINCLUIDES_H 2 | #define ESPMISSINGINCLUIDES_H 3 | 4 | #include 5 | 6 | //Missing function prototypes in include folders. Gcc will warn on these if we don't define 'em anywhere. 7 | //MOST OF THESE ARE GUESSED! but they seem to swork and shut up the compiler. 8 | 9 | int atoi(const char *nptr); 10 | void ets_delay_us(long us); 11 | void ets_install_putc1(void *routine); 12 | void ets_isr_attach(int intr, void *handler, void *arg); 13 | void ets_isr_mask(unsigned intr); 14 | void ets_isr_unmask(unsigned intr); 15 | int ets_memcmp(const void *s1, const void *s2, size_t n); 16 | void *ets_memcpy(void *dest, const void *src, size_t n); 17 | void *ets_memset(void *s, int c, size_t n); 18 | int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format (printf, 2, 3))); 19 | int ets_str2macaddr(void *, void *); 20 | int ets_strcmp(const char *s1, const char *s2); 21 | char *ets_strcpy(char *dest, const char *src); 22 | size_t ets_strlen(const char *s); 23 | int ets_strncmp(const char *s1, const char *s2, int len); 24 | char *ets_strncpy(char *dest, const char *src, size_t n); 25 | char *ets_strstr(const char *haystack, const char *needle); 26 | void ets_timer_arm_new(ETSTimer *a, int b, int c, int isMstimer); 27 | void ets_timer_disarm(ETSTimer *a); 28 | void ets_timer_setfn(ETSTimer *t, ETSTimerFunc *fn, void *parg); 29 | int os_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); 30 | int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__ ((format (printf, 3, 4))); 31 | void pvPortFree(void *ptr); 32 | void *pvPortMalloc(size_t xWantedSize); 33 | void *pvPortZalloc(size_t); 34 | void uart_div_modify(int no, unsigned int freq); 35 | void vPortFree(void *ptr); 36 | void *vPortMalloc(size_t xWantedSize); 37 | void ets_intr_lock(void); 38 | void ets_intr_unlock(void); 39 | void wdt_feed(void); 40 | #endif 41 | -------------------------------------------------------------------------------- /include/httpdconfig.h: -------------------------------------------------------------------------------- 1 | 2 | //Define this if you want to be able to use Heatshrink-compressed espfs images. 3 | #define EFS_HEATSHRINK 4 | 5 | //Pos of esp fs in flash 6 | #define ESPFS_POS 0x12000 7 | -------------------------------------------------------------------------------- /include/stdint.h: -------------------------------------------------------------------------------- 1 | 2 | //Including the system-wide stdint.h messes stuff up... but I don't want to change heatshrink 3 | //not to do it. Including this dummy file fixes it too, tho'. 4 | 5 | #ifndef __ets__ 6 | //Do include stdint for testing builds. 7 | #include_next 8 | #endif -------------------------------------------------------------------------------- /include/uart_hw.h: -------------------------------------------------------------------------------- 1 | //Generated at 2012-07-03 18:44:06 2 | /* 3 | * Copyright (c) 2010 - 2011 Espressif System 4 | * 5 | */ 6 | 7 | #ifndef UART_REGISTER_H_INCLUDED 8 | #define UART_REGISTER_H_INCLUDED 9 | #define REG_UART_BASE( i ) (0x60000000+(i)*0xf00) 10 | //version value:32'h062000 11 | 12 | #define UART_FIFO( i ) (REG_UART_BASE( i ) + 0x0) 13 | #define UART_RXFIFO_RD_BYTE 0x000000FF 14 | #define UART_RXFIFO_RD_BYTE_S 0 15 | 16 | #define UART_INT_RAW( i ) (REG_UART_BASE( i ) + 0x4) 17 | #define UART_RXFIFO_TOUT_INT_RAW (BIT(8)) 18 | #define UART_BRK_DET_INT_RAW (BIT(7)) 19 | #define UART_CTS_CHG_INT_RAW (BIT(6)) 20 | #define UART_DSR_CHG_INT_RAW (BIT(5)) 21 | #define UART_RXFIFO_OVF_INT_RAW (BIT(4)) 22 | #define UART_FRM_ERR_INT_RAW (BIT(3)) 23 | #define UART_PARITY_ERR_INT_RAW (BIT(2)) 24 | #define UART_TXFIFO_EMPTY_INT_RAW (BIT(1)) 25 | #define UART_RXFIFO_FULL_INT_RAW (BIT(0)) 26 | 27 | #define UART_INT_ST( i ) (REG_UART_BASE( i ) + 0x8) 28 | #define UART_RXFIFO_TOUT_INT_ST (BIT(8)) 29 | #define UART_BRK_DET_INT_ST (BIT(7)) 30 | #define UART_CTS_CHG_INT_ST (BIT(6)) 31 | #define UART_DSR_CHG_INT_ST (BIT(5)) 32 | #define UART_RXFIFO_OVF_INT_ST (BIT(4)) 33 | #define UART_FRM_ERR_INT_ST (BIT(3)) 34 | #define UART_PARITY_ERR_INT_ST (BIT(2)) 35 | #define UART_TXFIFO_EMPTY_INT_ST (BIT(1)) 36 | #define UART_RXFIFO_FULL_INT_ST (BIT(0)) 37 | 38 | #define UART_INT_ENA( i ) (REG_UART_BASE( i ) + 0xC) 39 | #define UART_RXFIFO_TOUT_INT_ENA (BIT(8)) 40 | #define UART_BRK_DET_INT_ENA (BIT(7)) 41 | #define UART_CTS_CHG_INT_ENA (BIT(6)) 42 | #define UART_DSR_CHG_INT_ENA (BIT(5)) 43 | #define UART_RXFIFO_OVF_INT_ENA (BIT(4)) 44 | #define UART_FRM_ERR_INT_ENA (BIT(3)) 45 | #define UART_PARITY_ERR_INT_ENA (BIT(2)) 46 | #define UART_TXFIFO_EMPTY_INT_ENA (BIT(1)) 47 | #define UART_RXFIFO_FULL_INT_ENA (BIT(0)) 48 | 49 | #define UART_INT_CLR( i ) (REG_UART_BASE( i ) + 0x10) 50 | #define UART_RXFIFO_TOUT_INT_CLR (BIT(8)) 51 | #define UART_BRK_DET_INT_CLR (BIT(7)) 52 | #define UART_CTS_CHG_INT_CLR (BIT(6)) 53 | #define UART_DSR_CHG_INT_CLR (BIT(5)) 54 | #define UART_RXFIFO_OVF_INT_CLR (BIT(4)) 55 | #define UART_FRM_ERR_INT_CLR (BIT(3)) 56 | #define UART_PARITY_ERR_INT_CLR (BIT(2)) 57 | #define UART_TXFIFO_EMPTY_INT_CLR (BIT(1)) 58 | #define UART_RXFIFO_FULL_INT_CLR (BIT(0)) 59 | 60 | #define UART_CLKDIV( i ) (REG_UART_BASE( i ) + 0x14) 61 | #define UART_CLKDIV_CNT 0x000FFFFF 62 | #define UART_CLKDIV_S 0 63 | 64 | #define UART_AUTOBAUD( i ) (REG_UART_BASE( i ) + 0x18) 65 | #define UART_GLITCH_FILT 0x000000FF 66 | #define UART_GLITCH_FILT_S 8 67 | #define UART_AUTOBAUD_EN (BIT(0)) 68 | 69 | #define UART_STATUS( i ) (REG_UART_BASE( i ) + 0x1C) 70 | #define UART_TXD (BIT(31)) 71 | #define UART_RTSN (BIT(30)) 72 | #define UART_DTRN (BIT(29)) 73 | #define UART_TXFIFO_CNT 0x000000FF 74 | #define UART_TXFIFO_CNT_S 16 75 | #define UART_RXD (BIT(15)) 76 | #define UART_CTSN (BIT(14)) 77 | #define UART_DSRN (BIT(13)) 78 | #define UART_RXFIFO_CNT 0x000000FF 79 | #define UART_RXFIFO_CNT_S 0 80 | 81 | #define UART_CONF0( i ) (REG_UART_BASE( i ) + 0x20) 82 | #define UART_TXFIFO_RST (BIT(18)) 83 | #define UART_RXFIFO_RST (BIT(17)) 84 | #define UART_IRDA_EN (BIT(16)) 85 | #define UART_TX_FLOW_EN (BIT(15)) 86 | #define UART_LOOPBACK (BIT(14)) 87 | #define UART_IRDA_RX_INV (BIT(13)) 88 | #define UART_IRDA_TX_INV (BIT(12)) 89 | #define UART_IRDA_WCTL (BIT(11)) 90 | #define UART_IRDA_TX_EN (BIT(10)) 91 | #define UART_IRDA_DPLX (BIT(9)) 92 | #define UART_TXD_BRK (BIT(8)) 93 | #define UART_SW_DTR (BIT(7)) 94 | #define UART_SW_RTS (BIT(6)) 95 | #define UART_STOP_BIT_NUM 0x00000003 96 | #define UART_STOP_BIT_NUM_S 4 97 | #define UART_BIT_NUM 0x00000003 98 | #define UART_BIT_NUM_S 2 99 | #define UART_PARITY_EN (BIT(1)) 100 | #define UART_PARITY (BIT(0)) 101 | 102 | #define UART_CONF1( i ) (REG_UART_BASE( i ) + 0x24) 103 | #define UART_RX_TOUT_EN (BIT(31)) 104 | #define UART_RX_TOUT_THRHD 0x0000007F 105 | #define UART_RX_TOUT_THRHD_S 24 106 | #define UART_RX_FLOW_EN (BIT(23)) 107 | #define UART_RX_FLOW_THRHD 0x0000007F 108 | #define UART_RX_FLOW_THRHD_S 16 109 | #define UART_TXFIFO_EMPTY_THRHD 0x0000007F 110 | #define UART_TXFIFO_EMPTY_THRHD_S 8 111 | #define UART_RXFIFO_FULL_THRHD 0x0000007F 112 | #define UART_RXFIFO_FULL_THRHD_S 0 113 | 114 | #define UART_LOWPULSE( i ) (REG_UART_BASE( i ) + 0x28) 115 | #define UART_LOWPULSE_MIN_CNT 0x000FFFFF 116 | #define UART_LOWPULSE_MIN_CNT_S 0 117 | 118 | #define UART_HIGHPULSE( i ) (REG_UART_BASE( i ) + 0x2C) 119 | #define UART_HIGHPULSE_MIN_CNT 0x000FFFFF 120 | #define UART_HIGHPULSE_MIN_CNT_S 0 121 | 122 | #define UART_PULSE_NUM( i ) (REG_UART_BASE( i ) + 0x30) 123 | #define UART_PULSE_NUM_CNT 0x0003FF 124 | #define UART_PULSE_NUM_CNT_S 0 125 | 126 | #define UART_DATE( i ) (REG_UART_BASE( i ) + 0x78) 127 | #define UART_ID( i ) (REG_UART_BASE( i ) + 0x7C) 128 | #endif // UART_REGISTER_H_INCLUDED 129 | 130 | #define RX_BUFF_SIZE 256 131 | #define TX_BUFF_SIZE 100 132 | #define UART0 0 133 | #define UART1 1 134 | 135 | typedef enum { 136 | FIVE_BITS = 0x0, 137 | SIX_BITS = 0x1, 138 | SEVEN_BITS = 0x2, 139 | EIGHT_BITS = 0x3 140 | } UartBitsNum4Char; 141 | 142 | typedef enum { 143 | ONE_STOP_BIT = 0, 144 | ONE_HALF_STOP_BIT = BIT2, 145 | TWO_STOP_BIT = BIT2 146 | } UartStopBitsNum; 147 | 148 | typedef enum { 149 | NONE_BITS = 0, 150 | ODD_BITS = 0, 151 | EVEN_BITS = BIT4 152 | } UartParityMode; 153 | 154 | typedef enum { 155 | STICK_PARITY_DIS = 0, 156 | STICK_PARITY_EN = BIT3 | BIT5 157 | } UartExistParity; 158 | 159 | typedef enum { 160 | BIT_RATE_9600 = 9600, 161 | BIT_RATE_19200 = 19200, 162 | BIT_RATE_38400 = 38400, 163 | BIT_RATE_57600 = 57600, 164 | BIT_RATE_74880 = 74880, 165 | BIT_RATE_115200 = 115200, 166 | BIT_RATE_230400 = 230400, 167 | BIT_RATE_460800 = 460800, 168 | BIT_RATE_921600 = 921600 169 | } UartBautRate; 170 | 171 | typedef enum { 172 | NONE_CTRL, 173 | HARDWARE_CTRL, 174 | XON_XOFF_CTRL 175 | } UartFlowCtrl; 176 | 177 | typedef enum { 178 | EMPTY, 179 | UNDER_WRITE, 180 | WRITE_OVER 181 | } RcvMsgBuffState; 182 | 183 | typedef struct { 184 | uint32 TrxBuffSize; 185 | uint8 *pTrxBuff; 186 | } TrxMsgBuff; 187 | 188 | typedef enum { 189 | BAUD_RATE_DET, 190 | WAIT_SYNC_FRM, 191 | SRCH_MSG_HEAD, 192 | RCV_MSG_BODY, 193 | RCV_ESC_CHAR, 194 | } RcvMsgState; 195 | 196 | -------------------------------------------------------------------------------- /include/user_config.h: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /mkespfsimage/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CFLAGS=-I../lib/heatshrink -std=gnu99 3 | OBJS=main.o heatshrink_encoder.o 4 | TARGET=mkespfsimage 5 | 6 | $(TARGET): $(OBJS) 7 | $(CC) -o $@ $^ 8 | 9 | clean: 10 | rm -f $(TARGET) $(OBJS) -------------------------------------------------------------------------------- /mkespfsimage/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 COMPRESS_NONE 0 20 | #define COMPRESS_HEATSHRINK 1 21 | 22 | typedef struct { 23 | int32_t magic; 24 | int8_t flags; 25 | int8_t compression; 26 | int16_t nameLen; 27 | int32_t fileLenComp; 28 | int32_t fileLenDecomp; 29 | } __attribute__((packed)) EspFsHeader; 30 | 31 | #endif -------------------------------------------------------------------------------- /mkespfsimage/heatshrink_encoder.c: -------------------------------------------------------------------------------- 1 | //Stupid wraparound include to make sure object file doesn't end up in heatshrink dir 2 | 3 | #include "../lib/heatshrink/heatshrink_encoder.c" 4 | -------------------------------------------------------------------------------- /mkespfsimage/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "espfsformat.h" 12 | 13 | //Heatshrink 14 | #include "heatshrink_common.h" 15 | #include "heatshrink_config.h" 16 | #include "heatshrink_encoder.h" 17 | 18 | 19 | //Routines to convert host format to the endianness used in the xtensa 20 | short htoxs(short in) { 21 | char r[2]; 22 | r[0]=in; 23 | r[1]=in>>8; 24 | return *((short *)r); 25 | } 26 | 27 | int htoxl(int in) { 28 | unsigned char r[4]; 29 | r[0]=in; 30 | r[1]=in>>8; 31 | r[2]=in>>16; 32 | r[3]=in>>24; 33 | return *((int *)r); 34 | } 35 | 36 | size_t compressHeatshrink(char *in, int insize, char *out, int outsize, int level) { 37 | char *inp=in; 38 | char *outp=out; 39 | int len; 40 | int ws[]={5, 6, 8, 11, 13}; 41 | int ls[]={3, 3, 4, 4, 4}; 42 | HSE_poll_res pres; 43 | HSE_sink_res sres; 44 | size_t r; 45 | if (level==-1) level=8; 46 | level=(level-1)/2; //level is now 0, 1, 2, 3, 4 47 | heatshrink_encoder *enc=heatshrink_encoder_alloc(ws[level], ls[level]); 48 | if (enc==NULL) { 49 | perror("allocating mem for heatshrink"); 50 | exit(1); 51 | } 52 | //Save encoder parms as first byte 53 | *outp=(ws[level]<<4)|ls[level]; 54 | outp++; outsize--; 55 | 56 | r=1; 57 | do { 58 | if (insize>0) { 59 | sres=heatshrink_encoder_sink(enc, inp, insize, &len); 60 | if (sres!=HSER_SINK_OK) break; 61 | inp+=len; insize-=len; 62 | if (insize==0) heatshrink_encoder_finish(enc); 63 | } 64 | do { 65 | pres=heatshrink_encoder_poll(enc, outp, outsize, &len); 66 | if (pres!=HSER_POLL_MORE && pres!=HSER_POLL_EMPTY) break; 67 | outp+=len; outsize-=len; 68 | r+=len; 69 | } while (pres==HSER_POLL_MORE); 70 | } while (insize!=0); 71 | 72 | if (insize!=0) { 73 | fprintf(stderr, "Heatshrink: Bug? insize is still %d. sres=%d pres=%d\n", insize, sres, pres); 74 | exit(1); 75 | } 76 | 77 | heatshrink_encoder_free(enc); 78 | return r; 79 | } 80 | 81 | int handleFile(int f, char *name, int compression, int level) { 82 | char *fdat, *cdat; 83 | off_t size, csize; 84 | EspFsHeader h; 85 | int nameLen; 86 | size=lseek(f, 0, SEEK_END); 87 | fdat=mmap(NULL, size, PROT_READ, MAP_SHARED, f, 0); 88 | if (fdat==MAP_FAILED) { 89 | perror("mmap"); 90 | return 0; 91 | } 92 | 93 | if (compression==COMPRESS_NONE) { 94 | csize=size; 95 | cdat=fdat; 96 | } else if (compression==COMPRESS_HEATSHRINK) { 97 | cdat=malloc(size*2); 98 | csize=compressHeatshrink(fdat, size, cdat, size*2, level); 99 | } else { 100 | fprintf(stderr, "Unknown compression - %d\n", compression); 101 | exit(1); 102 | } 103 | 104 | if (csize>size) { 105 | //Compressing enbiggened this file. Revert to uncompressed store. 106 | compression=COMPRESS_NONE; 107 | csize=size; 108 | cdat=fdat; 109 | } 110 | 111 | //Fill header data 112 | h.magic=('E'<<0)+('S'<<8)+('f'<<16)+('s'<<24); 113 | h.flags=0; 114 | h.compression=compression; 115 | nameLen=strlen(name)+1; 116 | if (nameLen&3) nameLen+=4-(nameLen&3); //Round to next 32bit boundary 117 | h.nameLen=htoxs(nameLen); 118 | h.fileLenComp=htoxl(csize); 119 | h.fileLenDecomp=htoxl(size); 120 | 121 | write(1, &h, sizeof(EspFsHeader)); 122 | write(1, name, nameLen); //ToDo: this can eat up a few bytes after the buffer. 123 | write(1, cdat, csize); 124 | //Pad out to 32bit boundary 125 | while (csize&3) { 126 | write(1, "\000", 1); 127 | csize++; 128 | } 129 | munmap(fdat, size); 130 | return (csize*100)/size; 131 | } 132 | 133 | //Write final dummy header with FLAG_LASTFILE set. 134 | void finishArchive() { 135 | EspFsHeader h; 136 | h.magic=('E'<<0)+('S'<<8)+('f'<<16)+('s'<<24); 137 | h.flags=FLAG_LASTFILE; 138 | h.compression=COMPRESS_NONE; 139 | h.nameLen=htoxs(0); 140 | h.fileLenComp=htoxl(0); 141 | h.fileLenDecomp=htoxl(0); 142 | write(1, &h, sizeof(EspFsHeader)); 143 | } 144 | 145 | 146 | int main(int argc, char **argv) { 147 | int f, x; 148 | char fileName[1024]; 149 | char *realName; 150 | struct stat statBuf; 151 | int serr; 152 | int rate; 153 | int err=0; 154 | int compType=1; //default compression type - heatshrink 155 | int compLvl=-1; 156 | 157 | for (x=1; x=x-2) { 159 | compType=atoi(argv[x=1]); 160 | x++; 161 | } else if (strcmp(argv[x], "-l")==0 && argc>=x-2) { 162 | compLvl=atoi(argv[x=1]); 163 | if (compLvl<1 || compLvl>9) err=1; 164 | x++; 165 | } else { 166 | err=1; 167 | } 168 | } 169 | 170 | if (err) { 171 | fprintf(stderr, "%s - Program to create espfs images\n", argv[0]); 172 | fprintf(stderr, "Usage: \nfind | %s [-c compressor] [-l compression_level] > out.espfs\n", argv[0]); 173 | fprintf(stderr, "Compressors:\n0 - None\n1 - Heatshrink(defautl\n"); 174 | fprintf(stderr, "Compression level: 1 is worst but low RAM usage, higher is better compression \nbut uses more ram on decompression. -1 = compressors default.\n"); 175 | exit(0); 176 | } 177 | 178 | while(fgets(fileName, sizeof(fileName), stdin)) { 179 | //Kill off '\n' at the end 180 | fileName[strlen(fileName)-1]=0; 181 | //Only include files 182 | serr=stat(fileName, &statBuf); 183 | if ((serr==0) && S_ISREG(statBuf.st_mode)) { 184 | //Strip off './' or '/' madness. 185 | realName=fileName; 186 | if (fileName[0]=='.') realName++; 187 | if (realName[0]=='/') realName++; 188 | f=open(fileName, O_RDONLY); 189 | if (f>0) { 190 | rate=handleFile(f, realName, compType, compLvl); 191 | fprintf(stderr, "%s (%d%%)\n", realName, rate); 192 | close(f); 193 | } else { 194 | perror(fileName); 195 | } 196 | } else { 197 | if (serr!=0) { 198 | perror(fileName); 199 | } 200 | } 201 | } 202 | finishArchive(); 203 | return 0; 204 | } 205 | 206 | -------------------------------------------------------------------------------- /user/cgi.c: -------------------------------------------------------------------------------- 1 | /* 2 | Some random cgi routines. 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 17 | #include "user_interface.h" 18 | #include "mem.h" 19 | #include "httpd.h" 20 | #include "cgi.h" 21 | #include "io.h" 22 | #include "dht.h" 23 | #include "ds18b20.h" 24 | #include "espmissingincludes.h" 25 | 26 | //cause I can't be bothered to write an ioGetLed() 27 | static char currLedState=0; 28 | 29 | //Cgi that turns the LED on or off according to the 'led' param in the GET data 30 | int ICACHE_FLASH_ATTR cgiLed(HttpdConnData *connData) { 31 | int len; 32 | char buff[1024]; 33 | 34 | if (connData->conn==NULL) { 35 | //Connection aborted. Clean up. 36 | return HTTPD_CGI_DONE; 37 | } 38 | 39 | len=httpdFindArg(connData->getArgs, "led", buff, sizeof(buff)); 40 | if (len!=0) { 41 | currLedState=atoi(buff); 42 | ioLed(currLedState); 43 | } 44 | 45 | httpdRedirect(connData, "led.tpl"); 46 | return HTTPD_CGI_DONE; 47 | } 48 | 49 | 50 | 51 | //Template code for the led page. 52 | void ICACHE_FLASH_ATTR tplLed(HttpdConnData *connData, char *token, void **arg) { 53 | char buff[128]; 54 | if (token==NULL) return; 55 | 56 | os_strcpy(buff, "Unknown"); 57 | if (os_strcmp(token, "ledstate")==0) { 58 | if (currLedState) { 59 | os_strcpy(buff, "on"); 60 | } else { 61 | os_strcpy(buff, "off"); 62 | } 63 | } 64 | espconn_sent(connData->conn, (uint8 *)buff, os_strlen(buff)); 65 | } 66 | 67 | static long hitCounter=0; 68 | 69 | //Template code for the counter on the index page. 70 | void ICACHE_FLASH_ATTR tplCounter(HttpdConnData *connData, char *token, void **arg) { 71 | char buff[128]; 72 | if (token==NULL) return; 73 | 74 | if (os_strcmp(token, "counter")==0) { 75 | hitCounter++; 76 | os_sprintf(buff, "%ld", hitCounter); 77 | } 78 | espconn_sent(connData->conn, (uint8 *)buff, os_strlen(buff)); 79 | } 80 | 81 | 82 | //Cgi that reads the SPI flash. Assumes 512KByte flash. 83 | int ICACHE_FLASH_ATTR cgiReadFlash(HttpdConnData *connData) { 84 | int *pos=(int *)&connData->cgiData; 85 | if (connData->conn==NULL) { 86 | //Connection aborted. Clean up. 87 | return HTTPD_CGI_DONE; 88 | } 89 | 90 | if (*pos==0) { 91 | os_printf("Start flash download.\n"); 92 | httpdStartResponse(connData, 200); 93 | httpdHeader(connData, "Content-Type", "application/bin"); 94 | httpdEndHeaders(connData); 95 | *pos=0x40200000; 96 | return HTTPD_CGI_MORE; 97 | } 98 | espconn_sent(connData->conn, (uint8 *)(*pos), 1024); 99 | *pos+=1024; 100 | if (*pos>=0x40200000+(512*1024)) return HTTPD_CGI_DONE; else return HTTPD_CGI_MORE; 101 | } 102 | 103 | //Template code for the DHT 22 page. 104 | void ICACHE_FLASH_ATTR tplDHT(HttpdConnData *connData, char *token, void **arg) { 105 | char buff[128]; 106 | if (token==NULL) return; 107 | 108 | struct sensor_reading* r = readDHT(0); 109 | float lastTemp=r->temperature; 110 | float lastHum=r->humidity; 111 | 112 | os_strcpy(buff, "Unknown"); 113 | if (os_strcmp(token, "temperature")==0) { 114 | os_sprintf(buff, "%d.%d", (int)(lastTemp),(int)((lastTemp - (int)lastTemp)*100) ); 115 | } 116 | if (os_strcmp(token, "humidity")==0) { 117 | os_sprintf(buff, "%d.%d", (int)(lastHum),(int)((lastHum - (int)lastHum)*100) ); 118 | } 119 | if(os_strcmp(token, "sensor_present") == 0){ 120 | os_sprintf(buff, r->success?"is":"isn't"); 121 | } 122 | r = readDS18B20(); 123 | lastTemp=r->temperature; 124 | if(os_strcmp(token, "ds_temperature") == 0){ 125 | if(r->success){ 126 | os_sprintf(buff,"%d.%d", (int)(lastTemp),(int)((lastTemp - (int)lastTemp)*100)); 127 | }else{ 128 | os_sprintf(buff, "NA"); 129 | } 130 | } 131 | 132 | espconn_sent(connData->conn, (uint8 *)buff, os_strlen(buff)); 133 | } 134 | -------------------------------------------------------------------------------- /user/cgi.h: -------------------------------------------------------------------------------- 1 | #ifndef CGI_H 2 | #define CGI_H 3 | 4 | #include "httpd.h" 5 | 6 | int cgiLed(HttpdConnData *connData); 7 | void tplLed(HttpdConnData *connData, char *token, void **arg); 8 | void tplDHT(HttpdConnData *connData, char *token, void **arg); 9 | int cgiReadFlash(HttpdConnData *connData); 10 | void tplCounter(HttpdConnData *connData, char *token, void **arg); 11 | 12 | #endif -------------------------------------------------------------------------------- /user/cgiwifi.c: -------------------------------------------------------------------------------- 1 | /* 2 | Cgi/template routines for the /wifi url. 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 17 | #include "user_interface.h" 18 | #include "mem.h" 19 | #include "httpd.h" 20 | #include "cgi.h" 21 | #include "io.h" 22 | #include "espmissingincludes.h" 23 | 24 | //WiFi access point data 25 | typedef struct { 26 | char ssid[32]; 27 | char rssi; 28 | char enc; 29 | } ApData; 30 | 31 | //Scan resolt 32 | typedef struct { 33 | char scanInProgress; 34 | ApData **apData; 35 | int noAps; 36 | } ScanResultData; 37 | 38 | //Static scan status storage. 39 | ScanResultData cgiWifiAps; 40 | 41 | //Callback the code calls when a wlan ap scan is done. Basically stores the result in 42 | //the cgiWifiAps struct. 43 | void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) { 44 | int n; 45 | struct bss_info *bss_link = (struct bss_info *)arg; 46 | os_printf("wifiScanDoneCb %d\n", status); 47 | if (status!=OK) { 48 | cgiWifiAps.scanInProgress=0; 49 | wifi_station_disconnect(); //test HACK 50 | return; 51 | } 52 | 53 | //Clear prev ap data if needed. 54 | if (cgiWifiAps.apData!=NULL) { 55 | for (n=0; nnext.stqe_next; 63 | n++; 64 | } 65 | //Allocate memory for access point data 66 | cgiWifiAps.apData=(ApData **)os_malloc(sizeof(ApData *)*n); 67 | cgiWifiAps.noAps=n; 68 | 69 | //Copy access point data to the static struct 70 | n=0; 71 | bss_link = (struct bss_info *)arg; 72 | while (bss_link != NULL) { 73 | cgiWifiAps.apData[n]=(ApData *)os_malloc(sizeof(ApData)); 74 | cgiWifiAps.apData[n]->rssi=bss_link->rssi; 75 | cgiWifiAps.apData[n]->enc=bss_link->authmode; 76 | strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32); 77 | 78 | bss_link = bss_link->next.stqe_next; 79 | n++; 80 | } 81 | os_printf("Scan done: found %d APs\n", n); 82 | //We're done. 83 | cgiWifiAps.scanInProgress=0; 84 | } 85 | 86 | static void print_wifi_status(uint8 station_status){ 87 | os_printf("Station status: "); 88 | switch(station_status){ 89 | case STATION_IDLE: 90 | os_printf("IDLE"); 91 | break; 92 | case STATION_CONNECTING: 93 | os_printf("CONNECTING"); 94 | break; 95 | case STATION_WRONG_PASSWORD: 96 | os_printf("WRONG_PASSWORD"); 97 | break; 98 | case STATION_NO_AP_FOUND: 99 | os_printf("NO_AP_FOUND"); 100 | break; 101 | case STATION_CONNECT_FAIL: 102 | os_printf("CONNECT_FAIL"); 103 | break; 104 | case STATION_GOT_IP: 105 | os_printf("GOT_IP"); 106 | break; 107 | } 108 | os_printf("\n"); 109 | } 110 | 111 | //Routine to start a WiFi access point scan. 112 | static void ICACHE_FLASH_ATTR wifiStartScan() { 113 | int x; 114 | cgiWifiAps.scanInProgress=1; 115 | x=wifi_station_get_connect_status(); 116 | 117 | os_printf("Starting AP Scan...\n"); 118 | print_wifi_status(x); 119 | if (x!=STATION_GOT_IP) { 120 | //Unit probably is trying to connect to a bogus AP. This messes up scanning. Stop that. 121 | os_printf("STA status = %d. Disconnecting STA...\n", x); 122 | wifi_station_disconnect(); 123 | } 124 | wifi_station_scan(NULL, wifiScanDoneCb); 125 | } 126 | 127 | //This CGI is called from the bit of AJAX-code in wifi.tpl. It will initiate a 128 | //scan for access points and if available will return the result of an earlier scan. 129 | //The result is embedded in a bit of JSON parsed by the javascript in wifi.tpl. 130 | int ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) { 131 | int len; 132 | int i; 133 | char buff[1024]; 134 | httpdStartResponse(connData, 200); 135 | httpdHeader(connData, "Content-Type", "text/json"); 136 | httpdEndHeaders(connData); 137 | 138 | if (cgiWifiAps.scanInProgress==1) { 139 | len=os_sprintf(buff, "{\n \"result\": { \n\"inProgress\": \"1\"\n }\n}\n"); 140 | espconn_sent(connData->conn, (uint8 *)buff, len); 141 | } else { 142 | len=os_sprintf(buff, "{\n \"result\": { \n\"inProgress\": \"0\", \n\"APs\": [\n"); 143 | espconn_sent(connData->conn, (uint8 *)buff, len); 144 | if (cgiWifiAps.apData==NULL) cgiWifiAps.noAps=0; 145 | for (i=0; issid, cgiWifiAps.apData[i]->rssi, 148 | cgiWifiAps.apData[i]->enc, (i==cgiWifiAps.noAps-1)?"":","); 149 | espconn_sent(connData->conn, (uint8 *)buff, len); 150 | } 151 | len=os_sprintf(buff, "]\n}\n}\n"); 152 | espconn_sent(connData->conn, (uint8 *)buff, len); 153 | wifiStartScan(); 154 | } 155 | return HTTPD_CGI_DONE; 156 | } 157 | 158 | //Temp store for new ap info. 159 | static struct station_config stconf; 160 | 161 | //This routine is ran some time after a connection attempt to an access point. If 162 | //the connect succeeds, this gets the module in STA-only mode. 163 | static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) { 164 | int x=wifi_station_get_connect_status(); 165 | os_printf("WiFi State Update:\n"); 166 | print_wifi_status(x); 167 | if (x==STATION_GOT_IP) { 168 | //Go to STA mode. This needs a reset, so do that. 169 | wifi_set_opmode(1); 170 | system_restart(); 171 | } else { 172 | os_printf("Connect fail. Not going into STA-only mode.\n"); 173 | } 174 | } 175 | 176 | //Actually connect to a station. This routine is timed because I had problems 177 | //with immediate connections earlier. It probably was something else that caused it, 178 | //but I can't be arsed to put the code back :P 179 | static void ICACHE_FLASH_ATTR reassTimerCb(void *arg) { 180 | int x; 181 | static ETSTimer resetTimer; 182 | wifi_station_disconnect(); 183 | wifi_station_set_config(&stconf); 184 | os_printf("Connecting to %s with password %s", stconf.ssid, stconf.password); 185 | wifi_station_connect(); 186 | x=wifi_get_opmode(); 187 | if (x!=1) { 188 | //Schedule disconnect/connect 189 | os_timer_disarm(&resetTimer); 190 | os_timer_setfn(&resetTimer, resetTimerCb, NULL); 191 | os_timer_arm(&resetTimer, 4000, 0); 192 | } 193 | } 194 | 195 | 196 | //This cgi uses the routines above to connect to a specific access point with the 197 | //given ESSID using the given password. 198 | int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) { 199 | char essid[128]; 200 | char passwd[128]; 201 | static ETSTimer reassTimer; 202 | 203 | if (connData->conn==NULL) { 204 | //Connection aborted. Clean up. 205 | return HTTPD_CGI_DONE; 206 | } 207 | 208 | httpdFindArg(connData->postBuff, "essid", essid, sizeof(essid)); 209 | httpdFindArg(connData->postBuff, "passwd", passwd, sizeof(passwd)); 210 | 211 | os_strncpy((char*)stconf.ssid, essid, 32); 212 | os_strncpy((char*)stconf.password, passwd, 64); 213 | 214 | os_printf("Will connect to %s using password %s", essid, passwd); 215 | 216 | //Schedule disconnect/connect 217 | os_timer_disarm(&reassTimer); 218 | os_timer_setfn(&reassTimer, reassTimerCb, NULL); 219 | #if 1 220 | os_timer_arm(&reassTimer, 1000, 0); 221 | 222 | httpdRedirect(connData, "connecting.html"); 223 | #else 224 | httpdRedirect(connData, "/wifi"); 225 | #endif 226 | return HTTPD_CGI_DONE; 227 | } 228 | 229 | //Template code for the WLAN page. 230 | void ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg) { 231 | char buff[1024]; 232 | int x; 233 | static struct station_config stconf; 234 | if (token==NULL) return; 235 | wifi_station_get_config(&stconf); 236 | 237 | os_strcpy(buff, "Unknown"); 238 | if (os_strcmp(token, "WiFiMode")==0) { 239 | x=wifi_get_opmode(); 240 | if (x==1) os_strcpy(buff, "Client"); 241 | if (x==2) os_strcpy(buff, "SoftAP"); 242 | if (x==3) os_strcpy(buff, "STA+AP"); 243 | } else if (os_strcmp(token, "currSsid")==0) { 244 | os_strcpy(buff, (char*)stconf.ssid); 245 | } else if (os_strcmp(token, "WiFiPasswd")==0) { 246 | os_strcpy(buff, (char*)stconf.password); 247 | } 248 | espconn_sent(connData->conn, (uint8 *)buff, os_strlen(buff)); 249 | } 250 | 251 | 252 | -------------------------------------------------------------------------------- /user/cgiwifi.h: -------------------------------------------------------------------------------- 1 | #ifndef CGIWIFI_H 2 | #define CGIWIFI_H 3 | 4 | #include "httpd.h" 5 | 6 | int cgiWiFiScan(HttpdConnData *connData); 7 | void tplWlan(HttpdConnData *connData, char *token, void **arg); 8 | int cgiWiFi(HttpdConnData *connData); 9 | int cgiWiFiConnect(HttpdConnData *connData); 10 | 11 | #endif -------------------------------------------------------------------------------- /user/dht.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * ---------------------------------------------------------------------------- 4 | * "THE BEER-WARE LICENSE" (Revision 42): 5 | * Jeroen Domburg wrote this file. As long as you 6 | * retain 7 | * this notice you can do whatever you want with this stuff. If we meet some 8 | * day, 9 | * and you think this stuff is worth it, you can buy me a beer in return. 10 | * ---------------------------------------------------------------------------- 11 | */ 12 | 13 | #include "ets_sys.h" 14 | #include "osapi.h" 15 | #include "espmissingincludes.h" 16 | #include "c_types.h" 17 | #include "user_interface.h" 18 | #include "espconn.h" 19 | #include "mem.h" 20 | #include "gpio.h" 21 | #include "dht.h" 22 | 23 | #define MAXTIMINGS 10000 24 | #define DHT_MAXCOUNT 32000 25 | #define BREAKTIME 32 26 | 27 | #define DHT_PIN 2 28 | 29 | enum sensor_type SENSOR; 30 | 31 | static inline float scale_humidity(int *data) { 32 | if (SENSOR == SENSOR_DHT11) { 33 | return data[0]; 34 | } else { 35 | float humidity = data[0] * 256 + data[1]; 36 | return humidity /= 10; 37 | } 38 | } 39 | 40 | static inline float scale_temperature(int *data) { 41 | if (SENSOR == SENSOR_DHT11) { 42 | return data[2]; 43 | } else { 44 | float temperature = data[2] & 0x7f; 45 | temperature *= 256; 46 | temperature += data[3]; 47 | temperature /= 10; 48 | if (data[2] & 0x80) 49 | temperature *= -1; 50 | return temperature; 51 | } 52 | } 53 | 54 | static inline void delay_ms(int sleep) { 55 | os_delay_us(1000 * sleep); 56 | } 57 | 58 | static struct sensor_reading reading = { 59 | .source = "DHT11", .success = 0 60 | }; 61 | 62 | 63 | 64 | 65 | /** 66 | Originally from: http://harizanov.com/2014/11/esp8266-powered-web-server-led-control-dht22-temperaturehumidity-sensor-reading/ 67 | Adapted from: https://github.com/adafruit/Adafruit_Python_DHT/blob/master/source/Raspberry_Pi/pi_dht_read.c 68 | LICENSE: 69 | // Copyright (c) 2014 Adafruit Industries 70 | // Author: Tony DiCola 71 | 72 | // Permission is hereby granted, free of charge, to any person obtaining a copy 73 | // of this software and associated documentation files (the "Software"), to deal 74 | // in the Software without restriction, including without limitation the rights 75 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 76 | // copies of the Software, and to permit persons to whom the Software is 77 | // furnished to do so, subject to the following conditions: 78 | 79 | // The above copyright notice and this permission notice shall be included in all 80 | // copies or substantial portions of the Software. 81 | 82 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 83 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 84 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 85 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 86 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 87 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 88 | // SOFTWARE. 89 | */ 90 | static void ICACHE_FLASH_ATTR pollDHTCb(void * arg){ 91 | int counter = 0; 92 | int laststate = 1; 93 | int i = 0; 94 | int bits_in = 0; 95 | // int bitidx = 0; 96 | // int bits[250]; 97 | 98 | int data[100]; 99 | 100 | data[0] = data[1] = data[2] = data[3] = data[4] = 0; 101 | 102 | //disable interrupts, start of critical section 103 | os_intr_lock(); 104 | wdt_feed(); 105 | // Wake up device, 250ms of high 106 | GPIO_OUTPUT_SET(DHT_PIN, 1); 107 | delay_ms(20); 108 | 109 | // Hold low for 20ms 110 | GPIO_OUTPUT_SET(DHT_PIN, 0); 111 | delay_ms(20); 112 | 113 | // High for 40ms 114 | // GPIO_OUTPUT_SET(2, 1); 115 | 116 | GPIO_DIS_OUTPUT(DHT_PIN); 117 | os_delay_us(40); 118 | 119 | // Set pin to input with pullup 120 | // PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO2_U); 121 | 122 | // os_printf("Waiting for gpio2 to drop \n"); 123 | 124 | // wait for pin to drop? 125 | while (GPIO_INPUT_GET(DHT_PIN) == 1 && i < DHT_MAXCOUNT) { 126 | if (i >= DHT_MAXCOUNT) { 127 | goto fail; 128 | } 129 | i++; 130 | } 131 | 132 | // os_printf("Reading DHT\n"); 133 | 134 | // read data! 135 | for (i = 0; i < MAXTIMINGS; i++) { 136 | // Count high time (in approx us) 137 | counter = 0; 138 | while (GPIO_INPUT_GET(DHT_PIN) == laststate) { 139 | counter++; 140 | os_delay_us(1); 141 | if (counter == 1000) 142 | break; 143 | } 144 | laststate = GPIO_INPUT_GET(DHT_PIN); 145 | 146 | if (counter == 1000) 147 | break; 148 | 149 | // store data after 3 reads 150 | if ((i > 3) && (i % 2 == 0)) { 151 | // shove each bit into the storage bytes 152 | data[bits_in / 8] <<= 1; 153 | if (counter > BREAKTIME) { 154 | //os_printf("1"); 155 | data[bits_in / 8] |= 1; 156 | } else { 157 | //os_printf("0"); 158 | } 159 | bits_in++; 160 | } 161 | } 162 | 163 | //Re-enable interrupts, end of critical section 164 | os_intr_unlock(); 165 | 166 | if (bits_in < 40) { 167 | os_printf("Got too few bits: %d should be at least 40", bits_in); 168 | goto fail; 169 | } 170 | 171 | 172 | int checksum = (data[0] + data[1] + data[2] + data[3]) & 0xFF; 173 | 174 | os_printf("DHT: %02x %02x %02x %02x [%02x] CS: %02x", data[0], data[1],data[2],data[3],data[4],checksum); 175 | 176 | if (data[4] != checksum) { 177 | os_printf("Checksum was incorrect after %d bits. Expected %d but got %d", 178 | bits_in, data[4], checksum); 179 | goto fail; 180 | } 181 | 182 | reading.temperature = scale_temperature(data); 183 | reading.humidity = scale_humidity(data); 184 | os_printf("Temp = %d *C, Hum = %d %%\n", (int)(reading.temperature * 100), 185 | (int)(reading.humidity * 100)); 186 | 187 | reading.success = 1; 188 | return; 189 | fail: 190 | 191 | os_printf("Failed to get reading, dying\n"); 192 | reading.success = 0; 193 | } 194 | 195 | struct sensor_reading *ICACHE_FLASH_ATTR readDHT(int force) { 196 | if(force){ 197 | pollDHTCb(NULL); 198 | } 199 | return &reading; 200 | } 201 | 202 | void DHTInit(enum sensor_type sensor_type, uint32_t polltime) { 203 | SENSOR = sensor_type; 204 | // Set GPIO2 to output mode for DHT22 205 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2); 206 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0); 207 | //PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO2_U); 208 | 209 | os_printf("DHT Setup for type %d, poll interval of %d\n", sensor_type, (int)polltime); 210 | 211 | static ETSTimer dhtTimer; 212 | os_timer_setfn(&dhtTimer, pollDHTCb, NULL); 213 | os_timer_arm(&dhtTimer, polltime, 1); 214 | } 215 | -------------------------------------------------------------------------------- /user/dht.h: -------------------------------------------------------------------------------- 1 | 2 | enum sensor_type{ 3 | SENSOR_DHT11,SENSOR_DHT22 4 | }; 5 | 6 | struct sensor_reading{ 7 | float temperature; 8 | float humidity; 9 | const char* source; 10 | uint8_t sensor_id[16]; 11 | BOOL success; 12 | }; 13 | 14 | 15 | void ICACHE_FLASH_ATTR DHT(void); 16 | struct sensor_reading * ICACHE_FLASH_ATTR readDHT(int force); 17 | void DHTInit(enum sensor_type, uint32_t polltime); 18 | -------------------------------------------------------------------------------- /user/ds18b20.c: -------------------------------------------------------------------------------- 1 | /* 2 | StellarisDS18B20.h - Yes I know it's a long name. 3 | 4 | This library works with both 430 and Stellaris using the Energia IDE. 5 | 6 | Not sure who the original author is, as I've seen the core code used in a few different One Wire lib's. 7 | Am sure it came from Arduino folks. 8 | There are no processor hardware dependant calls. 9 | Had troubles with delayMicrosecond() exiting early when the library used by Stellaris. 10 | So replaced them with a micros() while loop. The timing loop was tuned to suit the Stellaris 11 | It was disconcerting that a difference of 1 micro second would make the routines intermittingly fail. 12 | Anyway after nearly two weeks think I have solved all the delay problems. 13 | 14 | History: 15 | 29/Jan/13 - Finished porting MSP430 to Stellaris 16 | 17 | Grant.forest@live.com.au 18 | 19 | ----------------------------------------------------------- 20 | 21 | This library is free software; you can redistribute it and/or 22 | modify it under the terms of the GNU Lesser General Public 23 | License as published by the Free Software Foundation; either 24 | version 2.1 of the License, or (at your option) any later version. 25 | 26 | This library is distributed in the hope that it will be useful, 27 | but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 29 | Lesser General Public License for more details. 30 | 31 | You should have received a copy of the GNU Lesser General Public 32 | License along with this library; if not, write to the Free Software 33 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 34 | */ 35 | #include "ets_sys.h" 36 | #include "osapi.h" 37 | #include "espmissingincludes.h" 38 | #include "c_types.h" 39 | #include "user_interface.h" 40 | #include "espconn.h" 41 | #include "mem.h" 42 | #include "gpio.h" 43 | #include "dht.h" 44 | 45 | #include "ds18b20.h" 46 | 47 | // list of commands DS18B20: 48 | 49 | #define DS1820_WRITE_SCRATCHPAD 0x4E 50 | #define DS1820_READ_SCRATCHPAD 0xBE 51 | #define DS1820_COPY_SCRATCHPAD 0x48 52 | #define DS1820_READ_EEPROM 0xB8 53 | #define DS1820_READ_PWRSUPPLY 0xB4 54 | #define DS1820_SEARCHROM 0xF0 55 | #define DS1820_SKIP_ROM 0xCC 56 | #define DS1820_READROM 0x33 57 | #define DS1820_MATCHROM 0x55 58 | #define DS1820_ALARMSEARCH 0xEC 59 | #define DS1820_CONVERT_T 0x44 60 | 61 | #define DS1820_PIN 0 62 | 63 | static struct sensor_reading reading = { 64 | .source = "DS18B20", .humidity=-1, .success = 0 65 | }; 66 | 67 | 68 | /*** 69 | * CRC Table and code from Maxim AN 162: 70 | * http://www.maximintegrated.com/en/app-notes/index.mvp/id/162 71 | */ 72 | unsigned const char dscrc_table[] = { 73 | 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65, 74 | 157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220, 75 | 35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98, 76 | 190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255, 77 | 70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7, 78 | 219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154, 79 | 101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36, 80 | 248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185, 81 | 140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205, 82 | 17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80, 83 | 175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238, 84 | 50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115, 85 | 202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139, 86 | 87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22, 87 | 233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168, 88 | 116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53}; 89 | 90 | unsigned char dowcrc = 0; 91 | 92 | unsigned char ow_crc( unsigned char x){ 93 | dowcrc = dscrc_table[dowcrc^x]; 94 | return dowcrc; 95 | } 96 | 97 | //End of Maxim AN162 code 98 | 99 | unsigned char ROM_NO[8]; 100 | uint8_t LastDiscrepancy; 101 | uint8_t LastFamilyDiscrepancy; 102 | uint8_t LastDeviceFlag; 103 | 104 | 105 | void setup_DS1820(void){ 106 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0); 107 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2); 108 | 109 | readDS18B20(); 110 | 111 | } 112 | 113 | void write_bit(int bit) 114 | { 115 | os_delay_us(1); 116 | //Let bus get pulled high 117 | GPIO_DIS_OUTPUT(DS1820_PIN); 118 | 119 | 120 | if (bit) { 121 | //5us low pulse 122 | GPIO_OUTPUT_SET(DS1820_PIN,0); 123 | os_delay_us(5); 124 | //let bus go high again (device should read about 30us later) 125 | GPIO_DIS_OUTPUT(DS1820_PIN); // was OW_RLS // input 126 | os_delay_us(55); 127 | }else{ 128 | //55us low 129 | GPIO_OUTPUT_SET(DS1820_PIN,0); 130 | os_delay_us(55); 131 | //5us high 132 | GPIO_DIS_OUTPUT(DS1820_PIN); // was OW_RLS // input 133 | os_delay_us(5); 134 | } 135 | } 136 | 137 | //##################################################################### 138 | 139 | int read_bit() 140 | { 141 | int bit=0; 142 | os_delay_us(1); 143 | //Pull bus low for 15us 144 | GPIO_OUTPUT_SET(DS1820_PIN,0); 145 | os_delay_us(15); 146 | 147 | //Allow device to control bu 148 | GPIO_DIS_OUTPUT(DS1820_PIN); 149 | os_delay_us(5); 150 | 151 | //Read value 152 | if (GPIO_INPUT_GET(DS1820_PIN)){ //was if (OW_IN) 153 | bit = 1; 154 | } 155 | //Wait for end of bit 156 | os_delay_us(40); 157 | return bit; 158 | } 159 | 160 | //##################################################################### 161 | 162 | void write_byte(uint8_t byte) 163 | { 164 | int i; 165 | for(i = 0; i < 8; i++) 166 | { 167 | write_bit(byte & 1); 168 | byte >>= 1; 169 | } 170 | } 171 | 172 | //##################################################################### 173 | 174 | uint8_t read_byte() 175 | { 176 | unsigned int i; 177 | uint8_t byte = 0; 178 | for(i = 0; i < 8; i++) 179 | { 180 | byte >>= 1; 181 | if (read_bit()) byte |= 0x80; 182 | } 183 | return byte; 184 | } 185 | 186 | int reset(void) 187 | { 188 | //Hold the bus low for 500us 189 | GPIO_OUTPUT_SET(DS1820_PIN,0); //was OW_LO 190 | os_delay_us(500); 191 | 192 | //Give up control of the bus and wait for a device to reply 193 | GPIO_DIS_OUTPUT(DS1820_PIN); //set to input 194 | os_delay_us(80); 195 | 196 | //The bus should be low if the device is present: 197 | if (GPIO_INPUT_GET(DS1820_PIN)){ 198 | //No device 199 | return 1; 200 | } 201 | 202 | //The device should have stopped pulling the bus now: 203 | os_delay_us(300); 204 | if (! GPIO_INPUT_GET(DS1820_PIN)) { 205 | //Something's wrong, the bus should be high 206 | return 2; 207 | } 208 | 209 | //All good. 210 | return 0; 211 | } 212 | 213 | struct sensor_reading* readDS18B20(void) 214 | { 215 | 216 | reset(); 217 | write_byte(DS1820_SKIP_ROM); // skip ROM command 218 | write_byte(DS1820_CONVERT_T); // convert T command 219 | 220 | os_delay_us(750); 221 | reset(); 222 | write_byte(DS1820_SKIP_ROM); // skip ROM command 223 | write_byte(DS1820_READ_SCRATCHPAD); // read scratchpad command 224 | 225 | uint8_t get[10]; 226 | for (int k=0;k<9;k++){ 227 | get[k]=read_byte(); 228 | } 229 | //os_printf("\n ScratchPAD DATA = %X %X %X %X %X %X %X %X %X\n",get[8],get[7],get[6],get[5],get[4],get[3],get[2],get[1],get[0]); 230 | 231 | dowcrc = 0; 232 | for(int i = 0; i < 8; i++){ 233 | ow_crc(get[i]); 234 | } 235 | 236 | if(get[8] != dowcrc){ 237 | os_printf("CRC check failed: %02X %02X", get[8], dowcrc); 238 | reading.success = 0; 239 | return &reading; 240 | } 241 | uint8_t temp_msb = get[1]; // Sign byte + lsbit 242 | uint8_t temp_lsb = get[0]; // Temp data plus lsb 243 | 244 | 245 | 246 | uint16_t temp = temp_msb << 8 | temp_lsb; 247 | 248 | reading.success = 1; 249 | reading.temperature = (temp * 625.0)/10000; 250 | 251 | os_printf("Got a DS18B20 Reading: %d.%d" , 252 | (int)reading.temperature, 253 | (int)(reading.temperature - (int)reading.temperature) * 100 254 | ); 255 | 256 | return &reading; 257 | 258 | } 259 | 260 | 261 | 262 | void reset_search() 263 | { 264 | // reset the search state 265 | LastDiscrepancy = 0; 266 | LastDeviceFlag = FALSE; 267 | LastFamilyDiscrepancy = 0; 268 | for(int i = 7; ; i--) 269 | { 270 | ROM_NO[i] = 0; 271 | if ( i == 0) break; 272 | } 273 | } 274 | 275 | // 276 | // Perform a search. If this function returns a '1' then it has 277 | // enumerated the next device and you may retrieve the ROM from the 278 | // OneWire::address variable. If there are no devices, no further 279 | // devices, or something horrible happens in the middle of the 280 | // enumeration then a 0 is returned. If a new device is found then 281 | // its address is copied to newAddr. Use OneWire::reset_search() to 282 | // start over. 283 | // 284 | // --- Replaced by the one from the Dallas Semiconductor web site --- 285 | //-------------------------------------------------------------------------- 286 | // Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing 287 | // search state. 288 | // Return TRUE : device found, ROM number in ROM_NO buffer 289 | // FALSE : device not found, end of search 290 | // 291 | uint8_t search(uint8_t *newAddr) 292 | { 293 | uint8_t id_bit_number; 294 | uint8_t last_zero, rom_byte_number, search_result; 295 | uint8_t id_bit, cmp_id_bit; 296 | uint8_t ii=0; 297 | unsigned char ROM_NO[8]; 298 | unsigned char rom_byte_mask, search_direction; 299 | 300 | // initialize for search 301 | id_bit_number = 1; 302 | last_zero = 0; 303 | rom_byte_number = 0; 304 | rom_byte_mask = 1; 305 | search_result = 0; 306 | 307 | // if the last call was not the last one 308 | if (!LastDeviceFlag) 309 | { 310 | // 1-Wire reset 311 | ii=reset(); 312 | if (ii) // ii>0 313 | { 314 | // reset the search 315 | LastDiscrepancy = 0; 316 | LastDeviceFlag = FALSE; 317 | LastFamilyDiscrepancy = 0; 318 | return ii; // Pass back the reset error status gf*** 319 | } 320 | // issue the search command 321 | 322 | write_byte(DS1820_SEARCHROM); 323 | 324 | // loop to do the search 325 | do 326 | { 327 | // read a bit and its complement 328 | id_bit = read_bit(); 329 | cmp_id_bit = read_bit(); 330 | 331 | // check for no devices on 1-wire 332 | if ((id_bit == 1) && (cmp_id_bit == 1)) 333 | break; 334 | else 335 | { 336 | // all devices coupled have 0 or 1 337 | if (id_bit != cmp_id_bit) 338 | search_direction = id_bit; // bit write value for search 339 | else 340 | { 341 | // if this discrepancy if before the Last Discrepancy 342 | // on a previous next then pick the same as last time 343 | if (id_bit_number < LastDiscrepancy) 344 | search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0); 345 | else 346 | // if equal to last pick 1, if not then pick 0 347 | search_direction = (id_bit_number == LastDiscrepancy); 348 | 349 | // if 0 was picked then record its position in LastZero 350 | if (search_direction == 0) 351 | { 352 | last_zero = id_bit_number; 353 | 354 | // check for Last discrepancy in family 355 | if (last_zero < 9) 356 | LastFamilyDiscrepancy = last_zero; 357 | } 358 | } 359 | 360 | // set or clear the bit in the ROM byte rom_byte_number 361 | // with mask rom_byte_mask 362 | if (search_direction == 1) 363 | ROM_NO[rom_byte_number] |= rom_byte_mask; 364 | else 365 | ROM_NO[rom_byte_number] &= ~rom_byte_mask; 366 | 367 | // serial number search direction write bit 368 | write_bit(search_direction); 369 | 370 | // increment the byte counter id_bit_number 371 | // and shift the mask rom_byte_mask 372 | id_bit_number++; 373 | rom_byte_mask <<= 1; 374 | 375 | // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask 376 | if (rom_byte_mask == 0) 377 | { 378 | rom_byte_number++; 379 | rom_byte_mask = 1; 380 | } 381 | } 382 | } 383 | while(rom_byte_number < 8); // loop until through all ROM bytes 0-7 384 | 385 | // if the search was successful then 386 | if (!(id_bit_number < 65)) 387 | { 388 | // search successful so set LastDiscrepancy,LastDeviceFlag,search_result 389 | LastDiscrepancy = last_zero; 390 | 391 | // check for last device 392 | if (LastDiscrepancy == 0) 393 | LastDeviceFlag = TRUE; 394 | 395 | search_result = TRUE; // All OK status GF*** 396 | } 397 | } 398 | 399 | // if no device found then reset counters so next 'search' will be like a first 400 | //if (search_result || !ROM_NO[0]) 401 | //if (!ROM_NO[0]) 402 | if (!search_result || !ROM_NO[0]) 403 | { 404 | LastDiscrepancy = 0; 405 | LastDeviceFlag = FALSE; 406 | LastFamilyDiscrepancy = 0; 407 | search_result = FALSE; 408 | } 409 | for (int i = 0; i < 8; i++) newAddr[i] = ROM_NO[i]; 410 | return search_result; 411 | } 412 | 413 | -------------------------------------------------------------------------------- /user/ds18b20.h: -------------------------------------------------------------------------------- 1 | /* 2 | StellarisDS18B20.h - Yes I know it's a long name. 3 | 4 | This library works with both 430 and Stellaris using the Energia IDE. 5 | 6 | Not sure who the original author is, as I've seen the core code used in a few different One Wire lib's. 7 | Am sure it came from Arduino folks. 8 | There are no processor hardware dependant calls. 9 | Had troubles with delayMicrosecond() exiting early when the library used by Stellaris. 10 | So replaced them with a micros() while loop. The timing loop was tuned to suit the Stellaris 11 | It was disconcerting that a difference of 1 micro second would make the routines intermittingly fail. 12 | Anyway after nearly two weeks think I have solved all the delay problems. 13 | 14 | History: 15 | 29/Jan/13 - Finished porting MSP430 to Stellaris 16 | 17 | Grant.forest@live.com.au 18 | 19 | ----------------------------------------------------------- 20 | 21 | This library is free software; you can redistribute it and/or 22 | modify it under the terms of the GNU Lesser General Public 23 | License as published by the Free Software Foundation; either 24 | version 2.1 of the License, or (at your option) any later version. 25 | 26 | This library is distributed in the hope that it will be useful, 27 | but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 29 | Lesser General Public License for more details. 30 | 31 | You should have received a copy of the GNU Lesser General Public 32 | License along with this library; if not, write to the Free Software 33 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 34 | */ 35 | 36 | struct sensor_reading* readDS18B20(void); -------------------------------------------------------------------------------- /user/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 "c_types.h" 24 | #include "user_interface.h" 25 | #include "espconn.h" 26 | #include "mem.h" 27 | #include "osapi.h" 28 | #include "espmissingincludes.h" 29 | #else 30 | //Test build 31 | #include 32 | #include 33 | #include 34 | #include 35 | #define os_malloc malloc 36 | #define os_free free 37 | #define os_memcpy memcpy 38 | #define os_strncmp strncmp 39 | #define os_strcmp strcmp 40 | #define os_strcpy strcpy 41 | #define os_printf printf 42 | #define ICACHE_FLASH_ATTR 43 | extern char* espFsData; 44 | #endif 45 | 46 | #include "../mkespfsimage/espfsformat.h" 47 | #include "espfs.h" 48 | #include "httpdconfig.h" 49 | #ifdef EFS_HEATSHRINK 50 | #include "heatshrink_decoder.h" 51 | #endif 52 | 53 | 54 | 55 | 56 | struct EspFsFile { 57 | EspFsHeader *header; 58 | char decompressor; 59 | int32_t posDecomp; 60 | char *posStart; 61 | char *posComp; 62 | void *decompData; 63 | }; 64 | 65 | /* 66 | Available locations, at least in my flash, with boundaries partially guessed. This 67 | is using 0.9.1/0.9.2 SDK on a not-too-new module. 68 | 0x00000 (0x10000): Code/data (RAM data?) 69 | 0x10000 (0x02000): Gets erased by something? 70 | 0x12000 (0x2E000): Free (filled with zeroes) (parts used by ESPCloud and maybe SSL) 71 | 0x40000 (0x20000): Code/data (ROM data?) 72 | 0x60000 (0x1C000): Free 73 | 0x7c000 (0x04000): Param store 74 | 0x80000 - end of flash 75 | 76 | Accessing the flash through the mem emulation at 0x40200000 is a bit hairy: All accesses 77 | *must* be aligned 32-bit accesses. Reading a short, byte or unaligned word will result in 78 | a memory exception, crashing the program. 79 | */ 80 | 81 | 82 | //Copies len bytes over from dst to src, but does it using *only* 83 | //aligned 32-bit reads. Yes, it's no too optimized but it's short and sweet and it works. 84 | void ICACHE_FLASH_ATTR memcpyAligned(char *dst, char *src, int len) { 85 | int x; 86 | int w, b; 87 | for (x=0; x>0); 91 | if (b==1) *dst=(w>>8); 92 | if (b==2) *dst=(w>>16); 93 | if (b==3) *dst=(w>>24); 94 | dst++; src++; 95 | } 96 | } 97 | 98 | 99 | //Open a file and return a pointer to the file desc struct. 100 | EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) { 101 | #ifdef __ets__ 102 | char *p=(char *)(ESPFS_POS+0x40200000); 103 | #else 104 | char *p=espFsData; 105 | #endif 106 | char *hpos; 107 | char namebuf[256]; 108 | EspFsHeader h; 109 | EspFsFile *r; 110 | //Strip initial slashes 111 | while(fileName[0]=='/') fileName++; 112 | //Go find that file! 113 | while(1) { 114 | hpos=p; 115 | //Grab the next file header. 116 | os_memcpy(&h, p, sizeof(EspFsHeader)); 117 | if (h.magic!=0x73665345) { 118 | os_printf("Magic mismatch. EspFS image broken.\n"); 119 | return NULL; 120 | } 121 | if (h.flags&FLAG_LASTFILE) { 122 | os_printf("End of image.\n"); 123 | return NULL; 124 | } 125 | //Grab the name of the file. 126 | p+=sizeof(EspFsHeader); 127 | os_memcpy(namebuf, p, sizeof(namebuf)); 128 | // os_printf("Found file '%s'. Namelen=%x fileLenComp=%x, compr=%d flags=%d\n", 129 | // namebuf, (unsigned int)h.nameLen, (unsigned int)h.fileLenComp, h.compression, h.flags); 130 | if (os_strcmp(namebuf, fileName)==0) { 131 | //Yay, this is the file we need! 132 | p+=h.nameLen; //Skip to content. 133 | r=(EspFsFile *)os_malloc(sizeof(EspFsFile)); //Alloc file desc mem 134 | if (r==NULL) return NULL; 135 | r->header=(EspFsHeader *)hpos; 136 | r->decompressor=h.compression; 137 | r->posComp=p; 138 | r->posStart=p; 139 | r->posDecomp=0; 140 | if (h.compression==COMPRESS_NONE) { 141 | r->decompData=NULL; 142 | #ifdef EFS_HEATSHRINK 143 | } else if (h.compression==COMPRESS_HEATSHRINK) { 144 | //File is compressed with Heatshrink. 145 | char parm; 146 | heatshrink_decoder *dec; 147 | //Decoder params are stored in 1st byte. 148 | memcpyAligned(&parm, r->posComp, 1); 149 | r->posComp++; 150 | os_printf("Heatshrink compressed file; decode parms = %x\n", parm); 151 | dec=heatshrink_decoder_alloc(16, (parm>>4)&0xf, parm&0xf); 152 | r->decompData=dec; 153 | #endif 154 | } else { 155 | os_printf("Invalid compression: %d\n", h.compression); 156 | return NULL; 157 | } 158 | return r; 159 | } 160 | //We don't need this file. Skip name and file 161 | p+=h.nameLen+h.fileLenComp; 162 | if ((int)p&3) p+=4-((int)p&3); //align to next 32bit val 163 | } 164 | } 165 | 166 | //Read len bytes from the given file into buff. Returns the actual amount of bytes read. 167 | int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) { 168 | int flen; 169 | if (fh==NULL) return 0; 170 | //Cache file length. 171 | memcpyAligned((char*)&flen, (char*)&fh->header->fileLenComp, 4); 172 | //Do stuff depending on the way the file is compressed. 173 | if (fh->decompressor==COMPRESS_NONE) { 174 | int toRead; 175 | toRead=flen-(fh->posComp-fh->posStart); 176 | if (len>toRead) len=toRead; 177 | // os_printf("Reading %d bytes from %x\n", len, (unsigned int)fh->posComp); 178 | memcpyAligned(buff, fh->posComp, len); 179 | fh->posDecomp+=len; 180 | fh->posComp+=len; 181 | // os_printf("Done reading %d bytes, pos=%x\n", len, fh->posComp); 182 | return len; 183 | #ifdef EFS_HEATSHRINK 184 | } else if (fh->decompressor==COMPRESS_HEATSHRINK) { 185 | int decoded=0; 186 | unsigned int elen, rlen; 187 | char ebuff[16]; 188 | heatshrink_decoder *dec=(heatshrink_decoder *)fh->decompData; 189 | while(decodedposComp - fh->posStart); 193 | if (elen==0) return decoded; //file is read 194 | if (elen>0) { 195 | memcpyAligned(ebuff, fh->posComp, 16); 196 | heatshrink_decoder_sink(dec, (uint8_t *)ebuff, (elen>16)?16:elen, &rlen); 197 | fh->posComp+=rlen; 198 | if (rlen==elen) { 199 | heatshrink_decoder_finish(dec); 200 | } 201 | } 202 | //Grab decompressed data and put into buff 203 | heatshrink_decoder_poll(dec, (uint8_t *)buff, len-decoded, &rlen); 204 | fh->posDecomp+=rlen; 205 | buff+=rlen; 206 | decoded+=rlen; 207 | } 208 | return len; 209 | #endif 210 | } 211 | return 0; 212 | } 213 | 214 | //Close the file. 215 | void ICACHE_FLASH_ATTR espFsClose(EspFsFile *fh) { 216 | if (fh==NULL) return; 217 | #ifdef EFS_HEATSHRINK 218 | if (fh->decompressor==COMPRESS_HEATSHRINK) { 219 | heatshrink_decoder_free((heatshrink_decoder*)fh->decompData); 220 | } 221 | #endif 222 | os_free(fh); 223 | } 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /user/espfs.h: -------------------------------------------------------------------------------- 1 | #ifndef ESPFS_H 2 | #define ESPFS_H 3 | 4 | 5 | 6 | typedef struct EspFsFile EspFsFile; 7 | 8 | EspFsFile *espFsOpen(char *fileName); 9 | int espFsRead(EspFsFile *fh, char *buff, int len); 10 | void espFsClose(EspFsFile *fh); 11 | 12 | 13 | #endif -------------------------------------------------------------------------------- /user/heatshrink_config_httpd.h: -------------------------------------------------------------------------------- 1 | //Heatshrink config for the decompressor. 2 | #ifndef HEATSHRINK_CONFIG_H 3 | #define HEATSHRINK_CONFIG_H 4 | 5 | /* Should functionality assuming dynamic allocation be used? */ 6 | #define HEATSHRINK_DYNAMIC_ALLOC 1 7 | 8 | #if HEATSHRINK_DYNAMIC_ALLOC 9 | /* Optional replacement of malloc/free */ 10 | #define HEATSHRINK_MALLOC(SZ) os_malloc(SZ) 11 | #define HEATSHRINK_FREE(P, SZ) os_free(P) 12 | #else 13 | /* Required parameters for static configuration */ 14 | #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 32 15 | #define HEATSHRINK_STATIC_WINDOW_BITS 8 16 | #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4 17 | #endif 18 | 19 | /* Turn on logging for debugging. */ 20 | #define HEATSHRINK_DEBUGGING_LOGS 0 21 | 22 | /* Use indexing for faster compression. (This requires additional space.) */ 23 | #define HEATSHRINK_USE_INDEX 1 24 | 25 | #endif 26 | 27 | -------------------------------------------------------------------------------- /user/heatshrink_decoder.c: -------------------------------------------------------------------------------- 1 | #include "httpdconfig.h" 2 | #ifdef EFS_HEATSHRINK 3 | //Stupid wrapper so we don't have to move c-files around 4 | //Also loads httpd-specific config. 5 | 6 | #define _STDLIB_H_ 7 | #define _STRING_H_ 8 | #define _STDDEF_H 9 | #define _STDINT_H 10 | 11 | #include "espmissingincludes.h" 12 | #include "c_types.h" 13 | #include "mem.h" 14 | #include "osapi.h" 15 | #include "heatshrink_config_httpd.h" 16 | #define malloc(x) os_malloc(x) 17 | #define memset(x,y,z) os_memset(x,y,z) 18 | #define memcpy(x,y,z) os_memcpy(x,y,z) 19 | #include "../lib/heatshrink/heatshrink_decoder.c" 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /user/httpd.c: -------------------------------------------------------------------------------- 1 | //Esp8266 http server - core routines 2 | 3 | /* 4 | * ---------------------------------------------------------------------------- 5 | * "THE BEER-WARE LICENSE" (Revision 42): 6 | * Jeroen Domburg wrote this file. As long as you retain 7 | * this notice you can do whatever you want with this stuff. If we meet some day, 8 | * and you think this stuff is worth it, you can buy me a beer in return. 9 | * ---------------------------------------------------------------------------- 10 | */ 11 | 12 | 13 | #include "espmissingincludes.h" 14 | #include "c_types.h" 15 | #include "user_interface.h" 16 | #include "espconn.h" 17 | #include "mem.h" 18 | #include "osapi.h" 19 | 20 | #include "espconn.h" 21 | #include "httpd.h" 22 | #include "io.h" 23 | #include "espfs.h" 24 | 25 | //Max length of request head 26 | #define MAX_HEAD_LEN 1024 27 | //Max amount of connections 28 | #define MAX_CONN 8 29 | //Max post buffer len 30 | #define MAX_POST 1024 31 | 32 | //This gets set at init time. 33 | static HttpdBuiltInUrl *builtInUrls; 34 | 35 | //Private data for httpd thing 36 | struct HttpdPriv { 37 | char head[MAX_HEAD_LEN]; 38 | int headPos; 39 | int postPos; 40 | }; 41 | 42 | //Connection pool 43 | static HttpdPriv connPrivData[MAX_CONN]; 44 | static HttpdConnData connData[MAX_CONN]; 45 | 46 | static struct espconn httpdConn; 47 | static esp_tcp httpdTcp; 48 | 49 | 50 | typedef struct { 51 | const char *ext; 52 | const char *mimetype; 53 | } MimeMap; 54 | 55 | //The mappings from file extensions to mime types. If you need an extra mime type, 56 | //add it here. 57 | static const MimeMap mimeTypes[]={ 58 | {"htm", "text/htm"}, 59 | {"html", "text/html"}, 60 | {"js", "text/javascript"}, 61 | {"txt", "text/plain"}, 62 | {"jpg", "image/jpeg"}, 63 | {"jpeg", "image/jpeg"}, 64 | {"png", "image/png"}, 65 | {NULL, "text/html"}, //default value 66 | }; 67 | 68 | //Returns a static char* to a mime type for a given url to a file. 69 | const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) { 70 | int i=0; 71 | //Go find the extension 72 | char *ext=url+(strlen(url)-1); 73 | while (ext!=url && *ext!='.') ext--; 74 | if (*ext=='.') ext++; 75 | 76 | while (mimeTypes[i].ext!=NULL && os_strcmp(ext, mimeTypes[i].ext)!=0) i++; 77 | return mimeTypes[i].mimetype; 78 | } 79 | 80 | //Looks up the connData info for a specific esp connection 81 | static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) { 82 | int i; 83 | for (i=0; ipostBuff!=NULL) os_free(conn->postBuff); 93 | conn->postBuff=NULL; 94 | conn->cgi=NULL; 95 | conn->conn=NULL; 96 | } 97 | 98 | //Stupid li'l helper function that returns the value of a hex char. 99 | static int httpdHexVal(char c) { 100 | if (c>='0' && c<='9') return c-'0'; 101 | if (c>='A' && c<='F') return c-'A'+10; 102 | if (c>='a' && c<='f') return c-'a'+10; 103 | return 0; 104 | } 105 | 106 | //Decode a percent-encoded value. 107 | //Takes the valLen bytes stored in val, and converts it into at most retLen bytes that 108 | //are stored in the ret buffer. Returns the actual amount of bytes used in ret. Also 109 | //zero-terminates the ret buffer. 110 | int httpdUrlDecode(char *val, int valLen, char *ret, int retLen) { 111 | int s=0, d=0; 112 | int esced=0, escVal=0; 113 | while (sconn, (uint8 *)buff, l); 165 | } 166 | 167 | //Send a http header. 168 | void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const char *val) { 169 | char buff[256]; 170 | int l; 171 | l=os_sprintf(buff, "%s: %s\r\n", field, val); 172 | espconn_sent(conn->conn, (uint8 *)buff, l); 173 | } 174 | 175 | //Finish the headers. 176 | void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn) { 177 | espconn_sent(conn->conn, (uint8 *)"\r\n", 2); 178 | } 179 | 180 | //ToDo: sprintf->snprintf everywhere 181 | void ICACHE_FLASH_ATTR httpdRedirect(HttpdConnData *conn, char *newUrl) { 182 | char buff[1024]; 183 | int l; 184 | l=os_sprintf(buff, "HTTP/1.1 302 Found\r\nLocation: %s\r\n\r\nMoved to %s\r\n", newUrl, newUrl); 185 | espconn_sent(conn->conn, (uint8 *)buff, l); 186 | } 187 | 188 | int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData) { 189 | if (connData->conn==NULL) { 190 | //Connection aborted. Clean up. 191 | return HTTPD_CGI_DONE; 192 | } 193 | 194 | httpdRedirect(connData, (char*)connData->cgiArg); 195 | return HTTPD_CGI_DONE; 196 | } 197 | 198 | 199 | static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) { 200 | int r; 201 | HttpdConnData *conn=httpdFindConnData(arg); 202 | // os_printf("Sent callback on conn %p\n", conn); 203 | if (conn==NULL) return; 204 | if (conn->cgi==NULL) { //Marked for destruction? 205 | os_printf("Conn %p is done. Closing.\n", conn->conn); 206 | espconn_disconnect(conn->conn); 207 | httpdRetireConn(conn); 208 | return; 209 | } 210 | 211 | r=conn->cgi(conn); //Execute cgi fn. 212 | if (r==HTTPD_CGI_DONE) { 213 | conn->cgi=NULL; //mark for destruction. 214 | } 215 | } 216 | 217 | static const char *httpNotFoundHeader="HTTP/1.0 404 Not Found\r\nServer: esp8266-httpd/0.1\r\nContent-Type: text/plain\r\n\r\nNot Found.\r\n"; 218 | 219 | static void ICACHE_FLASH_ATTR httpdSendResp(HttpdConnData *conn) { 220 | int i=0; 221 | int r; 222 | //See if the url is somewhere in our internal url table. 223 | while (builtInUrls[i].url!=NULL && conn->url!=NULL) { 224 | // os_printf("%s == %s?\n", builtInUrls[i].url, conn->url); 225 | if (os_strcmp(builtInUrls[i].url, conn->url)==0 || builtInUrls[i].url[0]=='*') { 226 | os_printf("Is url index %d\n", i); 227 | conn->cgiData=NULL; 228 | conn->cgi=builtInUrls[i].cgiCb; 229 | conn->cgiArg=builtInUrls[i].cgiArg; 230 | r=conn->cgi(conn); 231 | if (r!=HTTPD_CGI_NOTFOUND) { 232 | if (r==HTTPD_CGI_DONE) conn->cgi=NULL; //If cgi finishes immediately: mark conn for destruction. 233 | return; 234 | } 235 | } 236 | i++; 237 | } 238 | //Can't find :/ 239 | os_printf("%s not found. 404!\n", conn->url); 240 | espconn_sent(conn->conn, (uint8 *)httpNotFoundHeader, os_strlen(httpNotFoundHeader)); 241 | conn->cgi=NULL; //mark for destruction 242 | } 243 | 244 | static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) { 245 | int i; 246 | // os_printf("Got header %s\n", h); 247 | if (os_strncmp(h, "GET ", 4)==0 || os_strncmp(h, "POST ", 5)==0) { 248 | char *e; 249 | 250 | //Skip past the space after POST/GET 251 | i=0; 252 | while (h[i]!=' ') i++; 253 | conn->url=h+i+1; 254 | 255 | //Figure out end of url. 256 | e=(char*)os_strstr(conn->url, " "); 257 | if (e==NULL) return; //wtf? 258 | *e=0; //terminate url part 259 | 260 | os_printf("URL = %s\n", conn->url); 261 | conn->getArgs=(char*)os_strstr(conn->url, "?"); 262 | if (conn->getArgs!=0) { 263 | *conn->getArgs=0; 264 | conn->getArgs++; 265 | os_printf("GET args = %s\n", conn->getArgs); 266 | } else { 267 | conn->getArgs=NULL; 268 | } 269 | } else if (os_strncmp(h, "Content-Length: ", 16)==0) { 270 | i=0; 271 | while (h[i]!=' ') i++; 272 | conn->postLen=atoi(h+i+1); 273 | if (conn->postLen>MAX_POST) conn->postLen=MAX_POST; 274 | os_printf("Mallocced buffer for %d bytes of post data.\n", conn->postLen); 275 | conn->postBuff=(char*)os_malloc(conn->postLen+1); 276 | conn->priv->postPos=0; 277 | } 278 | } 279 | 280 | static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short len) { 281 | int x; 282 | char *p, *e; 283 | HttpdConnData *conn=httpdFindConnData(arg); 284 | if (conn==NULL) return; 285 | 286 | 287 | for (x=0; xpriv->headPos!=-1) { 290 | //This byte is a header byte. 291 | if (conn->priv->headPos!=MAX_HEAD_LEN) conn->priv->head[conn->priv->headPos++]=data[x]; 292 | conn->priv->head[conn->priv->headPos]=0; 293 | //Scan for /r/n/r/n 294 | if (data[x]=='\n' && (char *)os_strstr(conn->priv->head, "\r\n\r\n")!=NULL) { 295 | //Reset url data 296 | conn->url=NULL; 297 | //Find end of next header line 298 | p=conn->priv->head; 299 | while(p<(&conn->priv->head[conn->priv->headPos-4])) { 300 | e=(char *)os_strstr(p, "\r\n"); 301 | if (e==NULL) break; 302 | e[0]=0; 303 | httpdParseHeader(p, conn); 304 | p=e+2; 305 | } 306 | //If we don't need to receive post data, we can send the response now. 307 | if (conn->postLen==0) { 308 | httpdSendResp(conn); 309 | } 310 | conn->priv->headPos=-1; //Indicate we're done with the headers. 311 | } 312 | } else if (conn->priv->postPos!=-1 && conn->postLen!=0 && conn->priv->postPos <= conn->postLen) { 313 | //This byte is a POST byte. 314 | conn->postBuff[conn->priv->postPos++]=data[x]; 315 | if (conn->priv->postPos>=conn->postLen) { 316 | //Received post stuff. 317 | conn->postBuff[conn->priv->postPos]=0; //zero-terminate 318 | conn->priv->postPos=-1; 319 | os_printf("Post data: %s\n", conn->postBuff); 320 | //Send the response. 321 | httpdSendResp(conn); 322 | return; 323 | } 324 | } 325 | } 326 | } 327 | 328 | static void ICACHE_FLASH_ATTR httpdReconCb(void *arg, sint8 err) { 329 | HttpdConnData *conn=httpdFindConnData(arg); 330 | os_printf("ReconCb\n"); 331 | if (conn==NULL) return; 332 | //Yeah... No idea what to do here. ToDo: figure something out. 333 | } 334 | 335 | static void ICACHE_FLASH_ATTR httpdDisconCb(void *arg) { 336 | #if 0 337 | //Stupid esp sdk passes through wrong arg here, namely the one of the *listening* socket. 338 | //If it ever gets fixed, be sure to update the code in this snippet; it's probably out-of-date. 339 | HttpdConnData *conn=httpdFindConnData(arg); 340 | os_printf("Disconnected, conn=%p\n", conn); 341 | if (conn==NULL) return; 342 | conn->conn=NULL; 343 | if (conn->cgi!=NULL) conn->cgi(conn); //flush cgi data 344 | #endif 345 | //Just look at all the sockets and kill the slot if needed. 346 | int i; 347 | for (i=0; istate==ESPCONN_NONE || connData[i].conn->state==ESPCONN_CLOSE) { 350 | connData[i].conn=NULL; 351 | if (connData[i].cgi!=NULL) connData[i].cgi(&connData[i]); //flush cgi data 352 | httpdRetireConn(&connData[i]); 353 | } 354 | } 355 | } 356 | } 357 | 358 | 359 | static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) { 360 | struct espconn *conn=arg; 361 | int i; 362 | //Find empty conndata in pool 363 | for (i=0; iheadPos=0; 373 | connData[i].postBuff=NULL; 374 | connData[i].priv->postPos=0; 375 | connData[i].postLen=0; 376 | 377 | espconn_regist_recvcb(conn, httpdRecvCb); 378 | espconn_regist_reconcb(conn, httpdReconCb); 379 | espconn_regist_disconcb(conn, httpdDisconCb); 380 | espconn_regist_sentcb(conn, httpdSentCb); 381 | } 382 | 383 | 384 | void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port) { 385 | int i; 386 | 387 | for (i=0; i 4 | #include 5 | #include 6 | 7 | #define HTTPD_CGI_MORE 0 8 | #define HTTPD_CGI_DONE 1 9 | #define HTTPD_CGI_NOTFOUND 2 10 | 11 | typedef struct HttpdPriv HttpdPriv; 12 | typedef struct HttpdConnData HttpdConnData; 13 | 14 | typedef int (* cgiSendCallback)(HttpdConnData *connData); 15 | 16 | //A struct describing a http connection. This gets passed to cgi functions. 17 | struct HttpdConnData { 18 | struct espconn *conn; 19 | char *url; 20 | char *getArgs; 21 | const void *cgiArg; 22 | void *cgiData; 23 | HttpdPriv *priv; 24 | cgiSendCallback cgi; 25 | int postLen; 26 | char *postBuff; 27 | }; 28 | 29 | //A struct describing an url. This is the main struct that's used to send different URL requests to 30 | //different routines. 31 | typedef struct { 32 | const char *url; 33 | cgiSendCallback cgiCb; 34 | const void *cgiArg; 35 | } HttpdBuiltInUrl; 36 | 37 | int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData); 38 | void ICACHE_FLASH_ATTR httpdRedirect(HttpdConnData *conn, char *newUrl); 39 | int httpdUrlDecode(char *val, int valLen, char *ret, int retLen); 40 | int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLen); 41 | void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port); 42 | const char *httpdGetMimetype(char *url); 43 | void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code); 44 | void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const char *val); 45 | void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn); 46 | 47 | #endif -------------------------------------------------------------------------------- /user/httpdespfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | Connector to let httpd use the espfs filesystem to serve the files in that. 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 | #include "espmissingincludes.h" 15 | #include 16 | #include 17 | #include "c_types.h" 18 | #include "user_interface.h" 19 | #include "espconn.h" 20 | #include "mem.h" 21 | 22 | #include "httpd.h" 23 | #include "espfs.h" 24 | #include "httpdespfs.h" 25 | 26 | 27 | //This is a catch-all cgi function. It takes the url passed to it, looks up the corresponding 28 | //path in the filesystem and if it exists, passes the file through. This simulates what a normal 29 | //webserver would do with static files. 30 | int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) { 31 | EspFsFile *file=connData->cgiData; 32 | int len; 33 | char buff[1024]; 34 | 35 | if (connData->conn==NULL) { 36 | //Connection aborted. Clean up. 37 | espFsClose(file); 38 | return HTTPD_CGI_DONE; 39 | } 40 | 41 | if (file==NULL) { 42 | //First call to this cgi. Open the file so we can read it. 43 | file=espFsOpen(connData->url); 44 | if (file==NULL) { 45 | return HTTPD_CGI_NOTFOUND; 46 | } 47 | connData->cgiData=file; 48 | httpdStartResponse(connData, 200); 49 | httpdHeader(connData, "Content-Type", httpdGetMimetype(connData->url)); 50 | httpdEndHeaders(connData); 51 | return HTTPD_CGI_MORE; 52 | } 53 | 54 | len=espFsRead(file, buff, 1024); 55 | if (len>0) espconn_sent(connData->conn, (uint8 *)buff, len); 56 | if (len!=1024) { 57 | //We're done. 58 | espFsClose(file); 59 | return HTTPD_CGI_DONE; 60 | } else { 61 | //Ok, till next time. 62 | return HTTPD_CGI_MORE; 63 | } 64 | } 65 | 66 | 67 | //cgiEspFsTemplate can be used as a template. 68 | 69 | typedef struct { 70 | EspFsFile *file; 71 | void *tplArg; 72 | char token[64]; 73 | int tokenPos; 74 | } TplData; 75 | 76 | typedef void (* TplCallback)(HttpdConnData *connData, char *token, void **arg); 77 | 78 | int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData) { 79 | TplData *tpd=connData->cgiData; 80 | int len; 81 | int x, sp=0; 82 | char *e=NULL; 83 | char buff[1025]; 84 | 85 | if (connData->conn==NULL) { 86 | //Connection aborted. Clean up. 87 | ((TplCallback)(connData->cgiArg))(connData, NULL, &tpd->tplArg); 88 | espFsClose(tpd->file); 89 | os_free(tpd); 90 | return HTTPD_CGI_DONE; 91 | } 92 | 93 | if (tpd==NULL) { 94 | //First call to this cgi. Open the file so we can read it. 95 | tpd=(TplData *)os_malloc(sizeof(TplData)); 96 | tpd->file=espFsOpen(connData->url); 97 | tpd->tplArg=NULL; 98 | tpd->tokenPos=-1; 99 | if (tpd->file==NULL) { 100 | return HTTPD_CGI_NOTFOUND; 101 | } 102 | connData->cgiData=tpd; 103 | httpdStartResponse(connData, 200); 104 | httpdHeader(connData, "Content-Type", httpdGetMimetype(connData->url)); 105 | httpdEndHeaders(connData); 106 | return HTTPD_CGI_MORE; 107 | } 108 | 109 | len=espFsRead(tpd->file, buff, 1024); 110 | if (len>0) { 111 | sp=0; 112 | e=buff; 113 | for (x=0; xtokenPos==-1) { 115 | //Inside ordinary text. 116 | if (buff[x]=='%') { 117 | //Send raw data up to now 118 | if (sp!=0) espconn_sent(connData->conn, (uint8 *)e, sp); 119 | sp=0; 120 | //Go collect token chars. 121 | tpd->tokenPos=0; 122 | } else { 123 | sp++; 124 | } 125 | } else { 126 | if (buff[x]=='%') { 127 | tpd->token[tpd->tokenPos++]=0; //zero-terminate token 128 | ((TplCallback)(connData->cgiArg))(connData, tpd->token, &tpd->tplArg); 129 | //Go collect normal chars again. 130 | e=&buff[x+1]; 131 | tpd->tokenPos=-1; 132 | } else { 133 | if (tpd->tokenPos<(sizeof(tpd->token)-1)) tpd->token[tpd->tokenPos++]=buff[x]; 134 | } 135 | } 136 | } 137 | } 138 | //Send remaining bit. 139 | if (sp!=0) espconn_sent(connData->conn, (uint8 *)e, sp); 140 | if (len!=1024) { 141 | //We're done. 142 | ((TplCallback)(connData->cgiArg))(connData, NULL, &tpd->tplArg); 143 | espFsClose(tpd->file); 144 | return HTTPD_CGI_DONE; 145 | } else { 146 | //Ok, till next time. 147 | return HTTPD_CGI_MORE; 148 | } 149 | } 150 | 151 | -------------------------------------------------------------------------------- /user/httpdespfs.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTPDESPFS_H 2 | #define HTTPDESPFS_H 3 | 4 | #include "httpd.h" 5 | #include "espfs.h" 6 | 7 | int cgiEspFsHook(HttpdConnData *connData); 8 | int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData); 9 | 10 | #endif -------------------------------------------------------------------------------- /user/io.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * ---------------------------------------------------------------------------- 4 | * "THE BEER-WARE LICENSE" (Revision 42): 5 | * Jeroen Domburg wrote this file. As long as you retain 6 | * this notice you can do whatever you want with this stuff. If we meet some day, 7 | * and you think this stuff is worth it, you can buy me a beer in return. 8 | * ---------------------------------------------------------------------------- 9 | */ 10 | 11 | #include "ets_sys.h" 12 | #include "osapi.h" 13 | #include "espmissingincludes.h" 14 | #include "c_types.h" 15 | #include "user_interface.h" 16 | #include "espconn.h" 17 | #include "mem.h" 18 | #include "gpio.h" 19 | 20 | #define LEDGPIO 13 21 | #define BTNGPIO 0 22 | 23 | static ETSTimer resetBtntimer; 24 | 25 | 26 | void ICACHE_FLASH_ATTR ioLed(int ena) { 27 | //gpio_output_set is overkill. ToDo: use better mactos 28 | if (ena) { 29 | gpio_output_set((1<=6) { //3 sec pressed 41 | wifi_station_disconnect(); 42 | wifi_set_opmode(0x3); //reset to AP+STA mode 43 | os_printf("Reset to AP mode. Restarting system...\n"); 44 | system_restart(); 45 | } 46 | resetCnt=0; 47 | } 48 | } 49 | 50 | void ioInit() { 51 | 52 | //Set GPIO13 to output mode for LED 53 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13); 54 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0); 55 | gpio_output_set(0, 0, (1< wrote this file. As long as you retain 7 | * this notice you can do whatever you want with this stuff. If we meet some day, 8 | * and you think this stuff is worth it, you can buy me a beer in return. 9 | * ---------------------------------------------------------------------------- 10 | */ 11 | 12 | 13 | #include "espmissingincludes.h" 14 | #include "ets_sys.h" 15 | #include "osapi.h" 16 | #include "uart_hw.h" 17 | 18 | static void ICACHE_FLASH_ATTR stdoutUartTxd(char c) { 19 | //Wait until there is room in the FIFO 20 | while (((READ_PERI_REG(UART_STATUS(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ; 21 | //Send the character 22 | WRITE_PERI_REG(UART_FIFO(0), c); 23 | } 24 | 25 | static void ICACHE_FLASH_ATTR stdoutPutchar(char c) { 26 | //convert \n -> \r\n 27 | if (c=='\n') stdoutUartTxd('\r'); 28 | stdoutUartTxd(c); 29 | } 30 | 31 | 32 | void stdoutInit() { 33 | //Enable TxD pin 34 | PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); 35 | PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD); 36 | 37 | //Set baud rate and other serial parameters to 115200,n,8,1 38 | uart_div_modify(0, UART_CLK_FREQ/BIT_RATE_115200); 39 | WRITE_PERI_REG(UART_CONF0(0), (STICK_PARITY_DIS)|(ONE_STOP_BIT << UART_STOP_BIT_NUM_S)| \ 40 | (EIGHT_BITS << UART_BIT_NUM_S)); 41 | 42 | //Reset tx & rx fifo 43 | SET_PERI_REG_MASK(UART_CONF0(0), UART_RXFIFO_RST|UART_TXFIFO_RST); 44 | CLEAR_PERI_REG_MASK(UART_CONF0(0), UART_RXFIFO_RST|UART_TXFIFO_RST); 45 | //Clear pending interrupts 46 | WRITE_PERI_REG(UART_INT_CLR(0), 0xffff); 47 | 48 | //Install our own putchar handler 49 | os_install_putc1((void *)stdoutPutchar); 50 | } -------------------------------------------------------------------------------- /user/stdout.h: -------------------------------------------------------------------------------- 1 | void stdoutInit(); 2 | -------------------------------------------------------------------------------- /user/tempd.c: -------------------------------------------------------------------------------- 1 | #include "espmissingincludes.h" 2 | #include "c_types.h" 3 | #include "user_interface.h" 4 | #include "espconn.h" 5 | #include "mem.h" 6 | #include "osapi.h" 7 | #include "ip_addr.h" 8 | 9 | #include "io.h" 10 | #include "tempd.h" 11 | #include "dht.h" 12 | #include "ds18b20.h" 13 | 14 | 15 | /* 16 | * ---------------------------------------------------------------------------- 17 | * "THE MODIFIED BEER-WARE LICENSE" (Revision 42): 18 | * Mathew Hall wrote this file. As long as you 19 | * retain 20 | * this notice you can do whatever you want with this stuff. If we meet some 21 | * day, 22 | * and you think this stuff is worth it, you can buy sprite_tm a beer in return. 23 | * ---------------------------------------------------------------------------- 24 | */ 25 | 26 | #define SLEEP_MODE 0 27 | #define SLEEP_TIME 45 28 | static struct espconn tempConn; 29 | static struct _esp_udp socket; 30 | static ip_addr_t master_addr; 31 | 32 | #define DATA_PORT 19252 33 | #define DATA_HOST "bmo.local" 34 | 35 | 36 | static ETSTimer broadcastTimer; 37 | static ETSTimer lookupTimer; 38 | static ETSTimer sleepTimer; 39 | 40 | 41 | static void transmitReading(struct sensor_reading* result){ 42 | char buf[256]; 43 | os_sprintf(buf, "{\"type\":\"%s\", \"temperature\":\"%d\", \"humidity\":\"%d\", \"scale\":\"0.01\", \"success\":\"%d\"}\n", result->source,(int)(100 * result->temperature), (int)(100 * result->humidity), result->success); 44 | espconn_sent(&tempConn, (uint8*)buf, os_strlen(buf)); 45 | } 46 | 47 | static void goToSleep(void* arg){ 48 | os_printf("Going to deep sleep mode now \n"); 49 | system_deep_sleep(SLEEP_TIME * 1000 * 1000); 50 | } 51 | 52 | static void broadcastReading(void* arg){ 53 | 54 | os_printf("Sending heartbeat\n"); 55 | #ifdef SLEEP_MODE 56 | struct sensor_reading* result = readDHT(1); 57 | #else 58 | struct sensor_reading* result = readDHT(0); 59 | #endif 60 | transmitReading(result); 61 | result = readDS18B20(); 62 | transmitReading(result); 63 | #ifdef SLEEP_MODE 64 | 65 | 66 | os_printf("Scheduling power down\n"); 67 | os_timer_setfn(&sleepTimer, goToSleep, NULL); 68 | os_timer_arm(&sleepTimer, 2000, 0); 69 | #endif 70 | } 71 | 72 | 73 | 74 | static void dnsLookupCb(const char *name, ip_addr_t *ipaddr, void *arg){ 75 | struct espconn* conn = arg; 76 | ip_addr_t broadcast; 77 | if(ipaddr == NULL){ 78 | os_printf("Logger: couldn't resolve IP address for %s; will broadcast instead\n", name); 79 | // return; 80 | 81 | 82 | broadcast.addr = (uint32)0x0A00007B; 83 | IP4_ADDR(&broadcast, 10,0,0,123); 84 | os_printf("Falling back on %x for logging\n",broadcast.addr); 85 | ipaddr = &broadcast; 86 | } 87 | 88 | os_printf("Successfully resolved %s as %x\n", name, ipaddr->addr); 89 | 90 | if(master_addr.addr == 0 && ipaddr->addr != 0){ 91 | master_addr.addr = ipaddr->addr; 92 | os_memcpy(conn->proto.udp->remote_ip, &ipaddr->addr, 4); 93 | os_printf("Will send to %d.%d.%d.%d\n", (int)conn->proto.udp->remote_ip[0], (int)conn->proto.udp->remote_ip[1], (int)conn->proto.udp->remote_ip[2], (int)conn->proto.udp->remote_ip[3]); 94 | conn->proto.udp->local_port = espconn_port(); 95 | } 96 | 97 | espconn_create(conn); 98 | #ifdef SLEEP_MODE 99 | os_printf("Sending data\n"); 100 | broadcastReading(NULL); 101 | #else 102 | os_printf("Arming broadcast timer\n"); 103 | os_timer_arm(&broadcastTimer, 60000, 1); 104 | #endif 105 | } 106 | 107 | static void lookupTask(void* arg){ 108 | os_sprintf("Attempting to resolve %s\n", DATA_HOST); 109 | espconn_gethostbyname(&tempConn, DATA_HOST, &master_addr, dnsLookupCb); 110 | os_timer_disarm(&lookupTimer); 111 | } 112 | 113 | void tempdInit(void){ 114 | os_printf("Temperature logging initialising\n"); 115 | tempConn.type=ESPCONN_UDP; 116 | tempConn.state=ESPCONN_NONE; 117 | tempConn.proto.udp=&socket; 118 | 119 | readDS18B20(); 120 | 121 | master_addr.addr = 0; 122 | socket.remote_port=DATA_PORT; 123 | os_timer_setfn(&lookupTimer, lookupTask, NULL); 124 | os_timer_arm(&lookupTimer, 10000, 1); 125 | 126 | os_timer_setfn(&broadcastTimer, broadcastReading, NULL); 127 | } -------------------------------------------------------------------------------- /user/tempd.h: -------------------------------------------------------------------------------- 1 | void tempdInit(void); -------------------------------------------------------------------------------- /user/user_main.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* 4 | * ---------------------------------------------------------------------------- 5 | * "THE BEER-WARE LICENSE" (Revision 42): 6 | * Jeroen Domburg wrote this file. As long as you retain 7 | * this notice you can do whatever you want with this stuff. If we meet some day, 8 | * and you think this stuff is worth it, you can buy me a beer in return. 9 | * ---------------------------------------------------------------------------- 10 | */ 11 | 12 | #include "espmissingincludes.h" 13 | #include "osapi.h" 14 | 15 | #include "ets_sys.h" 16 | #include "httpd.h" 17 | #include "io.h" 18 | #include "dht.h" 19 | #include "httpdespfs.h" 20 | #include "cgi.h" 21 | #include "cgiwifi.h" 22 | #include "stdout.h" 23 | #include "wifi.h" 24 | #include "tempd.h" 25 | 26 | HttpdBuiltInUrl builtInUrls[]={ 27 | {"/", cgiRedirect, "/index.tpl"}, 28 | {"/flash.bin", cgiReadFlash, NULL}, 29 | {"/led.tpl", cgiEspFsTemplate, tplLed}, 30 | {"/dht22.tpl", cgiEspFsTemplate, tplDHT}, 31 | {"/index.tpl", cgiEspFsTemplate, tplCounter}, 32 | {"/led.cgi", cgiLed, NULL}, 33 | 34 | //Routines to make the /wifi URL and everything beneath it work. 35 | {"/wifi", cgiRedirect, "/wifi/wifi.tpl"}, 36 | {"/wifi/", cgiRedirect, "/wifi/wifi.tpl"}, 37 | {"/wifi/wifiscan.cgi", cgiWiFiScan, NULL}, 38 | {"/wifi/wifi.tpl", cgiEspFsTemplate, tplWlan}, 39 | {"/wifi/connect.cgi", cgiWiFiConnect}, 40 | 41 | {"*", cgiEspFsHook, NULL}, //Catch-all cgi function for the filesystem 42 | {NULL, NULL, NULL} 43 | }; 44 | 45 | 46 | void user_init(void) { 47 | stdoutInit(); 48 | ioInit(); 49 | DHTInit(SENSOR_DHT11, 30000); 50 | httpdInit(builtInUrls, 80); 51 | wifiCheck(); 52 | tempdInit(); 53 | os_printf("\nReady\n"); 54 | } 55 | -------------------------------------------------------------------------------- /user/wifi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "user_interface.h" 4 | #include "espmissingincludes.h" 5 | #include "wifi.h" 6 | //Drop the soft AP if we are a valid client on the network. 7 | static void ICACHE_FLASH_ATTR wifiCheckCb(void *arg) { 8 | int x=wifi_get_opmode(); 9 | 10 | if (x!=1) { 11 | x=wifi_station_get_connect_status(); 12 | os_printf("WiFi State Update: %d - looking for %d (got IP)\n", x, STATION_GOT_IP); 13 | 14 | if (x==STATION_GOT_IP) { 15 | os_printf("Dropping soft AP..."); 16 | //Go to STA mode. This needs a reset, so do that. 17 | wifi_set_opmode(1); 18 | system_restart(); 19 | } else { 20 | os_printf("Connect fail. Not going into STA-only mode.\n"); 21 | } 22 | 23 | } 24 | } 25 | 26 | void ICACHE_FLASH_ATTR wifiCheck(void){ 27 | static ETSTimer wifiTimer; 28 | os_timer_setfn(&wifiTimer, wifiCheckCb, NULL); 29 | os_timer_arm(&wifiTimer, 5000, 0); 30 | } -------------------------------------------------------------------------------- /user/wifi.h: -------------------------------------------------------------------------------- 1 | void ICACHE_FLASH_ATTR wifiCheck(void); 2 | --------------------------------------------------------------------------------