ESP8266 RTOS : Device Firmware Update Over the Air (DFU-OTA) via the Private Server

ESP8266 RTOS : Device Firmware Update Over the Air (DFU-OTA) via the Private Server


      ESP8266 is a very popular micro-controller featuring WIFI with its bargaining price. There are some product based on ESP8266/ESP8285 (ESP8285 = ESP8266 embedded 1 MB flash) for home IoT using. The makers enjoy remolding the firmware of the products to fix their personal purposes.
      In most case, the maker re-program ESP8266 in the framework of Arduino, thus, there are lots post and discussion about how to deal with DFU-OTA based on ESP8266 Arduino. However, The information about the same goal but the ESP8266 based on RTOS framework is limited, undocument or incomplete. This post wants to fill the gap.
   By default, DFU-OTA server is based on Espressif: It forces the ESP8266 to be on internet. But some use case the ESP8266 would not be connected with internet (maybe for the security issue). Therefore, private DFU-OTA is very necessary. In here I assume your DFU-OTA server is linux based, But it is easy to port the DFU-OTA function to the other platform, for the  DFU-OTA working principle  is typical TCP-IP.

Note:  this example assumes your esp8266 is within 1MB flash!  It may work in otherwise size or not  I am not sure.

零. Download the requisite library:
     https://github.com/espressif/esp8266-rtos-sample-code/tree/master/06Cloud/FOTA_Demo/app
  Download the include/upgrade.h,   app/ upgrade_lib and  app/fota_crc32.c, then move them in your ESP8266 project, I assume the folder under your wokring project for DFU-OTA is dfu_ota.
    
一. My ESP8266 DFU-OTA  dfu_ota.c  code be :

#include "esp_common.h"
#include "espconn.h"


#include "dfu_ota.h"
#include "upgrade.h"



#define LOCAL static

LOCAL struct espconn g_tcp_client;
LOCAL esp_tcp g_tcp;
LOCAL int g_firmware_length = 0;

enum 
{
 TCP_CONNECTING   = 0x00,
 SENT_TARGET_SECTION  = 0x01,
 RECEIVED_LENGTH   = 0x02,
 KEEP_RECEIVING_DATA  = 0x03,
 RECEIVED_DATA   = 0x04,

 TCP_DISCONNECTED = 0xff,
};


LOCAL int g_sending_state = TCP_DISCONNECTED;


int firmware_upgrading_fail(void)
{ 
 printf("upgrading fail\r\n");
 espconn_disconnect(&g_tcp_client);

 printf("reboot the system\r\n");
 system_restart();
}/*firmware_upgrading_fail*/


//LOCAL uint8_t g_tcp_server_ip[4] = {0};
LOCAL uint8_t g_tcp_retry_connection_count = 0;

LOCAL void tcp_connection_error_callback(void *arg, int8_t err)
{
 struct espconn *p_tcp_server;
 p_tcp_server = arg;

 printf("tcp connection err = %d\r\n", err);

 if(ESPCONN_OK != err)
 {
#define MAX_RETRY_CONNECTION_COUNT (5)

  if(MAX_RETRY_CONNECTION_COUNT > g_tcp_retry_connection_count)
  {
   printf("retry the tcp connection...\r\n");
   delay_ms(1000);
   g_tcp_retry_connection_count++;
   espconn_connect(&g_tcp_client);
  }
  else
  {
   printf("relinquish this upgrading\r\n");
   g_tcp_retry_connection_count = 0;
   firmware_upgrading_fail();
  }/*if */
 }/*if ESPCONN_OK */

}/*tcp_connection_error_callback*/


LOCAL void tcp_connect_callback(void *arg)
{
 struct espconn *p_tcp_server;
 uint8_t tcp_state;
 uint8_t target_section;
 p_tcp_server = arg;

 if(USER_BIN1 == system_upgrade_userbin_check())
 {
  target_section = 2;
  printf("current firmware in user1\r\n");
 }
 else
 {
  target_section = 1;
  printf("current firmware in user2\r\n");
 }

 printf("fetch firmware%d\r\n", target_section);

 tcp_state = espconn_send(p_tcp_server, &target_section, sizeof(uint8_t));
 printf("target_section sending\r\n");
 g_sending_state = SENT_TARGET_SECTION;
#define INTERVAL_BETWEEN_PACKETS_IN_MSEC (10)

 delay_ms(INTERVAL_BETWEEN_PACKETS_IN_MSEC);
}/*tcp_connect_callback*/


