Software Update via Ethernet - the mbed application can pull down an updated application binary from a web server and activate that binary. This library works only with the LPC1768, as it relies on the magic-chip boot-loader mechanism.

Dependents:   WattEye X10Svr PUB_SWUpdate

Success!! With this library, a network connection, and a web server hosting a new binary image, you can update the mbed firmware over the air (FOTA) - well, at least via Ethernet so far.

As of March 2015, it has been tested with the following mbed official libraries:

And a custom derivation:

  • HTTPClient v33, v32, which includes a custom HTTPFile.

Part of the update process involves checking the integrity of the downloaded binary file, for both a checksum and the program (file) size. To create this additional information, a small perl script is used (the important part is only 20 lines of code). See the documentation in the header file.

After the new binary is successfully downloaded, the checksum and the size are evaluated and if correct, then the old binary file is removed (this is the only way to cause the new binary to activate).

The mbed can then be automatically reset to activate the new image, or this may be deferred in case there is some other process necessary for an orderly restart.

Details are in the SWUpdate header file, and PUB_SWUpdate is a publicly accessible demonstration program for this library.

Committer:
WiredHome
Date:
Sat Jun 28 19:46:46 2014 +0000
Revision:
15:49cc43dcbbf6
Parent:
14:0e012d53c6df
Child:
16:de99e872fc9d
Turned off debug, and added a disclaimer about which mbed firmware it is known to work with (in case it doesn't work with them all).

Who changed what in which revision?

UserRevisionLine numberNew contents of line
WiredHome 0:e221363f7942 1
WiredHome 0:e221363f7942 2 // Software Update via Ethernet from forum -
WiredHome 0:e221363f7942 3 // http://mbed.org/forum/mbed/topic/1183/
WiredHome 1:208de08b1a19 4 //
WiredHome 0:e221363f7942 5 #include "mbed.h"
WiredHome 0:e221363f7942 6 #include "SWUpdate.h"
WiredHome 0:e221363f7942 7 #include "HTTPClient.h"
WiredHome 0:e221363f7942 8 #include "HTTPText.h"
WiredHome 0:e221363f7942 9 #include "HTTPFile.h"
WiredHome 0:e221363f7942 10 #include <stdio.h>
WiredHome 0:e221363f7942 11
WiredHome 0:e221363f7942 12 extern "C" void mbed_reset();
WiredHome 0:e221363f7942 13
WiredHome 15:49cc43dcbbf6 14 //#define DEBUG "SWup"
WiredHome 0:e221363f7942 15 #include <cstdio>
WiredHome 0:e221363f7942 16 #if (defined(DEBUG) && !defined(TARGET_LPC11U24))
WiredHome 0:e221363f7942 17 #define DBG(x, ...) std::printf("[DBG %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 0:e221363f7942 18 #define WARN(x, ...) std::printf("[WRN %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 0:e221363f7942 19 #define ERR(x, ...) std::printf("[ERR %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 0:e221363f7942 20 #define INFO(x, ...) std::printf("[INF %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 0:e221363f7942 21 #else
WiredHome 0:e221363f7942 22 #define DBG(x, ...)
WiredHome 0:e221363f7942 23 #define WARN(x, ...)
WiredHome 0:e221363f7942 24 #define ERR(x, ...)
WiredHome 0:e221363f7942 25 #define INFO(x, ...)
WiredHome 0:e221363f7942 26 #endif
WiredHome 0:e221363f7942 27
WiredHome 3:c69fff55fc60 28 static bool PassesIntegrityCheck(const char * fname, int cksum, int fsize) {
WiredHome 3:c69fff55fc60 29 int res = false; // assume things go wrong...
WiredHome 3:c69fff55fc60 30 int newCksum = 0;
WiredHome 3:c69fff55fc60 31 int newFSize = 0;
WiredHome 3:c69fff55fc60 32 FILE *fh = fopen(fname, "rb");
WiredHome 9:73067ef14c30 33
WiredHome 3:c69fff55fc60 34 INFO("IntegrityCheck(%s,%d,%d)", fname, cksum, fsize);
WiredHome 3:c69fff55fc60 35 if (fh) {
WiredHome 3:c69fff55fc60 36 char buf;
WiredHome 3:c69fff55fc60 37 while (fread(&buf, 1, 1, fh)) {
WiredHome 3:c69fff55fc60 38 newCksum = (newCksum + buf) & 0xFFFF;
WiredHome 3:c69fff55fc60 39 newFSize++;
WiredHome 3:c69fff55fc60 40 }
WiredHome 3:c69fff55fc60 41 fclose(fh);
WiredHome 3:c69fff55fc60 42 INFO(" Check(...,%d,%d)", newCksum, newFSize);
WiredHome 3:c69fff55fc60 43 if (newCksum == cksum && newFSize == fsize)
WiredHome 3:c69fff55fc60 44 res = true;
WiredHome 3:c69fff55fc60 45 } else {
WiredHome 3:c69fff55fc60 46 WARN("failed to open %s.", fname);
WiredHome 3:c69fff55fc60 47 }
WiredHome 3:c69fff55fc60 48 return res;
WiredHome 1:208de08b1a19 49 }
WiredHome 1:208de08b1a19 50
WiredHome 9:73067ef14c30 51 /// mytolower exists because not all compiler libraries have this function
WiredHome 9:73067ef14c30 52 ///
WiredHome 9:73067ef14c30 53 /// This takes a character and if it is upper-case, it converts it to
WiredHome 9:73067ef14c30 54 /// lower-case and returns it.
WiredHome 9:73067ef14c30 55 ///
WiredHome 9:73067ef14c30 56 /// @param a is the character to convert
WiredHome 9:73067ef14c30 57 /// @returns the lower case equivalent to a
WiredHome 9:73067ef14c30 58 ///
WiredHome 9:73067ef14c30 59 static char mytolower(char a) {
WiredHome 9:73067ef14c30 60 if (a >= 'A' && a <= 'Z')
WiredHome 9:73067ef14c30 61 return (a - 'A' + 'a');
WiredHome 9:73067ef14c30 62 else
WiredHome 9:73067ef14c30 63 return a;
WiredHome 9:73067ef14c30 64 }
WiredHome 9:73067ef14c30 65
WiredHome 9:73067ef14c30 66 /// mystrnicmp exists because not all compiler libraries have this function.
WiredHome 9:73067ef14c30 67 ///
WiredHome 9:73067ef14c30 68 /// Some have strnicmp, others _strnicmp, and others have C++ methods, which
WiredHome 9:73067ef14c30 69 /// is outside the scope of this C-portable set of functions.
WiredHome 9:73067ef14c30 70 ///
WiredHome 9:73067ef14c30 71 /// @param l is a pointer to the string on the left
WiredHome 9:73067ef14c30 72 /// @param r is a pointer to the string on the right
WiredHome 9:73067ef14c30 73 /// @param n is the number of characters to compare
WiredHome 9:73067ef14c30 74 /// @returns -1 if l < r
WiredHome 9:73067ef14c30 75 /// @returns 0 if l == r
WiredHome 9:73067ef14c30 76 /// @returns +1 if l > r
WiredHome 9:73067ef14c30 77 ///
WiredHome 9:73067ef14c30 78 static int mystrnicmp(const char *l, const char *r, size_t n) {
WiredHome 9:73067ef14c30 79 int result = 0;
WiredHome 9:73067ef14c30 80
WiredHome 9:73067ef14c30 81 if (n != 0) {
WiredHome 9:73067ef14c30 82 do {
WiredHome 9:73067ef14c30 83 result = mytolower(*l++) - mytolower(*r++);
WiredHome 9:73067ef14c30 84 } while ((result == 0) && (*l != '\0') && (--n > 0));
WiredHome 9:73067ef14c30 85 }
WiredHome 9:73067ef14c30 86 if (result < -1)
WiredHome 9:73067ef14c30 87 result = -1;
WiredHome 9:73067ef14c30 88 else if (result > 1)
WiredHome 9:73067ef14c30 89 result = 1;
WiredHome 9:73067ef14c30 90 return result;
WiredHome 9:73067ef14c30 91 }
WiredHome 9:73067ef14c30 92
WiredHome 9:73067ef14c30 93
WiredHome 9:73067ef14c30 94 // Scan the local file system for any .bin files and
WiredHome 9:73067ef14c30 95 // if they don't match the current one, remove them.
WiredHome 9:73067ef14c30 96 //
WiredHome 9:73067ef14c30 97 static bool RemoveOtherBinFiles(const char * name, int ver)
WiredHome 9:73067ef14c30 98 {
WiredHome 9:73067ef14c30 99 char curbin[SW_MAX_FQFN];
WiredHome 9:73067ef14c30 100 DIR *d;
WiredHome 9:73067ef14c30 101 struct dirent *p;
WiredHome 9:73067ef14c30 102 bool noFailed = true;
WiredHome 9:73067ef14c30 103
WiredHome 14:0e012d53c6df 104 snprintf(curbin, SW_MAX_FQFN, "%s%02d.bin", name, ver);
WiredHome 9:73067ef14c30 105 INFO("Remove bin files excluding {%s}", curbin);
WiredHome 9:73067ef14c30 106 d = opendir("/local/");
WiredHome 9:73067ef14c30 107 // Get a directory handle
WiredHome 9:73067ef14c30 108 if ( d != NULL ) {
WiredHome 9:73067ef14c30 109 // Walk the directory
WiredHome 9:73067ef14c30 110 while ( (p = readdir(d)) != NULL ) {
WiredHome 9:73067ef14c30 111 INFO(" check {%s}", p->d_name);
WiredHome 9:73067ef14c30 112 // if the file is .bin and not curbin
WiredHome 9:73067ef14c30 113 if (0 == mystrnicmp(p->d_name + strlen(p->d_name) - 4, ".bin", 4)
WiredHome 9:73067ef14c30 114 && (0 != mystrnicmp(p->d_name, curbin, strlen(curbin)))) {
WiredHome 9:73067ef14c30 115 // remove the file
WiredHome 9:73067ef14c30 116 char toremove[SW_MAX_FQFN];
WiredHome 9:73067ef14c30 117 snprintf(toremove, SW_MAX_FQFN, "/local/%s", p->d_name);
WiredHome 9:73067ef14c30 118 INFO(" removing %s.", toremove);
WiredHome 9:73067ef14c30 119 if (remove(toremove)) {
WiredHome 9:73067ef14c30 120 // set flag if it could not be removed
WiredHome 9:73067ef14c30 121 noFailed = false;
WiredHome 9:73067ef14c30 122 }
WiredHome 9:73067ef14c30 123 }
WiredHome 9:73067ef14c30 124 }
WiredHome 9:73067ef14c30 125 closedir(d);
WiredHome 9:73067ef14c30 126 }
WiredHome 9:73067ef14c30 127 return noFailed;
WiredHome 9:73067ef14c30 128 }
WiredHome 9:73067ef14c30 129
WiredHome 9:73067ef14c30 130 SWUpdate_T SoftwareUpdate(const char *url, const char * name, Reboot_T action) {
WiredHome 0:e221363f7942 131 HTTPClient http;
WiredHome 0:e221363f7942 132 //http.setTimeout( 15000 );
WiredHome 9:73067ef14c30 133 char fqurl[SW_MAX_URL]; // fully qualified url
WiredHome 9:73067ef14c30 134 char verfn[SW_MAX_FQFN]; // local version file
WiredHome 9:73067ef14c30 135 char fwfn[SW_MAX_FQFN];
WiredHome 9:73067ef14c30 136 uint16_t result = SWUP_OK; // starting out quite optimistic, for all the things that can go wrong
WiredHome 9:73067ef14c30 137 char buf[50]; // long enough for 3 comma separated numbers...
WiredHome 0:e221363f7942 138
WiredHome 0:e221363f7942 139 INFO("SoftwareUpdate(%s,%s)", url, name);
WiredHome 9:73067ef14c30 140 snprintf(verfn, SW_MAX_FQFN, "/local/%s.ver", name);
WiredHome 0:e221363f7942 141
WiredHome 0:e221363f7942 142 /* Read installed version string */
WiredHome 0:e221363f7942 143 int inst_ver = -1;
WiredHome 0:e221363f7942 144 FILE *fv = fopen(verfn, "r");
WiredHome 0:e221363f7942 145 if (fv) {
WiredHome 0:e221363f7942 146 fscanf(fv, "%d", &inst_ver);
WiredHome 0:e221363f7942 147 fclose(fv);
WiredHome 0:e221363f7942 148 }
WiredHome 0:e221363f7942 149 INFO(" Installed version: %d", inst_ver);
WiredHome 0:e221363f7942 150
WiredHome 0:e221363f7942 151 /* Download latest version string */
WiredHome 0:e221363f7942 152 HTTPText server_ver("test message");
WiredHome 9:73067ef14c30 153 snprintf(fqurl, SW_MAX_URL, "%s/%s.txt", url, name);
WiredHome 0:e221363f7942 154 HTTPResult r = http.get(fqurl, buf, sizeof(buf));
WiredHome 0:e221363f7942 155 if (r == HTTP_OK) {
WiredHome 0:e221363f7942 156 int latest_ver = -1;
WiredHome 3:c69fff55fc60 157 int cksum = 0;
WiredHome 3:c69fff55fc60 158 int fsize = 0;
WiredHome 3:c69fff55fc60 159 int parseCount;
WiredHome 3:c69fff55fc60 160 parseCount = sscanf(buf, "%d,%d,%d", &latest_ver, &cksum, &fsize);
WiredHome 3:c69fff55fc60 161 if (parseCount == 3) {
WiredHome 9:73067ef14c30 162 INFO(" web version: %d", latest_ver);
WiredHome 9:73067ef14c30 163 INFO(" checksum: %d", cksum);
WiredHome 9:73067ef14c30 164 INFO(" file size: %d", fsize);
WiredHome 3:c69fff55fc60 165 if (inst_ver != latest_ver) {
WiredHome 3:c69fff55fc60 166 INFO(" Downloading firmware ver %d ...", latest_ver);
WiredHome 14:0e012d53c6df 167 sprintf(fwfn, "/local/%s%02d.BIN", name, latest_ver);
WiredHome 3:c69fff55fc60 168 snprintf(fqurl, 150, "%s/%s.bin", url, name);
WiredHome 3:c69fff55fc60 169
WiredHome 3:c69fff55fc60 170 HTTPFile latest(fwfn);
WiredHome 3:c69fff55fc60 171 r = http.get(fqurl, &latest);
WiredHome 3:c69fff55fc60 172 if (r == HTTP_OK) {
WiredHome 3:c69fff55fc60 173 if (PassesIntegrityCheck(fwfn, cksum, fsize)) {
WiredHome 9:73067ef14c30 174 if (!RemoveOtherBinFiles(name, latest_ver)) {
WiredHome 9:73067ef14c30 175 ERR(" *** Failed to remove old version(s). ***");
WiredHome 9:73067ef14c30 176 result |= SWUP_OLD_STUCK;
WiredHome 3:c69fff55fc60 177 }
WiredHome 3:c69fff55fc60 178 INFO("Updating stored version number.");
WiredHome 3:c69fff55fc60 179 fv = fopen(verfn, "w");
WiredHome 3:c69fff55fc60 180 if (fv) {
WiredHome 3:c69fff55fc60 181 int fr = fputs(buf, fv);
WiredHome 3:c69fff55fc60 182 if (fr < 0) {
WiredHome 3:c69fff55fc60 183 ERR("Failed (%d) to update stored version number.", fr);
WiredHome 3:c69fff55fc60 184 fclose( fv );
WiredHome 9:73067ef14c30 185 result |= SWUP_VER_STUCK;
WiredHome 3:c69fff55fc60 186 } else {
WiredHome 3:c69fff55fc60 187 fclose( fv );
WiredHome 9:73067ef14c30 188 if (action == AUTO_REBOOT) {
WiredHome 3:c69fff55fc60 189 WARN("Resetting...\n");
WiredHome 3:c69fff55fc60 190 wait_ms(200);
WiredHome 3:c69fff55fc60 191 mbed_reset();
WiredHome 3:c69fff55fc60 192 }
WiredHome 3:c69fff55fc60 193 }
WiredHome 1:208de08b1a19 194 } else {
WiredHome 3:c69fff55fc60 195 WARN("Failed to update local version info in %s.", verfn);
WiredHome 9:73067ef14c30 196 result |= SWUP_VWRITE_FAILED;
WiredHome 1:208de08b1a19 197 }
WiredHome 0:e221363f7942 198 } else {
WiredHome 3:c69fff55fc60 199 WARN("New file {%s} did not pass integrity check.", fwfn);
WiredHome 9:73067ef14c30 200 result |= SWUP_INTEGRITY_FAILED;
WiredHome 0:e221363f7942 201 }
WiredHome 1:208de08b1a19 202 } else {
WiredHome 3:c69fff55fc60 203 WARN("Failed to download lastest firmware.");
WiredHome 9:73067ef14c30 204 result |= SWUP_BAD_URL;
WiredHome 0:e221363f7942 205 }
WiredHome 0:e221363f7942 206 } else {
WiredHome 9:73067ef14c30 207 INFO("Online version is same as installed version.");
WiredHome 9:73067ef14c30 208 result |= SWUP_SAME_VER;
WiredHome 0:e221363f7942 209 }
WiredHome 0:e221363f7942 210 }
WiredHome 0:e221363f7942 211 } else {
WiredHome 1:208de08b1a19 212 WARN("Failed to download online firmware version number.");
WiredHome 9:73067ef14c30 213 result |= SWUP_HTTP_ERR;
WiredHome 0:e221363f7942 214 }
WiredHome 9:73067ef14c30 215 return (SWUpdate_T)result;
WiredHome 0:e221363f7942 216 }