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:
Thu Dec 29 20:16:46 2016 +0000
Revision:
22:2a010efe00da
Parent:
21:253e7da56ff9
Child:
23:cfe84db2b2cb
Minor change from sprintf to snprintf

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