feat(example): add OTA example

1. construct a valid http request header
   notice: URL path, HTTP version, Host,Accept, Accept-Encoding
2. check http response header
   notice: state code should be 200, should include Content-Length and application/octet-stream
3. download OTA bin from local OTA server
4. check bin CRC
5. reboot to run new bin if CRC passed
6. add README.md
This commit is contained in:
chenwu
2018-05-22 15:48:04 +08:00
parent 581aec0631
commit 5b339ee467
10 changed files with 1359 additions and 0 deletions

123
examples/ota_demo/Makefile Normal file
View File

@@ -0,0 +1,123 @@
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of object file images to be generated ()
# GEN_BINS - list of binaries to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
TARGET = eagle
#FLAVOR = release
FLAVOR = debug
#EXTRA_CCFLAGS += -u
ifndef PDIR # {
GEN_IMAGES= eagle.app.v6.out
GEN_BINS= eagle.app.v6.bin
SPECIAL_MKTARGETS=$(APP_MKTARGETS)
SUBDIRS= \
user \
ota
endif # } PDIR
LDDIR = $(SDK_PATH)/ld
CCFLAGS += -Os
TARGET_LDFLAGS = \
-nostdlib \
-Wl,-EL \
--longcalls \
--text-section-literals
ifeq ($(FLAVOR),debug)
TARGET_LDFLAGS += -g -O2
endif
ifeq ($(FLAVOR),release)
TARGET_LDFLAGS += -g -O0
endif
COMPONENTS_eagle.app.v6 = \
user/libuser.a \
ota/libota.a
LINKFLAGS_eagle.app.v6 = \
-L$(SDK_PATH)/lib \
-Wl,--gc-sections \
-nostdlib \
-T$(LD_FILE) \
-Wl,--no-check-sections \
-u call_user_start \
-Wl,-static \
-Wl,--start-group \
-lcirom \
-lgcc \
-lhal \
-lcrypto \
-lfreertos \
-llwip \
-lmain \
-lnet80211 \
-lphy \
-ldriver \
-lpp \
-lwpa \
$(DEP_LIBS_eagle.app.v6)\
-Wl,--end-group
DEPENDS_eagle.app.v6 = \
$(LD_FILE) \
$(LDDIR)/eagle.rom.addr.v6.ld
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#UNIVERSAL_TARGET_DEFINES = \
# Other potential configuration flags include:
# -DTXRX_TXBUF_DEBUG
# -DTXRX_RXBUF_DEBUG
# -DWLAN_CONFIG_CCX
# -DMQTT_TASK: Define MQTT_TASK to enable MQTT start background
# thread for a client.
CONFIGURATION_DEFINES = -DICACHE_FLASH
DEFINES += \
$(UNIVERSAL_TARGET_DEFINES) \
$(CONFIGURATION_DEFINES)
DDEFINES += \
$(UNIVERSAL_TARGET_DEFINES) \
$(CONFIGURATION_DEFINES)
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
sinclude $(SDK_PATH)/Makefile
.PHONY: FORCE
FORCE:

View File