LOCAL void tcp_sent_to_buffer_callback(void *arg)
{
 struct espconn *p_tcp_server;
 uint8_t tcp_state;

 p_tcp_server = arg;
}/*tcp_sent_to_buffer_callback*/


#define OTA_PACKET_SIZE (1280)
LOCAL int g_remained_firmware_length = 0;

LOCAL void tcp_received_callback(void *arg, char *p_data, unsigned short len)
{
 struct espconn *p_tcp_server;
 uint8_t tcp_state;
 uint8_t data_buffer[OTA_PACKET_SIZE];

 p_tcp_server = arg;

 switch(g_sending_state)
 {
  case SENT_TARGET_SECTION:
  if(sizeof(int) != len)
  {
   printf("received length error\r\n");
   goto Flag_Error_Occurring;
  }/*if */

  g_sending_state = RECEIVED_LENGTH;
  memcpy(&g_firmware_length, p_data, sizeof(int));
  printf("firmware size = %d\r\n", g_firmware_length);

  if(g_firmware_length < 0)
  {
   printf("firmware_length error\r\n");
   goto Flag_Error_Occurring;
  }
  g_remained_firmware_length = g_firmware_length;
  break;


  case RECEIVED_LENGTH:
  case KEEP_RECEIVING_DATA:

  memcpy(&data_buffer[0], p_data, len);
  g_remained_firmware_length -= len;

  if(0 > g_remained_firmware_length)
  {
   printf("received data size is not marched\r\n");
   goto Flag_Error_Occurring;
  }

  printf("received firmware data %d bytes, remain %d bytes...\r\n", 
   g_firmware_length - g_remained_firmware_length, g_remained_firmware_length);

  if(false == system_upgrade(&data_buffer[0], len))
  {
   printf("\r\nsystem_upgrade error\r\n");
   goto Flag_Error_Occurring;
  }/*if false == system_upgrade*/


  if(0 == g_remained_firmware_length)
  {
   printf("\r\n firmware has been downloaded done\r\n");
   g_sending_state = RECEIVED_DATA;

   if( false == upgrade_crc_check(system_get_fw_start_sec(), g_firmware_length))
   {
    printf("firmware upgrading FAIL : CRC is not consisted!!\r\n");
    system_upgrade_flag_set(UPGRADE_FLAG_IDLE);
    espconn_disconnect(&g_tcp_client);
   }
   else
   {
    printf("firmware upgrading is successful.\r\n");
    system_upgrade_flag_set(UPGRADE_FLAG_FINISH);
    espconn_disconnect(&g_tcp_client);
   }/* check crc*/
  }
  else
  {
   system_upgrade_flag_set(UPGRADE_FLAG_IDLE);
   g_sending_state = KEEP_RECEIVING_DATA;
  }
  break;
 default:
  break;
 }
 return;


Flag_Error_Occurring:
 firmware_upgrading_fail();
 return;
}/*tcp_received_callback*/


LOCAL void tcp_disconnect_callback(void* arg)
{
 printf("ota server disconnected\r\n");
 g_sending_state = TCP_DISCONNECTED;

 system_upgrade_deinit();
 if (UPGRADE_FLAG_FINISH == system_upgrade_flag_check())
 {
  printf("reboot the system for new firmware\r\n");
  system_upgrade_reboot();
 }
 else
 {
  printf("upgrade firmware fail\r\n");
  system_restart();
 }

}/*tcp_disconnect_callback*/



