FOTA remotely updating the firmware

From openPicus Wiki
Jump to: navigation, search

Contents

What you are going to learn

This tutorial will walk you through the steps required to remotely update the firmware on the Flyport G and Flyport Ethernet decives.
The FOTA feature requires an ftp server, to this end you'll find some details and links to setup your own ftp server.
The code will establish an update loop between two different firmwares: each one downloading the other

What you need

A Flyport Ethernet or WiFi G. This feature makes use of the FTP Library, further reading available here: FTP library reference.

How to use it

Setup an FTP Server on your PC

First of all install Filezilla Server on your PC. The operation is explained at the related filezilla-project wiki page.

For further information on network settings you can follow this article. Please note that this guide is about allowing access from outside of your LAN. We suggest to use the Flyport connected to the same LAN of your FTP Server before fiddling with Firewalls, NAT, Service Providers, etc.


After you correctly setup your PC with Filezilla Server, some more operations are required:

  • create a user (we will use username "flyport" and password "flypass")
  • create a folder "FileZillaFolder" inside C: disk
  • grant to this user permission to access the "C:\FileZillaFolder" folder

This is where we will put the firmwares we will generate.

Firmware Development on IDE

We will write a single firmware which will be able to appear as two different firmwares. This will allow us to implement an auto-update loop, thus showing the auto-update feature in real time.

Let's start from the basics of the FOTA library: how it works.


How the FOTA library works

Fotaflow.png

As can be seen from the above diagram, there are a few steps

  1. Configuration
  2. Initialization
  3. Starting
  4. Verification

Each one will be analyzed in detail.


Configuration

First of all we need to tell the library where the new firmware is. This can be done using a series of arrays to be correctly filled; there must also be a working ftp server to which both you and the flyport must have access (see section Setup an FTP Server for help setting up an FTP server).

Here is an example configuration

char servName[1][20] = {"ftp.ftpserver.com"};
char userName[1][20] = {"user"};
char password[1][20] = {"pass"};
char fileName[1][10] = {"FW02.bin"};
char  md5Name[1][10] = {"FW02.md5"};

The values reported above are just an example, take care to fill these arrays correctly.

As is easily noticed, the example above specifies just a single source. The library allows for multiple sources to be declared: having multiple sources allows for better redundancy by being able to upgrade even when a certain source is offline. You can specify multiple source just as easily

char servName[3][20] = {"ftp.ftpserver.com","ftp.myftp.com","ftp.isp.com"};
char userName[3][20] = {"user","user","user"};
char password[3][20] = {"pass","pass","pass"};
char fileName[3][10] = {"FW02.bin","FW02.bin","FW02.bin"};
char  md5Name[3][10] = {"FW02.md5","FW02.md5","FW02.md5"};


Initialization

Initialization is really easy: all you need to do is issue a single instruction

FWUpdateInit();


Starting the update process

Here is where things get interesting. There is a single call to start the download process

char buff[50];
int result = FWUpdateFTP(servName[id - 1], "21", userName[id - 1], password[id - 1], fileName[id - 1], md5Name[id - 1]);
sprintf(buff, "Event:%d\nsubevent:%d\n\n", result, FWUpdateEvent());
UARTWrite(1, buff);

The instruction will fetch the parameters from the mentioned arrays ('id' is the index of the parameters, essentially which server is used), initiate and complete -if possible- the download process. The result variable will carry an error value regarding the operation, while a more specific sub-event value is accessed with the FWUpdateEvent() function.

The md5 is a security measure: it's an alphanumeric string that represents a unique hash of the file, that is each file will have a unique string. Providing an md5 together with the bin will give a security layer, a way to check that the downloaded bin is really the intended one. BINs are binary files representing an exact byte-replica of the memory, while HEX files have some overhead. A method to convert from hex to bin will be showed further on in the tutorial.

Checking and flashing

The next steps are

  • checking if the firmware is valid, if so enable the update - which in this case means checking it doesn't overwrite the bootloader, other deeper checking methods are possible at the user discretion
  • restarting the micro