@@ -0,0 +1,86 @@
# Simple OTA Demo on ESP8266
This example demonstrates a working OTA (over the air) firmware update workflow.
---
# Introduce
there are two areas in flash for system to do OTA: user1 area and user2 area. they work same to each other, backup to each other.
for more OTA flash info, please look into [ESP8266 SDK
Getting Started Guide](https://www.espressif.com/sites/default/files/documentation/2a-esp8266-sdk_getting_started_guide_en.pdf)
# Aim
Flashing the example over serial with `esptool`. On first startup, the bootloader loads this user bin which then performs an OTA update (triggered in the example code). The update downloads a new user bin from a http server and saves it into the other user bin area. At this point the example code does bin CRC check, prepare to reboot to run new user bin if CRC passed, then the bootloader reads new user bin and runs it.
# Workflow
The OTA_workflow.png diagram demonstrates the overall workflow:
![OTA Workflow diagram](_static/OTA_workflow.png)
## Step 1: Connect to AP
Connect your host PC to the same AP that you will use for the ESP8266.
## Step 2: Run HTTP Server
Python has a built-in HTTP server that can be used for example purposes.
For our upgrade example OTA file, we're going to use the `ESP8266_RTOS_SDK/bin/upgrade` to do a cycle OTA workflow.
Open a new terminal to run the HTTP server, then run these commands to build the example and start the server:
```
cd $SDK_PATH/bin/upgrade
python -m SimpleHTTPServer 3344
```
While the server is running, the contents of the directory can be browsed at http://127.0.0.1:3344
NB: On some systems, the command may be `python2 -m SimpleHTTPServer`.
If you have any firewall software running that will block incoming access to port 3344, configure it to allow access while running the example.
## Step 3: Build OTA Example
Before compile OTA demo, please change the following details in `include/ota.h` if necessary:
* WiFi SSID & Password
* IP address of your host PC as "HTTP Server"
* HTTP Port number (if using the Python HTTP server above, the default is correct)
Save your changes, and run `./gen_misc.sh` to build the example.
## Step 4: Flash OTA Example
When flashing, use the `esptool` firstly to erase the entire flash. Then flash the user bin over serial.
## Step 5: Run the OTA Example
When the example starts up, it will print "local OTA task started..." then demo will follow the steps automatically:
1. Connect to the AP with configured SSID and password.
2. Connect to the HTTP server and download the new user bin.
3. Write the new user bin to flash, and configure the next boot.
4. check the bin CRC
4. Reboot to run new user bin if CRC passed
# Troubleshooting
* Check your PC can ping the ESP8266 at its IP, and that the IP, AP and other configuration settings are correct in `ota.h`.
* Check if any firewall software is preventing incoming connections on the PC.
## Production Implementation
If scaling this example for production use, please consider:
* Dealing with timeouts or WiFi disconnections while flashing.
# Adapter to your OTA scene
if change the demo to your OTA environment, please notice the following details.
1). OTA always was triggered passively, please change the necessary workflow to do OTA
2). do a domain name resolution of OTA server by `netconn_gethostbyname` to fetch a OTA IP address in real scene
3). must modify HTTP request according to your OTA server, you could use the firefox and wireshark to debug HTTP request
- escape the URL path if necessary
- there is always a slash at the beginning of the URL
- support HTTP version: `HTTP/1.0` or `HTTP/1.1`
- change `Host` item to your domain name/port according to your OTA server
- OTA server should transmit a Content-Type `application/octet-stream` according to `Accept` item
- OTA server should not encrypt the http body according to `Accept-Encoding` item
- new use bin size is decided by `Content-Length` Item
- `recv malformed http header` means that http server tranmits a http header item without `\r\n` end before ESP8266 receives the `\r\n\r\n`

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

191
examples/ota_demo/gen_misc.sh Executable file
View File