int device_firmware_upgrade_over_the_air(uint8_t *p_server_ip)
{
 uint8_t con_status;
 uint8_t tcp_state;

 printf("start device firmware upgrading over the air...\r\n");


 con_status = wifi_station_get_connect_status();
 
 if (con_status != STATION_GOT_IP) 
 {
  printf("error, this endpoint has not be connected with router.\r\n");
  printf("relinquish this firmware upgrading.\r\n");
  firmware_upgrading_fail();
  return -1;
 }/*if*/


#define TCP_SERVER_REMOTE_PORT (5555)
 g_tcp.remote_port = (TCP_SERVER_REMOTE_PORT);
 g_tcp_client.type = ESPCONN_TCP;
 g_tcp_client.proto.tcp = &g_tcp;

 memcpy(g_tcp.remote_ip, p_server_ip, 4*sizeof(uint8_t));

 espconn_regist_reconcb(&g_tcp_client, tcp_connection_error_callback);
 espconn_regist_connectcb(&g_tcp_client, tcp_connect_callback);
 espconn_regist_sentcb(&g_tcp_client, tcp_sent_to_buffer_callback);
  espconn_regist_recvcb(&g_tcp_client, tcp_received_callback);
 espconn_regist_disconcb(&g_tcp_client, tcp_disconnect_callback);

 system_upgrade_flag_set(UPGRADE_FLAG_START);
 system_upgrade_init();


#define RELATIVE_END_SECTOR_NUM (0x80 - 0x0c)
 {
  uint16_t begin_sector;
  uint16_t i;
  printf("erasing flash sector to store new firmware, it takes a while...\r\n");
  begin_sector = system_get_fw_start_sec();
  printf("erasing flash sector from 0x%02x to 0x%02x\r\n", begin_sector, 
   begin_sector + RELATIVE_END_SECTOR_NUM - 1);

  for(i = 0; i< RELATIVE_END_SECTOR_NUM; i++)
   spi_flash_erase_sector(begin_sector + i);
 }/*erase flash*/


 tcp_state = espconn_connect(&g_tcp_client);
 g_sending_state = TCP_CONNECTING;

 if(0 != tcp_state)
 {
  g_sending_state = TCP_DISCONNECTED;
  printf("espconn_connect error, give up this connection\r\n");
  firmware_upgrading_fail();
 }/*if 0*/

 return 0;
}/*device_firmware_upgrade_over_the_air*/


The corresponding header dfu_ota.h is 


#ifndef _DFU_OTA_H_
#define _DFU_OTA_H_


int device_firmware_upgrade_over_the_air(uint8_t *p_server_ip);

#endif

 The contents under dfu_ota folder should be :

$ ls dfu_ota/
dfu_ota.c  dfu_ota.h  fota_crc32.c  Makefile  upgrade.h  upgrade_lib.c

The Makefile under dfu_ota folder, is copied from common example but within the modification, about line 14:


ifndef PDIR GEN_LIBS = libdfu_ota.a endif

The Makefile under your project's root folder, Should be added the dfu_ota contents:


SUBDIRS=    \
 user \  :         dfu_ota
:
:
COMPONENTS_eagle.app.v6 = \
        user/libuser.a  \
        :
 dfu_ota/libdfu_ota.a


二.

The DFU-OTA server would be responding the DFU requesting, the code, dfu_ota_server be:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#include <linux/limits.h>

//#include <inttypes.h>

 #include <errno.h>

#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#include <signal.h> 

#include <sys/stat.h>

/*for show ip*/
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>

#include <pthread.h>

#ifdef __mips__
 #include <byteswap.h>
#endif

#include <sys/time.h>


#define PORT_NUMBER (5555)

#define ONE_PACKET_MAX_SIZE (4*1024)

#define LOCAL static

#define INVALID_SOCKET (int)(~0)
#define MAX_CLIENT_NUMER (32)



void delay_micro_sec(unsigned int delay_time_in_us)
{
 struct timeval now;
 struct timeval period;
 struct timeval end;

 gettimeofday(&now, NULL);

 period.tv_sec = delay_time_in_us / 1000000;
 period.tv_usec = delay_time_in_us % 1000000;

 timeradd(&now, &period, &end);

 while(timercmp(&now, &end, < ))
  gettimeofday(&now, NULL);

}/*delay_micro_sec*/


LOCAL int g_master_socket;
LOCAL int g_client_socket;