if (result == 0)
{
    UARTWrite(1, "OK - Firmware downloaded!\n");
    if (FWNewEnable())
    {
	UARTWrite(1, "OK - Firmware valid!\n");
	vTaskDelay(100);
	Reset();
    }
    else
        UARTWrite(1, "ERROR - Firmware NOT valid!\n");
}
else
{
    UARTWrite(1, "ERROR in download or MD5\n");
}

The lines of code above are pretty self-explanatory, the only important thing is calling FWNewEnable() and checking its return value before restarting. This way it is possible that, in case of a bad firmware, at least the bootloader is preserved.


Dual firmware loop

Now that the basics about the library are well explained, we can move on to show how to achieve the upgrade loop. We will use a define to switch the behaviour of the firmwares.

//taskFlyport.c
#include "taskFlyport.h"
#include "FWUpdate.h"
 
#define _FW01_ // or _FW02_
 
char servName[2][20] = {"ftp.ftpserver.com","ftp.ftpserver.com"};
char userName[2][20] = {"user","user"};
char password[2][20] = {"pass","pass"};
char fileName[2][10] = {"FW02.bin","FW01.bin"};
char  md5Name[2][10] = {"FW02.md5","FW01.md5"};
 
void FlyportTask()
{
	vTaskDelay(30);
	#ifdef _FW01_
	UARTWrite(1, "\n----- FW UPGRADE #1 -----\n");
	#elif defined _FW02_
	UARTWrite(1, "\n----- FW UPGRADE #2 -----\n");
	#endif
 
	#if defined(FLYPORT_WF)
	WFConnect(WF_DEFAULT);
	while (WFGetStat() != CONNECTED);
	while (!DHCPAssigned);
	#endif
 
	#if defined(FLYPORT_ETH)
	while (!MACLinked);
	#endif
 
	FWUpdateInit();
 
	while (1)
	{
		int result;
		char buff[50];
 
		#ifdef _FW01_
		#define ID 1
		#endif
 
		#ifdef _FW02_
		#define ID 2
		#endif
 
		result = FWUpdateFTP(servName[ID - 1], "21", userName[ID - 1], password[ID - 1], fileName[ID - 1], md5Name[ID - 1]);
 
		UARTWrite(1, "\n\n---------------");
		UARTWrite(1, "\nUPDATE finished\n");
		sprintf(buff, "Event:%d\nsubevent:%d\n\n", result, FWUpdateEvent());
		UARTWrite(1, buff);
 
		if (result == 0)
		{
			UARTWrite(1, "OK - Firmware downloaded!\n");
			if (FWNewEnable())
			{
				UARTWrite(1, "OK - Firmware valid!\n");
				vTaskDelay(100);
				Reset();
			}
			else
				UARTWrite(1, "ERROR - Firmware NOT valid!\n");
		}
		else
		{
			UARTWrite(1, "ERROR in download or MD5\n");
		}			
 
	}
}

The define will switch from fetching FW02.bin to fetching FW01.bin. So FW01 needs to fetch FW02.bin, while FW02 needs to fetch FW01.bin.

So we compile one time with #define _FW01_ and save this firmware as FW01.hex.
Then we compile a second time with #define _FW02_ and save this firmware as FW02.hex.


BIN and MD5 generation

As seen above we need two other steps:

  • convert the firmware HEX to BIN
  • generate an MD5 for the BIN

The instrument for both is our little tool Hex2bin. The zip contains the entire source (Microsoft VS) but you're only interested in the real executable available here

tools_hex2bin\Hex to Bin\bin\Release\Hex to Bin.exe

Once open it's really easy: if you need the MD5 just check the mark, then hit "New Conversion" and select the HEX you want to convert and you're done. Easy peasy!

Error Codes

Here is a list of the possible error codes and their meaning. Errors are accessed by checking the return value of the functions.

0 Update succeeded
1 No connection, the Flyport is not connected
2 Error connecting the remote server
3 Error retrieving the firmware file
4 Error in the format of the MD5 file
5 Error retrieving the MD5 file
6 Firmware hash doesn't match MD5
7 Transfer interrupted
8 Error in the configuration of the server parameters

Code Download

Remember to put the source code in the External Library folder, and the header in the relative Include folder. Source Code

Personal tools
Namespaces

Variants
Actions
START HERE
DEVELOPMENT
HARDWARE INFO
RESOURCES
PHASED OUT
Toolbox