Browse Source

rebirthing project

psy 10 months ago
commit
a33012f7ba
100 changed files with 25705 additions and 0 deletions
  1. 30 0
      README.md
  2. 4 0
      meta-id/.directory
  3. 24 0
      meta-id/.gitignore
  4. 36 0
      meta-id/.travis.yml
  5. 213 0
      meta-id/BOARDS.md
  6. 73 0
      meta-id/BUILDING.md
  7. 29 0
      meta-id/CONTRIBUTING.md
  8. 78 0
      meta-id/FLASH.md
  9. 204 0
      meta-id/FLASHING.md
  10. 123 0
      meta-id/LICENSE.txt
  11. 545 0
      meta-id/Makefile
  12. 14 0
      meta-id/RESTMQTT.md
  13. 36 0
      meta-id/TROUBLESHOOTING.md
  14. 183 0
      meta-id/UC-FLASHING.md
  15. 69 0
      meta-id/WEB-SERVER.md
  16. 87 0
      meta-id/WIFI-CONFIG.md
  17. 14 0
      meta-id/WINDOWS.md
  18. 110 0
      meta-id/avrflash
  19. 194 0
      meta-id/cmd/cmd.c
  20. 124 0
      meta-id/cmd/cmd.h
  21. 407 0
      meta-id/cmd/handlers.c
  22. 4 0
      meta-id/envvars.template
  23. 293 0
      meta-id/espfs/espfs.c
  24. 42 0
      meta-id/espfs/espfs.h
  25. 33 0
      meta-id/espfs/espfsformat.h
  26. 56 0
      meta-id/espfs/mkespfsimage/Makefile
  27. 310 0
      meta-id/espfs/mkespfsimage/main.c
  28. 48 0
      meta-id/espfs/mkespfsimage/mman-win32/Makefile
  29. 11 0
      meta-id/espfs/mkespfsimage/mman-win32/config.mak
  30. 157 0
      meta-id/espfs/mkespfsimage/mman-win32/configure
  31. 180 0
      meta-id/espfs/mkespfsimage/mman-win32/mman.c
  32. 55 0
      meta-id/espfs/mkespfsimage/mman-win32/mman.h
  33. 235 0
      meta-id/espfs/mkespfsimage/mman-win32/test.c
  34. 7 0
      meta-id/espmake.cmd
  35. 5 0
      meta-id/html/.directory
  36. BIN
      meta-id/html/Lato-Embed.ttf
  37. 275 0
      meta-id/html/META.css
  38. 41 0
      meta-id/html/about.html
  39. BIN
      meta-id/html/attic/Lato-Black.ttf
  40. BIN
      meta-id/html/attic/Lato-BlackItalic.ttf
  41. BIN
      meta-id/html/attic/Lato-Bold.ttf
  42. BIN
      meta-id/html/attic/Lato-BoldItalic.ttf
  43. 15601 0
      meta-id/html/attic/Lato-Embed.sfd
  44. BIN
      meta-id/html/attic/Lato-Hairline.ttf
  45. BIN
      meta-id/html/attic/Lato-HairlineItalic.ttf
  46. BIN
      meta-id/html/attic/Lato-Italic.ttf
  47. BIN
      meta-id/html/attic/Lato-Light.ttf
  48. BIN
      meta-id/html/attic/Lato-LightItalic.ttf
  49. BIN
      meta-id/html/attic/Lato-Regular.ttf
  50. 133 0
      meta-id/html/attic/console.html
  51. 152 0
      meta-id/html/attic/console.js
  52. 43 0
      meta-id/html/attic/flash.html
  53. 33 0
      meta-id/html/attic/flash.js
  54. 161 0
      meta-id/html/attic/home.html
  55. 25 0
      meta-id/html/attic/init_old.html
  56. 60 0
      meta-id/html/attic/log.html
  57. 80 0
      meta-id/html/attic/meta.html
  58. 71 0
      meta-id/html/attic/meta.js
  59. 228 0
      meta-id/html/attic/meta_old.css
  60. 123 0
      meta-id/html/attic/mqtt.html
  61. 87 0
      meta-id/html/attic/mqtt.js
  62. 97 0
      meta-id/html/attic/services.html
  63. 68 0
      meta-id/html/attic/services.js
  64. 484 0
      meta-id/html/attic/ui.js
  65. 21 0
      meta-id/html/attic/user.html
  66. 242 0
      meta-id/html/attic/userpage.js
  67. 59 0
      meta-id/html/attic/web-server.html
  68. 38 0
      meta-id/html/attic/welcome-old.html
  69. BIN
      meta-id/html/favicon.ico
  70. 15 0
      meta-id/html/head-
  71. 43 0
      meta-id/html/id.html
  72. 11 0
      meta-id/html/init.html
  73. BIN
      meta-id/html/jl-400x110.png-
  74. 669 0
      meta-id/html/meta-ui.js
  75. 22 0
      meta-id/html/start.html
  76. 101 0
      meta-id/html/text/t1.html
  77. 66 0
      meta-id/html/text/t2.html
  78. 54 0
      meta-id/html/text/t3.html
  79. 107 0
      meta-id/html/text/t4.html
  80. 73 0
      meta-id/html/text/t5.html
  81. 76 0
      meta-id/html/text/t6.html
  82. 383 0
      meta-id/html/text/t7.html.toobig
  83. 25 0
      meta-id/html/text/t8.html.toobig
  84. 34 0
      meta-id/html/text/text.html
  85. 283 0
      meta-id/html/texts-braman.html
  86. 90 0
      meta-id/html/texts-breke.html
  87. 220 0
      meta-id/html/texts-bullot.html
  88. 57 0
      meta-id/html/texts-daniela.html
  89. 314 0
      meta-id/html/texts-havas.html
  90. 127 0
      meta-id/html/texts-janich.html
  91. 63 0
      meta-id/html/texts-kiesewetter.html
  92. 200 0
      meta-id/html/texts-shah.html
  93. 60 0
      meta-id/html/texts-vega-mazon.html
  94. 75 0
      meta-id/html/texts.html
  95. 32 0
      meta-id/html/welcome.html
  96. BIN
      meta-id/html/wifi.gif
  97. 147 0
      meta-id/html/wifi.html
  98. 118 0
      meta-id/html/wifi/wifiAp.html
  99. 85 0
      meta-id/html/wifi/wifiAp.js
  100. 0 0
      meta-id/html/wifi/wifiSta.html

+ 30 - 0
README.md

