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:
Tue Nov 21 16:58:49 2017 +0000
Revision:
25:af99bdcca2b4
Parent:
24:e400edb8d2ee
Child:
26:f2bb6061dcb3
Minor code cleanup

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 17:1d318666246c 169 SWUpdate_T SoftwareUpdate(const char *url, const char * name, Reboot_T action)
WiredHome 17:1d318666246c 170 {
WiredHome 0:e221363f7942 171 HTTPClient http;
WiredHome 17:1d318666246c 172 //http.setTimeout( 15000 );
WiredHome 9:73067ef14c30 173 char fqurl[SW_MAX_URL]; // fully qualified url
WiredHome 9:73067ef14c30 174 char verfn[SW_MAX_FQFN]; // local version file
WiredHome 9:73067ef14c30 175 char fwfn[SW_MAX_FQFN];
WiredHome 16:de99e872fc9d 176 char nameroot[7];
WiredHome 9:73067ef14c30 177 uint16_t result = SWUP_OK; // starting out quite optimistic, for all the things that can go wrong
WiredHome 9:73067ef14c30 178 char buf[50]; // long enough for 3 comma separated numbers...
WiredHome 17:1d318666246c 179
WiredHome 22:2a010efe00da 180 INFO("SoftwareUpdate(%s , %s)", url, name);
WiredHome 16:de99e872fc9d 181 strncpy(nameroot, name, 6);
WiredHome 16:de99e872fc9d 182 nameroot[6] = '\0';
WiredHome 16:de99e872fc9d 183 snprintf(verfn, SW_MAX_FQFN, "/local/%s.ver", nameroot);
WiredHome 0:e221363f7942 184
WiredHome 0:e221363f7942 185 /* Read installed version string */
WiredHome 0:e221363f7942 186 int inst_ver = -1;
WiredHome 0:e221363f7942 187 FILE *fv = fopen(verfn, "r");
WiredHome 0:e221363f7942 188 if (fv) {
WiredHome 0:e221363f7942 189 fscanf(fv, "%d", &inst_ver);
WiredHome 0:e221363f7942 190 fclose(fv);
WiredHome 0:e221363f7942 191 }
WiredHome 0:e221363f7942 192 INFO(" Installed version: %d", inst_ver);
WiredHome 17:1d318666246c 193
WiredHome 0:e221363f7942 194 /* Download latest version string */
WiredHome 21:253e7da56ff9 195 //HTTPText server_ver("test message");
WiredHome 9:73067ef14c30 196 snprintf(fqurl, SW_MAX_URL, "%s/%s.txt", url, name);
WiredHome 23:cfe84db2b2cb 197 INFO("Query %s", fqurl);
WiredHome 17:1d318666246c 198 HTTPErrorCode = http.get(fqurl, buf, sizeof(buf));
WiredHome 17:1d318666246c 199 if (HTTPErrorCode == HTTP_OK) {
WiredHome 0:e221363f7942 200 int latest_ver = -1;
WiredHome 3:c69fff55fc60 201 int cksum = 0;
WiredHome 3:c69fff55fc60 202 int fsize = 0;
WiredHome 3:c69fff55fc60 203 int parseCount;
WiredHome 3:c69fff55fc60 204 parseCount = sscanf(buf, "%d,%d,%d", &latest_ver, &cksum, &fsize);
WiredHome 3:c69fff55fc60 205 if (parseCount == 3) {
WiredHome 9:73067ef14c30 206 INFO(" web version: %d", latest_ver);
WiredHome 9:73067ef14c30 207 INFO(" checksum: %d", cksum);
WiredHome 9:73067ef14c30 208 INFO(" file size: %d", fsize);
WiredHome 3:c69fff55fc60 209 if (inst_ver != latest_ver) {
WiredHome 3:c69fff55fc60 210 INFO(" Downloading firmware ver %d ...", latest_ver);
WiredHome 22:2a010efe00da 211 snprintf(fwfn, SW_MAX_FQFN, "/local/%s%02d.BIN", nameroot, (latest_ver % 100));
WiredHome 22:2a010efe00da 212 snprintf(fqurl, SW_MAX_URL, "%s/%s.bin", url, name);
WiredHome 17:1d318666246c 213
WiredHome 3:c69fff55fc60 214 HTTPFile latest(fwfn);
WiredHome 23:cfe84db2b2cb 215 INFO("Fetch %s", fqurl);
WiredHome 17:1d318666246c 216 HTTPErrorCode = http.get(fqurl, &latest);
WiredHome 17:1d318666246c 217 if (HTTPErrorCode == HTTP_OK) {
WiredHome 19:169aab9047bd 218 Integrity_t t = PassesIntegrityCheck(fwfn, cksum, fsize);
WiredHome 19:169aab9047bd 219 if (t == no_file) {
WiredHome 19:169aab9047bd 220 ERR(" *** No space on file system. ***");
WiredHome 19:169aab9047bd 221 result |= SWUP_NO_SPACE;
WiredHome 19:169aab9047bd 222 } else if (t == ok) {
WiredHome 16:de99e872fc9d 223 if (!RemoveOtherBinFiles(nameroot, latest_ver)) {
WiredHome 9:73067ef14c30 224 ERR(" *** Failed to remove old version(s). ***");
WiredHome 9:73067ef14c30 225 result |= SWUP_OLD_STUCK;
WiredHome 3:c69fff55fc60 226 }
WiredHome 3:c69fff55fc60 227 INFO("Updating stored version number.");
WiredHome 3:c69fff55fc60 228 fv = fopen(verfn, "w");
WiredHome 3:c69fff55fc60 229 if (fv) {
WiredHome 3:c69fff55fc60 230 int fr = fputs(buf, fv);
WiredHome 3:c69fff55fc60 231 if (fr < 0) {
WiredHome 3:c69fff55fc60 232 ERR("Failed (%d) to update stored version number.", fr);
WiredHome 3:c69fff55fc60 233 fclose( fv );
WiredHome 9:73067ef14c30 234 result |= SWUP_VER_STUCK;
WiredHome 3:c69fff55fc60 235 } else {
WiredHome 3:c69fff55fc60 236 fclose( fv );
WiredHome 9:73067ef14c30 237 if (action == AUTO_REBOOT) {
WiredHome 3:c69fff55fc60 238 WARN("Resetting...\n");
WiredHome 3:c69fff55fc60 239 wait_ms(200);
WiredHome 3:c69fff55fc60 240 mbed_reset();
WiredHome 3:c69fff55fc60 241 }
WiredHome 3:c69fff55fc60 242 }
WiredHome 1:208de08b1a19 243 } else {
WiredHome 3:c69fff55fc60 244 WARN("Failed to update local version info in %s.", verfn);
WiredHome 9:73067ef14c30 245 result |= SWUP_VWRITE_FAILED;
WiredHome 1:208de08b1a19 246 }
WiredHome 19:169aab9047bd 247 } else /* t == bad_crc */ {
WiredHome 3:c69fff55fc60 248 WARN("New file {%s} did not pass integrity check.", fwfn);
WiredHome 9:73067ef14c30 249 result |= SWUP_INTEGRITY_FAILED;
WiredHome 0:e221363f7942 250 }
WiredHome 1:208de08b1a19 251 } else {
WiredHome 3:c69fff55fc60 252 WARN("Failed to download lastest firmware.");
WiredHome 17:1d318666246c 253 result |= SWUP_HTTP_BIN;
WiredHome 0:e221363f7942 254 }
WiredHome 0:e221363f7942 255 } else {
WiredHome 9:73067ef14c30 256 INFO("Online version is same as installed version.");
WiredHome 9:73067ef14c30 257 result |= SWUP_SAME_VER;
WiredHome 0:e221363f7942 258 }
WiredHome 0:e221363f7942 259 }
WiredHome 0:e221363f7942 260 } else {
WiredHome 22:2a010efe00da 261 WARN("Failed accessing {%s}. Extended Error Code = %d", fqurl, HTTPErrorCode);
WiredHome 17:1d318666246c 262 result |= SWUP_HTTP_VER;
WiredHome 0:e221363f7942 263 }
WiredHome 9:73067ef14c30 264 return (SWUpdate_T)result;
WiredHome 0:e221363f7942 265 }