diff --git a/.gitignore b/.gitignore index e6a9b03..44d774e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ firmware-src/pages/pages.h esp-utils/build esp-utils/.settings sdk/lib/libc.a +release/build +release/*.zip +release/*.tar.gz +firmware-src/pages/debug.log diff --git a/DeviceHiveESP8266.md b/DeviceHiveESP8266.md index 5059b16..2c2588e 100644 --- a/DeviceHiveESP8266.md +++ b/DeviceHiveESP8266.md @@ -1,13 +1,15 @@ -#DeviceHive ESP8266 Firmware User Guide. -![](images/dh-logo.png?raw=true) -Table of contents -================= +# DeviceHive ESP8266 Firmware User Guide. + +![](images/dh-logo.png?raw=true) + +# Table of contents * [Overview](#overview) * [Getting started](#getting-started) - * [Local services ](#local-services) - * [mDNS](#mdns) + * [Local services](#local-services) + * [mDNS](#mdns) * [RESTful API](#restful-api) * [Web server](#web-server) + * [Uploadable page](#uploadable-page) * [Wireless configuring](#wireless-configuring) * [Pin definition](#pin-definition) * [GPIO](#gpio) @@ -51,10 +53,10 @@ Table of contents * [devices/mhz19/read](#devicesmhz19read) * [License](#license) -#Overview -This document explains the set of REST API commands to control your remote ESP8266 — an incredible all around IoT chip. For more information about ESP8266 please refer to https://en.wikipedia.org/wiki/ESP8266 +# Overview +This document explains the set of REST API commands to control your remote ESP8266 — an incredible all around IoT chip. For more information about ESP8266 please refer to [https://en.wikipedia.org/wiki/ESP8266](https://en.wikipedia.org/wiki/ESP8266) -Once ESP8266 device is connected you can issue commands using DeviceHive's REST API or local REST API hosted rigth on the chip. It can be a JavaScript, python or anything that supports HTTP and JSON, even command-line curl. +Once ESP8266 device is connected you can issue commands using DeviceHive's RESTful API or local REST API hosted right on the chip. It can be a JavaScript, python or anything that supports HTTP and JSON, even command-line curl. *Example using curl on Mac or Linux:* ```shell @@ -63,14 +65,14 @@ curl -H 'Authorization: Bearer eiMfp26Z+yRhiAafXWHCXT0LofwehgikmtygI6XoXIE=' \ -d '{"command":"gpio/write","parameters":{"1":0}}' \ http://nn8571.pg.devicehive.com/api/device/astaff/command ``` -This would set pin GPIO1 to 0. For expample on Adafruit's Huzzah ESP8266 modules (https://www.adafruit.com/products/2471) with PIN1 connected to LED it will turn the LED on. +This would set pin GPIO1 to 0. For example on Adafruit's Huzzah ESP8266 modules (https://www.adafruit.com/products/2471) with PIN1 connected to LED it will turn the LED on. -In the same way local REST api can be used. Just run: +In the same way local RESTful API can be used. Just run: ```shell curl -H 'Authorization: Bearer eiMfp26Z+yRhiAafXWHCXT0LofwehgikmtygI6XoXIE=' \ http://eps-device-id.local/api/gpio/read ``` -This would print input state of all GPIO pins with json format. +This command prints input state of all GPIO pins with json format. The same idea with other interfaces. To check if your hardware device is suitable with firmware check which interface your devices has and then check if this interface is supported by DeviceHive ESP8266 firmware. @@ -78,8 +80,8 @@ The latest version can be found in git project repository. There are sources cod The purpose of this firmware is to provide easy tool for building IoT solutions for developers which used to program on unsuitable for microcontroller programming languages. You can easily use AngularJS framework for example to implement your idea. Also, considering chip price, DeviceHive usability and plenty modules on market which are not require soldering, it looks like perfect tool for prototyping. DIY developers also may find this firmware very useful for their project. -#Getting started -First at all firmware have to be flashed into chip memory and chip have to be configured for using specified Wi-Fi network and DeviceHive server. Developers can build firmware and all tools for flashing and configuring by himself from sources. Or it can be downloaded from git repository: go to https://github.com/devicehive/esp8266-firmware/tree/master/release and download archive with the latest version. +# Getting started +First at all firmware have to be flashed into chip memory and chip have to be configured for using specified Wi-Fi network and DeviceHive server(can be omitted for local usage). Board should have at least 512KiB of ROM memory. Developers can build firmware and all tools for flashing and configuring by himself from sources. Or it can be downloaded from git repository: go to https://github.com/devicehive/esp8266-firmware/tree/master/release and download archive with the latest version. For flashing chip needs to be connected to computer via USB-UART converter, CH_PD pin have to be connected to Vcc, GPIO15 and GPIO0 have to be connected to ground and 3.3 power supply should be used. Sample for most popular pre soldered modules connection is below: @@ -98,55 +100,60 @@ Also, it's possible to use NodeMCU boards. It already has UART converter, reset ![](images/sample5.jpg?raw=true) After assembling, connect it to computer. Install driver for your USB->UART converter. The most popular chip and official sites with drivers below: -* CP210x: http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx -* PL230x: http://www.prolific.com.tw/US/ShowProduct.aspx?pcid=41 -* FTDI: http://www.ftdichip.com/Drivers/VCP.htm -* CH341: http://www.wch.cn/index.php?s=/page-search_content-keyword-CH341SER.html +* CP210x: [http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx](http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx) +* PL230x: [http://www.prolific.com.tw/US/ShowProduct.aspx?pcid=41](http://www.prolific.com.tw/US/ShowProduct.aspx?pcid=41) +* FTDI: [http://www.ftdichip.com/Drivers/VCP.htm](http://www.ftdichip.com/Drivers/VCP.htm) +* CH341: [http://www.wch.cn/index.php?s=/page-search_content-keyword-CH341SER.html](http://www.wch.cn/index.php?s=/page-search_content-keyword-CH341SER.html) -Make sure that virtual serial port is available in your system( virtual COM is present on Windows OS, '/dev/ttyUSB*' on Linux, '/dev/tty.*' on OS X). Unpack archive with firmware and flash it running 'esp-flasher' in terminal. Flasher automatically detects serial port and use 'devicehive.bin' file for flashing. Successful flasher output is below: +Make sure that virtual serial port is available in your system (virtual COM is present on Windows OS, '/dev/ttyUSB*' on Linux, '/dev/tty.*' on OS X). Unpack archive with firmware and flash it running 'esp-flasher' in terminal. Flasher automatically detects serial port and use 'devicehive.bin' file for flashing. Successful flasher output is below: ![](images/term.png?raw=true) -Now remove wire from GPIO0(live it float or connect to high), reboot device and connect to firmware with with 'esp-terminal' util. You can also use any other tool that can connect to terminal via UART and support escape sequences, PuTTY or GNU 'screen' for example. Port parameters are: 115200 8N1. +Now remove wire from GPIO0(live it float or connect to high), reboot device and connect to firmware with 'esp-terminal' util. You can also use any other tool that can connect to terminal via UART and support escape sequences, PuTTY or GNU 'screen' for example. Port parameters are: 115200 8N1. -_Notice: you can avoid configuring firmware with terminal and use wireless configuring procedure described in paragraph 3 instead. Wireless configuring procedure also can be used for end-user devices with this firmware._ +_Notice: you can avoid configuring firmware with terminal and use wireless configuring procedure described in [wireless configuring section](#wireless-configuring) instead. Wireless configuring procedure also can be used for end-user devices with this firmware._ -Firmware terminal is a unix like terminal with few commands. It exists for chip configuring and debugging. To see debug output type 'dmesg'. To configure run 'configure' command. Follow instructions in terminal. You need to know DeviceHive server credentials for configuring for working with cloud services. +Firmware terminal is a UNIX like terminal with few commands. It exists for chip configuring and debugging. To see debug output type 'dmesg'. To configure run 'configure' command. Follow instructions in terminal. You need to know DeviceHive server credentials for configuring for working with cloud services. -_For the very beginning or DIY purpose you can use DeviceHive free playground located here: http://playground.devicehive.com/ Register there and you will have your own DeviceHive server instance. DeviceHive server can be deployed in local network or on some cloud hosting services. Follow for DeviceHive server deployment instructions on http://devicehive.com_ +_For the very beginning or DIY purpose you can use DeviceHive free playground located here: [http://playground.devicehive.com/](http://playground.devicehive.com/) Register there and you will have your own DeviceHive server instance. DeviceHive server can be deployed in local network or on some cloud hosting services. Follow for DeviceHive server deployment instructions on [http://devicehive.com](http://devicehive.com)_ Configuring sample is below: ![](images/conf.png?raw=true) -_If DeviceHive API url isn't specified, chip will work only as local RESTful server. AccessKey is used for local RESTful API and remote DeviceHive server. See [Local RESTful API](#local-restful-api) for details._ +_If DeviceHive API URL isn't specified, chip will work only as local RESTful server. AccessKey is used for local RESTful API and remote DeviceHive server. See [Local RESTful API](#restful-api) for details._ After rebooting you can send commands to DeviceHive server or local RESTful API and ESP8266 perform them. List of accepted command is in this document. You can use DeviceHive web admin control panel to send command for test purpose or learning. Go in web admin, 'Devices' tab, 'commands' subtab, 'enter new command'. Type command and parameters and press 'push'. After ESP8266 perform your command you can press 'refresh' button to see result. For example 'gpio/read' command would look in admin control panel as below: ![](images/web.png?raw=true) -Now you can start writing your own program to create your own IoT devices with your favorite language and frameworks usigng DeviceHive RESTfull API: http://devicehive.com/restful which you can transmited with HTTP(S) or Websockets. List of accepted command for ESP8266 is listed in this document. +Now you can start writing your own program to create your own IoT devices with your favorite language and frameworks using DeviceHive RESTful API: [http://devicehive.com/restful](http://devicehive.com/restful) which you can transmitted with HTTP(S) or Websockets. List of accepted command for ESP8266 is listed in this document. -#Local services -Firmware sets chip hostname and announce chip with mDNS using configured DeviceId. Hostname is limited with 32 chars, further DeiviceId's chars are ommited. +# Local services +Firmware sets chip hostname and announce chip with mDNS using configured DeviceId. Hostname is limited with 32 chars, further DeiviceId's chars are omitted. -##mDNS -mDNS(multicast Domain Name System) can resolve local domain names to IP address. Firmware anounce itself in mDNS using DeiviceId. mDNS 2nd level domain is limited with 60 chars, so any subsequent chars of DeviceId are omitted. Top level domain is always '.local'. mDNS-SD (service discovery) is supported. Service name is '_esp8266-devicehive._tcp.local'. This service points to local web server with RESTful API. One TXT record with firmware version is present. +## mDNS +mDNS(multicast Domain Name System) can resolve local domain names to IP address. Firmware announce itself in mDNS using DeiviceId. mDNS 2nd level domain is limited with 60 chars, so any subsequent chars of DeviceId are omitted. Top level domain is always `.local`. mDNS-SD (service discovery) is supported. Service name is `_esp8266-devicehive._tcp.local`. This service points to local web server with RESTful API. One TXT record with firmware version is present. -##RESTful API -A RESTful API is an application program interface(API) which uses HTTP requests for calling remote procedures. In this implementation such procedures is commands for chip. There is a tiny web server on chip port 80 which provides local RESTful API. API endpoint is `http://device-id-or-ip.local/api/`. Firmware commands are available as subpaths of API endpoint. For example command `spi/master/read` available at `http://device-id-or-ip.local/api/spi/master/read`. Any parameters should be passed as json in request body. On success, request will be responded with 2xx HTTP code and 4xx on error. Commands, its parameters and return values are the same as for DeviceHive cloud server except notifications. Any notifications are not supported, so commands for subscribing on it also don't available. `GET` and `POST` method are supported, and there is no difference for API, but `GET` should be sent with a content in a single TCP packet and `POST` supports only one simultaneous connection. HTTP access control allows any request origin. If device has AccessKey, endpoint require authentication with HTTP header `Authorization: Bearer YourAccessKeyHere`. +## RESTful API +A RESTful API is an application program interface(API) which uses HTTP requests for calling remote procedures. In this implementation such procedures is commands for chip. There is a tiny web server on chip port 80 which provides local RESTful API. API endpoint is `http://device-id-or-ip.local/api/`. Firmware commands are available as sub paths of API endpoint. For example command `api/master/read` available at `http://device-id-or-ip.local/api/spi/master/read`. Any parameters should be passed as json in request body. On success, request will be responded with 2xx HTTP code and 4xx on error. Commands, its parameters and return values are the same as for DeviceHive cloud server except notifications. Any notifications are not supported, so commands for subscribing on it also don't available. `GET` and `POST` method are supported, and there is no difference for API, but `GET` should be sent with a content in a single TCP packet and `POST` supports only one simultaneous connection. HTTP access control allows any request origin. If device has AccessKey, endpoint require authentication with HTTP header `Authorization: Bearer YourAccessKeyHere`. For example, we would like to set up pin GPIO1 to high state and chip has AccessKey configured. `curl` request is: ```shell -curl -i -H 'Authorization: Bearer eiMfp26Z+yRhiAafXWHCXT0LofwehgikmtygI6XoXIE=' \ +curl -i -H 'Authorization: Bearer SomeAccessKeyHere' \ http://eps-device-id.local/api/gpio/write -d '{"1":1}' ``` Chip answers on this request '204 No content' which means that operation successfully completed. -##Web server -Firmware includes local HTTP server with tools for playing with API and some samples for some sensor. Web server aviliable at chip's 80 port. Having DeviceId configured and mDNS compatible OS, it is possible to to open web page at http://your-device-id-or-chip-ip.local/ in browser. To play with RESTful API there is a simple page http://your-device-id.local/tryapi.html where any command can be tried and command's output can be observed. +## Web server +Firmware includes local HTTP server with tools for playing with API and some samples for some sensors. Web server available at chip's 80 port. Having DeviceId configured and mDNS compatible OS, it is possible to open web page at http://your-device-id-or-chip-ip.local/ in browser. To play with RESTful API there is a simple page http://your-device-id-or-chip-ip.local/tryapi.html where any command can be tried and command's output can be observed. + +## Uploadable page +The original main page can be replaced with any other up to 65536 bytes. Only main page can be replaced, there is no way to add more pages. There is a tiny text editor at `http://device-id-or-ip.local/editor.html` which allows to edit page content in web browser and download/upload file. If page was changed, original page is always available at `http://device-id-or-ip.local/help.html`. Do not edit web page simultaneously from different tabs/browsers/computers. -#Wireless configuring +This feature can be used for creating web enabled IoT devices. Any HTML, CSS, JS or anything else can be saved there. Local web server provides this page as is, without any modifications. Embedded JS in web browser can be used for communicating with RESTful API. As an example, it's possible to build some sensor with web interface. A couple of such samples can found on the local web server. `http://device-id-or-ip.local/help.html` page contains a list of them. + +# Wireless configuring Since DeviceHive ESP8266 firmware flashed into chip, it can be configured without any special devices or software. So this mode can be used in end user projects to providing easy way for configuring device. To enter configuration mode just reset device three times with chip RESET pin. Intervals between resets should be more than half seconds and less than 3 seconds, i.e. simply reset device three times leisurely. If board has LED connected to TX pin, it turns on. ESP8266 will operate as Wi-Fi access point providing open wireless network with SSID 'DeviceHive'. Connect to this network with your laptop/phone/tablet or other device with Wi-Fi support. Device with iOS and OS X automatically will show configuration page like below: ![](images/phone1.jpg?raw=true) @@ -164,13 +171,13 @@ _Notice: You can automate configuration process for your devices. Actually to co ``` POST / HTTP/1.0 Host: devicehive.config -Content-Type:.application/x-www-form-urlencoded +Content-Type: application/x-www-form-urlencoded Content-Length: 80 ssid=ssid&pass=pass&url=http%3A%2F%2Fexample.com%2Fapi&id=deviceid&key=accesskey ``` -#Pin definition +# Pin definition Pin name in commands | Function |ESP8266 pin number |NodeMCU board pin ----------------------|------------------|-------------------|------------------ @@ -193,10 +200,10 @@ Common | | | _Notes: GPIO6-GPIO11 usually connected to on-module EEPROM, that is why no API for this pins._ -#GPIO +# GPIO Each ESP8266 pin can be loaded up to 12 mA. Pins also have overvoltage and reverse current protection. -##gpio/write +## gpio/write Sets gpio pins according to parameters specified. Pins will be automatically initialized as output when command is received. All pins will be set up simultaneously. Unlisted pins will remain unaffected. *Parameters*: @@ -213,7 +220,7 @@ JSON with a set of key-value pairs, where key is pin number and value '0' for LO Returns 'OK' on success or 'Error' with description in result. -##gpio/read +## gpio/read Reads the state of all GPIO pins. Only pins specified in the request will be initialized as input. *Parameters*: @@ -231,7 +238,7 @@ JSON with a set of key-value pairs, where key is pin number and value is one of } ``` -Note: pull up and pull down are the SoC feature that allows to set input to high or low through resistor with very high resistance. By default each pin is not connected (Z) and reading will return random value. Enabling pull up feature puts a very weak high level on input pin by default and pull down sets very weak low level, thus making it's state determined as 1 or 0. +Note: pull up and pull down are the SoC feature that allows to set input to high or low through resistor with very high resistance. By default each pin is not connected (Z) and reading will return random value. Enabling pull up feature puts a very weak high level on input pin by default and pull down sets very weak low level, thus making its state determined as 1 or 0. Returns 'OK' on success with result or 'Error' with description in result. @@ -244,7 +251,7 @@ Returns 'OK' on success with result or 'Error' with description in result. } ``` -##gpio/int +## gpio/int Allows you to subscribe to notifications (interrupts) on pin state change. *Parameters*: @@ -257,8 +264,8 @@ JSON with a set of key-value pairs. Where key is pin number and value is one of Mnemonic "all" can be used to set value for all pins. -*Note: Timeout feature shall be used whenever is practicle to avoid flooding with notifications.* -![](images/edge.jpg?raw=true) +*Note: Timeout feature shall be used whenever is practical to avoid flooding with notifications.* +![](images/edge.png?raw=true) *Example*: ```json { @@ -284,10 +291,10 @@ Notifications will be generated with the name 'gpio/int'. Each notification will "tick":123456 } ``` -#ADC +# ADC ESP8266 has just one ADC channel. This channel is connected to a dedicated pin 6 - ‘TOUT’. ADC can measure voltage in range from 0.0V to 1.0V with 10 bit resolution. -##adc/read +## adc/read Reads ADC channels values. ESP8266 has just one channel - ‘0’. *Parameters*: @@ -310,7 +317,7 @@ Returns 'OK' on success with result or 'Error' with description in result. Each } ``` -##adc/int +## adc/int Subscribes on notifications with ADC value with some period. *Parameters*: @@ -334,16 +341,16 @@ Return ‘OK’ in status. Or ‘Error’ and description in result on error. No Where "0" channel number, and "0.0566" current voltage in volts. # PWM -ESP8266 has only software implementation of PWM wich means there is no real-time guarantee on high frequency of PWM. PWM has just one channel, but this channel can control all GPIO outputs with different duty cycle. It also means that all outputs are synchronized and work with the same frequency. PWM depth is 100. PWM can be used as pulse generator with specified number of pulses. +ESP8266 has only software implementation of PWM which means there is no real-time guarantee on high frequency of PWM. PWM has just one channel, but this channel can control all GPIO outputs with different duty cycle. It also means that all outputs are synchronized and work with the same frequency. PWM depth is 100. PWM can be used as pulse generator with specified number of pulses. ## pwm/control Enable or disable PWM. *Parameters*: -Json with set of key-value, where key is pin name and value is duty cycle. Duty cycle is an integer between 0..100, ie percent. Mnemonic pin ‘all’ also can be used to control all GPIO pins simultaneously. To disable PWM for one of the outputs, just set value to ‘disable’ or ‘0’. PWM can be also disabled for pin if command ‘gpio/write’ or 'gpio/read'(only with some pins for init)' is called for pin. +Json with set of key-value, where key is pin name and value is duty cycle. Duty cycle is an integer between 0..100, i.e. percent. Mnemonic pin ‘all’ also can be used to control all GPIO pins simultaneously. To disable PWM for one of the outputs, just set value to ‘disable’ or ‘0’. PWM can be also disabled for pin if command ‘gpio/write’ or 'gpio/read'(only with some pins for initialize)' is called for pin. There are also additional parameters: ‘frequency’ - set PWM base frequency, if this parameter was omitted, previous frequency will be used. ‘frequency’ also can be set while PWM working or before command with pins duty cycles. Default frequency is 1 kHz. Minimum frequency is 0.0005 Hz, maximum is 2000 Hz -‘count’ - the number of pulses that PWM will generate after command, maximum is 4294967295, 0 means never stop. Pins with 100% duty cycle will be switched to low level when pwm stops. +‘count’ - the number of pulses that PWM will generate after command, maximum is 4294967295, 0 means never stop. Pins with 100% duty cycle will be switched to low level when PWM stops. *Example*: ```json { @@ -366,10 +373,10 @@ PWM is can be used to generate single or multiple pulses with specific length: ESP8266 has one UART interface. RX pin is 25(GPIO3), TX pin is 26(GPIO1). ## uart/read -Read data from UART interface. Receing buffer resets on each read or write command and main contain up to 264 bytes. +Read data from UART interface. Receiving buffer resets on each read or write command and may contain up to 264 bytes. *Parameters*: -"mode" - UART speed which can be in range 300..230400. After speed may contains space and UART framing *Parameters*: number of bits(5-8), parity mode(none - "N", odd - "O" or even - "E"), stop bits(one - "1", two - "2"). Framing can be omitted, 8N1 will be used in this case. If this parameter specified UART will be reinit with specified mode. If this parameter is omitted, port will use current settings("115200 8N1" by default) and will not reinit port. +"mode" - UART speed which can be in range 300..230400. After speed may contains space and UART framing *Parameters*: number of bits(5-8), parity mode(none - "N", odd - "O" or even - "E"), stop bits(one - "1", two - "2"). Framing can be omitted, 8N1 will be used in this case. If this parameter specified UART will be reinit with specified mode. If this parameter is omitted, port will use current settings ("115200 8N1" by default) and will not reinit port. "data" - data string encoded with base64 which would be sent before reading. Reading buffer will be cleared and will contain data which is received during "timeout" time only otherwise data will be since last read, write command or since last notification sent. Data size have to be equal or less than 264 bytes. "timeout" - Can be used only with "data" field. Delay in ms for collecting answer after transmitting data. Maximum 1000ms, if not specified 250 ms is used. @@ -407,8 +414,8 @@ Return ‘OK’ in status. Or ‘Error’ and description in result on error. Subscribe on notification which contains data that was read from UART. Firmware starts wait for data from and each time when byte is received byte puts into buffer (264 bytes len), then firmware starts wait for the next byte with some timeout. When timeout reached or buffer is full firmware sends notification. *Parameters*: -"mode" - the same "mode" parameter as in "uart/write"command, see description there. It also can be omitted to keep current parameters. Additionally this parameter can be "disable" or "0" for disabling notifications. -"timeout" - timeout for notifications in miliseconds. If internal buffer received something, notification will be sent with this timeout after last byte. Default is 250 ms. Maximum 5000 ms. +"mode" - the same "mode" parameter as in "uart/write" command, see description there. It also can be omitted to keep current parameters. Additionally this parameter can be "disable" or "0" for disabling notifications. +"timeout" - timeout for notifications in milliseconds. If internal buffer received something, notification will be sent with this timeout after last byte. Default is 250 ms. Maximum 5000 ms. *Example*: ```json @@ -459,7 +466,7 @@ Read specified number of bytes from bus. This command also can set up pins that ``` *Notes: -Very common situation when slave device needs to be written with register address and data can be readed after repeated START. Using this command with "data" field allow to organise repeated START for reading.* +Very common situation when slave device needs to be written with register address and data can be read after repeated START. Using this command with "data" field allow to organize repeated START for reading.* Return ‘OK’ in status and json like below in result on success. Or ‘Error’ and description in result on error. ```json @@ -592,7 +599,7 @@ Read specified data to onewire bus. Onewire pin can also be specified with this ```json { "data":"YWI=", - "pin": "2", + "pin": "2" } ``` Return ‘OK’ in status. Or ‘Error’ and description in result on error. @@ -609,7 +616,7 @@ Mnemonic "all" can be used as key to set up something for all pins. *Example*: ```json { - "2":"presence", + "2":"presence" } ``` Return ‘OK’ in status. Or ‘Error’ and description in result on error. @@ -629,7 +636,7 @@ Search bus for serial numbers of all attached devices. *Example*: ```json { - "pin":"2", + "pin":"2" } ``` Return ‘OK’ in status and result with list as below. Or ‘Error’ and description in result on error. @@ -649,7 +656,7 @@ Search bus for serial numbers of attached devices which are in alarm state. *Example*: ```json { - "pin":"0", + "pin":"0" } ``` @@ -663,7 +670,7 @@ Return ‘OK’ in status and result with list as below. Or ‘Error’ and desc ## onewire/dht/read -Read data from DHT11/DHT22/AM2302 or device with the same protocol. Number of readed data depends on device, but can not be more that 264. Any checksums will not be checked. +Read data from DHT11/DHT22/AM2302 or device with the same protocol. Number of bytes for reading depends on device, but can not be more that 264. Any checksums will not be checked. *Parameters*: "pin" - GPIO port number for onewire data line. If not specified, previous pins will be used. Default is "0". @@ -671,7 +678,7 @@ Read data from DHT11/DHT22/AM2302 or device with the same protocol. Number of re *Example*: ```json { - "pin":"0", + "pin":"0" } ``` Return ‘OK’ in status and json like below in result on success. Or ‘Error’ and description in result on error. @@ -683,7 +690,7 @@ Return ‘OK’ in status and json like below in result on success. Or ‘Error "data" field is base64 encoded data that was read from bus. # Devices -This section desribes simple API for handling some hardware sensors. Internally it uses one of interfaces described above, so parameters and error responses are mostly common. +This section describes simple API for handling some hardware sensors. Internally it uses one of interfaces described above, so parameters and error responses are mostly common. ## devices/ds18b20/read Read temperature from DS18B20 sensor. Only one sensor can be connected to pin(skip ROM is used). Measurement uses sensor defaults. @@ -751,8 +758,8 @@ Temperature unit in Celsius degrees. Humidity unit is percent. Read temperature and pressure from BMP180 sensor. *Parameters*: -"address" - I2C BMP180 device address. Behavior is the same as i2c interface, except it can be ommitted. If not specified, previous pin will be used. Default is 0xEE. -"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. +"address" - I2C BMP180 device address. Behavior is the same as i2c interface, except it can be omitted. If not specified, previous pin will be used. Default is 0xEE. +"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. "SCL" - GPIO port number for SCL data line. Behavior and default are common with i2c interface. *Example*: @@ -760,7 +767,7 @@ Read temperature and pressure from BMP180 sensor. { "SDA":"4", "SCL":"5", - "address":"0xEE", + "address":"0xEE" } ``` @@ -777,8 +784,8 @@ Temperature unit in Celsius degrees. Pressure unit is pascal. Read illuminance from BH1750 sensor. Mode is 'High Resolution2'. *Parameters*: -"address" - I2C BH1750 device address. Behavior is the same as i2c interface, except it can be ommitted. If not specified, previous pin will be used. Default is 0x46. -"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. +"address" - I2C BH1750 device address. Behavior is the same as i2c interface, except it can be omitted. If not specified, previous pin will be used. Default is 0x46. +"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. "SCL" - GPIO port number for SCL data line. Behavior and default are common with i2c interface. *Example*: @@ -786,7 +793,7 @@ Read illuminance from BH1750 sensor. Mode is 'High Resolution2'. { "SDA":"4", "SCL":"5", - "address":"0x46", + "address":"0x46" } ``` @@ -796,14 +803,14 @@ Return ‘OK’ in status and json like below in result on success. Or ‘Error "illuminance":59.1667 } ``` -Illuminance unit in lux(lumens per square metre). +Illuminance unit in lux(lumens per square meter). ## devices/mpu6050/read -Read accelerometer, gyroscope and temperature data from MPU6050 sensor. Accelerometer config gives -8g...+8g values, gyroscope -1000...+1000 dps. +Read accelerometer, gyroscope and temperature data from MPU6050 sensor. Accelerometer is configured -8g...+8g values, gyroscope -1000...+1000 dps. *Parameters*: -"address" - I2C MPU6050 device address. Behavior is the same as i2c interface, except it can be ommitted. If not specified, previous pin will be used. Default is 0xD0. -"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. +"address" - I2C MPU6050 device address. Behavior is the same as i2c interface, except it can be omitted. If not specified, previous pin will be used. Default is 0xD0. +"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. "SCL" - GPIO port number for SCL data line. Behavior and default are common with i2c interface. *Example*: @@ -811,7 +818,7 @@ Read accelerometer, gyroscope and temperature data from MPU6050 sensor. Accelero { "SDA":"4", "SCL":"5", - "address":"0xD0", + "address":"0xD0" } ``` @@ -833,14 +840,14 @@ Return ‘OK’ in status and json like below in result on success. Or ‘Error } } ``` -Temperature unit in Celsius degrees. Acceleration unit is metre per second squared. Rotation unit is degree per second. +Temperature unit in Celsius degrees. Acceleration unit is meter per second squared. Rotation unit is degree per second. ## devices/hmc5883l/read Read magnetometer, i.e. compass data. All configs are default, sensor field range is 1.3 gauss. *Parameters*: -"address" - I2C HMC5883L device address. Behavior is the same as i2c interface, except it can be ommitted. If not specified, previous pin will be used. Default is 0x3C. -"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. +"address" - I2C HMC5883L device address. Behavior is the same as i2c interface, except it can be omitted. If not specified, previous pin will be used. Default is 0x3C. +"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. "SCL" - GPIO port number for SCL data line. Behavior and default are common with i2c interface. *Example*: @@ -848,7 +855,7 @@ Read magnetometer, i.e. compass data. All configs are default, sensor field rang { "SDA":"4", "SCL":"5", - "address":"0x3C", + "address":"0x3C" } ``` @@ -869,10 +876,10 @@ Data unit is gauss. Proportional to the magnetic field component along its axis. Read GPIO extender pins state. All pins have pull up after powering on and this is the only one way to operate as inputs. *Parameters*: -"address" - I2C PCF8574 device address. Behavior is the same as i2c interface, except it can be ommitted. If not specified, previous pin will be used. Default is 0x4E. -"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. +"address" - I2C PCF8574 device address. Behavior is the same as i2c interface, except it can be omitted. If not specified, previous pin will be used. Default is 0x4E. +"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. "SCL" - GPIO port number for SCL data line. Behavior and default are common with i2c interface. -Pin numbers-value pairs where value can be only "pullup". If pin was used as output, "pullup" sets it back to input before reading. "all" key for all pins are supported. +Pin numbers-value pairs where value can be only "pullup". If pin was used as output, "pullup" sets it back to input before reading. "all" key for all pins are supported. *Example*: ```json @@ -889,8 +896,7 @@ Return ‘OK’ in status and json like below in result on success. Or ‘Error { "0":"1", "1":"1", - "2":"0", - ... + "2":"0" } ``` Chip has 8(0..7) ports. @@ -899,11 +905,10 @@ Chip has 8(0..7) ports. Write GPIO extender pins state. HIGH level provides small limited(100 uA) current and actually that is the same as "pullup" from hardware side and this API. See chip datasheet for details. *Parameters*: -"address" - I2C PCF8574 device address. Behavior is the same as i2c interface, except it can be ommitted. If not specified, previous pin will be used. Default is 0x4E. -"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. -"SCL" - GPIO port number for SCL data line. Behavior and default are common with i2c interface. -Set of key-value pairs, where key is pin number and value '0' for LOW, '1' for HIGH or 'x' for NOP, leaving pin unaffected. Sample below sets gpio0 to LOW and gpio1 to HGIH. - +"address" - I2C PCF8574 device address. Behavior is the same as i2c interface, except it can be omitted. If not specified, previous pin will be used. Default is 0x4E. +"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. +"SCL" - GPIO port number for SCL data line. Behavior and default are common with i2c interface. +Set of key-value pairs, where key is pin number and value '0' for LOW, '1' for HIGH or 'x' for NOP, leaving pin unaffected. Sample below sets gpio0 to LOW and gpio1 to HIGH. *Example*: ```json @@ -918,15 +923,14 @@ Set of key-value pairs, where key is pin number and value '0' for LOW, '1' for H Return ‘OK’ in status on success. Or ‘Error’ and description in result on error. ## devices/pcf8574/hd44780/write -Write with GPIO extender to HD44780 like display (1602A, KS0066 etc). It can have 16x2, 20x2, 20x4 or any other character array. Symbol "\n" (0x0A) for newline is supported. All display data erases on each command. PCF8574 should be connected to display with this pinmap: P0->RS, P1->RW, P2->E, P3->Backligth control or not connected, P4->D4, P5->D5, P6->D6 and P7->D7. +Write with GPIO extender to HD44780 like display (1602A, KS0066 etc). It can have 16x2, 20x2, 20x4 or any other character array. Symbol "\n" (0x0A) for newline is supported. All display data erases on each command. PCF8574 should be connected to display with this pinmap: P0->RS, P1->RW, P2->E, P3->Backlight control or not connected, P4->D4, P5->D5, P6->D6 and P7->D7. *Parameters*: -"address" - I2C PCF8574 device address. Behavior is the same as i2c interface, except it can be ommitted. If not specified, previous pin will be used. Default is 0x4E. -"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. -"SCL" - GPIO port number for SCL data line. Behavior and default are common with i2c interface. -"data" - Text to set up in base64 encoding. Cannot be combined with 'text' field in one command. -"text" - Plain text to set up. Cannot be combined with 'data' field in one command. - +"address" - I2C PCF8574 device address. Behavior is the same as i2c interface, except it can be omitted. If not specified, previous pin will be used. Default is 0x4E. +"SDA" - GPIO port number for SDA data line. Behavior and default are common with i2c interface. +"SCL" - GPIO port number for SCL data line. Behavior and default are common with i2c interface. +"data" - Text to set up in base64 encoding. Cannot be combined with 'text' field in one command. +"text" - Plain text to set up. Cannot be combined with 'data' field in one command. *Example*: ```json diff --git a/README.md b/README.md index 55b3936..45325a0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # DeviceHive ESP8266 Firmware -Special firmware for usage ESP8266 in DeviceHive clouds. +Special firmware for usage ESP8266 as local web server with RESTful API and as DeviceHive clouds client. This repo consist of few parts of this project which can be used with other projects. Each project has dedicated readme file. @@ -7,8 +7,14 @@ other projects. Each project has dedicated readme file. Photo above is a real photo of the demo device contructed with esp8266. See http://youtu.be/hzi4djt-wdg +# [DeviceHiveESP8266.md](DeviceHiveESP8266.md) +This is the main documentation file for this firmware. Document contains +commands specification, describes all features and firmware usage. +[Click here to open it.](DeviceHiveESP8266.md) + # Demo videos Zero wireless configuring with Android http://youtu.be/2J98YDpbJKo +DeviceHive clouds demos: BH1750 Ambient light http://youtu.be/AkSFdO0soyo DS18B20 + iButton + DHT11 http://youtu.be/IuvxwCPNZCc Muscle connected to the cloud http://youtu.be/8L96nBNHE14 @@ -58,15 +64,12 @@ means that command and parameters is written correctly and you can use it as a sample. # release -Pre built binaries of utils and released firmwares. +Scripts for generating binary releases. # sdk SDK from chip manufactor. Included in this repo to make sure that we are using the same version of this SDK to avoid any surprises from changing APIs -# DeviceHiveESP8266.md -Document contains cloud commands specification for this firmware. [Click here to open it.](DeviceHiveESP8266.md) - # License The MIT License. See LICENSE file. Except sdk directory, it has ESPRSSIF MIT License, see sdk/License file for details. diff --git a/changelog.txt b/changelog.txt index 12c5321..d346a8d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -22,7 +22,9 @@ v0.4 - new playgrounds support v0.5 +- uploadable web page +- mDNS - local embedded web server with tools and samples - local RESTful API - support for some popular sensor devices -- bug fixes \ No newline at end of file +- bug fixes diff --git a/esp-utils/Makefile b/esp-utils/Makefile index e5890e9..f22e390 100644 --- a/esp-utils/Makefile +++ b/esp-utils/Makefile @@ -1,6 +1,6 @@ -CC = g++ -CFLAGS = -c -Wall -LDFLAGS = -s -Os +CXX ?= g++ +CFLAGS += -c -Wall +LDFLAGS += -s -Os LIBS = OBJDIR = build SOURCES = $(wildcard common/*.cpp) @@ -19,18 +19,18 @@ all: $(SOURCES) $(ESPTERMINAL) $(ESPFLASHER) $(ESPTERMINAL): $(ALLOBJECTS) @echo "LD $@" - @$(CC) $(LDFLAGS) $(LIBS) $(COMMONOBJECTS) $@.o -o $@ + @$(CXX) $(LDFLAGS) $(LIBS) $(COMMONOBJECTS) $@.o -o $@ $(ESPFLASHER): $(ALLOBJECTS) @echo "LD $@" - @$(CC) $(LDFLAGS) $(LIBS) $(COMMONOBJECTS) $@.o -o $@ + @$(CXX) $(LDFLAGS) $(LIBS) $(COMMONOBJECTS) $@.o -o $@ $(OBJDIR)/%.o: %.cpp - @echo "CC $<" + @echo "CXX $<" @mkdir -p $(dir $@) - @$(CC) $(CFLAGS) $< -o $@ + @$(CXX) $(CFLAGS) $< -o $@ rebuild: clean all clean: - @rm -rf $(OBJDIR) \ No newline at end of file + @rm -rf $(OBJDIR) diff --git a/esp-utils/README.md b/esp-utils/README.md index d268d15..f1ca256 100644 --- a/esp-utils/README.md +++ b/esp-utils/README.md @@ -16,13 +16,19 @@ To quit from terminal press Ctrl+Q # esp-flasher usage Run application and it will try to detect device automatically. If no parameters -were specified it also will try to open files 0x00000.bin and 0x40000.bin in -current directory and flash them to corresponding addresses. +were specified it also will try to open files devicehive.bin in current directory +and directory with its binary and flash them to corresponding addresses. You can specify port name in first argument if you want to specify it manually. You also can specify which files have to be written in devices in arguments by pairs hex address file name. For exmaple: esp-flasher COM2 0x00000 boot.img 0x40000 spi.img esp-flasher 0x40000 myimagefile.bin +There also `--developer` and `--reboot` arguments which supposed to be used by +developers only. First enables incremental flash mode, it compares previosuly +flashed file (should be saved as `devicehive.bin.prev`) and if differences are +minimal it will flash only them to save time on flashing. `--reboot` argument +simply reboot chip (serial adapter RTS should connected to GPIO0, DTR to RTS +pin). # License see LICENSE file diff --git a/esp-utils/common/serialport_posix.cpp b/esp-utils/common/serialport_posix.cpp index 701b69c..c323b5e 100644 --- a/esp-utils/common/serialport_posix.cpp +++ b/esp-utils/common/serialport_posix.cpp @@ -104,7 +104,7 @@ SerialPort::~SerialPort() { unsigned int SerialPort::write_native(const void *data, unsigned int len) { ssize_t bw; - ssize_t total = 0; + size_t total = 0; do { bw = write(mCom, (char *)data + total, len - total); total += bw; diff --git a/esp-utils/esp-flasher.cpp b/esp-utils/esp-flasher.cpp index 03a8e24..83c2d01 100644 --- a/esp-utils/esp-flasher.cpp +++ b/esp-utils/esp-flasher.cpp @@ -405,6 +405,17 @@ void force_flash_mode(SerialPort *port) { port->setDtr(false); } +void reboot(SerialPort *port) { + // Typically dev boards have: + // RTS is connected to GPIO0 + // DTR is connected to RTS + port->setDtr(false); + port->sleep(50); + port->setDtr(true); + port->sleep(50); + port->setDtr(false); +} + int main(int argc, char* argv[]) { setbuf(stdout, NULL); int currentArg = 1; @@ -471,9 +482,12 @@ int main(int argc, char* argv[]) { bool developerMode = false; if(argc > currentArg) { - if(strncmp(argv[1], "--developer", 5) == 0) { + if(strncmp(argv[currentArg], "--developer", 5) == 0) { currentArg++; developerMode = true; + } else if(strncmp(argv[currentArg], "--reboot", 5) == 0) { + reboot(port); + return 0; } } @@ -493,7 +507,25 @@ int main(int argc, char* argv[]) { "0x7C000 <- default configuration\r\n" ); } - isSuccess = flash_file(port, (char*)"devicehive.bin", 0x00000, + char dh[] = "devicehive.bin"; + int l = strlen(argv[0]); + char df[l + sizeof(dh)]; + char *filename = dh; + FILE *fl = fopen(dh, "rb"); + if(fl) { // if in current dir present + fclose(fl); + } else { // check the directory with binary + strcpy(df, argv[0]); + while(l >= 0) { + if(df[l] == '/' || df[l] == '\\') { + strcpy(&df[l + 1], dh); + filename = df; + break; + } + l--; + } + } + isSuccess = flash_file(port, filename, 0x00000, developerMode ? (char*)"devicehive.bin.prev" : NULL); if(isSuccess && !developerMode) isSuccess = flash_default_config(port); diff --git a/firmware-src/.cproject b/firmware-src/.cproject index 988fb48..8ebbe5b 100644 --- a/firmware-src/.cproject +++ b/firmware-src/.cproject @@ -24,13 +24,13 @@ - - @@ -110,6 +110,21 @@ true true + + make + disassemble + true + true + true + + + make + + reboot + true + true + true + diff --git a/firmware-src/Makefile b/firmware-src/Makefile index 2ee71d4..6e457e2 100644 --- a/firmware-src/Makefile +++ b/firmware-src/Makefile @@ -25,7 +25,7 @@ AR = $(CROSS_COMPILE)ar SIZE = $(CROSS_COMPILE)size PAGESH = pages/pages.h -.PHONY: all flash full_flash terminal clean +.PHONY: all flash full_flash terminal clean disassemble reboot all: $(FIRMWARE) @@ -70,3 +70,8 @@ clean: @rm -f $(PAGESH) @rm -f $(FIRMWARE).prev +disassemble: + @$(CROSS_COMPILE)objdump -d $(TARGETELF) > $(OBJDIR)/disassemble.txt + +reboot: + @(cd $(dir $(FIRMWARE)) && ./../../esp-utils/build/esp-flasher --reboot) \ No newline at end of file diff --git a/firmware-src/README.md b/firmware-src/README.md index b360c55..1a6c234 100644 --- a/firmware-src/README.md +++ b/firmware-src/README.md @@ -9,13 +9,14 @@ Actually we need just: 'Xtensa crosstool-NG' (https://github.com/jcmvbkbc/crosst SDK is already in this repo. # Firmware usage -Flash firmware( 0x00000.bin and 0x40000 from firmware directory) to the device with +Flash firmware (devicehive.bin firmware directory) to the device with esp-flasher util (see esp-util project on top of the repo). You can also use any other flasher for esp8266 (esptool - https://github.com/themadinventor/esptool for example). -Connect to device terminal via serial port with esp-terminal (see esp-util project). -You can also use any other serial port terminal with escape sequences support. PuTTY -for Windows OS ( http://www.putty.org/ ) or 'screen' util under Linux for example. -This firmware has quite regular unix like terminal via UART. Autocomplete and command +Firmware should be flashed at 0x0 SPI chip address. Connect to device terminal +via serial port with esp-terminal (see esp-util project). You can also use any +other serial port terminal with escape sequences support. PuTTY for Windows OS +( http://www.putty.org/ ) or 'screen' util under Linux for example. This +firmware has quite regular unix like terminal via UART. Autocomplete and command history are supported. Type' 'help' in terminal to see all aviable commands. At first you need to configure device. Type 'configure' in terminal to run configuration util on it. Enter network @@ -23,11 +24,16 @@ and server parameters, device will be rebooted. Now you can send command via DeviceHive cloud to device. Please refer to ESP8266 firmware commands documentation to find which commands are supported(see the top of this repo). -#genbin.sh +# genbin.sh Small util to extract binary images for flashing for compiled file. Might be useful for other projects. Usage: genbin.sh [] +# pages +This directory contains web pages which will be embedded into firmware. Rebuild +(make rebuild) sources after changing content of this pages or run mannually +`gen_pages.sh` script to update it. + # License MIT. See LICENSE file. diff --git a/firmware-src/devicehive.ld b/firmware-src/devicehive.ld index 10b3b29..51ab688 100644 --- a/firmware-src/devicehive.ld +++ b/firmware-src/devicehive.ld @@ -5,7 +5,7 @@ MEMORY dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 iram1_0_seg : org = 0x40100000, len = 0x8000 - irom0_0_seg : org = 0x40210000, len = 0x7A000 + irom0_0_seg : org = 0x40210000, len = 0x58000 } PHDRS diff --git a/firmware-src/pages/editor.html b/firmware-src/pages/editor.html new file mode 100644 index 0000000..2c070dd --- /dev/null +++ b/firmware-src/pages/editor.html @@ -0,0 +1,162 @@ + + + + Editor + + + +

+ + + + + +


+
+ + diff --git a/firmware-src/pages/gen_pages.sh b/firmware-src/pages/gen_pages.sh index b7aa92d..bb5da26 100755 --- a/firmware-src/pages/gen_pages.sh +++ b/firmware-src/pages/gen_pages.sh @@ -22,11 +22,11 @@ index="WEBPAGE web_pages[] = { " comma="" for file in $DIR/*.html; do filename=$(basename "$file") - echo "Generating $filename ..." + echo "Parsing $filename ..." name=${filename/./_} data=$(cat "$file" | tr -d '\r' | sed 's/\\/\\\\/g' | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/"/\\"/g') print "RO_DATA char $name[] = \"$data\";" - index="$index$comma {\"/$filename\", $name, sizeof($name)}" + index="$index$comma {\"$filename\", $name, sizeof($name)}" comma=", " done print "$index };" diff --git a/firmware-src/pages/help.html b/firmware-src/pages/help.html index a9c1d9a..8a774f2 100644 --- a/firmware-src/pages/help.html +++ b/firmware-src/pages/help.html @@ -29,5 +29,8 @@
  • Draw an image with SSD1306 OLED display ssd1306.html
  • Print a text on LCD1602 with PCF8574 adapter hd44780.html
  • +

    Upload web page to chip.

    +

    The firmware allows to upload any custom web page which replaces the main page. This page is always available as help.html. It's totally possible to use RESTful API from embedded JavaScript. This feature allows to build web page enabled devices. Samples above can be used as reference.

    +

    There is an editor at editor.html page which can be user to edit page rigth in the web browser or upload from file/backup to file.

    diff --git a/firmware-src/sources/dhsettings.c b/firmware-src/sources/dhsettings.c index e9f3581..601919b 100644 --- a/firmware-src/sources/dhsettings.c +++ b/firmware-src/sources/dhsettings.c @@ -44,7 +44,8 @@ LOCAL uint32_t ICACHE_FLASH_ATTR getStorageCrc(DH_SETTINGS *storage) { return crc32(storage->storage, sizeof(storage->storage)); } -int ICACHE_FLASH_ATTR dhsettings_init() { +int ICACHE_FLASH_ATTR dhsettings_init(int *exist) { + *exist = 1; DH_SETTINGS *settings = (DH_SETTINGS *)os_malloc(sizeof(DH_SETTINGS)); if(settings == NULL) { dhdebug("Failed to read settings, no RAM."); @@ -67,6 +68,7 @@ int ICACHE_FLASH_ATTR dhsettings_init() { } else if(getStorageCrc(settings) != settings->crc) { dhdebug("Backup storage data corrupted or never saved, using empty settings"); os_memset(settings, 0, sizeof(DH_SETTINGS)); + *exist = 0; } else { read = 1; dhdebug("Settings successfully loaded from backup storage"); @@ -79,15 +81,16 @@ int ICACHE_FLASH_ATTR dhsettings_init() { return read; } -int ICACHE_FLASH_ATTR dhsettings_commit() { +LOCAL int ICACHE_FLASH_ATTR dhsettings_write(const DH_SETTINGS_DATA * data) { int res = 1; DH_SETTINGS *settings = (DH_SETTINGS *)os_malloc(sizeof(DH_SETTINGS)); if(settings == NULL) { dhdebug("Failed to write settings, no RAM."); return 0; } - os_memset(settings, 0, sizeof(DH_SETTINGS)); - os_memcpy(&settings->data, &mSettingsData, sizeof(DH_SETTINGS_DATA)); + os_memset(settings, 0xFF, sizeof(DH_SETTINGS)); + if(data) + os_memcpy(&settings->data, data, sizeof(DH_SETTINGS_DATA)); settings->crc = getStorageCrc(settings); if(spi_flash_erase_sector(ESP_SETTINGS_MAIN_SEC) == SPI_FLASH_RESULT_OK) { if(spi_flash_write(ESP_SETTINGS_MAIN_SEC* SPI_FLASH_SEC_SIZE, (uint32 *)settings, sizeof(DH_SETTINGS)) != SPI_FLASH_RESULT_OK) { @@ -113,21 +116,21 @@ int ICACHE_FLASH_ATTR dhsettings_commit() { return res; } -int ICACHE_FLASH_ATTR dhsettings_clear() { - int res = 1; - if(spi_flash_erase_sector(ESP_SETTINGS_MAIN_SEC) != SPI_FLASH_RESULT_OK) { - dhdebug("Erasing of main storage failed"); - res = 0; - } - if(spi_flash_erase_sector(ESP_SETTINGS_BACKUP_SEC) != SPI_FLASH_RESULT_OK) { - dhdebug("Erasing of backup storage failed"); - res = 0; - } - if(res) { - os_memset(&mSettingsData, 0, sizeof(DH_SETTINGS_DATA)); - dhdebug("Settings successfully cleared"); + +int ICACHE_FLASH_ATTR dhsettings_commit() { + return dhsettings_write(&mSettingsData); +} + +int ICACHE_FLASH_ATTR dhsettings_clear(int force) { + os_memset(mSettingsData, 0, sizeof(mSettingsData)); + if(force) { + if(spi_flash_erase_sector(ESP_SETTINGS_MAIN_SEC) == SPI_FLASH_RESULT_OK && + spi_flash_erase_sector(ESP_SETTINGS_BACKUP_SEC) == SPI_FLASH_RESULT_OK) { + return 1; + } + return 0; } - return res; + return dhsettings_write(NULL); } const char * ICACHE_FLASH_ATTR dhsettings_get_wifi_ssid() { diff --git a/firmware-src/sources/dhsettings.h b/firmware-src/sources/dhsettings.h index ba0c19e..99127b0 100644 --- a/firmware-src/sources/dhsettings.h +++ b/firmware-src/sources/dhsettings.h @@ -21,10 +21,13 @@ #define DHSETTINGS_ACCESSKEY_MAX_LENGTH 65 /** - * \brief Inits settings, reads values from storage. - * \return Non zero value on success. Zero on error. + * \brief Inits settings, reads values from storage. + * \param[out] saved Pointer to store confirm that settings were never saved + * before. If flash was successfully read and setting were + * never saved, zero will be stored. Non zero otherwise. + * \return Non zero value on success. Zero on error. */ -int dhsettings_init(); +int dhsettings_init(int *saved); /** * \brief Saves values to permanent storage. @@ -33,10 +36,11 @@ int dhsettings_init(); int dhsettings_commit(); /** - * \brief Destroy data in permanent storage. - * \return Non zero value on success. Zero on error. + * \brief Destroy data in permanent storage. + * \param[in] force If zero, just delete data, non zero means destroy mark that setting was ever saved. + * \return Non zero value on success. Zero on error. */ -int dhsettings_clear(); +int dhsettings_clear(int force); /** * \brief Get Wi-Fi SSID. diff --git a/firmware-src/sources/dhterminal_commands.c b/firmware-src/sources/dhterminal_commands.c index fb4b832..f58e6c4 100644 --- a/firmware-src/sources/dhterminal_commands.c +++ b/firmware-src/sources/dhterminal_commands.c @@ -292,8 +292,9 @@ void ICACHE_FLASH_ATTR dhterminal_commands_config(const char *args) { } void ICACHE_FLASH_ATTR dhterminal_commands_configure(const char *args) { - if(os_strcmp(args, "--clear") == 0) { - if(dhsettings_clear()) { + int force = (os_strcmp(args, "--force-clear") == 0) ? 1 : 0; + if(force || os_strcmp(args, "--clear") == 0) { + if(dhsettings_clear(force)) { dhuart_send_line("Settings was cleared, rebooting..."); system_restart(); } else { diff --git a/firmware-src/sources/httpd.c b/firmware-src/sources/httpd.c index 449f01e..9e7ac2c 100644 --- a/firmware-src/sources/httpd.c +++ b/firmware-src/sources/httpd.c @@ -108,7 +108,6 @@ LOCAL void ICACHE_FLASH_ATTR on_client_disconnect(struct espconn *conn) { LOCAL void ICACHE_FLASH_ATTR dhap_httpd_disconnect_cb(void *arg) { on_client_disconnect((struct espconn *)arg); - dhdebug("Httpd client disconnected, %u left", mConnected); } LOCAL void ICACHE_FLASH_ATTR dhap_httpd_sent_cb(void *arg) { @@ -117,7 +116,6 @@ LOCAL void ICACHE_FLASH_ATTR dhap_httpd_sent_cb(void *arg) { return; } espconn_disconnect(conn); - dhdebug("Httpd data sent"); } LOCAL HTTP_RESPONSE_STATUS ICACHE_FLASH_ATTR parse_request( @@ -235,7 +233,6 @@ LOCAL void ICACHE_FLASH_ATTR dhap_httpd_recv_cb(void *arg, char *data, unsigned answer.content.len = 0; answer.free_content = 0; answer.ok = 1; - dhdebug("Httpd received %d bytes", len); dhstatistic_add_bytes_received(len); HTTP_RESPONSE_STATUS res = HRCS_INTERNAL_ERROR; @@ -310,7 +307,7 @@ LOCAL void ICACHE_FLASH_ATTR dhap_httpd_recv_cb(void *arg, char *data, unsigned if(answer.ok) { send_res(conn, no_content, sizeof(no_content) - 1); } else { - response_len = snprintf(response, sizeof(response), forbidden, 0); + response_len = snprintf(response, sizeof(response), forbidden, plain, 0); send_res(conn, response, response_len); } return; @@ -389,7 +386,6 @@ LOCAL void ICACHE_FLASH_ATTR dhap_httpd_connect_cb(void *arg) { espconn_regist_recvcb(conn, dhap_httpd_recv_cb); espconn_regist_disconcb(conn, dhap_httpd_disconnect_cb); espconn_regist_sentcb(conn, dhap_httpd_sent_cb); - dhdebug("Httpd client connected"); } void ICACHE_FLASH_ATTR httpd_init(HttpRequestCb get_cb, HttpRequestCb post_cb) { diff --git a/firmware-src/sources/httpd.h b/firmware-src/sources/httpd.h index 75ec1eb..fc97cd8 100644 --- a/firmware-src/sources/httpd.h +++ b/firmware-src/sources/httpd.h @@ -20,7 +20,7 @@ typedef enum { HRCS_BAD_REQUEST, ///< Request is not correct. HRCS_NOT_IMPLEMENTED, ///< Method is not implemented. HRCS_UNAUTHORIZED, ///< Unauthorized. - HRCS_TOO_MANY_REQUESTS, ///< Unauthorized. + HRCS_TOO_MANY_REQUESTS, ///< Too many requests on server. HRCS_OPTIONS ///< Options. } HTTP_RESPONSE_STATUS; diff --git a/firmware-src/sources/irom.c b/firmware-src/sources/irom.c index 0571617..2e182b9 100644 --- a/firmware-src/sources/irom.c +++ b/firmware-src/sources/irom.c @@ -37,3 +37,22 @@ void ICACHE_FLASH_ATTR irom_read(char *buf, const char *addr, unsigned int len) } } } + +int ICACHE_FLASH_ATTR irom_cmp(char *buf, const char *addr, unsigned int len) { + while(len) { + if(((uint32_t)addr & 0b11) || len < 4) { + if(*buf != irom_char(addr)) + return 1; + buf++; + addr++; + len--; + } else { + if(*((uint32_t *)buf) != *((uint32_t *)addr)) + return 1; + buf += 4; + addr += 4; + len -= 4; + } + } + return 0; +} diff --git a/firmware-src/sources/irom.h b/firmware-src/sources/irom.h index b84cab3..f866334 100644 --- a/firmware-src/sources/irom.h +++ b/firmware-src/sources/irom.h @@ -17,12 +17,15 @@ #include +/** Address which is mapped to ROM */ +#define IROM_FLASH_BASE_ADDRESS 0x40200000 + /** Store data in ROM memory. Access to this data will be slower, but amount * of free RAM will be bigger. Never read such variables directly, except 32 bits.*/ #define RO_DATA const ICACHE_RODATA_ATTR STORE_ATTR static /** Checks if pointer stored in ROM */ -#define ifrom(data) if((uint32_t)data >= 0x40200000) +#define ifrom(data) if((uint32_t)data >= IROM_FLASH_BASE_ADDRESS) /** * \brief Read single char from ROM. @@ -34,9 +37,18 @@ char irom_char(const char *rostr); /** * \brief Read multiple bytes from ROM. * \param[out] buf Point where to store data in RAM. - * \param[in] addr Point to data in ROM. + * \param[in] addr Pointer to data in ROM. * \param[in] len Number of bytes to read. */ void irom_read(char *buf, const char *addr, unsigned int len); +/** + * \brief Compare some data with ROM memory. + * \param[in] buf Data in RAM to compare. + * \param[in] addr Pointer to data in ROM. + * \param[in] len Number of bytes to compare. + * \return Zero value if data is equal, non zero otherwise. + */ +int irom_cmp(char *buf, const char *addr, unsigned int len); + #endif /* _IROM_H_ */ diff --git a/firmware-src/sources/main.c b/firmware-src/sources/main.c index 43c0aff..a18494d 100644 --- a/firmware-src/sources/main.c +++ b/firmware-src/sources/main.c @@ -22,6 +22,7 @@ #include "dhap.h" #include "webserver.h" #include "irom.h" +#include "uploadable_page.h" typedef struct { unsigned int magic; @@ -101,13 +102,17 @@ void user_rf_pre_init(void) { } void user_init(void) { + int ever_saved; if(mSpecialMode) { system_set_os_print(0); - dhsettings_init(); + dhsettings_init(&ever_saved); dhap_init(); } else { dhdebug("*****************************"); - dhsettings_init(); + dhsettings_init(&ever_saved); + if(ever_saved == 0) { // if first run on this chip + uploadable_page_delete(); + } dhsender_queue_init(); dhconnector_init(dhcommands_do); dhgpio_init(); diff --git a/firmware-src/sources/rest.h b/firmware-src/sources/rest.h index b11e5e9..f31e1a2 100644 --- a/firmware-src/sources/rest.h +++ b/firmware-src/sources/rest.h @@ -16,7 +16,7 @@ * \param[in] path Url path. * \param[in] key Access key for API which was given in request. * \param[in] content_in Request body, typically json. - * \param[out] content_out Data for response + * \param[out] answer Data for response. * \return One of httpd statuses. */ diff --git a/firmware-src/sources/uploadable_api.c b/firmware-src/sources/uploadable_api.c new file mode 100644 index 0000000..852b312 --- /dev/null +++ b/firmware-src/sources/uploadable_api.c @@ -0,0 +1,58 @@ +/* + * rest.c + * + * Copyright 2016 DeviceHive + * + * Author: Nikolay Khabarov + * + */ + +#include "uploadable_api.h" + +#include +#include +#include "dhrequest.h" +#include "uploadable_page.h" + +HTTP_RESPONSE_STATUS ICACHE_FLASH_ATTR uploadable_api_handle(const char *path, const char *key, + HTTP_CONTENT *content_in, HTTP_ANSWER *answer) { + static const char flash[] = "/flash/page/"; + answer->content.len = 0; + if(os_strncmp(path, flash, sizeof(flash) - 1) == 0) { + if(dhrequest_current_accesskey()[0]) { + if(key == 0) { + return HRCS_UNAUTHORIZED; + } + if(os_strcmp(key, dhrequest_current_accesskey())) { + return HRCS_UNAUTHORIZED; + } + } + const char *p = &path[sizeof(flash) - 1]; + UP_STATUS res = UP_STATUS_WRONG_CALL; + if(os_strcmp(p, "begin") == 0) { + if(content_in->len == 0) + res = uploadable_page_begin(); + } else if (os_strcmp(p, "finish") == 0) { + if(content_in->len == 0) + res = uploadable_page_finish(); + } else if (os_strcmp(p, "put") == 0) { + if(content_in->len) + res = uploadable_page_put(content_in->data, content_in->len); + } else { + return HRCS_NOT_FOUND; + } + switch(res) { + case UP_STATUS_OK: + return HRCS_ANSWERED_PLAIN; + case UP_STATUS_INTERNAL_ERROR: + return HRCS_INTERNAL_ERROR; + case UP_STATUS_WRONG_CALL: + answer->ok = 0; + return HRCS_ANSWERED_PLAIN; + case UP_STATUS_OVERFLOW: + return HRCS_TOO_MANY_REQUESTS; + } + return HRCS_ANSWERED_PLAIN; + } + return HRCS_NOT_FOUND; +} diff --git a/firmware-src/sources/uploadable_api.h b/firmware-src/sources/uploadable_api.h new file mode 100644 index 0000000..24bdd5d --- /dev/null +++ b/firmware-src/sources/uploadable_api.h @@ -0,0 +1,24 @@ +/** + * \file httpd.h + * \brief Module for handling uploadable HTTP API calls. + * \author Nikolay Khabarov + * \date 2016 + * \copyright DeviceHive MIT + */ +#ifndef _UPLOADABLE_API_H_ +#define _UPLOADABLE_API_H_ + +#include "httpd.h" + +/** + * \brief Handle rest request. + * \param[in] path Url path. + * \param[in] key Access key for API which was given in request. + * \param[in] content_in Request body, typically json. + * \return One of httpd statuses. + */ + +HTTP_RESPONSE_STATUS uploadable_api_handle(const char *path, const char *key, + HTTP_CONTENT *content_in, HTTP_ANSWER *answer); + +#endif /* _UPLOADABLE_API_H_ */ diff --git a/firmware-src/sources/uploadable_page.c b/firmware-src/sources/uploadable_page.c new file mode 100644 index 0000000..f4b13f8 --- /dev/null +++ b/firmware-src/sources/uploadable_page.c @@ -0,0 +1,205 @@ +/* + * rest.c + * + * Copyright 2016 DeviceHive + * + * Author: Nikolay Khabarov + * + */ + +#include +#include +#include +#include +#include +#include "irom.h" +#include "uploadable_page.h" +#include "dhdebug.h" + +/** Uploadable web page maximum length and ROM addresses. */ +#define UPLOADABLE_PAGE_START_SECTOR 0x68 +#define UPLOADABLE_PAGE_END_SECTOR 0x77 +#define UPLOADABLE_PAGE_MAX_SIZE ((UPLOADABLE_PAGE_END_SECTOR - UPLOADABLE_PAGE_START_SECTOR + 1) * SPI_FLASH_SEC_SIZE) +/** Timeout for auto finish. */ +#define UPLOADABLE_PAGE_TIMEOUT_MS 60000 + +LOCAL unsigned int mPageLength = 0; +LOCAL unsigned int mFlashingSector = 0; +LOCAL os_timer_t mFlashingTimer; +LOCAL char *mBuffer = NULL; +LOCAL unsigned int mBufferPos = 0; + +LOCAL void ICACHE_FLASH_ATTR flash_timeout(void *arg) { + dhdebug("Flashing procedure isn't finished correctly, force to finish"); + uploadable_page_finish(); +} + +LOCAL void ICACHE_FLASH_ATTR reset_timer() { + os_timer_disarm(&mFlashingTimer); + os_timer_setfn(&mFlashingTimer, (os_timer_func_t *)flash_timeout, NULL); + os_timer_arm(&mFlashingTimer, UPLOADABLE_PAGE_TIMEOUT_MS, 0); +} + +const char *ICACHE_FLASH_ATTR uploadable_page_get(unsigned int *len) { + RO_DATA char flashing[] = "

    Flashing is in process...

    "; + if(mBuffer) { + *len = sizeof(flashing) - 1; + return flashing; + } + const uint32_t * const dwdata = (const uint32_t*)(IROM_FLASH_BASE_ADDRESS + + UPLOADABLE_PAGE_START_SECTOR * SPI_FLASH_SEC_SIZE); + if(mPageLength == 0) { + unsigned int i; + for(i = 0; i < UPLOADABLE_PAGE_MAX_SIZE; i += sizeof(uint32_t)) { + uint32_t b = dwdata[i / sizeof(uint32_t)]; + if((b & 0x000000FF) == 0) { + break; + } else if((b & 0x0000FF00) == 0) { + i++; + break; + } else if((b & 0x00FF0000) == 0) { + i += 2; + break; + } else if((b & 0xFF000000) == 0) { + i += 3; + break; + } + } + mPageLength = i; + } + *len = mPageLength; + return (const char *)dwdata; +} + +LOCAL SpiFlashOpResult ICACHE_FLASH_ATTR write_zero_byte(unsigned int sector) { + if(sector > UPLOADABLE_PAGE_END_SECTOR) { + return SPI_FLASH_RESULT_ERR; + } + uint8_t data[4]; + irom_read(data, (char *)(IROM_FLASH_BASE_ADDRESS + sector * SPI_FLASH_SEC_SIZE), sizeof(data)); + // since zero is going to be written here, there is no need in erase operation + data[0] = 0; + return spi_flash_write(sector * SPI_FLASH_SEC_SIZE, (uint32 *)data, sizeof(data)); +} + +UP_STATUS ICACHE_FLASH_ATTR uploadable_page_delete() { + // set first byte to zero + if(write_zero_byte(UPLOADABLE_PAGE_START_SECTOR) == SPI_FLASH_RESULT_OK) + return UP_STATUS_OK; + return UP_STATUS_INTERNAL_ERROR; +} + +UP_STATUS ICACHE_FLASH_ATTR uploadable_page_begin() { + ETS_INTR_LOCK(); + if(mBuffer == NULL) { + mBuffer = (char *)os_malloc(SPI_FLASH_SEC_SIZE); + } + mBufferPos = 0; + mFlashingSector = UPLOADABLE_PAGE_START_SECTOR; + ETS_INTR_UNLOCK(); + if(mBuffer == NULL) { + dhdebug("No memory to initialize page flashing"); + return UP_STATUS_INTERNAL_ERROR; + } + reset_timer(); + dhdebug("Page flashing is initialized"); + return UP_STATUS_OK; +} + +LOCAL UP_STATUS ICACHE_FLASH_ATTR flash_data() { + if(mFlashingSector > UPLOADABLE_PAGE_END_SECTOR) { + return UP_STATUS_OVERFLOW; + } + + if((SPI_FLASH_SEC_SIZE - mBufferPos) > 0) { + irom_read(&mBuffer[mBufferPos], (const char *) + (mFlashingSector * SPI_FLASH_SEC_SIZE + mBufferPos + IROM_FLASH_BASE_ADDRESS), + SPI_FLASH_SEC_SIZE - mBufferPos); + mBufferPos = SPI_FLASH_SEC_SIZE; + } + + if(irom_cmp(mBuffer, (const char *) + (mFlashingSector * SPI_FLASH_SEC_SIZE + IROM_FLASH_BASE_ADDRESS), + SPI_FLASH_SEC_SIZE) == 0) { + mFlashingSector++; + return UP_STATUS_OK; + } + + SpiFlashOpResult res; + res = spi_flash_erase_sector(mFlashingSector); + if(res == SPI_FLASH_RESULT_OK) { + dhdebug("Flashing page at address 0x%X", mFlashingSector * SPI_FLASH_SEC_SIZE); + res = spi_flash_write(mFlashingSector * SPI_FLASH_SEC_SIZE, + (uint32 *)mBuffer, SPI_FLASH_SEC_SIZE); + } + + system_soft_wdt_feed(); + if(res == SPI_FLASH_RESULT_OK) { + mFlashingSector++; + return UP_STATUS_OK; + } + return UP_STATUS_INTERNAL_ERROR; +} + +UP_STATUS ICACHE_FLASH_ATTR uploadable_page_put(const char *data, unsigned int data_len) { + if(mBuffer == NULL) + return UP_STATUS_WRONG_CALL; + if(mFlashingSector > UPLOADABLE_PAGE_END_SECTOR) + return UP_STATUS_OVERFLOW; + reset_timer(); + + ETS_INTR_LOCK(); + while(data_len) { + uint32_t tocopy = (data_len > (SPI_FLASH_SEC_SIZE - mBufferPos)) ? + (SPI_FLASH_SEC_SIZE - mBufferPos): data_len; + os_memcpy(&mBuffer[mBufferPos], data, tocopy); + mBufferPos += tocopy; + data_len -= tocopy; + data += tocopy; + if(mBufferPos == SPI_FLASH_SEC_SIZE) { + SpiFlashOpResult res = flash_data(); + mBufferPos = 0; + if(res != SPI_FLASH_RESULT_OK) { + ETS_INTR_UNLOCK(); + dhdebug("Error while writing page at 0x%X", + mFlashingSector * SPI_FLASH_SEC_SIZE); + return UP_STATUS_INTERNAL_ERROR; + } + } + } + ETS_INTR_UNLOCK(); + return UP_STATUS_OK; +} + +UP_STATUS ICACHE_FLASH_ATTR uploadable_page_finish() { + if(mBuffer == NULL) + return UP_STATUS_WRONG_CALL; + os_timer_disarm(&mFlashingTimer); + ETS_INTR_LOCK(); + // Mark data with null terminated char. If data takes whole available space, + // there is no need in null terminated char. + // At this point buffer should have at lest 1 free byte, since buffer + // reaches maximum size before, it had to be written to flash. + // If nothing was written, just report ok. + SpiFlashOpResult res = SPI_FLASH_RESULT_OK; + if(mBufferPos) { + mBuffer[mBufferPos] = 0; + mBufferPos++; + res = flash_data(); + } else if(mFlashingSector > UPLOADABLE_PAGE_START_SECTOR && + mFlashingSector <= UPLOADABLE_PAGE_END_SECTOR) { + res = write_zero_byte(mFlashingSector); + } + mBufferPos = 0; + os_free(mBuffer); + mBuffer = NULL; + // force to recalc page size + mPageLength = 0; + ETS_INTR_UNLOCK(); + if(res != SPI_FLASH_RESULT_OK) { + dhdebug("Error while finishing flash page"); + return UP_STATUS_INTERNAL_ERROR; + } + dhdebug("Flashing page has finished successfully"); + return UP_STATUS_OK; +} diff --git a/firmware-src/sources/uploadable_page.h b/firmware-src/sources/uploadable_page.h new file mode 100644 index 0000000..856f4d2 --- /dev/null +++ b/firmware-src/sources/uploadable_page.h @@ -0,0 +1,60 @@ +/** + * \file httpd.h + * \brief Module for handling uploadable web page. + * \author Nikolay Khabarov + * \date 2016 + * \copyright DeviceHive MIT + */ + +#ifndef _UPLOADABLE_PAGE_H_ +#define _UPLOADABLE_PAGE_H_ + +/** Uploadable page functions return status. */ +typedef enum { + UP_STATUS_OK, ///< Successfully done. + UP_STATUS_INTERNAL_ERROR, ///< Error while saving in ROM / no memory. + UP_STATUS_WRONG_CALL, ///< Method is not allowed to call at this point. + UP_STATUS_OVERFLOW ///< ROM storage is overflowed. +} UP_STATUS; + +/** + * \brief Get the content of uploadable page. + * \details Content is stored in ROM, it can be read only per 4 bytes. + * \param[in] data Pointer where to store data length in bytes. + * \return Pointer in ROM memory. + */ +const char *uploadable_page_get(unsigned int *len); + +/** + * \brief Destroy page data. + * \details Physically page will be kept without first byte. + * \return One of UP_STATUS statuses. + */ +UP_STATUS uploadable_page_delete(); + +/** + * \brief Initialize flashing new web page procedure. + * \details Pointer for writing will be moved to very beginning. This + * mode will be exited if no writes operation happen in last + * UPLOADABLE_PAGE_TIMEOUT_MS. + * \return One of UP_STATUS statuses. + */ +UP_STATUS uploadable_page_begin(); + +/** + * \brief Write piece of data. Address increments internally. + * \details Data is saved per 4 KiB blocks. Smaller buffer will be saved + * internally and will be written when 4 KiB is collected. + * \param[in] data Pointer to data. + * \param[in] data_len Data length. + * \return One of UP_STATUS statuses. + */ +UP_STATUS uploadable_page_put(const char *data, unsigned int data_len); + +/** + * \brief Flash all remains data and leave out flashing procedure. + * \return One of UP_STATUS statuses. + */ +UP_STATUS uploadable_page_finish(); + +#endif /* _UPLOADABLE_PAGE_H_ */ diff --git a/firmware-src/sources/user_config.h b/firmware-src/sources/user_config.h index 9b2c728..522c886 100644 --- a/firmware-src/sources/user_config.h +++ b/firmware-src/sources/user_config.h @@ -16,7 +16,7 @@ /** UART speed to terminal. */ #define UART_BAUND_RATE 115200 /** Current firmware version. */ -#define FIRMWARE_VERSION "0.4" +#define FIRMWARE_VERSION "0.5" /** Buffer size for data that uses for commands which require data transmition via interfaces like UART, I2C etc. */ #define INTERFACES_BUF_SIZE 264 /** Encode type for data field in commands. Can be DATAENCODEBASE64 or DATAENCODEHEX.*/ diff --git a/firmware-src/sources/webserver.c b/firmware-src/sources/webserver.c index c16e6a9..29b8e76 100644 --- a/firmware-src/sources/webserver.c +++ b/firmware-src/sources/webserver.c @@ -13,6 +13,8 @@ #include "httpd.h" #include "rest.h" #include "../pages/pages.h" +#include "uploadable_page.h" +#include "uploadable_api.h" LOCAL int ICACHE_FLASH_ATTR check_rest(HTTP_RESPONSE_STATUS *res, const char *path, const char *key, HTTP_CONTENT *content_in, HTTP_ANSWER *answer) { @@ -38,19 +40,22 @@ LOCAL HTTP_RESPONSE_STATUS ICACHE_FLASH_ATTR get_cb(const char *path, } if(path[0]=='/') { if(path[1]==0) { - // index page - answer->content.data = help_html; - answer->content.len = sizeof(help_html) - 1; + answer->content.data = uploadable_page_get(&answer->content.len); + if(answer->content.len == 0) { + // default page + answer->content.data = help_html; + answer->content.len = sizeof(help_html) - 1; + } return HRCS_ANSWERED_HTML; } - } - int i; - for(i = 0; i < sizeof(web_pages) / sizeof(WEBPAGE); i++) { - if(os_strcmp(web_pages[i].path, path) == 0) { - answer->content.data = web_pages[i].data; - answer->content.len = web_pages[i].data_len; - return HRCS_ANSWERED_HTML; + int i; + for(i = 0; i < sizeof(web_pages) / sizeof(WEBPAGE); i++) { + if(os_strcmp(web_pages[i].path, &path[1]) == 0) { + answer->content.data = web_pages[i].data; + answer->content.len = web_pages[i].data_len; + return HRCS_ANSWERED_HTML; + } } } @@ -63,7 +68,7 @@ LOCAL HTTP_RESPONSE_STATUS ICACHE_FLASH_ATTR post_cb(const char *path, if(check_rest(&res, path, key, content_in, answer)) { return res; } - return HRCS_NOT_FOUND; + return uploadable_api_handle(path, key, content_in, answer); } void ICACHE_FLASH_ATTR webserver_init() { diff --git a/gen_release.sh b/gen_release.sh deleted file mode 100755 index d299fd6..0000000 --- a/gen_release.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -set -e - -DIR=$(realpath $(dirname $0)) -VER=$(grep FIRMWARE_VERSION firmware-src/sources/user_config.h | cut -d'"' -f2) -DSTDIR=$DIR/../dh-esp-firmware-v$VER - -mkdir -p $DSTDIR -rm -rf $DSTDIR/* - -cp $DIR/examples $DSTDIR -r -cp $DIR/release/utils/* $DSTDIR -cp $DIR/*.pdf $DSTDIR -(cd $DIR/firmware-src && make clean all) && cp $DIR/firmware-src/firmware/* $DSTDIR -zip -r -q $DSTDIR/../dh-esp-firmware-v$VER.zip $DSTDIR -tar -czf $DSTDIR/../dh-esp-firmware-v$VER.tar.gz $DSTDIR - - diff --git a/images/sample5.jpg b/images/sample5.jpg index ce252d3..a182d8c 100644 Binary files a/images/sample5.jpg and b/images/sample5.jpg differ diff --git a/release/README.md b/release/README.md index f75757b..7b6a495 100644 --- a/release/README.md +++ b/release/README.md @@ -1,3 +1,34 @@ -Binary fimrware release in repository is depricated. To download firmware binaries follow this link: https://github.com/devicehive/esp8266-firmware/releases +Binary fimrware release in repository is depricated. To download firmware binaries follow this link: https://github.com/devicehive/esp8266-firmware/releases -Utils are still available here. \ No newline at end of file +This directory contains script for generating releases. There are some tools which should be installed. + +# win32 build tools +``` +sudo apt install binutils-mingw-w64-i686 gcc-mingw-w64-i686-dev gcc-mingw-w64-i686 g++-mingw-w64-i686 +``` + +# osx build tools +``` +sudo apt install ccache +wget http://security.ubuntu.com/ubuntu/pool/universe/o/openssl098/libssl0.9.8_0.9.8o-7ubuntu3.2_amd64.deb +wget https://launchpad.net/~flosoft/+archive/ubuntu/cross-apple/+files/apple-x86-odcctools_758.159-0flosoft11_amd64.deb +wget https://launchpad.net/~flosoft/+archive/ubuntu/cross-apple/+files/ccache-lipo_1.0-0flosoft3_amd64.deb +wget https://launchpad.net/~flosoft/+archive/ubuntu/cross-apple/+files/apple-x86-gcc_4.2.1~5646.1flosoft2_amd64.deb +wget https://launchpad.net/~flosoft/+archive/ubuntu/cross-apple/+files/apple-uni-sdk-10.5_20110407-0.flosoft1_amd64.deb +sudo dpkg -i libssl0.9.8_0.9.8o-7ubuntu3.2_amd64.deb +sudo dpkg -i apple-x86-odcctools_758.159-0flosoft11_amd64.deb +sudo dpkg -i ccache-lipo_1.0-0flosoft3_amd64.deb +sudo dpkg -i apple-uni-sdk-10.5_20110407-0.flosoft1_amd64.deb +sudo dpkg -i apple-x86-gcc_4.2.1~5646.1flosoft2_amd64.deb +``` + +# linux build tools +``` +sudo apt install linux-libc-dev:i386 +``` + +# converting markdown to pdf +``` +sudo apt install npm nodejs-legacy +sudo npm install -g markdown-pdf +``` \ No newline at end of file diff --git a/release/gen_release.sh b/release/gen_release.sh new file mode 100755 index 0000000..b611935 --- /dev/null +++ b/release/gen_release.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -e + +DIR=$(realpath $(dirname $0)) +BUILD=$DIR/build +VER=$(grep FIRMWARE_VERSION $DIR/../firmware-src/sources/user_config.h | cut -d'"' -f2) + +mkdir -p $BUILD +rm -rf $BUILD/* +rm -f $BUILD/../dh-esp-firmware-v$VER.zip +rm -f $BUILD/../dh-esp-firmware-v$VER.tar.gz +(cd $DIR/../esp-utils && CXX=x86_64-linux-gnu-g++ CFLAGS=-m32 LDFLAGS=-m32 make rebuild) +cp $DIR/../esp-utils/build/esp-terminal $BUILD/esp-terminal-linux +cp $DIR/../esp-utils/build/esp-flasher $BUILD/esp-flasher-linux +(cd $DIR/../esp-utils && CXX=i686-apple-darwin10-g++ make rebuild) +cp $DIR/../esp-utils/build/esp-terminal $BUILD/esp-terminal-osx +cp $DIR/../esp-utils/build/esp-flasher $BUILD/esp-flasher-osx +(cd $DIR/../esp-utils && CXX=i686-w64-mingw32-g++ LDFLAGS=-static make rebuild) +cp $DIR/../esp-utils/build/esp-terminal $BUILD/esp-terminal-win.exe +cp $DIR/../esp-utils/build/esp-flasher $BUILD/esp-flasher-win.exe +cp $DIR/utils/* $BUILD/ + +# since an issue - https://github.com/ariya/phantomjs/issues/13959 (markdown-pdf uses phantomjs) +# just remove the local links from document. +sed $DIR/../DeviceHiveESP8266.md -e 's/\[\([^]].*\)\](#.*)/\1/g' > $DIR/../DeviceHiveESP8266_hack.md +(cd $DIR/.. && markdown-pdf DeviceHiveESP8266_hack.md -o $BUILD/DeviceHiveESP8266.pdf --css-path $DIR/md.css) +rm $DIR/../DeviceHiveESP8266_hack.md + +(cd $DIR/../firmware-src && make rebuild) && cp $DIR/../firmware-src/firmware/* $BUILD + +(cd $BUILD && zip -r -q ../dh-esp-firmware-v$VER.zip *) +(cd $BUILD && tar -czf ../dh-esp-firmware-v$VER.tar.gz *) + + diff --git a/release/md.css b/release/md.css new file mode 100644 index 0000000..750e94f --- /dev/null +++ b/release/md.css @@ -0,0 +1,6 @@ +a[href]:after { + content: "" !important; +} +body { + font-size: 0.6em; +} diff --git a/release/utils/esp-flasher-linux b/release/utils/esp-flasher-linux deleted file mode 100755 index e7c5a58..0000000 Binary files a/release/utils/esp-flasher-linux and /dev/null differ diff --git a/release/utils/esp-flasher-osx b/release/utils/esp-flasher-osx deleted file mode 100755 index 56d439b..0000000 Binary files a/release/utils/esp-flasher-osx and /dev/null differ diff --git a/release/utils/esp-flasher-win.exe b/release/utils/esp-flasher-win.exe deleted file mode 100644 index 4a00ddc..0000000 Binary files a/release/utils/esp-flasher-win.exe and /dev/null differ diff --git a/release/utils/esp-terminal-linux b/release/utils/esp-terminal-linux deleted file mode 100755 index d703de8..0000000 Binary files a/release/utils/esp-terminal-linux and /dev/null differ diff --git a/release/utils/esp-terminal-osx b/release/utils/esp-terminal-osx deleted file mode 100755 index feb9dee..0000000 Binary files a/release/utils/esp-terminal-osx and /dev/null differ diff --git a/release/utils/esp-terminal-win.exe b/release/utils/esp-terminal-win.exe deleted file mode 100644 index 83e4bfe..0000000 Binary files a/release/utils/esp-terminal-win.exe and /dev/null differ