int esp8266_ota_server_start(char **pp_firmware_location)
{
 int i;
 struct sockaddr_in server_addr;

 if(NULL == pp_firmware_location[0] || NULL == pp_firmware_location[1])
 {
  return -1;
 }

 g_master_socket = socket(AF_INET , SOCK_STREAM , 0);

 if(-1 == g_master_socket)
 {
  printf("Could not create socket\r\n");
  return -1;
 }/*if*/

 //Prepare the sockaddr_in structure
 server_addr.sin_family = AF_INET;
 server_addr.sin_addr.s_addr = INADDR_ANY;
 server_addr.sin_port = htons(PORT_NUMBER);

 {
  int reuseaddr;
  int reuseaddr_len;
  reuseaddr = 1;
  reuseaddr_len = sizeof(reuseaddr);
  setsockopt(g_master_socket, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, reuseaddr_len);
 }/*local variabe */

 //Bind
 if(0 > bind(g_master_socket,(struct sockaddr *)&server_addr , sizeof(server_addr)) )
 {
  perror("bind failed. Error\r\n");
  return -2;
 }/*if bind*/
 printf("ota server is ready to be connected\r\n\r\n");


#define MAX_PENDING_CONNECTION_NUMBER (3)

 listen(g_master_socket , MAX_PENDING_CONNECTION_NUMBER);

 while(1)
 {
  struct sockaddr_in client_addr;
  int socket_len;

  int written_len;
  unsigned int firmware_size;
  int is_succ_sent;

  unsigned char target_firmware_section;

  g_client_socket = accept(g_master_socket, 
   (struct sockaddr *)&client_addr, (socklen_t*)&socket_len);

  if(INVALID_SOCKET == g_client_socket)
  {
   printf("accepted ERROR\r\n");
   continue;
  }/*if */

  printf("\r\n\r\n%s connected\r\n", inet_ntoa(client_addr.sin_addr));

  is_succ_sent = 0;
  {
   socklen_t addr_len;

   addr_len = sizeof(client_addr);
   getpeername(g_client_socket, (struct sockaddr*)&client_addr, &addr_len);
  }/*local variable*/


  {
   struct timeval timeout_val;

#define MAX_ONE_PACKET_TIMEOUT_IN_SEC (10)
   timeout_val.tv_sec = MAX_ONE_PACKET_TIMEOUT_IN_SEC;
   timeout_val.tv_usec = 0;

   setsockopt(g_client_socket, SOL_SOCKET, SO_SNDTIMEO, 
    &timeout_val, sizeof(timeout_val));

   setsockopt(g_client_socket, SOL_SOCKET, SO_RCVTIMEO, 
    &timeout_val, sizeof(timeout_val));
  }/*local variables*/

  {

   unsigned char read_buffer[16];
   int read_size;

   read_size = read(g_client_socket, &read_buffer[0], sizeof(read_buffer));
   if(1 != read_size)
   {
    printf("received firmware region index size error\r\n");
    goto Flag_end_connection;
   }
   target_firmware_section = 0;
   memcpy(&target_firmware_section, &read_buffer[0], sizeof(unsigned char));


   if(!(1 == target_firmware_section || 2 == target_firmware_section) )
   {
    printf("received firmware region index is not valid\r\n");
    printf("firmware_region_index = %d\r\n", (int)target_firmware_section);
    goto Flag_end_connection;
   }/*if*/

   printf("firmware%d would be transmitted to endpoint\r\n", (int)target_firmware_section);
  }/*local variable*/

  {
   struct stat fw_dir_st = {0};

   if(-1 == stat(pp_firmware_location[target_firmware_section - 1], &fw_dir_st))
   {
    printf("ERROR :: %s does not exist!\r\n", 
     pp_firmware_location[target_firmware_section - 1]);

    goto Flag_end_connection;
   }/*if */

   firmware_size = (int)fw_dir_st.st_size;
   printf("firmware%d size = %3.1f KB\r\n", target_firmware_section, firmware_size/1024.0);
  }/*local variable*/

  {
   unsigned int firmware_size_le;
#ifdef __mips__
   firmware_size_le = __bswap_32(firmware_size);
#else
   firmware_size_le = firmware_size;
#endif
   written_len = write(g_client_socket, &firmware_size_le, sizeof(unsigned int));

   if(sizeof(unsigned int) != written_len)
   {
    printf("\r\nsend firmware length error\r\n");
    goto Flag_end_connection;
   }/*if */
  }/*send data length*/

 {
  unsigned int remain_firmware_size;
  FILE *fp;

  remain_firmware_size = firmware_size;

  fp = fopen(pp_firmware_location[target_firmware_section - 1], "rb");

  if(NULL == fp)
  {
   printf("open file %s error\r\n", 
    pp_firmware_location[target_firmware_section - 1]);
   goto Flag_end_connection;
  }/*if */

  while(remain_firmware_size > 0)
  {
#define OTA_PACKET_SIZE (1280)
   unsigned char data_buffer[OTA_PACKET_SIZE];
   unsigned int sending_size;

   sending_size = fread(&data_buffer[0], 1, sizeof(data_buffer), fp);
   written_len = write(g_client_socket, &data_buffer[0], sending_size);

   if(sending_size != written_len)
   {
    printf("\r\nsend firmware data error\r\n");
    goto Flag_end_connection;
   }/*if*/

   remain_firmware_size -= written_len;

   printf("\rfirmware data has sent %3.1f KB, remain %3.1f KB ", 
    (firmware_size - remain_firmware_size)/1024.0, remain_firmware_size/1024.0);
   fflush(stdout);

#define PACKET_INTERVAL_IN_MSEC (400)
   delay_micro_sec(PACKET_INTERVAL_IN_MSEC*1000);
  }/*while*/

  fclose(fp); fp = NULL;
  printf("\r\n");

  is_succ_sent = 1;
 }/*send data */

 printf("firmware %3.1f KB has been sent successfully \r\n", firmware_size/1024.0);

Flag_end_connection:
 if(0 == is_succ_sent)
  printf("WARNING:: firmware upgrading has been FAIL\r\n");

 close(g_client_socket); g_client_socket = INVALID_SOCKET;
 }/*while*/

 close(g_master_socket);
 return 0;
}/*esp8266_ota_server_start*/


