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:
Mon May 07 19:56:00 2018 +0000
Revision:
26:f2bb6061dcb3
Parent:
25:af99bdcca2b4
Child:
27:3d3089b8212d
Child:
28:881661d475c1
Added a method to get the current build number (SW version number).

Who changed what in which revision?

UserRevisionLine numberNew contents of line
WiredHome 0:e221363f7942 1
WiredHome 17:1d318666246c 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 "HTTPFile.h"
WiredHome 0:e221363f7942 8 #include <stdio.h>
WiredHome 0:e221363f7942 9
WiredHome 0:e221363f7942 10 extern "C" void mbed_reset();
WiredHome 0:e221363f7942 11
WiredHome 15:49cc43dcbbf6 12 //#define DEBUG "SWup"
WiredHome 0:e221363f7942 13 #include <cstdio>
WiredHome 0:e221363f7942 14 #if (defined(DEBUG) && !defined(TARGET_LPC11U24))
WiredHome 0:e221363f7942 15 #define DBG(x, ...) std::printf("[DBG %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 0:e221363f7942 16 #define WARN(x, ...) std::printf("[WRN %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 0:e221363f7942 17 #define ERR(x, ...) std::printf("[ERR %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 0:e221363f7942 18 #define INFO(x, ...) std::printf("[INF %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 0:e221363f7942 19 #else
WiredHome 0:e221363f7942 20 #define DBG(x, ...)
WiredHome 0:e221363f7942 21 #define WARN(x, ...)
WiredHome 0:e221363f7942 22 #define ERR(x, ...)
WiredHome 0:e221363f7942 23 #define INFO(x, ...)
WiredHome 0:e221363f7942 24 #endif
WiredHome 0:e221363f7942 25
WiredHome 17:1d318666246c 26 static HTTPResult HTTPErrorCode;
WiredHome 17:1d318666246c 27
WiredHome 19:169aab9047bd 28 typedef enum {
WiredHome 19:169aab9047bd 29 ok,
WiredHome 19:169aab9047bd 30 no_file,
WiredHome 19:169aab9047bd 31 bad_crc
WiredHome 19:169aab9047bd 32 } Integrity_t;
WiredHome 19:169aab9047bd 33
WiredHome 24:e400edb8d2ee 34
WiredHome 24:e400edb8d2ee 35 const char * SWErrorMsg[] = {
WiredHome 24:e400edb8d2ee 36 "OK", // SWUP_OK = 0x00, ///< Software Update succeeded as planned.
WiredHome 24:e400edb8d2ee 37 "Same version", // SWUP_SAME_VER = 0x01, ///< Online version is the same as the installed version.
WiredHome 24:e400edb8d2ee 38 "Get bin error", // SWUP_HTTP_BIN = 0x02, ///< HTTP get returned an error while trying to fetch the bin file.
WiredHome 24:e400edb8d2ee 39 "Old file stuck", // SWUP_OLD_STUCK = 0x04, ///< Old file could not be removed.
WiredHome 24:e400edb8d2ee 40 "Old vers stuck", // SWUP_VER_STUCK = 0x08, ///< Old version number could not be updated.
WiredHome 24:e400edb8d2ee 41 "Ver write fail", // SWUP_VWRITE_FAILED = 0x10, ///< Can't open for write the version tracking file.
WiredHome 24:e400edb8d2ee 42 "Integrity fail", // SWUP_INTEGRITY_FAILED = 0x20, ///< Integrity check of downloaded file failed.
WiredHome 24:e400edb8d2ee 43 "Get ver fail", // SWUP_HTTP_VER = 0x40, ///< HTTP get returned an error while trying to fetch the version file.
WiredHome 24:e400edb8d2ee 44 "Filesys full", // SWUP_NO_SPACE = 0x80, ///< No space on file system for new version.
WiredHome 24:e400edb8d2ee 45 };
WiredHome 24:e400edb8d2ee 46
WiredHome 24:e400edb8d2ee 47 const char * SoftwareUpdateGetHTTPErrorMsg(HTTPResult r)
WiredHome 24:e400edb8d2ee 48 {
WiredHome 24:e400edb8d2ee 49 const char * p = "invalid result code";
WiredHome 25:af99bdcca2b4 50 if (r <= SWUP_NO_SPACE)
WiredHome 24:e400edb8d2ee 51 p = SWErrorMsg[r];
WiredHome 24:e400edb8d2ee 52 return p;
WiredHome 24:e400edb8d2ee 53 }
WiredHome 24:e400edb8d2ee 54
WiredHome 24:e400edb8d2ee 55 HTTPResult SoftwareUpdateGetHTTPErrorCode(void)
WiredHome 24:e400edb8d2ee 56 {
WiredHome 24:e400edb8d2ee 57 return HTTPErrorCode;
WiredHome 24:e400edb8d2ee 58 }
WiredHome 24:e400edb8d2ee 59
WiredHome 24:e400edb8d2ee 60
WiredHome 19:169aab9047bd 61 static Integrity_t PassesIntegrityCheck(const char * fname, int cksum, int fsize)
WiredHome 17:1d318666246c 62 {
WiredHome 19:169aab9047bd 63 Integrity_t res = bad_crc; // assume things go wrong...
WiredHome 3:c69fff55fc60 64 int newCksum = 0;
WiredHome 3:c69fff55fc60 65 int newFSize = 0;
WiredHome 3:c69fff55fc60 66 FILE *fh = fopen(fname, "rb");
WiredHome 17:1d318666246c 67
WiredHome 3:c69fff55fc60 68 INFO("IntegrityCheck(%s,%d,%d)", fname, cksum, fsize);
WiredHome 3:c69fff55fc60 69 if (fh) {
WiredHome 3:c69fff55fc60 70 char buf;
WiredHome 3:c69fff55fc60 71 while (fread(&buf, 1, 1, fh)) {
WiredHome 3:c69fff55fc60 72 newCksum = (newCksum + buf) & 0xFFFF;
WiredHome 3:c69fff55fc60 73 newFSize++;
WiredHome 3:c69fff55fc60 74 }
WiredHome 3:c69fff55fc60 75 fclose(fh);
WiredHome 3:c69fff55fc60 76 INFO(" Check(...,%d,%d)", newCksum, newFSize);
WiredHome 3:c69fff55fc60 77 if (newCksum == cksum && newFSize == fsize)
WiredHome 19:169aab9047bd 78 res = ok;
WiredHome 3:c69fff55fc60 79 } else {
WiredHome 3:c69fff55fc60 80 WARN("failed to open %s.", fname);
WiredHome 19:169aab9047bd 81 res = no_file;
WiredHome 3:c69fff55fc60 82 }
WiredHome 3:c69fff55fc60 83 return res;
WiredHome 1:208de08b1a19 84 }
WiredHome 1:208de08b1a19 85
WiredHome 9:73067ef14c30 86 /// mytolower exists because not all compiler libraries have this function
WiredHome 9:73067ef14c30 87 ///
WiredHome 9:73067ef14c30 88 /// This takes a character and if it is upper-case, it converts it to
WiredHome 9:73067ef14c30 89 /// lower-case and returns it.
WiredHome 9:73067ef14c30 90 ///
WiredHome 17:1d318666246c 91 /// @note this only works for characters in the range 'A' - 'Z'.
WiredHome 17:1d318666246c 92 ///
WiredHome 18:5f7667d63a27 93 /// a is the character to convert
WiredHome 18:5f7667d63a27 94 /// returns the lower case equivalent to the supplied character.
WiredHome 9:73067ef14c30 95 ///
WiredHome 17:1d318666246c 96 static char mytolower(char a)
WiredHome 17:1d318666246c 97 {
WiredHome 9:73067ef14c30 98 if (a >= 'A' && a <= 'Z')
WiredHome 9:73067ef14c30 99 return (a - 'A' + 'a');
WiredHome 9:73067ef14c30 100 else
WiredHome 9:73067ef14c30 101 return a;
WiredHome 9:73067ef14c30 102 }
WiredHome 9:73067ef14c30 103
WiredHome 9:73067ef14c30 104 /// mystrnicmp exists because not all compiler libraries have this function.
WiredHome 9:73067ef14c30 105 ///
WiredHome 9:73067ef14c30 106 /// Some have strnicmp, others _strnicmp, and others have C++ methods, which
WiredHome 9:73067ef14c30 107 /// is outside the scope of this C-portable set of functions.
WiredHome 9:73067ef14c30 108 ///
WiredHome 18:5f7667d63a27 109 /// l is a pointer to the string on the left
WiredHome 18:5f7667d63a27 110 /// r is a pointer to the string on the right
WiredHome 18:5f7667d63a27 111 /// n is the number of characters to compare
WiredHome 18:5f7667d63a27 112 /// returns -1 if l < r
WiredHome 18:5f7667d63a27 113 /// returns 0 if l == r
WiredHome 18:5f7667d63a27 114 /// returns +1 if l > r
WiredHome 9:73067ef14c30 115 ///
WiredHome 17:1d318666246c 116 static int mystrnicmp(const char *l, const char *r, size_t n)
WiredHome 17:1d318666246c 117 {
WiredHome 9:73067ef14c30 118 int result = 0;
WiredHome 9:73067ef14c30 119
WiredHome 9:73067ef14c30 120 if (n != 0) {
WiredHome 9:73067ef14c30 121 do {
WiredHome 9:73067ef14c30 122 result = mytolower(*l++) - mytolower(*r++);
WiredHome 9:73067ef14c30 123 } while ((result == 0) && (*l != '\0') && (--n > 0));
WiredHome 9:73067ef14c30 124 }
WiredHome 9:73067ef14c30 125 if (result < -1)
WiredHome 9:73067ef14c30 126 result = -1;
WiredHome 9:73067ef14c30 127 else if (result > 1)
WiredHome 9:73067ef14c30 128 result = 1;
WiredHome 9:73067ef14c30 129 return result;
WiredHome 9:73067ef14c30 130 }
WiredHome 9:73067ef14c30 131
WiredHome 9:73067ef14c30 132
WiredHome 17:1d318666246c 133 // Scan the local file system for any .bin files and
WiredHome 9:73067ef14c30 134 // if they don't match the current one, remove them.
WiredHome 9:73067ef14c30 135 //
WiredHome 9:73067ef14c30 136 static bool RemoveOtherBinFiles(const char * name, int ver)
WiredHome 9:73067ef14c30 137 {
WiredHome 9:73067ef14c30 138 char curbin[SW_MAX_FQFN];
WiredHome 9:73067ef14c30 139 DIR *d;
WiredHome 9:73067ef14c30 140 struct dirent *p;
WiredHome 9:73067ef14c30 141 bool noFailed = true;
WiredHome 17:1d318666246c 142
WiredHome 17:1d318666246c 143 snprintf(curbin, SW_MAX_FQFN, "%s%02d.bin", name, (ver % 100));
WiredHome 9:73067ef14c30 144 INFO("Remove bin files excluding {%s}", curbin);
WiredHome 9:73067ef14c30 145 d = opendir("/local/");
WiredHome 9:73067ef14c30 146 // Get a directory handle
WiredHome 9:73067ef14c30 147 if ( d != NULL ) {
WiredHome 9:73067ef14c30 148 // Walk the directory
WiredHome 9:73067ef14c30 149 while ( (p = readdir(d)) != NULL ) {
WiredHome 9:73067ef14c30 150 INFO(" check {%s}", p->d_name);
WiredHome 9:73067ef14c30 151 // if the file is .bin and not curbin
WiredHome 9:73067ef14c30 152 if (0 == mystrnicmp(p->d_name + strlen(p->d_name) - 4, ".bin", 4)
WiredHome 17:1d318666246c 153 && (0 != mystrnicmp(p->d_name, curbin, strlen(curbin)))) {
WiredHome 9:73067ef14c30 154 // remove the file
WiredHome 9:73067ef14c30 155 char toremove[SW_MAX_FQFN];
WiredHome 9:73067ef14c30 156 snprintf(toremove, SW_MAX_FQFN, "/local/%s", p->d_name);
WiredHome 9:73067ef14c30 157 INFO(" removing %s.", toremove);
WiredHome 9:73067ef14c30 158 if (remove(toremove)) {
WiredHome 9:73067ef14c30 159 // set flag if it could not be removed
WiredHome 9:73067ef14c30 160 noFailed = false;
WiredHome 9:73067ef14c30 161 }
WiredHome 9:73067ef14c30 162 }
WiredHome 9:73067ef14c30 163 }
WiredHome 9:73067ef14c30 164 closedir(d);
WiredHome 9:73067ef14c30 165 }
WiredHome 9:73067ef14c30 166 return noFailed;
WiredHome 9:73067ef14c30 167 }
WiredHome 9:73067ef14c30 168
WiredHome 26:f2bb6061dcb3 169
WiredHome 26:f2bb6061dcb3 170 int GetSoftwareVersionNumber(const char * name)
WiredHome 17:1d318666246c 171 {
WiredHome 26:f2bb6061dcb3 172 char nameroot[7];
WiredHome 9:73067ef14c30 173 char verfn[SW_MAX_FQFN]; // local version file
WiredHome 26:f2bb6061dcb3 174
WiredHome 16:de99e872fc9d 175 strncpy(nameroot, name, 6);
WiredHome 16:de99e872fc9d 176 nameroot[6] = '\0';
WiredHome 16:de99e872fc9d 177 snprintf(verfn, SW_MAX_FQFN, "/local/%s.ver", nameroot);
WiredHome 0:e221363f7942 178
WiredHome 0:e221363f7942 179 /* Read installed version string */
WiredHome 0:e221363f7942 180 int inst_ver = -1;
WiredHome 0:e221363f7942 181 FILE *fv = fopen(verfn, "r");
WiredHome 0:e221363f7942 182 if (fv) {
WiredHome 0:e221363f7942 183 fscanf(fv, "%d", &inst_ver);
WiredHome 0:e221363f7942 184 fclose(fv);
WiredHome 26:f2bb6061dcb3 185 } else {
WiredHome 26:f2bb6061dcb3 186 inst_ver = -1;
WiredHome 0:e221363f7942 187 }
WiredHome 0:e221363f7942 188 INFO(" Installed version: %d", inst_ver);
WiredHome 26:f2bb6061dcb3 189 return inst_ver;
WiredHome 26:f2bb6061dcb3 190 }
WiredHome 17:1d318666246c 191
WiredHome 26:f2bb6061dcb3 192 bool SetSoftwareVersionNumber(const char * name, int ver, int cksum, int filesize)
WiredHome 26:f2bb6061dcb3 193 {
WiredHome 26:f2bb6061dcb3 194 char nameroot[7];
WiredHome 26:f2bb6061dcb3 195 char verfn[SW_MAX_FQFN]; // local version file
WiredHome 26:f2bb6061dcb3 196 char buf[40];
WiredHome 26:f2bb6061dcb3 197
WiredHome 26:f2bb6061dcb3 198 strncpy(nameroot, name, 6);
WiredHome 26:f2bb6061dcb3 199 nameroot[6] = '\0';
WiredHome 26:f2bb6061dcb3 200 snprintf(verfn, SW_MAX_FQFN, "/local/%s.ver", nameroot);
WiredHome 26:f2bb6061dcb3 201 snprintf(buf, 40, "%d,%d,%d", ver, cksum, filesize);
WiredHome 26:f2bb6061dcb3 202 FILE *fv = fopen(verfn, "w");
WiredHome 26:f2bb6061dcb3 203 if (fv) {
WiredHome 26:f2bb6061dcb3 204 int fr = fputs(buf, fv);
WiredHome 26:f2bb6061dcb3 205 fclose( fv );
WiredHome 26:f2bb6061dcb3 206 if (fr >= 0) {
WiredHome 26:f2bb6061dcb3 207 return true;
WiredHome 26:f2bb6061dcb3 208 } else {
WiredHome 26:f2bb6061dcb3 209 ERR("Failed (%d) to update stored version number.", fr);
WiredHome 26:f2bb6061dcb3 210 }
WiredHome 26:f2bb6061dcb3 211 } else {
WiredHome 26:f2bb6061dcb3 212 WARN("Failed to update local version info in %s.", verfn);
WiredHome 26:f2bb6061dcb3 213 }
WiredHome 26:f2bb6061dcb3 214 return false;
WiredHome 26:f2bb6061dcb3 215 }
WiredHome 26:f2bb6061dcb3 216
WiredHome 26:f2bb6061dcb3 217 SWUpdate_T SoftwareUpdate(const char *url, const char * name, Reboot_T action)
WiredHome 26:f2bb6061dcb3 218 {
WiredHome 26:f2bb6061dcb3 219 HTTPClient http;
WiredHome 26:f2bb6061dcb3 220 //http.setTimeout( 15000 );
WiredHome 26:f2bb6061dcb3 221 char fqurl[SW_MAX_URL]; // fully qualified url
WiredHome 26:f2bb6061dcb3 222 char fwfn[SW_MAX_FQFN];
WiredHome 26:f2bb6061dcb3 223 char nameroot[7];
WiredHome 26:f2bb6061dcb3 224 uint16_t result = SWUP_OK; // starting out quite optimistic, for all the things that can go wrong
WiredHome 26:f2bb6061dcb3 225 char buf[50]; // long enough for 3 comma separated numbers...
WiredHome 26:f2bb6061dcb3 226
WiredHome 26:f2bb6061dcb3 227 INFO("SoftwareUpdate(%s , %s)", url, name);
WiredHome 26:f2bb6061dcb3 228 strncpy(nameroot, name, 6);
WiredHome 26:f2bb6061dcb3 229 nameroot[6] = '\0';
WiredHome 26:f2bb6061dcb3 230 int inst_ver = GetSoftwareVersionNumber(name);
WiredHome 0:e221363f7942 231 /* Download latest version string */
WiredHome 21:253e7da56ff9 232 //HTTPText server_ver("test message");
WiredHome 9:73067ef14c30 233 snprintf(fqurl, SW_MAX_URL, "%s/%s.txt", url, name);
WiredHome 23:cfe84db2b2cb 234 INFO("Query %s", fqurl);
WiredHome 17:1d318666246c 235 HTTPErrorCode = http.get(fqurl, buf, sizeof(buf));
WiredHome 17:1d318666246c 236 if (HTTPErrorCode == HTTP_OK) {
WiredHome 0:e221363f7942 237 int latest_ver = -1;
WiredHome 3:c69fff55fc60 238 int cksum = 0;
WiredHome 3:c69fff55fc60 239 int fsize = 0;
WiredHome 3:c69fff55fc60 240 int parseCount;
WiredHome 3:c69fff55fc60 241 parseCount = sscanf(buf, "%d,%d,%d", &latest_ver, &cksum, &fsize);
WiredHome 3:c69fff55fc60 242 if (parseCount == 3) {
WiredHome 9:73067ef14c30 243 INFO(" web version: %d", latest_ver);
WiredHome 9:73067ef14c30 244 INFO(" checksum: %d", cksum);
WiredHome 9:73067ef14c30 245 INFO(" file size: %d", fsize);
WiredHome 3:c69fff55fc60 246 if (inst_ver != latest_ver) {
WiredHome 3:c69fff55fc60 247 INFO(" Downloading firmware ver %d ...", latest_ver);
WiredHome 22:2a010efe00da 248 snprintf(fwfn, SW_MAX_FQFN, "/local/%s%02d.BIN", nameroot, (latest_ver % 100));
WiredHome 22:2a010efe00da 249 snprintf(fqurl, SW_MAX_URL, "%s/%s.bin", url, name);
WiredHome 17:1d318666246c 250
WiredHome 3:c69fff55fc60 251 HTTPFile latest(fwfn);
WiredHome 23:cfe84db2b2cb 252 INFO("Fetch %s", fqurl);
WiredHome 17:1d318666246c 253 HTTPErrorCode = http.get(fqurl, &latest);
WiredHome 17:1d318666246c 254 if (HTTPErrorCode == HTTP_OK) {
WiredHome 19:169aab9047bd 255 Integrity_t t = PassesIntegrityCheck(fwfn, cksum, fsize);
WiredHome 19:169aab9047bd 256 if (t == no_file) {
WiredHome 19:169aab9047bd 257 ERR(" *** No space on file system. ***");
WiredHome 19:169aab9047bd 258 result |= SWUP_NO_SPACE;
WiredHome 19:169aab9047bd 259 } else if (t == ok) {
WiredHome 16:de99e872fc9d 260 if (!RemoveOtherBinFiles(nameroot, latest_ver)) {
WiredHome 9:73067ef14c30 261 ERR(" *** Failed to remove old version(s). ***");
WiredHome 9:73067ef14c30 262 result |= SWUP_OLD_STUCK;
WiredHome 3:c69fff55fc60 263 }
WiredHome 3:c69fff55fc60 264 INFO("Updating stored version number.");
WiredHome 26:f2bb6061dcb3 265 if (SetSoftwareVersionNumber(name, latest_ver, cksum, fsize)) {
WiredHome 26:f2bb6061dcb3 266 // ok
WiredHome 26:f2bb6061dcb3 267 if (action == AUTO_REBOOT) {
WiredHome 26:f2bb6061dcb3 268 WARN("Resetting...\n");
WiredHome 26:f2bb6061dcb3 269 wait_ms(200);
WiredHome 26:f2bb6061dcb3 270 mbed_reset();
WiredHome 3:c69fff55fc60 271 }
WiredHome 1:208de08b1a19 272 } else {
WiredHome 26:f2bb6061dcb3 273 // failed
WiredHome 26:f2bb6061dcb3 274 ERR("Failed to update stored version number.");
WiredHome 9:73067ef14c30 275 result |= SWUP_VWRITE_FAILED;
WiredHome 1:208de08b1a19 276 }
WiredHome 19:169aab9047bd 277 } else /* t == bad_crc */ {
WiredHome 3:c69fff55fc60 278 WARN("New file {%s} did not pass integrity check.", fwfn);
WiredHome 9:73067ef14c30 279 result |= SWUP_INTEGRITY_FAILED;
WiredHome 0:e221363f7942 280 }
WiredHome 1:208de08b1a19 281 } else {
WiredHome 3:c69fff55fc60 282 WARN("Failed to download lastest firmware.");
WiredHome 17:1d318666246c 283 result |= SWUP_HTTP_BIN;
WiredHome 0:e221363f7942 284 }
WiredHome 0:e221363f7942 285 } else {
WiredHome 9:73067ef14c30 286 INFO("Online version is same as installed version.");
WiredHome 9:73067ef14c30 287 result |= SWUP_SAME_VER;
WiredHome 0:e221363f7942 288 }
WiredHome 0:e221363f7942 289 }
WiredHome 0:e221363f7942 290 } else {
WiredHome 22:2a010efe00da 291 WARN("Failed accessing {%s}. Extended Error Code = %d", fqurl, HTTPErrorCode);
WiredHome 17:1d318666246c 292 result |= SWUP_HTTP_VER;
WiredHome 0:e221363f7942 293 }
WiredHome 9:73067ef14c30 294 return (SWUpdate_T)result;
WiredHome 0:e221363f7942 295 }