@@ -0,0 +1,30 @@
+META-ID: Wifi-Serial Bridge w/REST&MQTT
+========================================
+
+http://www.meta-id.info/
+
+The meta-id firmware connects a micro-controller to the internet using an ESP8266 Wifi module.
+
+it is based on the awesome esp-link firmware from jeelabs : https://github.com/jeelabs/esp-link
+
+THIS PROJECT IS WORK IN PROGRESS
+================================
+
+It implements a number of features:
+
+- transparent bridge between Wifi and serial, useful for debugging or inputting into a uC
+- flash-programming attached Arduino/AVR microcontrollers and
+  LPC800-series and other ARM microcontrollers via Wifi
+- built-in stk500v1 programmer for AVR uC's: program using HTTP upload of hex file
+- outbound REST HTTP requests from the attached micro-controller to the internet
+- MQTT client pub/sub from the attached micro-controller to the internet
+- serve custom web pages containing data that is dynamically pulled from the attached uC and
+  that contain buttons and fields that are transmitted to the attached uC (feature not
+  fully ready yet)
+
+The firmware includes a tiny HTTP server based on
+[esphttpd](http://www.esp8266.com/viewforum.php?f=34)
+with a simple web interface, many thanks to Jeroen Domburg for making it available!
+The REST and MQTT functionality are loosely based on [espduino](https://github.com/tuanpmt/espduino)
+but significantly rewritten and no longer protocol compatible, thanks to tuanpmt for the
+inspiration!

+ 4 - 0
meta-id/.directory

@@ -0,0 +1,4 @@
+[Dolphin]
+Timestamp=2018,2,18,13,25,15
+Version=3
+ViewMode=1

+ 24 - 0
meta-id/.gitignore

@@ -0,0 +1,24 @@
+build/
+firmware/
+espfs/mkespfsimage/*.o
+espfs/mkespfsimage/mkespfsimage
+webpages.espfs
+espfs/espfstest/*.o
+espfs/espfstest/espfstest
+*.DS_Store
+html_compressed/
+esp-link.tgz
+tve-patch/
+yui
+espfs/mkespfsimage/mman-win32/mman.o
+esp-link.opensdf
+esp-link.sdf
+espfs/mkespfsimage/mman-win32/libmman.a
+.localhistory/
+local.conf
+*.tgz
+esp-open-sdk/
+*~
+envvars
+esphttpclient meta.wav dnsd/DNSServer.cpp
+dnsd

+ 36 - 0
meta-id/.travis.yml

@@ -0,0 +1,36 @@
+# Travis-CI file for Esp-Link
+
+language: c
+
+git:
+  depth: 1000
+
+before_install:
+  - curl -Ls http://s3.voneicken.com/xtensa-lx106-elf-20160330.tgx | tar Jxf -
+  - curl -Ls http://s3.voneicken.com/esp_iot_sdk_v2.1.0.tgx | tar -C .. -Jxf -
+
+after_script:
+ # upload to an S3 bucket, requires S3_BUCKET, AWS_ACCESS_KEY_ID and AWS_SECRET_KEY to be set
+ # in environment using travis' repository settings
+  - "if [[ -n \"$S3_BUCKET\" && -n \"$AWS_ACCESS_KEY_ID\" ]]; then
+     echo Uploading *.tgz to $S3_BUCKET;
+     curl -Ls https://github.com/rlmcpherson/s3gof3r/releases/download/v0.5.0/gof3r_0.5.0_linux_amd64.tar.gz | tar zxf - gof3r_0.5.0_linux_amd64/gof3r;
+     mv gof3r*/gof3r .;
+     ls *.tgz | xargs -I {} ./gof3r put -b $S3_BUCKET -k esp-link/{} --acl public-read -p {};
+     ls *.tgz | xargs -I {} echo \"URL: http://$S3_BUCKET/esp-link/{}\";
+     fi"
+
+compiler: gcc
+
+env:
+
+script:
+  - export XTENSA_TOOLS_ROOT=$PWD/xtensa-lx106-elf/bin/
+  - export BRANCH=$TRAVIS_BRANCH
+    #- export SDK_BASE=$PWD/esp_iot_sdk_v2.0.0.p1
+  - git tag -n1
+  - git describe --tags --match 'v*.0' --long --debug
+  - make release
+
+notifications:
+  email: false

+ 213 - 0
meta-id/BOARDS.md

@@ -0,0 +1,213 @@
+Boards with meta-id
+====================
+
+This readme provides instructions for PCBs that I've made that are designed for meta-id.
+Some of the instructions may be helpful to others as well.
+
+esp-bridge
+----------
+![dsc_5127](https://cloud.githubusercontent.com/assets/39480/8509323/11037aa2-2252-11e5-9bd2-6c86c9a3b2ed.jpg)
+
+The esp-bridge has an esp-03 modulde, an FTDI connector (with wrong pinout!), a 3-pin power
+and debug connector, and two buttons.
+Next to the buttons it is marked "TvE2015 esp-ftdi".
+It comes preloaded with the latest version of meta-id.
+
+Power: the on-board MCP1825S-33 regulator can provide 500mA and is good from about 3.6v to 6v.
+Connect power either to the 3-pin connector (GND in center, 5v towards the esp module), or to
+the FTDI connector (GND marked next to the buttons, 5V on 3rd pin).
+
+On power-up you should see the green LED on for ~1 second (the yellow should go on too, but
+the firmware may not be configured correctly). After that the green should blink according to the
+patterns described in the README's LED indicators section. Follow the Wifi configuration details
+section thereafter.
+
+To connect a JeeNode to the esp-bridge to flash the AVR or debug it, plug it into the FTDI
+port flipped-over, i.e. the component side of the JeeNode will be on the bottom and the
+components of the esp-bridge will be on the top. (Yes, the FTDI port should have been reversed
+on the esp-bridge...)
+
+To program the JeeNode, having set-up the Wifi through the web pages, run avrdude with an
+option like "-Pnet:esp8266:23" (you can use an IP address instead of `esp8266`). My test command
+line is as follows:
+```
+/home/arduino/arduino-1.0.5/hardware/tools/avrdude \
+  -C /home/arduino/arduino-1.0.5/hardware/tools/avrdude.conf -DV -patmega328p \
+  -Pnet:esp8266:23 -carduino -b115200 -U flash:w:greenhouse.hex:i
+```
+If you're using "edam's Arduino makefile" then you can simply set `SERIALDEV=net:bbb:2000` in your
+sketch's Makefile.
+
+To program an LPC processor using the JeeLabs uploader. follow the instructions below for the jn-esp.
+
+Reflashing the esp-bridge itself (as opposed to the attached uController):
+_you should not need to do this!_, in general use the over-the-air reflashing by downloading the latest release.
+If you cannot reflash over-the-air and need to reflash serially, connect TX of a
+USB BUB to RX of the esp-bridge and RX to TX (i.e. cross-over). Hold the flash button down
+and briefly press the reset button. Then run esptool.py.as described below.
+
+jn-esp
+-------
+![dsc_5125](https://cloud.githubusercontent.com/assets/39480/8509322/08194674-2252-11e5-8539-4eacb0d79304.jpg)
+
+The jn-esp has an esp-03 module, an LPC824, a pseudo-FTDI connector (marked in tiny letters)
+and a JeePort (also marked). On the bottom it is marked "JN-ESP-V2".
+It comes preloaded with the latest version of meta-id.
+
+Power: the on-board MCP1825S-33 regulator can provide 500mA and is good from about 3.6v to 6v.
+Connect power to the FTDI connector (GND and 5V marked on bottom).
+
+On power-up you should see the green LED on for ~1 second (the yellow should go on too, but
+the firmware may not be configured correctly). After that the green should blink according to the
+patterns described in the README's LED indicators section. Follow the Wifi configuration details
+section thereafter.
+
+To program the LPC824 _ensure that you have a recent version of the Embello uploader_
+and point the Embello uploader at port 23. Something like:
+```
+uploader -w -t -s 192.168.0.92:23 build/firmware.bin
+```
+Remove the -s option if you don't want to stay connected. A simple sketch to try this out
+with is the [hello sketch](https://github.com/jeelabs/embello/tree/master/projects/jnp/hello).
+The result should look something like:
+```
+$ uploader -w -t -s jn-esp:23 build/firmware.bin
+found: 8242 - LPC824: 32 KB flash, 8 KB RAM, TSSOP20
+hwuid: 16500407679C61AE7189A053830200F5
+flash: 0640 done, 1540 bytes
+entering terminal mode, press <ESC> to quit:
+
+
+[hello]
+500
+1000
+1500
+2000
+2500
+...
+```
+
+The pseudo-ftdi connector has the following pin-out:
+ - 1: GND (marked on bottom)
+ - 2: LPC824 P17/A9
+ - 3: 5V (marked on bottom)
+ - 4: LPC824 P11/SDA
+ - 5: LPC824 P10/SCL
+ - 6: LCP824 P23/A3/C4
+
+The JeePort connector has the following pin-out:
+ - 1: LPC824 SWDIO/P2 (not 5v unlike JeeNodes!)
+ - 2: LPC824 P14/A2/C3
+ - 3: GND
+ - 4: 3.3V (reg output)
+ - 5: LPC824 P13/A10
+ - 6: LPC824 SWCLK/P2
+
+Reflashing the jn-esp's esp8266 itself (as opposed to the attached uController):
+_you should not need to do this!_, in general use the over-the-air reflashing by downloading the latest release.
+If you cannot reflash over-the-air and need to reflash serially, there are SMD pads for an FTDI connector on the
+bottom of the PCB below the esp-03 module. GND is marked. The best is to solder a right-angle
+connector to it such that the pins point up (i.e. to the component side). You can then
+hook-up a USB-BUB. I recommend jumpering the flash pin (next to GND) to GND and to
+hook the reset pin (6) to the USB-BUB's DTR (should happen automatically). RX&TX also go
+straight through).
+
+Wifi-link-12
+------------
+
+The wifi-link has an esp-12 modulde, an FTDI connector, and a 2-pin power connector.
+The underside is marked "Wifi-link-12-v2 Jeelabs TvE2015".
+It comes preloaded with the latest version of meta-id V2.
+
+The ftdi connector has the following pin-out:
+ - 1: GND (marked top&bottom)
+ - 2: CTS (used as ISP to program ARM processors, also esp's TX when flashing the esp)
+ - 3: 5V (marked on bottom)
+ - 4: TX (from the esp)
+ - 5: RX (to the esp)
+ - 6: DTR (used ass reset to program AVR and ARM processors, also esp's RX when flashing the esp)
+
+Power: the on-board LM3671 switching regulator can provide 600mA while staying cool and is good from about 3.6v to 5.5v!
+Connect power to the marked power connector or to the FTDI connector (GND marked, 5V on 3rd pin). Do not exceed 5.5v!!
+
+It is possible to bypass the on-board regulator and power the wifi-link directly with 3.3v: connect the 3.3v to the FTDI connector ("5v" pin) and switch the jumper on the bottom from 5v to 3v3 (you need to cut the tiny trace on the 5v side and jumper the two pads on the 3v3 side).
+
+On power-up you should see the green LED on for ~1 second (the yellow should go on too, but
+the firmware may not be configured correctly). After that the green should blink according to the
+patterns described in the README's LED indicators section. Follow the Wifi configuration details
+section thereafter.
+
+The bottom also has a "sleep" jumper which connects the esp's reset pin with its gpio16 to enable deep-sleep
+mode. This jumper is open and must be bridged to use the timed-wake-up feature of the esp's deep-sleep mode.
+
+To connect a JeeNode to the esp-bridge to flash the AVR or debug it, plug it into the FTDI
+port straight, i.e. the component side of the JeeNode and of the wifi-link will be on the top.
+
+To connect an arduino, jumper gnd, tx, rx, and dtr for reset. The wifi-link-12-v2 has a 2.2K resistor on rx (serial going from AVR to esp) in order to protect it from 5v signals.
+
+To program the JeeNode or AVR, having set-up the Wifi through the web pages, run avrdude with an
+option like "-Pnet:meta-id:23" (you can use an IP address instead of `meta-id`). My test command
+line is as follows:
+```
+/home/arduino/arduino-1.0.5/hardware/tools/avrdude \
+  -C /home/arduino/arduino-1.0.5/hardware/tools/avrdude.conf -DV -patmega328p \
+  -Pnet:meta-id:23 -carduino -b115200 -U flash:w:greenhouse.hex:i
+```
+If you're using "edam's Arduino makefile" then you can simply set `SERIALDEV=net:meta-id:23` in your
+sketch's Makefile. You can also use port 2323 which forces programming mode (no real benefit with avrdude, but can
+enable programming PICs).
+
+Serially reflashing the wifi-link itself (as opposed to the attached uController):
+_you should not need to do this!_, in general use the over-the-air reflashing by downloading the latest release.
+If you cannot reflash over-the-air and need to reflash serially, follow this process:
+- connect TX of the programmer (such as USB-BUB or a FDTI-friend) to DTR (FTDI pin 6)
+- connect RX of the programmer to CTS (FTDI pin 2)
+- short the flash jumper on the top of the board (push tweezers or a piece of wire into the jumper gap),
+  the green LED will be on solid when you have contact
+- briefly interrupt the power to reset the esp
+- it will now be in flash mode: the green LED should be off (assuming you release the flash jumper),
+  if you see the green LED come on it has rebooted into meta-id and you need to reset it again
+  (the whole proess take some fiddling, you can solder a real jumper or switch to the flash pads and there's a similar
+  reset jumper pad on the bottom of the PCB).
+- use your favorite programming tool to reflash, you have time if the esp really entered programming mode, it often takes me
+  2-3 tries until the programming works
+
+Serial flashing
+---------------
+
+Once you have a version of meta-id flashed to your module or if you received a pre-flashed
+module from me you should not need this section. But sometimes things fail badly and your
+module is "brocked", this is how you receover.
+
+### Installing esptool.py
+
+On Linux I am using [esptool.py](https://github.com/themadinventor/esptool) to flash the esp8266.
+If you're a little python challenged (like I am) then the following install instructions might help:
+ - Install ez_setup with the following two commands (I believe this will do something
+   reasonable if you already have it):
+
+        wget https://bootstrap.pypa.io/ez_setup.py
+        python ez_setup.py
+
+ - Install esptool.py:
+
+        git clone https://github.com/themadinventor/esptool.git
+        cd esptool
+        python setup.py install
+        cd ..
+        esptool.py -h
+
+### Flashing meta-id
+
+Using esptool.py a meta-id release can be flashed as follows:
+```
+curl -L https://github.com/jeelabs/esp-link/releases/download/0.10.1/esp-link.tgz | tar xzf -
+cd esp-link
+esptool.py write_flash 0x00000 boot_v1.4\(b1\).bin 0x1000 user1.bin 0x7e000 blank.bin
+```
+If you want to speed things up a bit and if you need to specify the port you can use a command
+line like:
+```
+esptool.py --port /dev/ttyUSB0 --baud 460880 write_flash 0x00000 boot_v1.4\(b1\).bin \
+           0x1000 user1.bin 0x7e000 blank.bin
+```

+ 73 - 0
meta-id/BUILDING.md

@@ -0,0 +1,73 @@
+Building meta-id
+=================
+
+Before you build meta-id, consider that you can download ready-made firmware images!
+Just head over to the [release section](https://github.com/jeelabs/meta-id/releases)
+and download the tgz archive.
+
+If you decide to build your own, there are a number of options:
+- On linux x86 download the ready-built toolchain and patched SDK like the automated build does
+  and compile the firmware
+- On linux download and build the toolchain, download and patch the SDK, then compile the firmware
+- On windows install mingw, python, java, and a slew of other tools and then build the
+  firmware
+
+Once you have built the firmware you will want to flash it to your esp8266 module.
+Assuming you already have meta-id running you can either go back to the initial flashing
+via the serial port or you can use the over-the-air (i.e. Wifi) update method, which is faster
+and more reliable (unless you have a non-booting version of meta-id).
+The OTA flashing is described at the end of this page,
+the serial flashing is described in [FLASHING.md](FLASHING.md).
+
+### Automated builds
+
+For every commit on github an automated build is made. This means that every branch, including
+master, and every pull request always has an up-to-date build. These builds are made by Travis
+using the instructions in `.travis.yml`, which basically consist of cloning the meta-id repo,
+downloading the compiler toolchain, downloading the Espressif SDK, and running `make`.
+If you're looking for how to build meta-id the travis instructions will always give you
+accurate pointers to what to download.
+
+### Linux
+
+The firmware has been built using the https://github.com/pfalcon/esp-open-sdk[esp-open-sdk]
+on a Linux system. Create an esp8266 directory, install the esp-open-sdk into a sub-directory
+using the *non-standalone* install (i.e., there should not be an sdk directory in the esp-open-sdk
+dir when done installing, *if you use the standalone install you will get compilation errors*
+with std types, such as `uint32_t`).
+
+Download the Espressif "NONOS" SDK (use the version mentioned in the release notes) from their
+http://bbs.espressif.com/viewforum.php?f=5[download forum] and also expand it into a
+sub-directory. Often there are patches to apply, in that case you need to download the patches
+from the same source and apply them.
+
+You can simplify your life (and avoid the hour-long build time for esp-open-sdk) if you are
+on an x86 box by downloading the packaged and built esp-open-sdk and the fully patches SDKfrom the
+links used in the `.travis.yaml`.
+
+Clone the meta-id repository into a third sub-directory and check out the tag you would like,
+such as `git checkout v2.2.3`.
+This way the relative paths in the Makefile will work.
+If you choose a different directory structure look at the top of the Makefile for the
+appropriate environment variables to define.
+Do not use the source tarballs from the release page on github,
+these will give you trouble compiling because the Makefile uses git to determine the meta-id
+version being built.
+
+In order to OTA-update the esp8266 you should `export ESP_HOSTNAME=...` with the hostname or
+IP address of your module.
+
+Now, build the code: `make` in the top-level of meta-id. If you want to se the commands being
+issued, use `VERBOSE=1 make`.
+
+A few notes from others (I can't fully verify these):
+
+- You may need to install `zlib1g-dev` and `python-serial`
+- Make sure you have the correct version of the SDK
+- Make sure the paths at the beginning of the makefile are correct
+- Make sure `esp-open-sdk/xtensa-lx106-elf/bin` is in the PATH set in the Makefile
+
+### Windows
+
+It is possible to build meta-id on Windows, but it requires a 
+[gaggle of software to be installed](WINDOWS.md)

+ 29 - 0
meta-id/CONTRIBUTING.md

@@ -0,0 +1,29 @@
+Contributing to Esp-Link
+========================
+
+Esp-link is not the work of a single individual, rather many people have contributed directly or indirectly.
+Your contribution is very much appreciated, but please follow these guidelines to make the task easier on
+everyone.
+
+- Contributions do not have to be in the form of code: often documentation, how-tos are very valuable and answering questions
+  in github issues as well as gitter is also very valuable and welcome!
+- Before you make a change or submit a change via a pull request, **open an issue and discuss your proposed change**. Gitter
+  is a good alternative to a github issue. This ensures that you don't spend time doing work that ultimately won't be accepted.
+  There's nothing more frustrating than receiving a pull-request that has lots of goodies but doesn't fit because it wasn't
+  discussed and agreed upon up-front.
+- Keep your pull request as small as practical, if you have 3 things you want to change, please create 3 pull requests,
+  or at the very least, make sure your 3 changes are in different commits. This makes the review and testing easier
+  and ensures that if one feature is good to go it can be merged even if another feature needs more tweaking.
+- The esp-link codebase is not uniform, it comes from a variety of sources, in particular esphttpd. A result of this is
+  that there is more than one coding style in use. If you make changes to existing files, please respect the file's
+  coding style (yes, sometimes that's not even totally uniform). Your overall goal should be for your code or changes to
+  look as if the original author had made them, not how you would like them to look.
+- Changes that reformat or reorganize code will generally not be accepted, please do not mix them with other functionality
+  changes you are making and certainly discuss them first. Accept the fact that some people prefer bastards over pure-breads ;-).
+- Esp-link has a mission stated in the readme.md, changes that deviate from that mission will generally be rejected. The reason
+  is that at the end of the day focusing on doing one thing well has a higher chance of succeeding than doing many things.
+  In that sense, esp-link is not a swiss-army knife firmware. (This being said, many people have used esp-link as a basis to add
+  their own functionality, which is very cool.)
+  
+I believe the above guidelines are pretty standard across a very large number of open source projects and not unique to esp-link,
+so please do not get discouraged. Thank you for taking a look at esp-link!

+ 78 - 0
meta-id/FLASH.md

@@ -0,0 +1,78 @@
+ESP-LINK OTA Flash Layout
+=========================
+
+The flash layout dictated by the bootloader is the following (all this assumes a 512KB flash chip
+and is documented in Espressif's `99C-ESP8266__OTA_Upgrade__EN_v1.5.pdf`):
+ - @0x00000 4KB bootloader
+ - @0x01000 236KB partition1
+ - @0x3E000 16KB esp-link parameters
+ - @0x40000 4KB unused
+ - @0x41000 236KB partition2
+ - @0x7E000 16KB system wifi parameters
+
+What this means is that we can flash just about anything into partition1 or partition2 as long
+as it doesn't take more than 236KB and has the right format that the boot loader understands.
+We can't mess with the first 4KB nor the last 16KB of the flash.
+
+Now how does a code partition break down? that is reflected in the following definition found in
+the loader scripts:
+```
+  dram0_0_seg :                         org = 0x3FFE8000, len = 0x14000
+  iram1_0_seg :                         org = 0x40100000, len = 0x8000
+  irom0_0_seg :                         org = 0x40201010, len = 0x2B000
+```
+This means that 80KB (0x14000) are reserved for "dram0_0", 32KB (0x8000) for "iram1_0" and
+172KB (0x2B000) are reserved for irom0_0. The segments are used as follows:
+ - dram0_0 is the data RAM and some of that gets initialized at boot time from flash (static variable initialization)
+ - iram1_0 is the instruction RAM and all of that gets loaded at boot time from flash
+ - irom0_0 is the instruction cache which gets loaded on-demand from flash (all functions
+   with the `ICACHE_FLASH_ATTR` attribute go there)
+
+You might notice that 80KB+32KB+172KB is more than 236KB and that's because not the entire dram0_0
+segment needs to be loaded from flash, only the portion with statically initialized data.
+You might also notice that while iram1_0 is as large as the chip's instruction RAM (at least
+according to the info I've seen) the size of the irom0_0 segment is smaller than it could be,
+since it's really not bounded by any limitation of the processor (it simply backs the cache).
+
+When putting the OTA flash process together I ran into loader issues, namely, while I was having
+relatively little initialized data and also not 32KB of iram1_0 instructions I was overflowing
+the allotted 172KB of irom0_0. To fix the problem the build process modifies the loader scripts
+(see the `build/eagle.esphttpd1.v6.ld` target in the Makefile) to increase the irom0_0 segment
+to 224KB (a somewhat arbitrary value). This doesn't mean that there will be 224KB of irom0_0
+in flash, it just means that that's the maximum the linker will put there without giving an error.
+In the end what has to fit into the magic 236KB is the sum of the actual initialized data,
+the actually used iram1_0 segment, and the irom0_0 segment.
+In addition, the dram0_0 and iram1_0 segments can't exceed what's specified
+in the loader script 'cause those are the limitations of the processor.
+
+Now that you hopefully understand the above you can understand the line printed by the Makefile
+when linking the firmware, which looks something like:
+```
+** user1.bin uses 218592 bytes of 241664 available
+```
+Here 241664 is 236KB and 218592 is the size of what's getting flashed, so you can tell that you have
+another 22KB to spend (modulo some 4KB flash segment rounding).
+(Note that user2.bin has exactly the same size, so the Makefile doesn't print its info.)
+The Makefile also prints a few more details:
+```
+ls -ls eagle*bin
+  4 -rwxrwxr-x 1 tve tve   2652 May 24 10:12 eagle.app.v6.data.bin
+176 -rwxrwxr-x 1 tve tve 179732 May 24 10:12 eagle.app.v6.irom0text.bin
+  8 -rwxrwxr-x 1 tve tve   5732 May 24 10:12 eagle.app.v6.rodata.bin
+ 32 -rwxrwxr-x 1 tve tve  30402 May 24 10:12 eagle.app.v6.text.bin
+```
+This says that we have 179732 bytes of irom0_0, we have 5732+2652 bytes of dram0_0 (read-only data
+plus initialized read-write data), and we have 30402 bytes of iram1_0.
+
+There's an additional twist to all this for the espfs "file system" that esphttpd uses.
+The data for this is loaded at the end of irom0_0 and is called espfs.
+The Makefile modifies the loader script to place the espfs at the start of irom0_0 and
+ensure that it's 32-bit aligned. The size of the espfs is shown here:
+```
+4026be14 g       .irom0.text    00000000 _binary_espfs_img_end
+40269e98 g       .irom0.text    00000000 _binary_espfs_img_start
+00001f7c g       *ABS*  00000000 _binary_espfs_img_size
+```
+Namely, 0x1f7c = 8060 bytes.
+
+

+ 204 - 0
meta-id/FLASHING.md

@@ -0,0 +1,204 @@
+Flashing meta-id
+=================
+
+### Hardware configuration for normal operation
+
+This firmware is designed for any esp8266 module.
+The recommended connections for an esp-01 module are:
+
+- URXD: connect to TX of microcontroller
+- UTXD: connect to RX of microcontroller
+- GPIO0: connect to RESET of microcontroller
+- GPIO2: optionally connect green LED to 3.3V (indicates wifi status)
+
+The recommended connections for an esp-12 module are:
+
+- URXD: connect to TX of microcontroller
+- UTXD: connect to RX of microcontroller
+- GPIO12: connect to RESET of microcontroller
+- GPIO13: connect to ISP of LPC/ARM microcontroller (not used with Arduino/AVR)
+- GPIO0: either a 1k-10k pull-up resistor to 3.3v or a green "conn" LED via a 1k-2.2k
+  resistor to 3.3V (indicates wifi status)
+- GPIO2: either a 1k-10k pull-up resistor to 3.3v or a yellow "ser" LED via a 1k-2.2k
+  resistor to 3.3V (indicates serial activity)
+
+At boot time the esp8266 ROM outputs a boot message on UTXD, this can cause problems to the attached
+microcontroller. If you need to avoid this, you can configure meta-id to swap the uart pins.
+You should then connect the esp-12 module as follows and choose the "swap_uart" pin assignment
+in the meta-id web interface:
+
+- GPIO13: connect to TX of microcontroller
+- GPIO15: connect to RX of microcontroller and use a pull-down to ensure proper booting
+- GPIO12: connect to RESET of microcontroller
+- GPIO14: connect to ISP of LPC/ARM microcontroller (not used with Arduino/AVR)
+- GPIO0: either a 1k-10k pull-up resistor to 3.3v or a green "conn" LED via a 1k-2.2k
+  resistor to 3.3V (indicates wifi status)
+- GPIO2: either a 1k-10k pull-up resistor to 3.3v or a yellow "ser" LED via a 1k-2.2k
+  resistor to 3.3V (indicates serial activity)
+
+The GPIO pin assignments can be changed dynamically in the web UI and are saved in flash.
+
+### Hardware configuration for flashing
+
+To flash firmware onto the esp8266 via the serial port the following must be observed:
+- GPIO0 must be low when reset ends to put the esp8266 into flash programming mode, it must be high
+  to enter normal run mode
+- GPIO2 must be high (pull-up resistor)
+- GPIO15 must be low (pull-down resistor)
+
+### Initial serial flashing
+
+Download the latest [release](https://github.com/jeelabs/meta-id/releases) or use the
+`user1.bin` file that is produced by the build process.
+You will need to flash the bootloader, the `user1.bin` firmware, blank wifi settings, and init data
+as described below.
+
+_Important_: the firmware adapts to the size of the flash chip using information
+stored in the boot sector (address 0). This is the standard way that the esp8266 SDK detects
+the flash size. What this means is that you need to set this properly when you flash the bootloader.
+If you use esptool.py you can do it using the -ff and -fs options. See the end of this page for
+instructions on installing esptool.py.
+
+The short version for the serial flashing is:
+- flash `boot_v1.X.bin` from the official SDK or from the release tgz to `0x00000`
+- flash `blank.bin` from the official SDK or from the tgz to `0x3FE000`
+- flash `esp_init_data_default.bin` from the official SDK or from the tgz to `0x3FC000`
+- flash `user1.bin` to `0x01000`
+- be sure to use the commandline flags to set the correct flash size when flashing the bootloader
+- some of the addresses vary with flash chip size
+
+After the initial flashing if you want to update the firmware it is recommended to use the
+over-the-air update described further down. If you want to update serially you only need to
+reflash `user1.bin`.
+
+### 32Mbit / 4Mbyte module
+On Linux using esptool.py this turns into the following for a 32mbit=4MByte flash chip,
+such as an esp-12 module typically has (_substitute the appropriate release number and bootloader
+version number_):
+```
+curl -L https://github.com/jeelabs/meta-id/releases/download/v2.2.3/meta-id-v2.2.3.tgz | \
+    tar xzf -
+cd meta-id-v2.2.3
+esptool.py --port /dev/ttyUSB0 --baud 230400 write_flash -fs 32m -ff 80m \
+    0x00000 boot_v1.5.bin 0x1000 user1.bin \
+    0x3FC000 esp_init_data_default.bin 0x3FE000 blank.bin
+```
+I use a high baud rate as shown above because I'm impatient, but that's not required.
+
+### 4Mbit / 512Kbyte module
+```
+curl -L https://github.com/jeelabs/meta-id/releases/download/v2.2.3/meta-id-v2.2.3.tgz | \
+    tar xzf -
+cd meta-id-v2.2.3
+esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash -fs 4m -ff 40m \
+    0x00000 boot_v1.5.bin 0x1000 user1.bin \
+    0x7C000 esp_init_data_default.bin 0x7E000 blank.bin
+```
+The `-fs 4m -ff40m` options say 4Mbits and 40Mhz as opposed to 32Mbits at 80Mhz for the 4MByte
+flash modules. Note the different address for esp_init_data_default.bin and blank.bin
+(the SDK stores its wifi settings near the end of flash, so it changes with flash size).
+
+For __8Mbit / 1MByte__ modules the addresses are 0xFC000 and 0xFE000.
+
+__Warning__: there is a bug in boot_v1.5.bin which causes it to only boot into user1 once.
+If that fails it gets stuck trying to boot into user2. If this happens (can be seen in the
+boot output on uart2 at 76600 baud) reflash just blank.bin at 0x7E000 (4Mbit module). (Sigh)
+
+## Updating the firmware over-the-air
+
+This firmware supports over-the-air (OTA) flashing for modules with 1MByte or more flash,
+so you do not have to deal with serial flashing again after the initial one!
+The recommended way to flash is to use `make wiflash`
+if you are also building the firmware and `./wiflash` if you are downloading firmware binaries.
+
+The resulting commandlines are:
+```
+ESP_HOSTNAME=192.168.1.5 make wiflash
+```
+or assuming mDNS is working:
+```
+ESP_HOSTNAME=meta-id.local make wiflash
+```
+or using wiflash.sh:
+```
+./wiflash.sh <esp-hostname> user1.bin user2.bin
+```
+
+The flashing, restart, and re-associating with your wireless network takes about 15 seconds
+and is fully automatic. The first 1MB of flash are divided into two 512KB partitions allowing for new
+code to be uploaded into one partition while running from the other. This is the official
+OTA upgrade method supported by the SDK, except that the firmware is POSTed to the module
+using curl as opposed to having the module download it from a cloud server. On a module with
+512KB flash there is only space for one partition and thus no way to do an OTA update.
+
+If you need to clear the wifi settings you need to reflash the `blank.bin`
+using the serial method.
+
+The flash configuration and the OTA upgrade process is described in more detail
+in [FLASH.md](FLASH.md).
+
+## Installing esptool.py on Linux
+
+On Linux use [esptool.py](https://github.com/themadinventor/esptool) to flash the esp8266.
+If you're a little python challenged then the following install instructions might help:
+ - Install ez_setup with the following two commands (I believe this will do something
+   reasonable if you already have it):
+
+        wget https://bootstrap.pypa.io/ez_setup.py
+        python ez_setup.py
+
+ - Install esptool.py:
+
+        git clone https://github.com/themadinventor/esptool.git
+        cd esptool
+        python setup.py install
+        cd ..
+        esptool.py -h
+
+## Installing esptool.py on Windows
+
+Esptool is a pythin pgm that works just fine on windows. These instructions assume that git and
+python are available from the commandline.
+
+Start a command line, clone esptool, and run `python setup.py install` in esptool's
+directory (this step needs to be done only once):
+```
+> git clone https://github.com/themadinventor/esptool.git
+Cloning into 'esptool'...
+remote: Counting objects: 268, done.
+emote: Total 268 (delta 0), reused 0 (delta 0), pack-reused 268
+Receiving objects: 100% (268/268), 99.66 KiB | 0 bytes/s, done.
+Resolving deltas: 100% (142/142), done.
+Checking connectivity... done.
+
+> cd esptool
+
+> python setup.py install
+running install
+...
+...
+...
+Finished processing dependencies for esptool==0.1.0
+```
+
+Download and unzip the latest meta-id release package, and start a commandline
+in that directory. The command to run is pretty much the same as for linux.
+Adjust the path to esptool and the COM port if you don't have the ESP on COM12. 460800
+baud worked just fine for me, writing at ~260kbit/s instead of ~80kbit/s.
+```
+>python "../esptool/esptool.py" --port COM12 --baud 115200 write_flash \
+  --flash_freq 80m --flash_mode qio --flash_size 32m \
+  0x0000 boot_v1.6.bin 0x1000 user1.bin \
+  0x3FC000 esp_init_data_default.bin 0x3FE000 blank.bin
+Connecting...
+Erasing flash...
+Wrote 3072 bytes at 0x00000000 in 0.3 seconds (79.8 kbit/s)...
+Erasing flash...
+Wrote 438272 bytes at 0x00001000 in 43.4 seconds (80.7 kbit/s)...
+Erasing flash...
+Wrote 1024 bytes at 0x003fc000 in 0.1 seconds (83.6 kbit/s)...
+Erasing flash...
+Wrote 4096 bytes at 0x003fe000 in 0.4 seconds (83.4 kbit/s)...
+
+Leaving...
+```

+ 123 - 0
meta-id/LICENSE.txt

@@ -0,0 +1,123 @@
+"THE BEER-WARE LICENSE" (Revision 42):
+ikujam (ikujam@ikujam.org) adapted this project. As long as you retain
+this notice you can do whatever you want with this stuff. If we meet some day,
+and you think this stuff is worth it, you can buy me a beer in return.
+
+
+----
+This project is based on esp-link (https://github.com/jeelabs/esp-link)
+
+having the following licence:
+
+Copyright (c) Thorsten von Eicken, 2015
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---------------------------------------------------------------------
+
+Portions of this software are derived from esphttpd and have the following
+license:
+
+"THE BEER-WARE LICENSE" (Revision 42):
+Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
+this notice you can do whatever you want with this stuff. If we meet some day,
+and you think this stuff is worth it, you can buy me a beer in return.
+
+---------------------------------------------------------------------
+
+Portions of this software are derived from Espressif's SDK and carry the
+following copyright notice:
+
+This file is part of Espressif's AT+ command set program.
+Copyright (C) 2013 - 2016, Espressif Systems
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of version 3 of the GNU General Public License as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+---------------------------------------------------------------------
+
+The Pure CSS portions of this software carry the following license:
+
+Copyright 2014 Yahoo! Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the name of the Yahoo! Inc. nor the
+  names of its contributors may be used to endorse or promote products
+  derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL YAHOO! INC. BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+---------------------------------------------------------------------
+
+Normalize.css used in this firmware carries the following license:
+
+Copyright (c) Nicolas Gallagher and Jonathan Neal
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+

+ 545 - 0
meta-id/Makefile

@@ -0,0 +1,545 @@
+#
+# Makefile for meta-id - https://github.com/jeelabs/meta-id
+#
+# Makefile heavily adapted to meta-id and wireless flashing by Thorsten von Eicken
+# Lots of work, in particular to support windows, by brunnels
+# Original from esphttpd and others...
+#
+# Start by setting the directories for the toolchain a few lines down
+# the default target will build the firmware images
+# `make flash` will flash the esp serially
+# `make wiflash` will flash the esp over wifi
+# `VERBOSE=1 make ...` will print debug info
+# `ESP_HOSTNAME=my.esp.example.com make wiflash` is an easy way to override a variable
+
+# optional local configuration file
+-include local.conf
+
+# The Wifi station configuration can be hard-coded here, which makes meta-id come up in STA+AP
+# mode trying to connect to the specified AP *only* if the flash wireless settings are empty!
+# This happens on a full serial flash and avoids having to hunt for the AP...
+# STA_SSID ?=
+# STA_PASS ?= 
+
+# The SOFTAP configuration can be hard-coded here, the minimum parameters to set are AP_SSID && AP_PASS
+# The AP SSID has to be at least 8 characters long, same for AP PASSWORD
+# The AP AUTH MODE can be set to:
+#  0 = AUTH_OPEN, 
+#  1 = AUTH_WEP, 
+#  2 = AUTH_WPA_PSK, 
+#  3 = AUTH_WPA2_PSK, 
+#  4 = AUTH_WPA_WPA2_PSK
+# SSID hidden default 0, ( 0 | 1 ) 
+# Max connections default 4, ( 1 ~ 4 )
+# Beacon interval default 100, ( 100 ~ 60000ms )
+#
+# AP_SSID ?=esp_link_test
+# AP_PASS ?=esp_link_test
+# AP_AUTH_MODE ?=4
+# AP_SSID_HIDDEN ?=0
+# AP_MAX_CONN ?=4
+# AP_BEACON_INTERVAL ?=100
+AP_SSID ?=.META_NNNN
+AP_PASS ?=
+AP_AUTH_MODE ?=0
+
+# If CHANGE_TO_STA is set to "yes" the meta-id module will switch to station mode
+# once successfully connected to an access point. Else it will stay in STA+AP mode.
+CHANGE_TO_STA ?= no
+
+# hostname or IP address for wifi flashing
+ESP_HOSTNAME  ?= meta-id
+
+# --------------- toolchain configuration ---------------
+
+# Base directory for the compiler. Needs a / at the end.
+# Typically you'll install https://github.com/pfalcon/esp-open-sdk
+# IMPORTANT: use esp-open-sdk `make STANDALONE=n`: the SDK bundled with esp-open-sdk will *not* work!
+XTENSA_TOOLS_ROOT ?= $(abspath ../esp-open-sdk/xtensa-lx106-elf/bin)/
+
+# Firmware version 
+# WARNING: if you change this expect to make code adjustments elsewhere, don't expect
+# that meta-id will magically work with a different version of the SDK!!!
+SDK_VERS ?= esp_iot_sdk_v2.1.0
+
+# Try to find the firmware manually extracted, e.g. after downloading from Espressif's BBS,
+# http://bbs.espressif.com/viewforum.php?f=46
+# USING THE SDK BUNDLED WITH ESP-OPEN-SDK WILL NOT WORK!!!
+SDK_BASE ?= $(wildcard ../$(SDK_VERS))
+
+# If the firmware isn't there, see whether it got downloaded as part of esp-open-sdk
+# This used to work at some point, but is not supported, uncomment if you feel lucky ;-)
+#ifeq ($(SDK_BASE),)
+#SDK_BASE := $(wildcard $(XTENSA_TOOLS_ROOT)/../../$(SDK_VERS))
+#endif
+
+# Clean up SDK path
+SDK_BASE := $(abspath $(SDK_BASE))
+$(info SDK     is $(SDK_BASE))
+
+# Path to bootloader file
+BOOTFILE	?= $(SDK_BASE/bin/boot_v1.6.bin)
+
+# Esptool.py path and port, only used for 1-time serial flashing
+# Typically you'll use https://github.com/themadinventor/esptool
+# Windows users use the com port i.e: ESPPORT ?= com3
+ESPTOOL		?= $(abspath ../esp-open-sdk/esptool/esptool.py)
+ESPPORT		?= /dev/ttyUSB0
+ESPBAUD		?= 230400
+
+# --------------- chipset configuration   ---------------
+
+# Pick your flash size: "512KB", "1MB", or "4MB"
+FLASH_SIZE ?= 4MB
+
+# The pin assignments below are used when the settings in flash are invalid, they
+# can be changed via the web interface
+# GPIO pin used to reset attached microcontroller, acative low
+MCU_RESET_PIN       ?= 12
+# GPIO pin used with reset to reprogram MCU (ISP=in-system-programming, unused with AVRs), active low
+MCU_ISP_PIN         ?= 13
+# GPIO pin used for "connectivity" LED, active low
+LED_CONN_PIN        ?= 0
+# GPIO pin used for "serial activity" LED, active low
+LED_SERIAL_PIN      ?= 14
+
+# --------------- meta-id modules config options ---------------
+
+# Optional Modules: mqtt rest socket web-server syslog
+MODULES ?= mqtt
+
+# --------------- esphttpd config options ---------------
+
+# If GZIP_COMPRESSION is set to "yes" then the static css, js, and html files will be compressed
+# with gzip before added to the espfs image and will be served with gzip Content-Encoding header.
+# This could speed up the downloading of these files, but might break compatibility with older
+# web browsers not supporting gzip encoding because Accept-Encoding is simply ignored.
+# Enable this option if you have large static files to serve (for e.g. JQuery, Twitter bootstrap)
+# If you have text based static files with different extensions what you want to serve compressed
+# then you will need to add the extension to the following places:
+# - Add the extension to this Makefile at the webpages.espfs target to the find command
+# - Add the extension to the gzippedFileTypes array in the user/httpd.c file
+#
+# Adding JPG or PNG files (and any other compressed formats) is not recommended, because GZIP
+# compression does not work effectively on compressed files.
+GZIP_COMPRESSION ?= yes
+
+# If COMPRESS_W_HTMLCOMPRESSOR is set to "yes" then the static css and js files will be compressed with
+# htmlcompressor and yui-compressor. This option works only when GZIP_COMPRESSION is set to "yes".
+# https://code.google.com/p/htmlcompressor/#For_Non-Java_Projects
+# http://yui.github.io/yuicompressor/
+# enabled by default.
+COMPRESS_W_HTMLCOMPRESSOR ?= yes
+HTML_COMPRESSOR ?= htmlcompressor-1.5.3.jar
+YUI_COMPRESSOR ?= yuicompressor-2.4.8.jar
+
+# -------------- End of config options -------------
+
+HTML_PATH = $(abspath ./html)/
+WIFI_PATH = $(HTML_PATH)wifi/
+TEXT_PATH = $(HTML_PATH)text/
+FONT_PATH = $(HTML_PATH)fonts/
+
+ESP_FLASH_MAX       ?= 503808  # max bin file
+
+ifeq ("$(FLASH_SIZE)","512KB")
+# Winbond 25Q40 512KB flash, typ for esp-01 thru esp-11
+ESP_SPI_SIZE        ?= 0       # 0->512KB (256KB+256KB)
+ESP_FLASH_MODE      ?= 0       # 0->QIO
+ESP_FLASH_FREQ_DIV  ?= 0       # 0->40Mhz
+ET_FS               ?= 4m      # 4Mbit flash size in esptool flash command
+ET_FF               ?= 40m     # 40Mhz flash speed in esptool flash command
+ET_INIT_DATA		?= 0x7C000 # where to flash esp_init_data_default.bin
+ET_BLANK            ?= 0x7E000 # where to flash blank.bin to erase wireless settings
+
+else ifeq ("$(FLASH_SIZE)","1MB")
+# ESP-01E
+ESP_SPI_SIZE        ?= 2       # 2->1MB (512KB+512KB)
+ESP_FLASH_MODE      ?= 0       # 0->QIO
+ESP_FLASH_FREQ_DIV  ?= 15      # 15->80MHz
+ET_FS               ?= 8m      # 8Mbit flash size in esptool flash command
+ET_FF               ?= 80m     # 80Mhz flash speed in esptool flash command
+ET_INIT_DATA		?= 0xFC000 # where to flash esp_init_data_default.bin
+ET_BLANK            ?= 0xFE000 # where to flash blank.bin to erase wireless settings
+
+else ifeq ("$(FLASH_SIZE)","2MB")
+# Manuf 0xA1 Chip 0x4015 found on wroom-02 modules
+# Here we're using two partitions of approx 0.5MB because that's what's easily available in terms
+# of linker scripts in the SDK. Ideally we'd use two partitions of approx 1MB, the remaining 2MB
+# cannot be used for code (esp8266 limitation).
+ESP_SPI_SIZE        ?= 4       # 6->4MB (1MB+1MB) or 4->4MB (512KB+512KB)
+ESP_FLASH_MODE      ?= 0       # 0->QIO, 2->DIO
+ESP_FLASH_FREQ_DIV  ?= 15      # 15->80Mhz
+:q
+ET_FS               ?= 16m     # 16Mbit flash size in esptool flash command
+ET_FF               ?= 80m     # 80Mhz flash speed in esptool flash command
+ET_INIT_DATA		?= 0x1FC000 # where to flash esp_init_data_default.bin
+ET_BLANK            ?= 0x1FE000 # where to flash blank.bin to erase wireless settings
+
+else
+# Winbond 25Q32 4MB flash, typ for esp-12
+# Here we're using two partitions of approx 0.5MB because that's what's easily available in terms
+# of linker scripts in the SDK. Ideally we'd use two partitions of approx 1MB, the remaining 2MB
+# cannot be used for code (esp8266 limitation).
+ESP_SPI_SIZE        ?= 4       # 6->4MB (1MB+1MB) or 4->4MB (512KB+512KB)
+ESP_FLASH_MODE      ?= 0       # 0->QIO, 2->DIO
+ESP_FLASH_FREQ_DIV  ?= 15      # 15->80Mhz
+ET_FS               ?= 32m     # 32Mbit flash size in esptool flash command
+ET_FF               ?= 80m     # 80Mhz flash speed in esptool flash command
+ET_INIT_DATA		?= 0x3FC000 # where to flash esp_init_data_default.bin
+ET_BLANK            ?= 0x3FE000 # where to flash blank.bin to erase wireless settings
+endif
+
+# --------------- meta-id version        ---------------
+
+# Version-fu :-) This code assumes that a new maj.minor is started using a "vN.M.0" tag on master
+# and that thereafter the desired patchlevel number is just the number of commits since the tag.
+#
+# Get the current branch name if not using travis
+TRAVIS_BRANCH?=$(shell git symbolic-ref --short HEAD --quiet)
+# Use git describe to get the latest version tag, commits since then, sha and dirty flag, this
+# results is something like "v1.2.0-13-ab6cedf-dirty"
+NO_TAG ?= "no-tag"
+VERSION := $(shell (git describe --tags --match 'v*.0' --long --dirty || echo $(NO_TAG)) | sed -re 's/(\.0)?-/./')
+# If not on master then insert the branch name
+ifneq ($(TRAVIS_BRANCH),master)
+ifneq ($(findstring V%,$(TRAVIS_BRANCH)),)
+VERSION := $(shell echo $(VERSION) | sed -e 's/-/-$(TRAVIS_BRANCH)-/')
+endif
+endif
+VERSION :=$(VERSION)
+$(info VERSION is $(VERSION))
+
+# Output directors to store intermediate compiled files
+# relative to the project directory
+BUILD_BASE	= build
+FW_BASE		= firmware
+
+# name for the target project
+TARGET		= httpd
+
+# espressif tool to concatenate sections for OTA upload using bootloader v1.2+
+APPGEN_TOOL	?= gen_appbin.py
+
+CFLAGS=
+
+# set defines for optional modules
+ifneq (,$(findstring mqtt,$(MODULES)))
+	CFLAGS		+= -DMQTT
+endif
+
+ifneq (,$(findstring rest,$(MODULES)))
+	CFLAGS		+= -DREST
+endif
+
+ifneq (,$(findstring syslog,$(MODULES)))
+	CFLAGS		+= -DSYSLOG
+endif
+
+ifneq (,$(findstring web-server,$(MODULES)))
+	CFLAGS		+= -DWEBSERVER
+endif
+
+ifneq (,$(findstring socket,$(MODULES)))
+	CFLAGS		+= -DSOCKET
+endif
+
+# flag to disable espconn_secure functions
+CFLAGS		+= -DNOSSL 
+
+# which modules (subdirectories) of the project to include in compiling
+LIBRARIES_DIR 	= libraries
+MODULES		+= espfs httpd cmd serial meta-id user
+MODULES		+= $(foreach sdir,$(LIBRARIES_DIR),$(wildcard $(sdir)/*))
+EXTRA_INCDIR 	= include .
+
+# libraries used in this project, mainly provided by the SDK
+LIBS = c gcc hal phy pp net80211 wpa main lwip_536 crypto pwm
+
+# compiler flags using during compilation of source files
+CFLAGS	+= -Os -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \
+	-nostdlib -mlongcalls -mtext-section-literals -ffunction-sections -fdata-sections \
+	-D__ets__ -DICACHE_FLASH -Wno-address -DFIRMWARE_SIZE=$(ESP_FLASH_MAX) \
+	-DMCU_RESET_PIN=$(MCU_RESET_PIN) -DMCU_ISP_PIN=$(MCU_ISP_PIN) \
+	-DLED_CONN_PIN=$(LED_CONN_PIN) -DLED_SERIAL_PIN=$(LED_SERIAL_PIN) \
+	-DVERSION="meta-id $(VERSION)"
+
+# linker flags used to generate the main object file
+LDFLAGS		= -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static -Wl,--gc-sections
+
+# linker script used for the above linker step
+LD_SCRIPT 	:= build/eagle.esphttpd.v6.ld
+LD_SCRIPT1	:= build/eagle.esphttpd1.v6.ld
+LD_SCRIPT2	:= build/eagle.esphttpd2.v6.ld
+
+# various paths from the SDK used in this project
+SDK_LIBDIR	= lib
+SDK_LDDIR	= ld
+SDK_INCDIR	= include include/json
+SDK_TOOLSDIR	= tools
+
+# select which tools to use as compiler, librarian and linker
+CC		:= $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-gcc
+AR		:= $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-ar
+LD		:= $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-gcc
+OBJCP		:= $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-objcopy
+OBJDP		:= $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-objdump
+ELF_SIZE	:= $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-size
+
+####
+SRC_DIR		:= $(MODULES)
+BUILD_DIR	:= $(addprefix $(BUILD_BASE)/,$(MODULES))
+
+SDK_LIBDIR	:= $(addprefix $(SDK_BASE)/,$(SDK_LIBDIR))
+SDK_LDDIR 	:= $(addprefix $(SDK_BASE)/,$(SDK_LDDIR))
+SDK_INCDIR	:= $(addprefix -I$(SDK_BASE)/,$(SDK_INCDIR))
+SDK_TOOLS	:= $(addprefix $(SDK_BASE)/,$(SDK_TOOLSDIR))
+APPGEN_TOOL	:= $(addprefix $(SDK_TOOLS)/,$(APPGEN_TOOL))
+
+SRC		:= $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c))
+OBJ		:= $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC)) $(BUILD_BASE)/espfs_img.o
+LIBS		:= $(addprefix -l,$(LIBS))
+APP_AR		:= $(addprefix $(BUILD_BASE)/,$(TARGET)_app.a)
+USER1_OUT 	:= $(addprefix $(BUILD_BASE)/,$(TARGET).user1.out)
+USER2_OUT 	:= $(addprefix $(BUILD_BASE)/,$(TARGET).user2.out)
+
+INCDIR		:= $(addprefix -I,$(SRC_DIR))
+EXTRA_INCDIR	:= $(addprefix -I,$(EXTRA_INCDIR))
+MODULE_INCDIR	:= $(addsuffix /include,$(INCDIR))
+
+V ?= $(VERBOSE)
+ifeq ("$(V)","1")
+Q :=
+vecho := @true
+else
+Q := @
+vecho := @echo
+endif
+
+ifneq ($(strip $(STA_SSID)),)
+CFLAGS		+= -DSTA_SSID="$(STA_SSID)"
+endif
+
+ifneq ($(strip $(STA_PASS)),)
+CFLAGS		+= -DSTA_PASS="$(STA_PASS)"
+endif
+
+ifneq ($(strip $(AP_SSID)),)
+CFLAGS		+= -DAP_SSID="$(AP_SSID)"
+endif
+
+ifneq ($(strip $(AP_PASS)),)
+CFLAGS		+= -DAP_PASS="$(AP_PASS)"
+endif
+
+ifneq ($(strip $(AP_AUTH_MODE)),)
+CFLAGS		+= -DAP_AUTH_MODE="$(AP_AUTH_MODE)"
+endif
+
+ifneq ($(strip $(AP_SSID_HIDDEN)),)
+CFLAGS		+= -DAP_SSID_HIDDEN="$(AP_SSID_HIDDEN)"
+endif
+
+ifneq ($(strip $(AP_MAX_CONN)),)
+CFLAGS		+= -DAP_MAX_CONN="$(AP_MAX_CONN)"
+endif
+
+ifneq ($(strip $(AP_BEACON_INTERVAL)),)
+CFLAGS		+= -DAP_BEACON_INTERVAL="$(AP_BEACON_INTERVAL)"
+endif
+
+ifeq ("$(GZIP_COMPRESSION)","yes")
+CFLAGS		+= -DGZIP_COMPRESSION
+endif
+
+ifeq ("$(CHANGE_TO_STA)","yes")
+CFLAGS		+= -DCHANGE_TO_STA
+endif
+
+vpath %.c $(SRC_DIR)
+
+define compile-objects
+$1/%.o: %.c
+	$(vecho) "CC $$<"
+	$(Q)$(CC) $(INCDIR) $(MODULE_INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) $(CFLAGS)  -c $$< -o $$@
+endef
+
+.PHONY: all checkdirs clean webpages.espfs wiflash
+
+all: checkdirs $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin
+
+$(USER1_OUT): $(APP_AR) $(LD_SCRIPT1)
+	$(vecho) "LD $@"
+	$(Q) $(LD) -L$(SDK_LIBDIR) -T$(LD_SCRIPT1) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@
+	@echo Dump  : $(OBJDP) -x $(USER1_OUT)
+	@echo Disass: $(OBJDP) -d -l -x $(USER1_OUT)
+#	$(Q) $(OBJDP) -x $(TARGET_OUT) | egrep espfs_img
+
+$(USER2_OUT): $(APP_AR) $(LD_SCRIPT2)
+	$(vecho) "LD $@"
+	$(Q) $(LD) -L$(SDK_LIBDIR) -T$(LD_SCRIPT2) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@
+#	$(Q) $(OBJDP) -x $(TARGET_OUT) | egrep espfs_img
+
+$(FW_BASE):
+	$(vecho) "FW $@"
+	$(Q) mkdir -p $@
+
+$(FW_BASE)/user1.bin: $(USER1_OUT) $(FW_BASE)
+	$(Q) $(OBJCP) --only-section .text -O binary $(USER1_OUT) eagle.app.v6.text.bin
+	$(Q) $(OBJCP) --only-section .data -O binary $(USER1_OUT) eagle.app.v6.data.bin
+	$(Q) $(OBJCP) --only-section .rodata -O binary $(USER1_OUT) eagle.app.v6.rodata.bin
+	$(Q) $(OBJCP) --only-section .irom0.text -O binary $(USER1_OUT) eagle.app.v6.irom0text.bin
+	$(Q) $(ELF_SIZE) -A $(USER1_OUT) |grep -v " 0$$" |grep .
+	$(Q) COMPILE=gcc PATH=$(XTENSA_TOOLS_ROOT):$(PATH) python2 $(APPGEN_TOOL) $(USER1_OUT) 2 $(ESP_FLASH_MODE) $(ESP_FLASH_FREQ_DIV) $(ESP_SPI_SIZE) 0 >/dev/null
+	$(Q) rm -f eagle.app.v6.*.bin
+	$(Q) mv eagle.app.flash.bin $@
+	@echo "    user1.bin uses $$(stat -c '%s' $@) bytes of" $(ESP_FLASH_MAX) "available"
+	$(Q) if [ $$(stat -c '%s' $@) -gt $$(( $(ESP_FLASH_MAX) )) ]; then echo "$@ too big!"; false; fi
+
+$(FW_BASE)/user2.bin: $(USER2_OUT) $(FW_BASE)
+	$(Q) $(OBJCP) --only-section .text -O binary $(USER2_OUT) eagle.app.v6.text.bin
+	$(Q) $(OBJCP) --only-section .data -O binary $(USER2_OUT) eagle.app.v6.data.bin
+	$(Q) $(OBJCP) --only-section .rodata -O binary $(USER2_OUT) eagle.app.v6.rodata.bin
+	$(Q) $(OBJCP) --only-section .irom0.text -O binary $(USER2_OUT) eagle.app.v6.irom0text.bin
+	$(Q) COMPILE=gcc PATH=$(XTENSA_TOOLS_ROOT):$(PATH) python2 $(APPGEN_TOOL) $(USER2_OUT) 2 $(ESP_FLASH_MODE) $(ESP_FLASH_FREQ_DIV) $(ESP_SPI_SIZE) 1 >/dev/null
+	$(Q) rm -f eagle.app.v6.*.bin
+	$(Q) mv eagle.app.flash.bin $@
+	$(Q) if [ $$(stat -c '%s' $@) -gt $$(( $(ESP_FLASH_MAX) )) ]; then echo "$@ too big!"; false; fi
+
+$(APP_AR): $(OBJ)
+	$(vecho) "AR $@"
+	$(Q) $(AR) cru $@ $^
+
+checkdirs: $(BUILD_DIR)
+
+$(BUILD_DIR):
+	$(Q) mkdir -p $@
+
+wiflash: all
+	./wiflash $(ESP_HOSTNAME) $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin
+
+baseflash: all
+	$(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash 0x01000 $(FW_BASE)/user1.bin
+
+flash: all
+	$(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash -fs $(ET_FS) -ff $(ET_FF) \
+	  0x00000 "$(SDK_BASE)/bin/boot_v1.6.bin" 0x01000 $(FW_BASE)/user1.bin \
+	  $(ET_BLANK) $(SDK_BASE)/bin/blank.bin
+
+fullflash: all
+	$(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash -fs $(ET_FS) -ff $(ET_FF) \
+	  0x00000 "$(SDK_BASE)/bin/boot_v1.6.bin" 0x01000 $(FW_BASE)/user1.bin \
+	  $(ET_INIT_DATA) $(SDK_BASE)/bin/esp_init_data_default.bin $(ET_BLANK) $(SDK_BASE)/bin/blank.bin
+
+
+
+tools/$(HTML_COMPRESSOR):
+	$(Q) echo "The jar files in the tools dir are missing, they should be in the source repo"
+	$(Q) echo "The following commands can be used to fetch them, but the URLs have changed..."
+	$(Q) echo mkdir -p tools
+	$(Q) echo "cd tools; wget --no-check-certificate https://github.com/yui/yuicompressor/releases/download/v2.4.8/$(YUI_COMPRESSOR) -O $(YUI_COMPRESSOR)"
+	$(Q) echo "cd tools; wget --no-check-certificate https://htmlcompressor.googlecode.com/files/$(HTML_COMPRESSOR) -O $(HTML_COMPRESSOR)"
+
+ifeq ("$(COMPRESS_W_HTMLCOMPRESSOR)","yes")
+$(BUILD_BASE)/espfs_img.o: tools/$(HTML_COMPRESSOR)
+endif
+
+$(BUILD_BASE)/espfs_img.o: html/ html/wifi/ html/text/ espfs/mkespfsimage/mkespfsimage
+#	$(Q) rm -rf html_compressed; mkdir html_compressed; mkdir html_compressed/wifi; mkdir html_compressed/text;mkdir html_compressed/fonts;
+	$(Q) rm -rf html_compressed; mkdir html_compressed; mkdir html_compressed/fonts;
+	$(Q) cp -r html/*.ico html_compressed;
+	$(Q) cp -r html/*.css html_compressed;
+	$(Q) cp -r html/*.js html_compressed;
+#	$(Q) cp -r html/wifi/*.js html_compressed/wifi;
+	$(Q) cp -r html/Lato-Embed.ttf html_compressed/fonts;
+ifeq ("$(COMPRESS_W_HTMLCOMPRESSOR)","yes")
+	$(Q) echo "Compressing assets with htmlcompressor. This may take a while..."
+	$(Q) java -jar tools/$(HTML_COMPRESSOR) \
+	  -t html --remove-surrounding-spaces max --remove-quotes --remove-intertag-spaces \
+	  -o $(abspath ./html_compressed)/ \
+	  $(HTML_PATH)head- \
+	  $(HTML_PATH)*.html
+#	$(Q) java -jar tools/$(HTML_COMPRESSOR) \
+#	  -t html --remove-surrounding-spaces max --remove-quotes --remove-intertag-spaces \
+#	  -o $(abspath ./html_compressed)/text/ \
+#	  $(TEXT_PATH)*.html
+#	$(Q) java -jar tools/$(HTML_COMPRESSOR) \
+#	  -t html --remove-surrounding-spaces max --remove-quotes --remove-intertag-spaces \
+#	  -o $(abspath ./html_compressed)/wifi/ \
+#	  $(WIFI_PATH)*.html
+	$(Q) echo "Compressing assets with yui-compressor. This may take a while..."
+	$(Q) for file in `find html_compressed -type f -name "*.js"`; do \
+	    java -jar tools/$(YUI_COMPRESSOR) $$file --line-break 0 -o $$file; \
+	  done
+	$(Q) for file in `find html_compressed -type f -name "*.css"`; do \
+	    java -jar tools/$(YUI_COMPRESSOR) $$file -o $$file; \
+	  done
+else
+	$(Q) cp -r html/head- html_compressed;
+	$(Q) cp -r html/*.html html_compressed;
+	$(Q) cp -r html/text/*.html html_compressed/text;	
+#	$(Q) cp -r html/wifi/*.html html_compressed/wifi;	
+endif
+ifeq (,$(findstring mqtt,$(MODULES)))
+	$(Q) rm -rf html_compressed/mqtt.html
+	$(Q) rm -rf html_compressed/mqtt.js
+endif
+	$(Q) for file in `find html_compressed -type f -name "*.htm*"`; do \
+	    cat html_compressed/head- $$file >$${file}-; \
+	    mv $$file- $$file; \
+	  done
+	$(Q) rm html_compressed/head-
+	$(Q) cd html_compressed; find . \! -name \*- | ../espfs/mkespfsimage/mkespfsimage > ../build/espfs.img; cd ..;
+	$(Q) ls -sl build/espfs.img
+	$(Q) cd build; $(OBJCP) -I binary -O elf32-xtensa-le -B xtensa --rename-section .data=.espfs \
+	  espfs.img espfs_img.o; cd ..
+
+# edit the loader script to add the espfs section to the end of irom with a 4 byte alignment.
+# we also adjust the sizes of the segments 'cause we need more irom0
+build/eagle.esphttpd1.v6.ld: $(SDK_LDDIR)/eagle.app.v6.new.1024.app1.ld
+	$(Q) sed -e '/\.irom\.text/{' -e 'a . = ALIGN (4);' -e 'a *(.espfs)' -e '}'  \
+		-e '/^  irom0_0_seg/ s/6B000/7C000/' \
+		$(SDK_LDDIR)/eagle.app.v6.new.1024.app1.ld >$@
+build/eagle.esphttpd2.v6.ld: $(SDK_LDDIR)/eagle.app.v6.new.1024.app2.ld
+	$(Q) sed -e '/\.irom\.text/{' -e 'a . = ALIGN (4);' -e 'a *(.espfs)' -e '}'  \
+		-e '/^  irom0_0_seg/ s/6B000/7C000/' \
+		$(SDK_LDDIR)/eagle.app.v6.new.1024.app2.ld >$@
+
+espfs/mkespfsimage/mkespfsimage: espfs/mkespfsimage/
+	$(Q) $(MAKE) -C espfs/mkespfsimage GZIP_COMPRESSION="$(GZIP_COMPRESSION)"
+
+release: all
+	$(Q) rm -rf release; mkdir -p release/meta-id-$(VERSION)
+	$(Q) egrep -a 'meta-id [a-z0-9.]+ - 201' $(FW_BASE)/user1.bin | cut -b 1-80
+	$(Q) egrep -a 'meta-id [a-z0-9.]+ - 201' $(FW_BASE)/user2.bin | cut -b 1-80
+	$(Q) cp $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin $(SDK_BASE)/bin/blank.bin \
+	       "$(SDK_BASE)/bin/boot_v1.7.bin" "$(SDK_BASE)/bin/esp_init_data_default.bin" \
+	       wiflash avrflash megaflash release/meta-id-$(VERSION)
+	$(Q) tar zcf meta-id-$(VERSION).tgz -C release meta-id-$(VERSION)
+	$(Q) echo "Release file: meta-id-$(VERSION).tgz"
+	$(Q) rm -rf release
+
+docker:
+	$(Q) docker build -t jeelabs/meta-id .
+clean:
+	$(Q) rm -f $(APP_AR)
+	$(Q) rm -f $(TARGET_OUT)
+	$(Q) find $(BUILD_BASE) -type f | xargs rm -f
+	$(Q) make -C espfs/mkespfsimage/ clean
+	$(Q) rm -rf $(FW_BASE)
+	$(Q) rm -f webpages.espfs
+ifeq ("$(COMPRESS_W_HTMLCOMPRESSOR)","yes")
+	$(Q) rm -rf html_compressed
+endif
+
+$(foreach bdir,$(BUILD_DIR),$(eval $(call compile-objects,$(bdir))))
+
+depend:
+	makedepend -p${BUILD_BASE}/ -Y -- $(INCDIR) $(MODULE_INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) -I${XTENSA_TOOLS_ROOT}../xtensa-lx106-elf/include -I${XTENSA_TOOLS_ROOT}../lib/gcc/xtensa-lx106-elf/4.8.2/include -- */*.c
+
+# Rebuild version at least at every Makefile change
+
+${BUILD_BASE}/meta-id/main.o: Makefile
+
+# DO NOT DELETE
+

+ 14 - 0
meta-id/RESTMQTT.md

@@ -0,0 +1,14 @@
+Meta-ID: Outbound HTTP REST requests and MQTT client
+-------------------------------------------
+
+The V2 versions of esp-link use the SLIP protocol over the serial link to support simple outbound
+HTTP REST requests as well as an MQTT client. The SLIP protocol consists of commands with
+binary arguments sent from the
+attached microcontroller to the esp8266, which then performs the command and responds back.
+The responses back use a callback address in the attached microcontroller code, i.e., the
+command sent by the uC contains a callback address and the response from the esp8266 starts
+with that callback address. This enables asynchronous communication where meta-id can notify the
+uC when requests complete or when other actions happen, such as wifi connectivity status changes.
+
+You can find REST and MQTT libraries as well as demo sketches in the
+[el-client](https://github.com/jeelabs/el-client) repository.

+ 36 - 0
meta-id/TROUBLESHOOTING.md

@@ -0,0 +1,36 @@
+Esp-Link troubleshooting
+========================
+
+### Troubleshooting
+
+- verify that you have sufficient power, borderline power can cause the esp module to seemingly
+  function until it tries to transmit and the power rail collapses
+- if you just cannot flash your esp8266 module (some people call it the zombie mode) make sure you
+  have gpio0 and gpio15 pulled to gnd with a 1K resistor, gpio2 tied to 3.3V with 1K resistor, and
+  RX/TX connected without anything in series. If you need to level shift the signal going into the
+  esp8266's RX use a 1K resistor. Use 115200 baud in the flasher.
+  (For a permanent set-up I would use higher resistor values but
+  when nothing seems to work these are the ones I try.)
+- if the flashing succeeded, check the "conn" LED to see which mode esp-link is in (see LED info above)
+- reset or power-cycle the esp-link to force it to become an access-point if it can't
+  connect to your network within 15-20 seconds
+- if the LED says that esp-link is on your network but you can't get to it, make sure your
+  laptop is on the same network (and no longer on the esp's network)
+- if you do not know the esp-link's IP address on your network, try `esp-link.local`, try to find
+  the lease in your DHCP server; if all fails, you may have to turn off your access point (or walk
+  far enough away) and reset/power-cycle esp-link, it will then fail to connect and start its
+  own AP after 15-20 seconds
+
+### LED indicators
+
+Assuming appropriate hardware attached to GPIO pins, the green "conn" LED will show the wifi
+status as follows:
+
+- Very short flash once a second: not connected to a network and running as AP+STA, i.e.
+  trying to connect to the configured network
+- Very short flash once every two seconds: not connected to a network and running as AP-only
+- Even on/off at 1HZ: connected to the configured network but no IP address (waiting on DHCP)
+- Steady on with very short off every 3 seconds: connected to the configured network with an
+  IP address (esp-link shuts down its AP after 60 seconds)
+
+The yellow "ser" LED will blink briefly every time serial data is sent or received by the esp-link.

+ 183 - 0
meta-id/UC-FLASHING.md

@@ -0,0 +1,183 @@
+Flashing an attached Microcontroller
+====================================
+
+In order to connect through the esp-link to a microcontroller use port 23. For example,
+on linux you can use `nc esp-hostname 23` or `telnet esp-hostname 23`.
+
+Note that multiple connections to port 23 and 2323 can be made simultaneously. Esp-link will
+intermix characters received on all these connections onto the serial TX and it will
+broadcast incoming characters from the serial RX to all connections. Use with caution!
+
+### Flashing an attached AVR/Arduino
+
+There are multiple options for reprogramming an attached AVR/Arduino microcontroller:
+
+- Use avrdude and point it at port 23 of esp-link. Esp-link automatically detects the programming
+  sequence and issues a reset to the AVR.
+- Use avrdude and point it at port 2323 of esp-link. This is the same as port 23 except that the
+  autodectection is not used and the reset happens because port 2323 is used
+- Use curl or a similar tool to HTTP POST the firmware to esp-link. This uses the built-in
+  programmer, which only works for AVRs/Arduinos with the optiboot bootloader (which is std).
+- Use some serial port forwarding software, such as com2com, or hwvsp (you have to uncheck
+  nvt in the settings when using the latter).
+
+To reprogram an Arduino / AVR microcontroller by pointing avrdude at port 23 or 2323 you
+specify a serial port of the form `net:esp-link:23` in avrdude's -P option, where
+`esp-link` is either the hostname of your esp-link or its IP address).
+This is instead of specifying a serial port of the form /dev/ttyUSB0.
+Esp-link detects that avrdude starts its connection with a flash synchronization sequence
+and sends a reset to the AVR microcontroller so it can switch into flash programming mode.
+
+Note for Windows users: very recent avrdude versions on Windows support the -P option, while older
+ones don't. See the second-to-last bullet in the
+[avrdude 6.3 release notes]http://savannah.nongnu.org/forum/forum.php?forum_id=8461).
+
+To reprogram using the HTTP POST method you need to first issue a POST to put optiboot into
+programming mode: POST to `http://esp-link/pgm/sync`, this starts the process. Then check that
+synchronization with optiboot has been achieved by issuing a GET to the same URL
+(`http://esp-link/pgm/sync`). Repeat until you have sync (takes <500ms normally). Finally
+issue a POST request to `http://esp-link/pgm/upload` with your hex file as POST data (raw,
+not url-encoded or multipart-mime. Please look into the avrflash script for the curl command-line
+details or use that script directly (`./avrflash esp-link.local my_sketch.hex`).
+_Important_: after the initial sync request that resets the AVR you have 10 seconds to get to the
+upload post or esp-link will time-out. So if you're manually entering curl commands have them
+prepared so you can copy&paste!
+
+Beware of the baud rate, which you can set on the uC Console page. Sometimes you may be using
+115200 baud in sketches but the bootloader may use 57600 baud. When you use port 23 or 2323 you
+need to set the baud rate correctly. If you use the built-in programmer (HTTP POST method) then
+esp-link will try the configured baud rate and also 9600, 57600, and 115200 baud, so it should
+work even if you have the wrong baud rate configured...
+
+When to use which method? If port 23 works then go with that. If you have trouble getting sync
+or it craps out in the middle too often then try the built-in programmer with the HTTP POST.
+If your AVR doesn't use optiboot then use port 2323 since esp-link may not recognize the programming
+sequence and not issue a reset if you use port 23.
+
+If you are having trouble with the built-in programmer and see something like this:
+
+```
+# ./avrflash 192.168.3.104 blink.hex
+Error checking sync: FAILED to SYNC: abandoned after timeout, got:
+:\xF/\x00\xCj\xCz\xCJ\xCZ\xC\xAÜ\xC\xAä\xC\xAÜ\xC\xAä\xC\xBì\xC\xBô\xC\xBì\xC\xBô\xC\xAÜ\xC\xAä\xC
+```
+
+the most likely cause is a baud rate mismatch and/or a bad connection from the esp8266 to the
+AVRs reset line.
+The baud rate used by esp-link is set on the uC Console web page and, as mentioned above, it will
+automatically try 9600, 57600, and 115200 as well.
+The above garbage characters are most likely due to optiboot timing out and starting the sketch
+and then the sketch sending data at a different baud rate than configured into esp-link.
+Note that sketches don't necessarily use the same baud rate as optiboot, so you may have the
+correct baud rate configured but reset isn't functioning, or reset may be functioning but the
+baud rate may be incorrect.
+
+The output of a successful flash using the built-in programmer looks like this:
+
+```
+Success. 3098 bytes at 57600 baud in 0.8s, 3674B/s 63% efficient
+```
+
+This says that the sketch comprises 3098 bytes of flash, was written in 0.8 seconds
+(excludes the initial sync time) at 57600 baud,
+and the 3098 bytes were flashed at a rate of 3674 bytes per second.
+The efficiency measure is the ratio of the actual rate to the serial baud rate,
+thus 3674/5760 = 0.63 (there are 10 baud per character).
+The efficiency is not 100% because there is protocol overhead (such as sync, record type, and
+length characters)
+and there is dead time waiting for an ack or preparing the next record to be sent.
+
+### Details of built-in AVR flash algorithm
+
+The built-in flashing algorithm differs a bit from what avrdude does. The programming protocol
+states that STK_GET_SYNC+CRC_EOP (0x30 0x20) should be sent to synchronize, but that works poorly
+because the AVR's UART only buffers one character. This means that if STK_GET_SYNC+CRC_EOP is
+sent twice there is a high chance that only the last character (CRC_EOP) is actually
+received. If that is followed by another STK_GET_SYNC+CRC_EOP sequence then optiboot receives
+CRC_EOP+STK_GET_SYNC+CRC_EOP which causes it to abort and run the old sketch. Ending up in that
+situation is quite likely because optiboot initializes the UART as one of the first things, but
+then goes off an flashes an LED for ~300ms during which it doesn't empty the UART.
+
+Looking at the optiboot code, the good news is that CRC_EOP+CRC_EOP can be used to get an initial
+response without the overrun danger of the normal sync sequence and this is what esp-link does.
+The programming sequence runs as follows:
+
+- esp-link sends a brief reset pulse (1ms)
+- esp-link sends CRC_EOP+CRC_EOP ~50ms later
+- esp-link sends CRC_EOP+CRC_EOP every ~70-80ms
+- eventually optiboot responds with STK_INSYNC+STK_OK (0x14;0x10)
+- esp-link sends one CRC_EOP to sort out the even/odd issue
+- either optiboot responds with STK_INSYNC+STK_OK or nothing happens for 70-80ms, in which case
+  esp-link sends another CRC_EOP
+- esp-link sends STK_GET_SYNC+CRC_EOP and optiboot responds with STK_INSYNC+STK_OK and we're in
+  sync now
+- esp-link sends the next command (starts with 'u') and programming starts...
+
+If no sync is achieved, esp-link changes baud rate and the whole thing starts over with a reset
+pulse about 600ms, esp-link gives up after about 5 seconds and reports an error.
+
+### Flashing an attached ARM processor
+
+You can reprogram NXP's LPC800-series and many other ARM processors as well by pointing your
+programmer similarly at the esp-link's port 23. For example, if you are using
+https://github.com/jeelabs/embello/tree/master/tools/uploader a command line like
+`uploader -t -s -w esp-link:23 build/firmware.bin` does the trick.
+The way it works is that the uploader uses telnet protocol escape sequences in order to
+make esp-link issue the appropriate "ISP" and reset sequence to the microcontroller to start the
+flash programming. If you use a different ARM programming tool it will work as well as long as
+it starts the connection with the `?\r\n` synchronization sequence.
+
+### Flashing an attached esp8266
+
+__Flashing another esp8266 module is possible in theory but real-world attempts have so far been
+rather unsuccessful due to Wifi interference. This section is left here in case someone else
+wants to dig in and find a solution.__
+
+You can use esp-link running on one esp8266 module to flash another esp8266 module,
+however it is rather tricky! The problem is not electric, it is wifi interference.
+The basic idea is to use some method to direct the esp8266 flash program to port 2323 of
+esp-link. Using port 2323 with the appropriate wiring will cause the esp8266's reset and 
+gpio0 pins to be toggled such that the chip enters the flash programming mode.
+
+One option for connecting the programmer with esp-link is to use my version of esptool.py
+at http://github.com/tve/esptool, which supports specifying a URL instead of a port. Thus
+instead of specifying something like `--port /dev/ttyUSB0` or `--port COM1` you specify
+`--port socket://esp-link.local:2323`. Important: the baud rate specified on the esptool.py
+command-line is irrelevant as the baud rate used by esp-link will be the one set in the
+uC console page. Fortunately the esp8266 bootloader does auto-baud detection. (Setting the
+baud rate to 115200 is recommended.)
+
+Another option is to use a serial-to-tcp port forwarding driver and point that to port 2323
+of esp-link. On windows users have reported success with
+http://www.hw-group.com/products/hw_vsp/hw_vsp2_en.html[HW Virtual Serial Port]
+
+Now to the interference problem: once the attached esp8266 is reset it
+starts outputting its 26Mhz clock on gpio0, which needs to be attached to
+the esp8266 running esp-link (since it needs to drive gpio0 low during
+the reset to enter flash mode). This 26Mhz signal on gpio0 causes a
+significant amount of radio interference with the result that the esp8266
+running esp-link has trouble receiving Wifi packets. You can observe this
+by running a ping to esp-link in another window: as soon as the target
+esp8266 is reset, the pings become very slow or stop altogetehr. As soon
+as you remove power to the attached esp8266 the pings resume beautifully.
+
+To try and get the interference under control, try some of the following:
+add a series 100ohm resistor and 100pf capacitor to ground as close to
+the gpio0 pin as possible (basically a low pass filter); and/or pass
+the cable connecting the two esp8266's through a ferrite bead.
+
+### Debug log
+
+The esp-link web UI can display the esp-link debug log (os_printf statements in the code). This
+is handy but sometimes not sufficient. Esp-link also prints the debug info to the UART where
+it is sometimes more convenient and sometimes less... For this reason three UART debug log
+modes are supported that can be set in the web UI (and the mode is saved in flash):
+
+- auto: the UART log starts enabled at boot using uart0 and disables itself when esp-link
+  associates with an AP. It re-enables itself if the association is lost.
+- off: the UART log is always off
+- on0: the UART log is always on using uart0
+- on1: the UART log is always on using uart1 (gpio2 pin)
+
+Note that even if the UART log is always off the ROM prints to uart0 whenever the
+esp8266 comes out of reset. This cannot be disabled.

+ 69 - 0
meta-id/WEB-SERVER.md

@@ -0,0 +1,69 @@
+ESP-LINK web-server tutorial
+============================
+
+Video
+--------------------
+
+https://www.youtube.com/watch?v=vBESCO0UhYI
+
+
+Installing el-client Arduino library
+--------------------
+
+Download and install ELClient library.
+
+https://github.com/jeelabs/el-client
+
+
+LED flashing sample
+--------------------
+
+Circuit:
+
+ - 1: connect a Nodemcu (ESP8266) board and an Arduino Nano / UNO:
+   (RX - levelshifter - TX, TX - levelshifter - RX)
+ - 2: optionally connect RESET-s with a level shifter
+
+
+Installation steps:
+
+ - 1: open webserver_led ELClient sample file in Arduino
+ - 2: upload the code onto an Arduino Nano/Uno
+ - 3: open the Web Server page on esp-link UI
+ - 4: upload LED.html from webserver_led ( ELCient/examples/webserver_led/LED.html )
+ - 5: choose LED page on esp-link UI
+ - 6: turn on/off the LED
+
+ 
+HTML controls sample
+--------------------------
+
+Circuit:
+
+ - 1: connect a Nodemcu (ESP8266) board and an Arduino Nano / UNO:
+   (RX - levelshifter - TX, TX - levelshifter - RX)
+ - 2: optionally connect RESET-s with a level shifter
+ - 3: add a trimmer to A0 for voltage measurement
+
+Installation steps:
+
+ - 1: open webserver_controls ELClient sample file in Arduino
+ - 2: upload the code onto an Arduino Nano/Uno
+ - 3: open the Web Server page on esp-link UI
+ - 4: upload the 3 HTML files from webserver_controls ( select multiple htmls from  ELCient/examples/webserver_controls/ )
+ - 5: jump to LED/User/Voltage pages
+ - 6: try out different settings
+
+ 
+Supported HTML controls
+--------------------
+
+HTML&nbsp;control&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Value Type  | Description  | Form Submission |
+ ------------------------ | ----- | ------------ | ------- |
+&lt;p id="id"/&gt; <br/> &lt;div id="id"/&gt; <br/> &lt;tr id="id"/&gt; <br/> &lt;th id="id"/&gt; <br/> &lt;td id="id"/&gt; <br/> &lt;textarea id="id"/&gt; | String&nbsp;(HTML) | MCU can replace the inner HTML part of the control at LOAD/REFRESH queries. The string (sent by MCU) is handled as HTML, so &lt;img...&gt; will be displayed as an image on the page | NO |
+&lt;button id="id"/&gt; | String | When button is pressed, a message is transmitted to MCU containing the id (BUTTON_PRESS) | NO |
+&lt;input name="id"/&gt; | String <br/> Integer <br/> Float <br/> Boolean | MCU can replace the value or checked properties of the HTML control in the form (LOAD/REFRESH). At form submission, the content of value will be transmitted to MCU (SET_FIELD). | YES |
+&lt;select name="id"/&gt; | String | MCU can choose a value from the drop down (LOAD/REFRESH). At form submission the currently selected value will be transmitted to MCU (SET_FIELD) | YES |
+&lt;ul id="id"/&gt; <br/> &lt;ol id="id"/&gt; | JSON list <br/> ["1","2","3"] | MCU can send a JSON list which is transformed to an HTML list ( &lt;li/&gt; )  (LOAD/REFRESH) | NO |
+&lt;table id="id"/&gt; | JSON table <br/> [["1","2"], <br/> ["3","4"]] | MCU sends a JSON table which is transformed to an HTML table  (LOAD/REFRESH) | NO |
+

+ 87 - 0
meta-id/WIFI-CONFIG.md

@@ -0,0 +1,87 @@
+meta-id Wifi configuration
+===========================
+
+For proper operation the end state that meta-id needs to arrive at is to have it
+join your pre-existing wifi network as a pure station.
+However, in order to get there meta-id will start out as an access point and you'll have
+to join its network to configure it. The short version is:
+
+ 1. meta-id creates a wifi access point with an SSID of the form `ESP_012ABC` (some modules
+    use a different SSID form, such as `ai-thinker-012ABC`)
+ 2. you join your laptop or phone to meta-id's network as a station and you configure
+    meta-id wifi with your network info by pointing your browser at `http://192.168.4.1/`
+ 3. you set a hostname for meta-id on the "home" page, or leave the default ("meta-id")
+ 4. meta-id starts to connect to your network while continuing to also be an access point
+    ("AP+STA"), the meta-id may show up with a `${hostname}.local` hostname
+    (depends on your DHCP/DNS config)
+ 4. meta-id succeeds in connecting and shuts down its own access point after 15 seconds,
+    you reconnect your laptop/phone to your normal network and access meta-id via its hostname
+    or IP address
+
+### Notes on using AP (access point) mode
+
+meta-id does not support STA+AP mode, however it does support STA mode and AP mode. What happens
+is that STA+AP mode is used at boot and when making STA changes to allow for recovery: the AP
+mode stays on for a while so you can connect to it and fix the STA mode. Once STA has connected,
+meta-id switches to STA-only mode. There is no setting to stay in STA+AP mode. So... if you want
+to use AP ensure you set meta-id to AP-only mode. If you want STA+AP mode you're gonna have to
+modify the source for yourself. (This stuff is painful to test and rather tricky, so don't expect
+the way it works to change.)
+
+Configuration details
+---------------------
+
+### Wifi
+
+After you have serially flashed the module it will create a wifi access point (AP) with an
+SSID of the form `ESP_012ABC` where 012ABC is a piece of the module's MAC address.
+Using a laptop, phone, or tablet connect to this SSID and then open a browser pointed at
+http://192.168.4.1/, you should then see the meta-id web site.
+
+Now configure the wifi. The desired configuration is for the meta-id to be a
+station on your local wifi network so you can communicate with it from all your computers.
+
+To make this happen, navigate to the wifi page and you should see the meta-id scan
+for available networks. You should then see a list of detected networks on the web page and you
+can select yours.
+Enter a password if your network is secure (highly recommended...) and hit the connect button.
+
+You should now see that the meta-id has connected to your network and it should show you
+its IP address. _Write it down_. You will then have to switch your laptop, phone, or tablet
+back to your network and then you can connect to the meta-id's IP address or, depending on your
+network's DHCP/DNS config you may be able to go to http://meta-id.local
+
+At this point the meta-id will have switched to STA mode and be just a station on your
+wifi network. These settings are stored in flash and thereby remembered through resets and
+power cycles. They are also remembered when you flash new firmware. Only flashing `blank.bin`
+via the serial port as indicated above will reset the wifi settings.
+
+There is a fail-safe, which is that after a reset or a configuration change, if the meta-id
+cannot connect to your network it will revert back to AP+STA mode after 15 seconds and thus
+both present its `ESP_012ABC`-style network and continue trying to reconnect to the requested network.
+You can then connect to the meta-id's AP and reconfigure the station part.
+
+One open issue (#28) is that meta-id cannot always display the IP address it is getting to the browser
+used to configure the ssid/password info. The problem is that the initial STA+AP mode may use
+channel 1 and you configure it to connect to an AP on channel 6. This requires the ESP8266's AP
+to also switch to channel 6 disconnecting you in the meantime. 
+
+### Hostname, description, DHCP, mDNS
+
+You can set a hostname on the "home" page, this should be just the hostname and not a domain
+name, i.e., something like "test-module-1" and not "test-module-1.mydomain.com".
+This has a number of effects:
+
+- you will see the first 12 chars of the hostname in the menu bar (top left of the page) so
+  if you have multiple modules you can distinguish them visually
+- meta-id will use the hostname in its DHCP request, which allows you to identify the module's
+  MAC and IP addresses in your DHCP server (typ. your wifi router). In addition, some DHCP
+  servers will inject these names into the local DNS cache so you can use URLs like
+  `hostname.local`.
+- someday, meta-id will inject the hostname into mDNS (multicast DNS, bonjour, etc...) so 
+  URLs of the form `hostname.local` work for everyone (as of v2.1.beta5 mDNS is disabled due
+  to reliability issues with it)
+
+You can also enter a description of up to 128 characters on the home page (bottom right). This
+allows you to leave a memo for yourself, such as "installed in basement to control the heating
+system". This descritpion is not used anywhere else.

+ 14 - 0
meta-id/WINDOWS.md

@@ -0,0 +1,14 @@
+* Install [SourceTree](https://www.sourcetreeapp.com) and check CLI git or other git distribution to obtain git from CLI
+* Install the latest Java JRE
+* Install Python 2.7 to C:\Python27
+* Install link shell extension from [here](http://schinagl.priv.at/nt/hardlinkshellext/linkshellextension.html)
+* Download and install the [Windows Unofficial Development Kit for Espressif ESP8266](http://programs74.ru/get.php?file=EspressifESP8266DevKit) to c:\espressif
+* Create a symbolic link for java/bin and git/bin directories under C:\espressif\git-bin and C:\espressif\java-bin. You must do this because "make" doesn't work properly with paths like "program files(x86)".  You can see all the expected paths in the [espmake.cmd](https://github.com/jeelabs/esp-link/blob/master/espmake.cmd)
+* [Download](http://sourceforge.net/projects/mingw/files/Installer/) and install MinGW. Run mingw-get-setup.exe.  During the installation process select without GUI.  (uncheck "... also install support for the graphical user interface")
+* [Download](http://programs74.ru/get.php?file=EspressifESP8266DevKitAddon) the scripts to automate the installation of additional modules for MinGW.
+* Run install-mingw-package.bat. This will install the basic modules required for MinGW to build esp8266.
+* Checkout esp-link from git to C:\espressif\esp-link 
+* When you're done open a command prompt and run: espmake.cmd "make all wiflash" 
+* For a new flash over serial use: espmake.cmd "make all flash"
+* If you want to program with serial but not loose your config each time use: espmake.cmd "make all baseflash"
+* You can open the esp-link.sln file in Visual Studio 2013.  "Build Solution" will issue "make all wiflash".  "Clean Solution" will issue "make clean".  "Rebuild Solution" will issue "make clean all".  This can be changed under solution properties -> Configuration Properties -> NMake

+ 110 - 0
meta-id/avrflash

@@ -0,0 +1,110 @@
+#! /bin/bash
+#
+# Flash an AVR with optiboot using the meta-id built-in programmer
+# Basically we first reset the AVR and get in sync, and then send the hex file
+#
+# ----------------------------------------------------------------------------
+# "THE BEER-WARE LICENSE" (Revision 42):
+# Thorsten von Eicken wrote this file. As long as you retain 
+# this notice you can do whatever you want with this stuff. If we meet some day, 
+# and you think this stuff is worth it, you can buy me a beer in return. 
+# ----------------------------------------------------------------------------
+
+show_help() {
+  cat <<EOT
+Usage: ${0##*/} [-options...] hostname sketch.hex
+Flash the AVR running optiboot attached to meta-id with the sketch.
+  -v                    Be verbose
+  -h                    show this help
+
+Example: ${0##*/} -v meta-id mysketch.hex
+         ${0##*/} 192.168.4.1 mysketch.hex
+EOT
+}
+
+if ! which curl >/dev/null; then
+  echo "ERROR: Cannot find curl: it is required for this script." >&2
+  exit 1
+fi
+
+start=`date +%s`
+
+# ===== Parse arguments
+
+verbose=
+
+while getopts "hvx:" opt; do
+  case "$opt" in
+    h) show_help; exit 0 ;;
+    v) verbose=1 ;;
+    x) foo="$OPTARG" ;;
+    '?') show_help >&2; exit 1 ;;
+  esac
+done
+
+# Shift off the options and optional --.
+shift "$((OPTIND-1))"
+
+# Get the fixed arguments
+if [[ $# != 2 ]]; then
+  show_help >&2
+  exit 1
+fi
+hostname=$1
+hex=$2
+
+re='[-A-Za-z0-9.]+'
+if [[ ! "$hostname" =~ $re ]]; then
+  echo "ERROR: hostname ${hostname} is not a valid hostname or ip address" >&2
+  exit 1
+fi
+
+if [[ ! -r "$hex" ]]; then
+  echo "ERROR: cannot read hex file ($hex)" >&2
+  exit 1
+fi
+
+# ===== Get AVR in sync
+
+[[ -n "$verbose" ]] && echo "Resetting AVR with http://$hostname/pgm/sync" >&2
+v=; [[ -n "$verbose" ]] && v=-v
+sync=`curl -m 10 $v -s -w '%{http_code}' -XPOST "http://$hostname/pgm/sync"`
+if [[ $? != 0 || "$sync" != 204 ]]; then
+  echo "Error resetting AVR" >&2
+  exit 1
+fi
+
+while true; do
+  sync=`curl -m 10 $v -s "http://$hostname/pgm/sync"`
+  if [[ $? != 0 ]]; then
+    echo "Error checking sync" >&2
+    exit 1
+  fi
+  case "$sync" in
+  SYNC*)
+    echo "AVR in $sync"  >&2
+    break;;
+  "NOT READY"*)
+    [[ -n "$verbose" ]] && echo "  Waiting for sync..." >&2
+    ;;
+  *)
+    echo "Error checking sync: $sync" >&2
+    exit 1
+    ;;
+  esac
+  sleep 0.1
+done
+
+# ===== Send HEX file
+
+[[ -n "$verbose" ]] && echo "Sending HEX file for programming" >&2
+sync=`curl -m 10 $v -s -g -d "@$hex" "http://$hostname/pgm/upload"`
+echo $sync
+if [[ $? != 0 || ! "$sync" =~ ^Success ]]; then
+  echo "Error programming AVR" >&2
+  exit 1
+fi
+
+sec=$(( `date +%s` - $start ))
+echo "Success, took $sec seconds" >&2
+exit 0

+ 194 - 0
meta-id/cmd/cmd.c

@@ -0,0 +1,194 @@
+// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
+//
+// Adapted from: github.com/tuanpmt/esp_bridge, Created on: Jan 9, 2015, Author: Minh
+
+#include "esp8266.h"
+#include "cmd.h"
+#include "crc16.h"
+#include "uart.h"
+
+#ifdef CMD_DBG
+#define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0)
+#else
+#define DBG(format, ...) do { } while(0)
+#endif
+
+//===== ESP -> Serial responses
+
+static void ICACHE_FLASH_ATTR
+cmdProtoWrite(uint8_t data) {
+  switch(data){
+  case SLIP_END:
+    uart0_write_char(SLIP_ESC);
+    uart0_write_char(SLIP_ESC_END);
+    break;
+  case SLIP_ESC:
+    uart0_write_char(SLIP_ESC);
+    uart0_write_char(SLIP_ESC_ESC);
+    break;
+  default:
+    uart0_write_char(data);
+  }
+}
+
+static void ICACHE_FLASH_ATTR
+cmdProtoWriteBuf(const uint8_t *data, short len) {
+  while (len--) cmdProtoWrite(*data++);
+}
+
+static uint16_t resp_crc;
+
+// Start a response, returns the partial CRC
+void ICACHE_FLASH_ATTR
+cmdResponseStart(uint16_t cmd, uint32_t value, uint16_t argc) {
+  DBG("cmdResponse: cmd=%d val=%d argc=%d\n", cmd, value, argc);
+
+  uart0_write_char(SLIP_END);
+  cmdProtoWriteBuf((uint8_t*)&cmd, 2);
+  resp_crc = crc16_data((uint8_t*)&cmd, 2, 0);
+  cmdProtoWriteBuf((uint8_t*)&argc, 2);
+  resp_crc = crc16_data((uint8_t*)&argc, 2, resp_crc);
+  cmdProtoWriteBuf((uint8_t*)&value, 4);
+  resp_crc = crc16_data((uint8_t*)&value, 4, resp_crc);
+}
+
+// Adds data to a response, returns the partial CRC
+void ICACHE_FLASH_ATTR
+cmdResponseBody(const void *data, uint16_t len) {
+  cmdProtoWriteBuf((uint8_t*)&len, 2);
+  resp_crc = crc16_data((uint8_t*)&len, 2, resp_crc);
+
+  cmdProtoWriteBuf(data, len);
+  resp_crc = crc16_data(data, len, resp_crc);
+
+  uint16_t pad = (4-((len+2)&3))&3; // get to multiple of 4
+  if (pad > 0) {
+    uint32_t temp = 0;
+    cmdProtoWriteBuf((uint8_t*)&temp, pad);
+    resp_crc = crc16_data((uint8_t*)&temp, pad, resp_crc);
+  }
+}
+
+// Ends a response
+void ICACHE_FLASH_ATTR
+cmdResponseEnd() {
+  cmdProtoWriteBuf((uint8_t*)&resp_crc, 2);
+  uart0_write_char(SLIP_END);
+}
+
+//===== serial -> ESP commands
+
+// Execute a parsed command
+static void ICACHE_FLASH_ATTR
+cmdExec(const CmdList *scp, CmdPacket *packet) {
+  // Iterate through the command table and call the appropriate function
+  while (scp->sc_function != NULL) {
+    if(scp->sc_name == packet->cmd) {
+      DBG("cmdExec: Dispatching cmd=%s\n", scp->sc_text);
+      // call command function
+      scp->sc_function(packet);
+      return;
+    }
+    scp++;
+  }
+  DBG("cmdExec: cmd=%d not found\n", packet->cmd);
+}
+
+// Parse a packet and print info about it
+void ICACHE_FLASH_ATTR
+cmdParsePacket(uint8_t *buf, short len) {
+  // minimum command length
+  if (len < sizeof(CmdPacket)) return;
+
+  // init pointers into buffer
+  CmdPacket *packet = (CmdPacket*)buf;
+  uint8_t *data_ptr = (uint8_t*)&packet->args;
+  uint8_t *data_limit = data_ptr+len;
+
+  DBG("cmdParsePacket: cmd=%d argc=%d value=%u\n",
+      packet->cmd,
+      packet->argc,
+      packet->value
+  );
+
+#if 0
+  // print out arguments
+  uint16_t argn = 0;
+  uint16_t argc = packet->argc;
+  while (data_ptr+2 < data_limit && argc--) {
+    short l = *(uint16_t*)data_ptr;
+    os_printf("cmdParsePacket: arg[%d] len=%d:", argn++, l);
+    data_ptr += 2;
+    while (data_ptr < data_limit && l--) {
+      os_printf(" %02X", *data_ptr++);
+    }
+    os_printf("\n");
+  }
+#endif
+
+  if (!cmdInSync && packet->cmd != CMD_SYNC) {
+    // we have not received a sync, perhaps we reset? Tell MCU to do a sync
+    cmdResponseStart(CMD_SYNC, 0, 0);
+    cmdResponseEnd();
+  } else if (data_ptr <= data_limit) {
+    cmdExec(commands, packet);
+  } else {
+    DBG("cmdParsePacket: packet length overrun, parsing arg %d\n", packet->argc);
+  }
+}
+
+//===== Helpers to parse a command packet
+
+// Fill out a CmdRequest struct given a CmdPacket
+void ICACHE_FLASH_ATTR
+cmdRequest(CmdRequest *req, CmdPacket* cmd) {
+  req->cmd = cmd;
+  req->arg_num = 0;
+  req->arg_ptr = (uint8_t*)&cmd->args;
+}
+
+// Return the number of arguments given a command struct
+uint32_t ICACHE_FLASH_ATTR
+cmdGetArgc(CmdRequest *req) {
+  return req->cmd->argc;
+}
+
+// Copy the next argument from a command structure into the data pointer, returns 0 on success
+// -1 on error
+int32_t ICACHE_FLASH_ATTR
+cmdPopArg(CmdRequest *req, void *data, uint16_t len) {
+  uint16_t length;
+
+  if (req->arg_num >= req->cmd->argc)
+    return -1;
+
+  length = *(uint16_t*)req->arg_ptr;
+  if (length != len) return -1; // safety check
+
+  req->arg_ptr += 2;
+  os_memcpy(data, req->arg_ptr, length);
+  req->arg_ptr += (length+3)&~3; // round up to multiple of 4
+
+  req->arg_num ++;
+  return 0;
+}
+
+// Skip the next argument
+void ICACHE_FLASH_ATTR
+cmdSkipArg(CmdRequest *req) {
+  uint16_t length;
+
+  if (req->arg_num >= req->cmd->argc) return;
+
+  length = *(uint16_t*)req->arg_ptr;
+
+  req->arg_ptr += 2;
+  req->arg_ptr += (length+3)&~3;
+  req->arg_num ++;
+}
+
+// Return the length of the next argument
+uint16_t ICACHE_FLASH_ATTR
+cmdArgLen(CmdRequest *req) {
+  return *(uint16_t*)req->arg_ptr;
+}

+ 124 - 0
meta-id/cmd/cmd.h

@@ -0,0 +1,124 @@
+// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
+//
+// Adapted from: github.com/tuanpmt/esp_bridge, Created on: Jan 9, 2015, Author: Minh
+
+#ifndef CMD_H
+#define CMD_H
+#include <esp8266.h>
+
+// keep track of whether we received a sync command from uC
+extern bool cmdInSync;
+
+// Standard SLIP escape chars from RFC
+#define SLIP_END      0300    // indicates end of packet
+#define SLIP_ESC      0333    // indicates byte stuffing
+#define SLIP_ESC_END  0334    // ESC ESC_END means END data byte
+#define SLIP_ESC_ESC  0335    // ESC ESC_ESC means ESC data byte
+
+typedef struct __attribute__((__packed__)) {
+  uint16_t  len;      // length of data
+  uint8_t   data[0];  // really data[len]
+} CmdArg;
+
+typedef struct __attribute__((__packed__)) {
+  uint16_t  cmd;      // command to perform, from CmdName enum
+  uint16_t  argc;     // number of arguments to command
+  uint32_t  value;    // callback pointer for response or first argument
+  CmdArg    args[0];  // really args[argc]
+} CmdPacket;
+
+typedef struct {
+  CmdPacket *cmd;     // command packet header
+  uint32_t  arg_num;  // number of args parsed
+  uint8_t   *arg_ptr; // pointer to ??
+} CmdRequest;
+
+typedef enum {
+  CMD_NULL = 0,
+  CMD_SYNC,           // synchronize and clear
+  CMD_RESP_V,         // response with a value
+  CMD_RESP_CB,        // response with a callback
+  CMD_WIFI_STATUS,    // get the current wifi status
+  CMD_CB_ADD,
+  CMD_CB_EVENTS,
+  CMD_GET_TIME,       // get current time in seconds since the unix epoch
+  CMD_GET_WIFI_INFO,	// query ip address info
+  CMD_SET_WIFI_INFO,	// set ip address info
+
+  CMD_MQTT_SETUP = 10,  // set-up callbacks
+  CMD_MQTT_PUBLISH,     // publish a message
+  CMD_MQTT_SUBSCRIBE,   // subscribe to a topic
+  CMD_MQTT_LWT,         // set the last-will-topic and messge
+  CMD_MQTT_GET_CLIENTID,
+
+  CMD_REST_SETUP = 20,  // set-up callbacks
+  CMD_REST_REQUEST,     // do REST request
+  CMD_REST_SETHEADER,	// define header
+
+  CMD_WEB_SETUP = 30,   // set-up WEB callback
+  CMD_WEB_DATA,         // WEB data from MCU
+
+  CMD_SOCKET_SETUP = 40, // set-up callbacks
+  CMD_SOCKET_SEND,       // send data over UDP socket
+
+  CMD_WIFI_GET_APCOUNT = 50,  // Query the number of networks / Access Points known
+  CMD_WIFI_GET_APNAME,        // Query the name (SSID) of an Access Point (AP)
+  CMD_WIFI_SELECT_SSID,       // Connect to a specific network
+  CMD_WIFI_SIGNAL_STRENGTH,   // Query RSSI
+  CMD_WIFI_GET_SSID,          // Query SSID currently connected to
+  CMD_WIFI_START_SCAN,        // Trigger a scan (takes a long time)
+
+} CmdName;
+
+typedef void (*cmdfunc_t)(CmdPacket *cmd);
+
+typedef struct {
+  CmdName   sc_name;     // name as CmdName enum
+  char      *sc_text;    // name as string
+  cmdfunc_t sc_function; // pointer to function
+} CmdList;
+
+// command dispatch table
+extern const CmdList commands[];
+
+#define CMD_CBNLEN 16
+typedef struct {
+  char name[CMD_CBNLEN];
+  uint32_t callback;
+} CmdCallback;
+
+// Used by slip protocol to cause parsing of a received packet
+void cmdParsePacket(uint8_t *buf, short len);
+
+// Return the info about a callback to the attached uC by name, these are callbacks that the
+// attached uC registers using the ADD_SENSOR command
+CmdCallback* cmdGetCbByName(char* name);
+
+// Add a callback
+uint32_t cmdAddCb(char *name, uint32_t callback);
+
+// Responses
+
+// Start a response
+void cmdResponseStart(uint16_t cmd, uint32_t value, uint16_t argc);
+// Adds data to a response
+void cmdResponseBody(const void* data, uint16_t len);
+// Ends a response
+void cmdResponseEnd();
+
+//void cmdResponse(uint16_t cmd, uint32_t callback, uint32_t value, uint16_t argc, CmdArg* args[]);
+
+// Requests
+
+// Fill out a CmdRequest struct given a CmdPacket
+void cmdRequest(CmdRequest *req, CmdPacket* cmd);
+// Return the number of arguments given a request
+uint32_t cmdGetArgc(CmdRequest *req);
+// Return the length of the next argument
+uint16_t cmdArgLen(CmdRequest *req);
+// Copy next arg from request into the data pointer, returns 0 on success, -1 on error
+int32_t cmdPopArg(CmdRequest *req, void *data, uint16_t len);
+// Skip next arg
+void cmdSkipArg(CmdRequest *req);
+
+#endif

+ 407 - 0
meta-id/cmd/handlers.c

@@ -0,0 +1,407 @@
+// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
+//
+// Adapted from: github.com/tuanpmt/esp_bridge, Created on: Jan 9, 2015, Author: Minh
+
+#include "esp8266.h"
+#include "sntp.h"
+#include "cmd.h"
+#include "uart.h"
+#include <cgiwifi.h>
+#ifdef MQTT
+#include <mqtt_cmd.h>
+#endif
+#ifdef REST
+#include <rest.h>
+#endif
+#ifdef WEBSERVER
+#include <web-server.h>
+#endif
+#ifdef SOCKET
+#include <socket.h>
+#endif
+#include <ip_addr.h>
+#include "meta-id/cgi.h"
+
+#include "config.h"
+
+#ifdef CMD_DBG
+#define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0)
+#else
+#define DBG(format, ...) do { } while(0)
+#endif
+
+static void cmdNull(CmdPacket *cmd);
+static void cmdSync(CmdPacket *cmd);
+static void cmdWifiStatus(CmdPacket *cmd);
+static void cmdGetTime(CmdPacket *cmd);
+static void cmdGetWifiInfo(CmdPacket *cmd);
+// static void cmdSetWifiInfo(CmdPacket *cmd);
+static void cmdAddCallback(CmdPacket *cmd);
+
+static void cmdWifiGetApCount(CmdPacket *cmd);
+static void cmdWifiGetApName(CmdPacket *cmd);
+static void cmdWifiSelectSSID(CmdPacket *cmd);
+static void cmdWifiSignalStrength(CmdPacket *cmd);
+static void cmdWifiQuerySSID(CmdPacket *cmd);
+static void cmdWifiStartScan(CmdPacket *cmd);
+
+void cmdMqttGetClientId(CmdPacket *cmd);
+
+// keep track of last status sent to uC so we can notify it when it changes
+static uint8_t lastWifiStatus = wifiIsDisconnected;
+// keep track of whether we have registered our cb handler with the wifi subsystem
+static bool wifiCbAdded = false;
+// keep track of whether we received a sync command from uC
+bool cmdInSync = false;
+
+// Command dispatch table for serial -> ESP commands
+const CmdList commands[] = {
+  {CMD_NULL,            "NULL",           cmdNull},        // no-op
+  {CMD_SYNC,            "SYNC",           cmdSync},        // synchronize
+  {CMD_WIFI_STATUS,     "WIFI_STATUS",    cmdWifiStatus},
+  {CMD_CB_ADD,          "ADD_CB",         cmdAddCallback},
+  {CMD_GET_TIME,        "GET_TIME",       cmdGetTime},
+  {CMD_GET_WIFI_INFO,   "GET_WIFI_INFO",  cmdGetWifiInfo},
+  // {CMD_SET_WIFI_INFO,   "SET_WIFI_INFO",  cmdSetWifiInfo},
+
+  {CMD_WIFI_GET_APCOUNT,	"WIFI_GET_APCOUNT",	cmdWifiGetApCount},
+  {CMD_WIFI_GET_APNAME,		"WIFI_GET_APNAME",	cmdWifiGetApName},
+  {CMD_WIFI_SELECT_SSID,	"WIFI_SELECT_SSID",	cmdWifiSelectSSID},
+  {CMD_WIFI_SIGNAL_STRENGTH,	"WIFI_SIGNAL_STRENGTH",	cmdWifiSignalStrength},
+  {CMD_WIFI_GET_SSID,		"WIFI_GET_SSID",	cmdWifiQuerySSID},
+  {CMD_WIFI_START_SCAN,		"WIFI_START_SCAN",	cmdWifiStartScan},
+
+#ifdef MQTT
+  {CMD_MQTT_SETUP,      "MQTT_SETUP",     MQTTCMD_Setup},
+  {CMD_MQTT_PUBLISH,    "MQTT_PUB",       MQTTCMD_Publish},
+  {CMD_MQTT_SUBSCRIBE , "MQTT_SUB",       MQTTCMD_Subscribe},
+  {CMD_MQTT_LWT,        "MQTT_LWT",       MQTTCMD_Lwt},
+  {CMD_MQTT_GET_CLIENTID,"MQTT_CLIENTID", cmdMqttGetClientId},
+#endif
+#ifdef REST
+  {CMD_REST_SETUP,      "REST_SETUP",     REST_Setup},
+  {CMD_REST_REQUEST,    "REST_REQ",       REST_Request},
+  {CMD_REST_SETHEADER,  "REST_SETHDR",    REST_SetHeader},
+#endif
+#ifdef WEBSERVER
+  {CMD_WEB_SETUP,       "WEB_SETUP",      WEB_Setup},
+  {CMD_WEB_DATA,        "WEB_DATA",       WEB_Data},
+#endif
+#ifdef SOCKET
+  {CMD_SOCKET_SETUP,    "SOCKET_SETUP",   SOCKET_Setup},
+  {CMD_SOCKET_SEND,     "SOCKET_SEND",    SOCKET_Send},
+#endif
+};
+
+//===== List of registered callbacks (to uC)
+
+// WifiCb plus 10 for other stuff
+#define MAX_CALLBACKS 12
+CmdCallback callbacks[MAX_CALLBACKS]; // cleared in cmdSync
+
+uint32_t ICACHE_FLASH_ATTR
+cmdAddCb(char* name, uint32_t cb) {
+  for (uint8_t i = 0; i < MAX_CALLBACKS; i++) {
+    //DBG("cmdAddCb: index %d name=%s cb=%p\n", i, callbacks[i].name,
+    //  (void *)callbacks[i].callback);
+    // find existing callback or add to the end
+    if (os_strncmp(callbacks[i].name, name, CMD_CBNLEN) == 0 || callbacks[i].name[0] == '\0') {
+      os_strncpy(callbacks[i].name, name, sizeof(callbacks[i].name));
+      callbacks[i].name[CMD_CBNLEN-1] = 0; // strncpy doesn't null terminate
+      callbacks[i].callback = cb;
+      DBG("cmdAddCb: '%s'->0x%x added at %d\n", callbacks[i].name, cb, i);
+      return 1;
+    }
+  }
+  return 0;
+}
+
+CmdCallback* ICACHE_FLASH_ATTR
+cmdGetCbByName(char* name) {
+  for (uint8_t i = 0; i < MAX_CALLBACKS; i++) {
+    //DBG("cmdGetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name,
+    //  (void *)callbacks[i].callback);
+    // if callback doesn't exist or it's null
+    if (os_strncmp(callbacks[i].name, name, CMD_CBNLEN) == 0) {
+      DBG("cmdGetCbByName: cb %s found at index %d\n", name, i);
+      return &callbacks[i];
+    }
+  }
+  DBG("cmdGetCbByName: cb %s not found\n", name);
+  return 0;
+}
+
+//===== Wifi callback
+
+// Callback from wifi subsystem to notify us of status changes
+static void ICACHE_FLASH_ATTR
+cmdWifiCb(uint8_t wifiStatus) {
+  if (wifiStatus != lastWifiStatus){
+    DBG("cmdWifiCb: wifiStatus=%d\n", wifiStatus);
+    lastWifiStatus = wifiStatus;
+    CmdCallback *wifiCb = cmdGetCbByName("wifiCb");
+    if ((uint32_t)wifiCb->callback != -1) {
+      uint8_t status = wifiStatus == wifiGotIP ? 5 : 1;
+      cmdResponseStart(CMD_RESP_CB, (uint32_t)wifiCb->callback, 1);
+      cmdResponseBody((uint8_t*)&status, 1);
+      cmdResponseEnd();
+    }
+  }
+}
+
+//===== Command handlers
+
+// Command handler for Null command
+static void ICACHE_FLASH_ATTR
+cmdNull(CmdPacket *cmd) {
+}
+
+// Command handler for sync command
+static void ICACHE_FLASH_ATTR
+cmdSync(CmdPacket *cmd) {
+  CmdRequest req;
+  uart0_write_char(SLIP_END); // prefix with a SLIP END to ensure we get a clean start
+  cmdRequest(&req, cmd);
+  if(cmd->argc != 0 || cmd->value == 0) {
+    cmdResponseStart(CMD_RESP_V, 0, 0);
+    cmdResponseEnd();
+    return;
+  }
+
+  // clear callbacks table
+  os_memset(callbacks, 0, sizeof(callbacks));
+
+  // TODO: call other protocols back to tell them to reset
+
+  // register our callback with wifi subsystem
+  if (!wifiCbAdded) {
+    wifiAddStateChangeCb(cmdWifiCb);
+    wifiCbAdded = true;
+  }
+
+  // send OK response
+  cmdResponseStart(CMD_RESP_V, cmd->value, 0);
+  cmdResponseEnd();
+  cmdInSync = true;
+
+  // save the MCU's callback and trigger an initial callback
+  cmdAddCb("wifiCb", cmd->value);
+  lastWifiStatus = 0xff; // set to invalid value so we immediately send status cb in all cases
+  cmdWifiCb(wifiState);
+
+  return;
+}
+
+// Command handler for wifi status command
+static void ICACHE_FLASH_ATTR
+cmdWifiStatus(CmdPacket *cmd) {
+  cmdResponseStart(CMD_RESP_V, wifiState, 0);
+  cmdResponseEnd();
+  return;
+}
+
+// Command handler for time
+static void ICACHE_FLASH_ATTR
+cmdGetTime(CmdPacket *cmd) {
+  cmdResponseStart(CMD_RESP_V, sntp_get_current_timestamp(), 0);
+  cmdResponseEnd();
+  return;
+}
+
+// Command handler for IP information
+static void ICACHE_FLASH_ATTR
+cmdGetWifiInfo(CmdPacket *cmd) {
+  CmdRequest req;
+
+  cmdRequest(&req, cmd);
+  if(cmd->argc != 0 || cmd->value == 0) {
+    cmdResponseStart(CMD_RESP_V, 0, 0);
+    cmdResponseEnd();
+    return;
+  }
+
+  uint32_t callback = req.cmd->value;
+
+  struct ip_info info;
+  wifi_get_ip_info(0, &info);
+  uint8_t mac[6];
+  wifi_get_macaddr(0, mac);
+
+  cmdResponseStart(CMD_RESP_CB, callback, 4);
+  cmdResponseBody(&info.ip.addr, sizeof(info.ip.addr));
+  cmdResponseBody(&info.netmask.addr, sizeof(info.netmask.addr));
+  cmdResponseBody(&info.gw.addr, sizeof(info.gw.addr));
+  cmdResponseBody(mac, sizeof(mac));
+  cmdResponseEnd();
+}
+
+// Command handler to add a callback to the named-callbacks list, this is for a callback to the uC
+static void ICACHE_FLASH_ATTR
+cmdAddCallback(CmdPacket *cmd) {
+  CmdRequest req;
+  cmdRequest(&req, cmd);
+  if (cmd->argc != 1 || cmd->value == 0) return;
+
+  char name[16];
+  uint16_t len;
+
+  // get the callback name
+  len = cmdArgLen(&req);
+  if (len > 15) return; // max size of name is 15 characters
+  if (cmdPopArg(&req, (uint8_t *)name, len)) return;
+  name[len] = 0;
+  DBG("cmdAddCallback: name=%s\n", name);
+
+  cmdAddCb(name, cmd->value); // save the sensor callback
+}
+
+// Query the number of wifi access points
+static void ICACHE_FLASH_ATTR cmdWifiGetApCount(CmdPacket *cmd) {
+  int n = wifiGetApCount();
+  DBG("WifiGetApCount : %d\n", n);
+  cmdResponseStart(CMD_RESP_V, n, 0);
+  cmdResponseEnd();
+}
+
+// Query the name of a wifi access point
+static void ICACHE_FLASH_ATTR cmdWifiGetApName(CmdPacket *cmd) {
+  CmdRequest req;
+
+  cmdRequest(&req, cmd);
+
+  int argc = cmdGetArgc(&req);
+  DBG("cmdWifiGetApName: argc %d\n", argc);
+  if (argc != 1)
+    return;
+
+  uint16_t i;
+  cmdPopArg(&req, (uint8_t*)&i, 2);
+
+  uint32_t callback = req.cmd->value;
+
+  char myssid[33];
+  wifiGetApName(i, myssid);
+  myssid[32] = '\0';
+  DBG("wifiGetApName(%d) -> {%s}\n", i, myssid);
+
+  cmdResponseStart(CMD_RESP_CB, callback, 1);
+  cmdResponseBody(myssid, strlen(myssid)+1);
+  cmdResponseEnd();
+}
+
+/*
+ * Select a wireless network.
+ * This can be called in two ways :
+ * - with a pair of strings (SSID, password)
+ * - with a number and a string (index into network array, password)
+ */
+static void ICACHE_FLASH_ATTR cmdWifiSelectSSID(CmdPacket *cmd) {
+  CmdRequest req;
+  cmdRequest(&req, cmd);
+  int argc = cmdGetArgc(&req);
+  char ssid[33], pass[65];
+
+  if (argc != 2) return;
+
+  int len = cmdArgLen(&req);
+  if (len == 1) {
+    // Assume this is the index
+    uint8_t ix;
+    cmdPopArg(&req, &ix, 1);
+    wifiGetApName(ix, ssid);
+    ssid[32] = '\0';
+  } else {
+    // Longer than 1 byte: must be SSID
+    if (len > 32) return;
+    cmdPopArg(&req, ssid, len);
+    ssid[len] = 0;
+  }
+
+  // Extract password from message
+  len = cmdArgLen(&req);
+  if (len > 64) return;
+  cmdPopArg(&req, pass, len);
+  pass[len] = 0;
+
+  DBG("SelectSSID(%s,%s)", ssid, pass);
+  connectToNetwork(ssid, pass);
+}
+
+#if 0
+/*
+ * Once we're attached to some wireless network, choose not to pick up address from
+ * DHCP or so but set our own.
+ */
+static void ICACHE_FLASH_ATTR cmdSetWifiInfo(CmdPacket *cmd) {
+  DBG("SetWifiInfo()\n");
+}
+#endif
+
+static void ICACHE_FLASH_ATTR cmdWifiSignalStrength(CmdPacket *cmd) {
+  CmdRequest req;
+
+  cmdRequest(&req, cmd);
+
+  int argc = cmdGetArgc(&req);
+  if (argc != 1) {
+    DBG("cmdWifiSignalStrength: argc %d\n", argc);
+    return;
+  }
+
+  char x;
+  cmdPopArg(&req, (uint8_t*)&x, 1);
+  int i = x;
+  DBG("cmdWifiSignalStrength: argc %d, ", argc);
+  DBG("i %d\n", i);
+
+  int rssi = wifiSignalStrength(i);
+
+  cmdResponseStart(CMD_RESP_V, rssi, 0);
+  cmdResponseEnd();
+}
+
+//
+static void ICACHE_FLASH_ATTR cmdWifiQuerySSID(CmdPacket *cmd) {
+  CmdRequest req;
+  cmdRequest(&req, cmd);
+  uint32_t callback = req.cmd->value;
+
+  struct station_config conf;
+  bool res = wifi_station_get_config(&conf);
+  if (res) {
+  // #warning handle me
+  } else {
+  }
+
+  DBG("QuerySSID : %s\n", conf.ssid);
+
+  cmdResponseStart(CMD_RESP_CB, callback, 1);
+  cmdResponseBody(conf.ssid, strlen((char *)conf.ssid)+1);
+  cmdResponseEnd();
+}
+
+// Start scanning, API interface
+static void ICACHE_FLASH_ATTR cmdWifiStartScan(CmdPacket *cmd) {
+  // call a function that belongs in meta-id/cgiwifi.c due to variable access
+  wifiStartScan();
+}
+
+// Command handler for MQTT information
+void ICACHE_FLASH_ATTR cmdMqttGetClientId(CmdPacket *cmd) {
+  CmdRequest req;
+
+  cmdRequest(&req, cmd);
+  if(cmd->argc != 0 || cmd->value == 0) {
+    cmdResponseStart(CMD_RESP_V, 0, 0);
+    cmdResponseEnd();
+    return;
+  }
+
+  uint32_t callback = req.cmd->value;
+
+  cmdResponseStart(CMD_RESP_CB, callback, 1);
+  cmdResponseBody(flashConfig.mqtt_clientid, strlen(flashConfig.mqtt_clientid)+1);
+  cmdResponseEnd();
+
+  os_printf("MqttGetClientId : %s\n", flashConfig.mqtt_clientid);
+}

+ 4 - 0
meta-id/envvars.template

@@ -0,0 +1,4 @@
+export SDK_BASE=~/esp-open-sdk
+export XTENSA_TOOLS_ROOT=~/esp-open-sdk/xtensa-lx106-elf/xtensa-lx106-elf/bin/
+export PATH=$PATH:$XTENSA_TOOLS_ROOT
+

+ 293 - 0
meta-id/espfs/espfs.c

@@ -0,0 +1,293 @@
+/*
+This is a simple read-only implementation of a file system. It uses a block of data coming from the
+mkespfsimg tool, and can use that block to do abstracted operations on the files that are in there.
+It's written for use with httpd, but doesn't need to be used as such.
+*/
+
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
+ * this notice you can do whatever you want with this stuff. If we meet some day,
+ * and you think this stuff is worth it, you can buy me a beer in return.
+ * ----------------------------------------------------------------------------
+ */
+
+
+//These routines can also be tested by comping them in with the espfstest tool. This
+//simplifies debugging, but needs some slightly different headers. The #ifdef takes
+//care of that.
+
+#ifdef __ets__
+//esp build
+#include <esp8266.h>
+#else
+//Test build
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#define os_malloc malloc
+#define os_free free
+#define os_memcpy memcpy
+#define os_memset memset
+#define os_strncmp strncmp
+#define os_strcmp strcmp
+#define os_strcpy strcpy
+#define os_printf printf
+#define ICACHE_FLASH_ATTR
+#endif
+
+#include "espfsformat.h"
+#include "espfs.h"
+
+EspFsContext espLinkCtxDef;
+EspFsContext userPageCtxDef;
+
+EspFsContext * espLinkCtx = &espLinkCtxDef;
+EspFsContext * userPageCtx = &userPageCtxDef;
+
+struct EspFsContext
+{
+	char*       data;
+	EspFsSource source;
+	uint8_t     valid;
+};
+
+struct EspFsFile {
+	EspFsContext *ctx;
+	EspFsHeader *header;
+	char decompressor;
+	int32_t posDecomp;
+	char *posStart;
+	char *posComp;
+	void *decompData;
+};
+
+/*
+Available locations, at least in my flash, with boundaries partially guessed. This
+is using 0.9.1/0.9.2 SDK on a not-too-new module.
+0x00000 (0x10000): Code/data (RAM data?)
+0x10000 (0x02000): Gets erased by something?
+0x12000 (0x2E000): Free (filled with zeroes) (parts used by ESPCloud and maybe SSL)
+0x40000 (0x20000): Code/data (ROM data?)
+0x60000 (0x1C000): Free
+0x7c000 (0x04000): Param store
+0x80000 - end of flash
+
+Accessing the flash through the mem emulation at 0x40200000 is a bit hairy: All accesses
+*must* be aligned 32-bit accesses. Reading a short, byte or unaligned word will result in
+a memory exception, crashing the program.
+*/
+
+//Copies len bytes over from dst to src, but does it using *only*
+//aligned 32-bit reads. Yes, it's no too optimized but it's short and sweet and it works.
+
+//ToDo: perhaps os_memcpy also does unaligned accesses?
+#ifdef __ets__
+void ICACHE_FLASH_ATTR memcpyAligned(char *dst, const char *src, int len) {
+	int x;
+	int w, b;
+	for (x=0; x<len; x++) {
+		b=((int)src&3);
+		w=*((int *)(src-b));
+		if (b==0) *dst=(w>>0);
+		if (b==1) *dst=(w>>8);
+		if (b==2) *dst=(w>>16);
+		if (b==3) *dst=(w>>24);
+		dst++; src++;
+	}
+}
+#else
+#define memcpyAligned memcpy
+#endif
+
+void ICACHE_FLASH_ATTR memcpyFromFlash(char *dst, const char *src, int len)
+{
+	if( spi_flash_read( (int)src, (void *)dst, len ) != SPI_FLASH_RESULT_OK )
+		os_memset( dst, 0, len ); // if read was not successful, reply with zeroes
+}
+
+// memcpy on MEMORY/FLASH file systems
+void espfs_memcpy( EspFsContext * ctx, void * dest, const void * src, int count )
+{
+	if( ctx->source == ESPFS_MEMORY )
+		os_memcpy( dest, src, count );
+	else
+		memcpyFromFlash(dest, src, count);
+}
+
+// aligned memcpy on MEMORY/FLASH file systems
+void espfs_memcpyAligned( EspFsContext * ctx, void * dest, const void * src, int count )
+{
+	if( ctx->source == ESPFS_MEMORY )
+		memcpyAligned(dest, src,