int esp8266_ota_server_end(void)
{
 if( INVALID_SOCKET != g_client_socket)
  close(g_client_socket);
 g_client_socket = INVALID_SOCKET;

 if(INVALID_SOCKET != g_master_socket)
  close(g_master_socket);
 g_master_socket = INVALID_SOCKET;

 return 0;
}/*esp8266_ota_server_end*/


void InterruptHandler(int sig)
{
 printf("\r\n__FUNCTION__ = %s\r\n", __FUNCTION__);
 esp8266_ota_server_end();

 exit(0);
}/*InterruptHandler*/


int main(int argc, char *argv[])
{
 extern char *__progname;
 int k;
 char firmware_location[2][PATH_MAX];
 char *p_firmware_location[2];

 signal(SIGINT, InterruptHandler); 

 printf("\r\n%s\r\n", __progname);

 for(k = 0; k < 2; k++){
  memset(&firmware_location[k][0], 0, PATH_MAX);
  p_firmware_location[k] = NULL;
 }/*for k*/

 k = 1;
 while(k < argc)
 {
  if(0 == strncmp("-fw1", argv[k], strlen("-fw1")) 
   || 0 == strncmp("--user1", argv[k], strlen("-user1")) )
  {
   if(k + 1 < argc)
   {
    strncpy(&firmware_location[0][0], argv[k + 1], PATH_MAX);
    p_firmware_location[0] = &firmware_location[0][0];
   }
   else
   {
    printf("ERROR :: -fw1/--user1 should be followed by firmware location.\r\n");
    return -1;
   }/*if */
  }/*if */

  if(0 == strncmp("-fw2", argv[k], strlen("-fw1")) 
   || 0 == strncmp("--user2", argv[k], strlen("-user1")) )
  {
   if(k + 1 < argc)
   {
    strncpy(&firmware_location[1][0], argv[k + 1], PATH_MAX);
    p_firmware_location[1] = &firmware_location[1][0];
   }
   else
   {
    printf("ERROR :: -fw1/--user2 should be followed by firmware location.\r\n");
    return -1;
   }/*if */
  }/*if */

  k++;
 }/*while*/


 if(NULL == p_firmware_location[0])
 {
  printf("-fw1 should be specified\r\n");
  return -1;
 }/*if non specified fw1 location*/

 if(NULL == p_firmware_location[1])
 {
  printf("-fw2 should be specified\r\n");
  return -1;
 }/*if non specified fw2 location*/


 for(k = 0; k < 2; k++)
 {
  struct stat fw_dir_st = {0};

  if(-1 == stat(p_firmware_location[k], &fw_dir_st))
  {
   printf("ERROR :: %s does not exist!\r\n", p_firmware_location[k]);
   return -1;
  }/*if */

  {
   char firmware_absolute_location[PATH_MAX];
   char *p;

   p = realpath(p_firmware_location[k], &firmware_absolute_location[0]);
   if(NULL == p)
   {
    printf("realpath error :: %s\r\n", strerror(errno));
    return -1;
   }/*if*/

   printf("firmware%d : %s\r\n", k + 1, firmware_absolute_location);
  }
  //printf("firmware%d size = %3.1f KB\r\n", k + 1, fw_dir_st.st_size/1024.0);
  
 }/*local variable*/


 printf("\r\nip address of this server:\r\n");
 {
  struct ifaddrs *ifaddr, *ifa;
  int s;
  char host[NI_MAXHOST];

  if (getifaddrs(&ifaddr) == -1)
  {
   perror("getifaddrs");
   exit(EXIT_FAILURE);
  }

  for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
  {
   if (ifa->ifa_addr == NULL)
    continue;

   s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), 
     &host[0], NI_MAXHOST, NULL, 0, NI_NUMERICHOST);

   if( AF_INET == ifa->ifa_addr->sa_family)
   {
    if (s != 0)
    {
     printf("getnameinfo() failed: %s\n", gai_strerror(s));
     exit(EXIT_FAILURE);
    }
    //printf("\tInterface : <%s>\n",ifa->ifa_name );
    //printf("\t Address : <%s>\n", host);
    printf("\t<%s> : %s\r\n", ifa->ifa_name, &host[0]);
   }
  }/*for ifa*/

  freeifaddrs(ifaddr);
 }/*local variabes*/
 printf("\r\n");
 esp8266_ota_server_start(&p_firmware_location[0]);

 return 0;
}/*main*/

