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 Jul 05 16:06:55 2014 +0000
Revision:
16:de99e872fc9d
Parent:
15:49cc43dcbbf6
Child:
17:1d318666246c
Permit reference to "long file" name on server, but truncate binary file name on local file system to 6 chars, allowing 2 for version number => staying at final of 8.3.

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 16:de99e872fc9d 136 char nameroot[7];
WiredHome 9:73067ef14c30 137 uint16_t result = SWUP_OK; // starting out quite optimistic, for all the things that can go wrong
WiredHome 9:73067ef14c30 138 char buf[50]; // long enough for 3 comma separated numbers...
WiredHome 0:e221363f7942 139
WiredHome 0:e221363f7942 140 INFO("SoftwareUpdate(%s,%s)", url, name);
WiredHome 16:de99e872fc9d 141 strncpy(nameroot, name, 6);
WiredHome 16:de99e872fc9d 142 nameroot[6] = '\0';
WiredHome 16:de99e872fc9d 143 snprintf(verfn, SW_MAX_FQFN, "/local/%s.ver", nameroot);
WiredHome 0:e221363f7942 144
WiredHome 0:e221363f7942 145 /* Read installed version string */
WiredHome 0:e221363f7942 146 int inst_ver = -1;
WiredHome 0:e221363f7942 147 FILE *fv = fopen(verfn, "r");
WiredHome 0:e221363f7942 148 if (fv) {
WiredHome 0:e221363f7942 149 fscanf(fv, "%d", &inst_ver);
WiredHome 0:e221363f7942 150 fclose(fv);
WiredHome 0:e221363f7942 151 }
WiredHome 0:e221363f7942 152 INFO(" Installed version: %d", inst_ver);
WiredHome 0:e221363f7942 153
WiredHome 0:e221363f7942 154 /* Download latest version string */
WiredHome 0:e221363f7942 155 HTTPText server_ver("test message");
WiredHome 9:73067ef14c30 156 snprintf(fqurl, SW_MAX_URL, "%s/%s.txt", url, name);
WiredHome 0:e221363f7942 157 HTTPResult r = http.get(fqurl, buf, sizeof(buf));
WiredHome 0:e221363f7942 158 if (r == HTTP_OK) {
WiredHome 0:e221363f7942 159 int latest_ver = -1;
WiredHome 3:c69fff55fc60 160 int cksum = 0;
WiredHome 3:c69fff55fc60 161 int fsize = 0;
WiredHome 3:c69fff55fc60 162 int parseCount;
WiredHome 3:c69fff55fc60 163 parseCount = sscanf(buf, "%d,%d,%d", &latest_ver, &cksum, &fsize);
WiredHome 3:c69fff55fc60 164 if (parseCount == 3) {
WiredHome 9:73067ef14c30 165 INFO(" web version: %d", latest_ver);
WiredHome 9:73067ef14c30 166 INFO(" checksum: %d", cksum);
WiredHome 9:73067ef14c30 167 INFO(" file size: %d", fsize);
WiredHome 3:c69fff55fc60 168 if (inst_ver != latest_ver) {
WiredHome 3:c69fff55fc60 169 INFO(" Downloading firmware ver %d ...", latest_ver);
WiredHome 16:de99e872fc9d 170 sprintf(fwfn, "/local/%s%02d.BIN", nameroot, (latest_ver % 100));
WiredHome 3:c69fff55fc60 171 snprintf(fqurl, 150, "%s/%s.bin", url, name);
WiredHome 3:c69fff55fc60 172
WiredHome 3:c69fff55fc60 173 HTTPFile latest(fwfn);
WiredHome 3:c69fff55fc60 174 r = http.get(fqurl, &latest);
WiredHome 3:c69fff55fc60 175 if (r == HTTP_OK) {
WiredHome 3:c69fff55fc60 176 if (PassesIntegrityCheck(fwfn, cksum, fsize)) {
WiredHome 16:de99e872fc9d 177 if (!RemoveOtherBinFiles(nameroot, latest_ver)) {
WiredHome 9:73067ef14c30 178 ERR(" *** Failed to remove old version(s). ***");
WiredHome 9:73067ef14c30 179 result |= SWUP_OLD_STUCK;
WiredHome 3:c69fff55fc60 180 }
WiredHome 3:c69fff55fc60 181 INFO("Updating stored version number.");
WiredHome 3:c69fff55fc60 182 fv = fopen(verfn, "w");
WiredHome 3:c69fff55fc60 183 if (fv) {
WiredHome 3:c69fff55fc60 184 int fr = fputs(buf, fv);
WiredHome 3:c69fff55fc60 185 if (fr < 0) {
WiredHome 3:c69fff55fc60 186 ERR("Failed (%d) to update stored version number.", fr);
WiredHome 3:c69fff55fc60 187 fclose( fv );
WiredHome 9:73067ef14c30 188 result |= SWUP_VER_STUCK;
WiredHome 3:c69fff55fc60 189 } else {
WiredHome 3:c69fff55fc60 190 fclose( fv );
WiredHome 9:73067ef14c30 191 if (action == AUTO_REBOOT) {
WiredHome 3:c69fff55fc60 192 WARN("Resetting...\n");
WiredHome 3:c69fff55fc60 193 wait_ms(200);
WiredHome 3:c69fff55fc60 194 mbed_reset();
WiredHome 3:c69fff55fc60 195 }
WiredHome 3:c69fff55fc60 196 }
WiredHome 1:208de08b1a19 197 } else {
WiredHome 3:c69fff55fc60 198 WARN("Failed to update local version info in %s.", verfn);
WiredHome 9:73067ef14c30 199 result |= SWUP_VWRITE_FAILED;
WiredHome 1:208de08b1a19 200 }
WiredHome 0:e221363f7942 201 } else {
WiredHome 3:c69fff55fc60 202 WARN("New file {%s} did not pass integrity check.", fwfn);
WiredHome 9:73067ef14c30 203 result |= SWUP_INTEGRITY_FAILED;
WiredHome 0:e221363f7942 204 }
WiredHome 1:208de08b1a19 205 } else {
WiredHome 3:c69fff55fc60 206 WARN("Failed to download lastest firmware.");
WiredHome 9:73067ef14c30 207 result |= SWUP_BAD_URL;
WiredHome 0:e221363f7942 208 }
WiredHome 0:e221363f7942 209 } else {
WiredHome 9:73067ef14c30 210 INFO("Online version is same as installed version.");
WiredHome 9:73067ef14c30 211 result |= SWUP_SAME_VER;
WiredHome 0:e221363f7942 212 }
WiredHome 0:e221363f7942 213 }
WiredHome 0:e221363f7942 214 } else {
WiredHome 16:de99e872fc9d 215 WARN("Failed to download online firmware version number. r= %d", r);
WiredHome 9:73067ef14c30 216 result |= SWUP_HTTP_ERR;
WiredHome 0:e221363f7942 217 }
WiredHome 9:73067ef14c30 218 return (SWUpdate_T)result;
WiredHome 0:e221363f7942 219 }