@@ -0,0 +1,191 @@
#!/bin/bash
:<<!
******NOTICE******
MUST set SDK_PATH & BIN_PATH firstly!!!
example:
export SDK_PATH=~/esp_iot_sdk_freertos
export BIN_PATH=~/esp8266_bin
!
export SDK_PATH=$SDK_PATH
export BIN_PATH=$BIN_PATH
echo "gen_misc.sh version 20150911"
echo ""
if [ $SDK_PATH ]; then
echo "SDK_PATH:"
echo "$SDK_PATH"
echo ""
else
echo "ERROR: Please export SDK_PATH in gen_misc.sh firstly, exit!!!"
exit
fi
if [ $BIN_PATH ]; then
echo "BIN_PATH:"
echo "$BIN_PATH"
echo ""
else
echo "ERROR: Please export BIN_PATH in gen_misc.sh firstly, exit!!!"
exit
fi
echo "Please check SDK_PATH & BIN_PATH, enter (Y/y) to continue:"
read input
if [[ $input != Y ]] && [[ $input != y ]]; then
exit
fi
echo ""
echo "Please follow below steps(1-5) to generate specific bin(s):"
echo "STEP 1: use boot_v1.2+ by default"
boot=new
echo "boot mode: $boot"
echo ""
echo "STEP 2: choose bin generate(0=eagle.flash.bin+eagle.irom0text.bin, 1=user1.bin, 2=user2.bin)"
echo "enter (0/1/2, default 0):"
read input
if [ -z "$input" ]; then
if [ $boot != none ]; then
boot=none
echo "ignore boot"
fi
app=0
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
elif [ $input == 1 ]; then
if [ $boot == none ]; then
app=0
echo "choose no boot before"
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
else
app=1
echo "generate bin: user1.bin"
fi
elif [ $input == 2 ]; then
if [ $boot == none ]; then
app=0
echo "choose no boot before"
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
else
app=2
echo "generate bin: user2.bin"
fi
else
if [ $boot != none ]; then
boot=none
echo "ignore boot"
fi
app=0
echo "generate bin: eagle.flash.bin+eagle.irom0text.bin"
fi
echo ""
echo "STEP 3: choose spi speed(0=20MHz, 1=26.7MHz, 2=40MHz, 3=80MHz)"
echo "enter (0/1/2/3, default 2):"
read input
if [ -z "$input" ]; then
spi_speed=40
elif [ $input == 0 ]; then
spi_speed=20
elif [ $input == 1 ]; then
spi_speed=26.7
elif [ $input == 3 ]; then
spi_speed=80
else
spi_speed=40
fi
echo "spi speed: $spi_speed MHz"
echo ""
echo "STEP 4: choose spi mode(0=QIO, 1=QOUT, 2=DIO, 3=DOUT)"
echo "enter (0/1/2/3, default 0):"
read input
if [ -z "$input" ]; then
spi_mode=QIO
elif [ $input == 1 ]; then
spi_mode=QOUT
elif [ $input == 2 ]; then
spi_mode=DIO
elif [ $input == 3 ]; then
spi_mode=DOUT
else
spi_mode=QIO
fi
echo "spi mode: $spi_mode"
echo ""
echo "STEP 5: choose spi size and map"
echo " 0= 512KB( 256KB+ 256KB)"
echo " 2=1024KB( 512KB+ 512KB)"
echo " 3=2048KB( 512KB+ 512KB)"
echo " 4=4096KB( 512KB+ 512KB)"
echo " 5=2048KB(1024KB+1024KB)"
echo " 6=4096KB(1024KB+1024KB)"
echo " 7=4096KB(2048KB+2048KB) not support ,just for compatible with nodeMCU board"
echo " 8=8192KB(1024KB+1024KB)"
echo " 9=16384KB(1024KB+1024KB)"
echo "enter (0/2/3/4/5/6/7/8/9, default 0):"
read input
if [ -z "$input" ]; then
spi_size_map=0
echo "spi size: 512KB"
echo "spi ota map: 256KB + 256KB"
elif [ $input == 2 ]; then
spi_size_map=2
echo "spi size: 1024KB"
echo "spi ota map: 512KB + 512KB"
elif [ $input == 3 ]; then
spi_size_map=3
echo "spi size: 2048KB"
echo "spi ota map: 512KB + 512KB"
elif [ $input == 4 ]; then
spi_size_map=4
echo "spi size: 4096KB"
echo "spi ota map: 512KB + 512KB"
elif [ $input == 5 ]; then
spi_size_map=5
echo "spi size: 2048KB"
echo "spi ota map: 1024KB + 1024KB"
elif [ $input == 6 ]; then
spi_size_map=6
echo "spi size: 4096KB"
echo "spi ota map: 1024KB + 1024KB"
elif [ $input == 7 ]; then
spi_size_map=7
echo"not support ,just for compatible with nodeMCU board"
exit
elif [ $input == 8 ]; then
spi_size_map=8
echo "spi size: 8192KB"
echo "spi ota map: 1024KB + 1024KB"
elif [ $input == 9 ]; then
spi_size_map=9
echo "spi size: 16384KB"
echo "spi ota map: 1024KB + 1024KB"
else
spi_size_map=0
echo "spi size: 512KB"
echo "spi ota map: 256KB + 256KB"
fi
echo ""
echo "start..."
echo ""
make clean
make BOOT=$boot APP=$app SPI_SPEED=$spi_speed SPI_MODE=$spi_mode SPI_SIZE_MAP=$spi_size_map

View File