Build the dfu-ota server is pretty simple: just run the command:


gcc dfu_ota_server.c -o dfu_ota_server

三. Modify your Working project and call the function int  device_firmware_upgrade_over_the_air(uint8_t *p_server_ip) while it is time for DFU-OTA. 
   build the binary twice,  one for user1 (common bininary downloaded to ESP8266 via USB-UART), one for user2. (while the scrip gen_misc.sh has been executed, "STEP 2: choose bin generate" would the binary is for user1 or user2),  Once the user1 has been build, you should clean the whole output, especially the .o files, before building user2.

 四. Move the built binaries, user1.1024.new.2.bin and user2.1024.new.2.bin to your OTA server and run the dfu_ota_server as:


./dfu_ota_server -fw1 user1.1024.new.2.bin -fw2 user2.1024.new.2.bin


五. Before you download the user1 binary to ESP8266 via UART (USB),  It is necessary to  ERASE the whole flash . In linux it is very easy,  


sudo python ~/.local/lib/python2.7/site-packages/esptool.py --port /dev/ttyUSB0 erase_flash

But if you are  in Windows , as my knowing,  the flashing tool would not erase the flash throughly,  it cleans the beginng flag only. How to erase the flash in Windows  authentically I do not know.


It is all, once it is in DFU-OTA, the server would send the Binary to the ESP8266, then ESP8266 would write the binary to the non-current working zone. If the CRC-checking has been passed, ESP8266 would reboot from the other zone whitch content the binary just downloaded.
相关文章
相关标签/搜索