@@ -0,0 +1,44 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
* it is 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.
*
*/
#ifndef OTA_H_
#define OTA_H_
#ifdef __cplusplus
extern "C" {
#endif
// WiFi
#define WIFI_SSID "BL_841R" // type:string, your AP/router SSID to config your device networking
#define WIFI_PASSWORD "1234567890" // type:string, your AP/router password
// OTA
#define LOCAL_OTA_SERVER_IP "192.168.111.104" // local OTA server ip
#define LOCAL_OTA_SERVER_PORT 3344 // local OTA server port
#define OTA_TIMEOUT 120000
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,45 @@
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
UP_EXTRACT_DIR = ..
GEN_LIBS = libota.a
endif
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ../include
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile

View File

@@ -0,0 +1,533 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
* it is 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.
*
*/
#include <sys/socket.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_timer.h"
#include "esp_system.h"
#include "spi_flash.h"
#include "upgrade.h"
#include "ota.h"
static bool http_200_check = false; // http state code
static bool resp_body_start = false; // whether http header over
static uint32_t download_length = 0; // current download length
static int content_len = 0; // parsed by Content-Length item
static char *content_type = NULL; // Content-type should be bin type(application/octet-stream)
extern int got_ip_flag;
static struct upgrade_param *upgrade;
struct upgrade_param {
uint32_t fw_bin_addr;
uint16_t fw_bin_sec;
uint16_t fw_bin_sec_num;
uint16_t fw_bin_sec_earse;
uint8_t extra;
uint8_t save[4];
uint8_t *buffer;
};
static void __attribute__((noreturn)) task_fatal_error()
{
printf("Exiting task due to fatal error...\n");
(void)vTaskDelete(NULL);
while (1) {
;
}
}
static bool OUT_OF_RANGE(uint16 erase_sec)
{
uint8_t spi_size_map = system_get_flash_size_map();
uint16_t sec_num = 0;
uint16_t start_sec = 0;
if (spi_size_map == FLASH_SIZE_8M_MAP_512_512 ||
spi_size_map == FLASH_SIZE_16M_MAP_512_512 ||
spi_size_map == FLASH_SIZE_32M_MAP_512_512) {
start_sec = (system_upgrade_userbin_check() == USER_BIN2) ? 1 : 129;
sec_num = 123;
} else if (spi_size_map == FLASH_SIZE_16M_MAP_1024_1024 ||
spi_size_map == FLASH_SIZE_32M_MAP_1024_1024) {
start_sec = (system_upgrade_userbin_check() == USER_BIN2) ? 1 : 257;
sec_num = 251;
} else {
start_sec = (system_upgrade_userbin_check() == USER_BIN2) ? 1 : 65;
sec_num = 59;
}
if ((erase_sec >= start_sec) && (erase_sec <= (start_sec + sec_num))) {
return false;
} else {
return true;
}
}
/******************************************************************************
* FunctionName : user_upgrade_internal
* Description : a
* Parameters :
* Returns :
*******************************************************************************/
static bool system_upgrade_internal(struct upgrade_param *upgrade, uint8_t *data, u32_t len)
{
bool ret = false;
uint16_t secnm = 0;
if (data == NULL || len == 0) {
return true;
}
/*got the sumlngth,erase all upgrade sector*/
if (len > SPI_FLASH_SEC_SIZE) {
upgrade->fw_bin_sec_earse = upgrade->fw_bin_sec;
secnm = ((upgrade->fw_bin_addr + len) >> 12) + (len & 0xfff ? 1 : 0);
while (upgrade->fw_bin_sec_earse != secnm) {
taskENTER_CRITICAL();
if (OUT_OF_RANGE(upgrade->fw_bin_sec_earse)) {
printf("fw_bin_sec_earse:%d, Out of range\n", upgrade->fw_bin_sec_earse);
break;
} else {
spi_flash_erase_sector(upgrade->fw_bin_sec_earse);
upgrade->fw_bin_sec_earse++;
}
taskEXIT_CRITICAL();
vTaskDelay(10 / portTICK_RATE_MS);
}
printf("flash erase over\n");
return true;
}
upgrade->buffer = (uint8_t *)calloc(1, len + upgrade->extra);
memcpy(upgrade->buffer, upgrade->save, upgrade->extra);
memcpy(upgrade->buffer + upgrade->extra, data, len);
len += upgrade->extra;
upgrade->extra = len & 0x03;
len -= upgrade->extra;
if (upgrade->extra <= 4) {
memcpy(upgrade->save, upgrade->buffer + len, upgrade->extra);
} else {
printf("ERR3:arr_overflow,%u,%d\n", __LINE__, upgrade->extra);
}
do {
if (upgrade->fw_bin_addr + len >= (upgrade->fw_bin_sec + upgrade->fw_bin_sec_num) * SPI_FLASH_SEC_SIZE) {
printf("spi_flash_write exceed\n");
break;
}
if (spi_flash_write(upgrade->fw_bin_addr, (uint32 *)upgrade->buffer, len) != SPI_FLASH_RESULT_OK) {
printf("spi_flash_write failed\n");
break;
}
ret = true;
upgrade->fw_bin_addr += len;
} while (0);
free(upgrade->buffer);
upgrade->buffer = NULL;
return ret;
}
/******************************************************************************
* FunctionName : system_get_fw_start_sec
* Description : a
* Parameters :
* Returns :
*******************************************************************************/
uint16_t system_get_fw_start_sec()
{
if (upgrade != NULL) {
return upgrade->fw_bin_sec;
} else {
return 0;
}
}
/******************************************************************************
* FunctionName : user_upgrade
* Description : a
* Parameters :
* Returns :
*******************************************************************************/
bool system_upgrade(uint8_t *data, uint32_t len)
{
bool ret;
ret = system_upgrade_internal(upgrade, data, len);
return ret;
}
void upgrade_recycle(void)
{
printf("upgrade recycle\n");
download_length = 0;
http_200_check = false;
resp_body_start = false;
content_len = 0;
content_type = NULL;
system_upgrade_deinit();
if (system_upgrade_flag_check() == UPGRADE_FLAG_FINISH) {
system_upgrade_reboot(); // if need
}
return;
}
/******************************************************************************
* FunctionName : system_upgrade_init
* Description : a
* Parameters :
* Returns :
*******************************************************************************/
void system_upgrade_init(void)
{
uint32_t user_bin2_start, user_bin1_start;
uint8_t spi_size_map = system_get_flash_size_map();
if (upgrade == NULL) {
upgrade = (struct upgrade_param *)calloc(1, sizeof(struct upgrade_param));
}
user_bin1_start = 1;
if (spi_size_map == FLASH_SIZE_8M_MAP_512_512 ||
spi_size_map == FLASH_SIZE_16M_MAP_512_512 ||
spi_size_map == FLASH_SIZE_32M_MAP_512_512) {
user_bin2_start = 129;
upgrade->fw_bin_sec_num = 123;
} else if (spi_size_map == FLASH_SIZE_16M_MAP_1024_1024 ||
spi_size_map == FLASH_SIZE_32M_MAP_1024_1024) {
user_bin2_start = 257;
upgrade->fw_bin_sec_num = 251;
} else {
user_bin2_start = 65;
upgrade->fw_bin_sec_num = 59;
}
upgrade->fw_bin_sec = (system_upgrade_userbin_check() == USER_BIN1) ? user_bin2_start : user_bin1_start;
upgrade->fw_bin_addr = upgrade->fw_bin_sec * SPI_FLASH_SEC_SIZE;
upgrade->fw_bin_sec_earse = upgrade->fw_bin_sec;
}
/******************************************************************************
* FunctionName : system_upgrade_deinit
* Description : a
* Parameters :
* Returns :
*******************************************************************************/
void system_upgrade_deinit(void)
{
if (upgrade != NULL) {
free(upgrade);
upgrade = NULL;
}
printf("ota end, free heap size:%d\n", system_get_free_heap_size());
return;
}
/*read buffer by byte still delim ,return read bytes counts*/
static int read_until(char *buffer, char delim, int len)
{
// /*TODO: delim check,buffer check,further: do an buffer length limited*/
int i = 0;
while (buffer[i] != delim && i < len) {
++i;
}
return i + 1;
}
bool read_past_http_header(char text[], int total_len)
{
/* i means current position */
int i = 0, i_read_len = 0;
char *ptr = NULL, *ptr2 = NULL;
char length_str[32] = {0};
while (text[i] != 0 && i < total_len) {
if (content_len == 0 && (ptr = (char *)strstr(text, "Content-Length")) != NULL) {
ptr += 16;
ptr2 = (char *)strstr(ptr, "\r\n");
memset(length_str, 0, sizeof(length_str));
memcpy(length_str, ptr, ptr2 - ptr);
content_len = atoi(length_str);
printf("parse Content-Length:%d\n", content_len);
}
if (content_type == NULL) {
content_type = (char *)strstr(text, "application/octet-stream");
}
i_read_len = read_until(&text[i], '\n', total_len - i);
if (i_read_len > total_len - i) {
printf("recv malformed http header\n");
task_fatal_error();
}
// if resolve \r\n line, http header is finished
if (i_read_len == 2) {
if (content_len == 0) {
printf("did not parse Content-Length item\n");
task_fatal_error();
}
if (content_type == NULL) {
printf("server did not return \"Content-type: application/octet-stream\"\n");
task_fatal_error();
}
// erase flash when first flash, for save new bin
if (false == system_upgrade(text, content_len)) {
system_upgrade_flag_set(UPGRADE_FLAG_IDLE);
upgrade_recycle();
task_fatal_error();
}
// the valid left http body length
int i_write_len = total_len - (i + 2);
if (false == system_upgrade(&(text[i + 2]), i_write_len)) {
system_upgrade_flag_set(UPGRADE_FLAG_IDLE);
upgrade_recycle();
printf("system upgrade error");
task_fatal_error();
}
download_length += i_write_len;
printf("first download len:%d\n", download_length);
return true;
}
i += i_read_len;
}
return false;
}
/******************************************************************************
* FunctionName : upgrade_download
* Description : parse http response ,and download remote data and write in flash
* Parameters : int sta_socket : ota client socket fd
* char *pusrdata : remote data
* length : data length
* Returns : none
*******************************************************************************/
void upgrade_download(int sta_socket, char *pusrdata, unsigned short length)
{
char *ptr = NULL;
char *ptmp2 = NULL;
char lengthbuffer[32];
// first response should include state code:200
if (!http_200_check && strstr(pusrdata, "200") == NULL) {
printf("ota url is invalid or bin is not exist");
task_fatal_error();
}
http_200_check = true;
if (!resp_body_start) {
// deal with http header
resp_body_start = read_past_http_header(pusrdata, length);
return;
}
// deal with http body
// http transmit body more than content-length occasionally
// default bin size = content-length, throw up the other http body
if (download_length + length > content_len) {
length = content_len - download_length;
download_length = content_len;
} else {
download_length += length;
}
printf("downloaded len:%d\n", download_length);
// save http body(bin) to flash
if (false == system_upgrade(pusrdata, length)) {
system_upgrade_flag_set(UPGRADE_FLAG_IDLE);
upgrade_recycle();
}
return;
}
/******************************************************************************
* FunctionName : local_ota_begin
* Description : ota_task function
* Parameters : task param
* Returns : none
*******************************************************************************/
void local_ota_begin()
{
int read_bytes;
int sin_size;
int sta_socket;
char recv_buf[1460];
uint8_t user_bin[21] = {0};
struct sockaddr_in remote_ip;
printf("Hello, welcome to local ota!\r\n");
printf("ota server addr %s port %d\n", LOCAL_OTA_SERVER_IP, LOCAL_OTA_SERVER_PORT);
while (1) {
sta_socket = socket(PF_INET, SOCK_STREAM, 0);
if (-1 == sta_socket) {
close(sta_socket);
printf("socket fail !\r\n");
continue;
}
printf("socket ok!\r\n");
bzero(&remote_ip, sizeof(struct sockaddr_in));
remote_ip.sin_family = AF_INET;
remote_ip.sin_addr.s_addr = inet_addr(LOCAL_OTA_SERVER_IP);
remote_ip.sin_port = htons(LOCAL_OTA_SERVER_PORT);
if (0 != connect(sta_socket, (struct sockaddr *)(&remote_ip), sizeof(struct sockaddr))) {
close(sta_socket);
printf("connect fail!\r\n");
system_upgrade_flag_set(UPGRADE_FLAG_IDLE);
upgrade_recycle();
continue;
}
printf("connect ok!\r\n");
if (system_upgrade_userbin_check() == UPGRADE_FW_BIN1) {
memcpy(user_bin, "user2.2048.new.5.bin", 21);
} else if (system_upgrade_userbin_check() == UPGRADE_FW_BIN2) {
memcpy(user_bin, "user1.2048.new.5.bin", 21);
}
/*send GET request to http server*/
const char *GET_FORMAT =
"GET %s HTTP/1.0\r\n"
"Host: %s:%d\r\n"
"Accept: application/octet-stream\r\n"
"Accept-Encoding: identity\r\n"
"User-Agent: esp8266-rtos-sdk/1.0 esp8266\r\n\r\n";
char *http_request = NULL;
int get_len = asprintf(&http_request, GET_FORMAT, user_bin, LOCAL_OTA_SERVER_IP, LOCAL_OTA_SERVER_PORT);
if (get_len < 0) {
printf("malloc memory failed.\n");
system_upgrade_flag_set(UPGRADE_FLAG_IDLE);
upgrade_recycle();
free(http_request);
close(sta_socket);
continue;
}
if (write(sta_socket, http_request, strlen(http_request) + 1) < 0) {
close(sta_socket);
printf("send fail\n");
free(http_request);
upgrade_recycle();
continue;
}
printf("send success\n");
free(http_request);
while ((read_bytes = read(sta_socket , recv_buf, 1460)) >= 0) {
if (read_bytes > 0) {
upgrade_download(sta_socket, recv_buf, read_bytes);
} else {
printf("peer close socket\n");
break;
}
// default bin size equal to content-length
if (download_length == content_len && download_length != 0) {
printf("upgrade file download finished, bin size:%d\n", download_length);
if (upgrade_crc_check(system_get_fw_start_sec(), download_length) != true) {
printf("upgrade crc check failed !\n");
system_upgrade_flag_set(UPGRADE_FLAG_IDLE);
} else {
printf("bin check crc ok\n");
system_upgrade_flag_set(UPGRADE_FLAG_FINISH);
}
upgrade_recycle();
}
}
printf("read data fail! ret:%d\r\n", read_bytes);
close(sta_socket);
system_upgrade_flag_set(UPGRADE_FLAG_IDLE);
upgrade_recycle();
}
}
// local_ota_task
void local_ota_task(void *pvParameter)
{
os_timer_t upgrade_timer;
printf("\nlocal OTA task started...\n");
while (!got_ip_flag) {
vTaskDelay(2000 / portTICK_RATE_MS);
printf("wait for fetching IP...\n");
}
printf("ota begin, free heap size:%d\n", system_get_free_heap_size());
system_upgrade_flag_set(UPGRADE_FLAG_START);
system_upgrade_init();
local_ota_begin();
// OTA timeout, shutdown OTA
os_timer_disarm(&upgrade_timer);
os_timer_setfn(&upgrade_timer, (os_timer_func_t *)upgrade_recycle, NULL);
os_timer_arm(&upgrade_timer, OTA_TIMEOUT, 0);
}

View File

@@ -0,0 +1,159 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
* it is 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.
*
*/
#include <stdlib.h>
#include <stdint.h>
#include "c_types.h"
#include "spi_flash.h"
#include "lwip/err.h"
#include "lwip/ip_addr.h"
#define BUFSIZE 512
#define CRC_BLOCK_SIZE 512
uint32_t start_sec;
static uint32_t *crc_table;
static int init_crc_table(void);
static uint32_t crc32(uint32_t crc, unsigned char *buffer, uint32_t size);
static int init_crc_table(void)
{
uint32_t c;
uint32_t i, j;
crc_table = (uint32_t *)calloc(1, 256 * 4);
if (crc_table == NULL) {
printf("malloc crc table failed\n");
return -1;
}
for (i = 0; i < 256; i++) {
c = (uint32_t)i;
for (j = 0; j < 8; j++) {
if (c & 1) {
c = 0xedb88320L ^ (c >> 1);
} else {
c = c >> 1;
}
}
crc_table[i] = c;
}
return 0;
}
static uint32_t crc32(uint32_t crc, unsigned char *buffer, uint32_t size)
{
uint32_t i;
for (i = 0; i < size; i++) {
crc = crc_table[(crc ^ buffer[i]) & 0xff] ^ (crc >> 8);
}
return crc ;
}
static int calc_img_crc(uint32_t sumlength, uint32_t *img_crc)
{
int fd;
int ret;
int i = 0;
uint8 error = 0;
unsigned char *buf = (char *)calloc(1, BUFSIZE);
if (buf == NULL) {
printf("malloc crc buf failed\n");
free(crc_table);
return -1;
}
uint32_t crc = 0xffffffff;
uint16_t sec_block = sumlength / CRC_BLOCK_SIZE ;
uint32_t sec_last = sumlength % CRC_BLOCK_SIZE;
for (i = 0; i < sec_block; i++) {
if (0 != (error = spi_flash_read(start_sec * SPI_FLASH_SEC_SIZE + i * CRC_BLOCK_SIZE , (uint32 *)buf, BUFSIZE))) {
free(crc_table);
free(buf);
printf("spi_flash_read error %d\n", error);
return -1;
}
crc = crc32(crc, buf, BUFSIZE);
}
if (sec_last > 0) {
if (0 != (error = spi_flash_read(start_sec * SPI_FLASH_SEC_SIZE + i * CRC_BLOCK_SIZE, (uint32 *)buf, sec_last))) {
free(crc_table);
free(buf);
printf("spi_flash_read error %d\n", error);
return -1;
}
crc = crc32(crc, buf, sec_last);
}
*img_crc = crc;
free(crc_table);
free(buf);
return 0;
}
bool upgrade_crc_check(uint16 fw_bin_sec , uint32_t sumlength)
{
int ret;
uint32_t img_crc;
uint32_t flash_crc = 0xFF;
start_sec = fw_bin_sec;
printf("fw_bin_sec %d sumlength %d\n", fw_bin_sec, sumlength);
if (0 != init_crc_table()) {
return false;
}
ret = calc_img_crc(sumlength - 4, &img_crc);
if (ret < 0) {
return false;
}
img_crc = abs(img_crc);
printf("img_crc = %u\n", img_crc);
spi_flash_read(start_sec * SPI_FLASH_SEC_SIZE + sumlength - 4, &flash_crc, 4);
printf("flash_crc = %u\n", flash_crc);
if (img_crc == flash_crc) {
return true;
} else {
return false;
}
}

View File

@@ -0,0 +1,44 @@
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
GEN_LIBS = libuser.a
endif
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile

View File

@@ -0,0 +1,134 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
* it is 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.
*
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "lwip/ip_addr.h"
#include "esp_sta.h"
#include "esp_wifi.h"
#include "ota.h"
int got_ip_flag = 0;
extern void local_ota_task(void *pvParameter);
/******************************************************************************
* FunctionName : user_rf_cal_sector_set
* Description : SDK just reversed 4 sectors, used for rf init data and paramters.
* We add this function to force users to set rf cal sector, since
* we don't know which sector is free in user's application.
* sector map for last several sectors : ABCCC
* A : rf cal
* B : rf init data
* C : sdk parameters
* Parameters : none
* Returns : rf cal sector
*******************************************************************************/
uint32_t user_rf_cal_sector_set(void)
{
flash_size_map size_map = system_get_flash_size_map();
uint32_t rf_cal_sec = 0;
switch (size_map) {
case FLASH_SIZE_4M_MAP_256_256:
rf_cal_sec = 128 - 5;
break;
case FLASH_SIZE_8M_MAP_512_512:
rf_cal_sec = 256 - 5;
break;
case FLASH_SIZE_16M_MAP_512_512:
case FLASH_SIZE_16M_MAP_1024_1024:
rf_cal_sec = 512 - 5;
break;
case FLASH_SIZE_32M_MAP_512_512:
case FLASH_SIZE_32M_MAP_1024_1024:
rf_cal_sec = 1024 - 5;
break;
case FLASH_SIZE_64M_MAP_1024_1024:
rf_cal_sec = 2048 - 5;
break;
case FLASH_SIZE_128M_MAP_1024_1024:
rf_cal_sec = 4096 - 5;
break;
default:
rf_cal_sec = 0;
break;
}
return rf_cal_sec;
}
// WiFi callback function
void event_handler(System_Event_t *event)
{
switch (event->event_id) {
case EVENT_STAMODE_GOT_IP:
printf("WiFi connected\n");
got_ip_flag = 1;
break;
case EVENT_STAMODE_DISCONNECTED:
printf("WiFi disconnected, try to connect...\n");
got_ip_flag = 0;
wifi_station_connect();
break;
default:
break;
}
}
void initialize_wifi(void)
{
wifi_set_opmode(STATION_MODE);
// set AP parameter
struct station_config config;
bzero(&config, sizeof(struct station_config));
sprintf(config.ssid, WIFI_SSID);
sprintf(config.password, WIFI_PASSWORD);
wifi_station_set_config(&config);
wifi_station_set_auto_connect(true);
wifi_station_set_reconnect_policy(true);
wifi_set_event_handler_cb(event_handler);
}
void user_init(void)
{
initialize_wifi();
if (xTaskCreate(local_ota_task, "main_process", 1024, NULL, 5, NULL) != pdPASS) {
printf("lota create failed\n");
}
}