Bläddra i källkod

Move bootloader logic to board. Removes magic number loops. Listens for successful syncs

craigsdennis 9 år sedan
förälder
incheckning
a476922120
3 ändrade filer med 52 tillägg och 39 borttagningar
  1. 2 0
      back-end/boards.js
  2. 23 24
      back-end/rom_comm.js
  3. 27 15
      back-end/utilities.js

+ 2 - 0
back-end/boards.js

@@ -31,6 +31,7 @@ const FLASH_SIZES = {
 class EspBoard {
     constructor(port) {
         this.port = port;
+        this.isInBootLoader = false;
     }
 
     portSet(options) {
@@ -82,6 +83,7 @@ class Esp12 extends EspBoard {
             .then(() => this.portSet({rts: false, dtr: true}))
             .then(() => delay(50))
             .then(() => this.portSet({rts: false, dtr: false}));
+
     }
 }
 

+ 23 - 24
back-end/rom_comm.js

@@ -6,7 +6,7 @@ const log = require("./logger");
 const slip = require("./streams/slip");
 const boards = require("./boards");
 const delay = require("./utilities").delay;
-const repeatPromise = require("./utilities").repeatPromise;
+const retryPromiseUntil = require("./utilities").retryPromiseUntil;
 const promiseChain = require("./utilities").promiseChain;
 
 
@@ -27,6 +27,7 @@ const commands = {
 };
 
 const validateCommandSuccess = [
+    commands.SYNC_FRAME,
     commands.FLASH_DOWNLOAD_BEGIN,
     commands.FLASH_DOWNLOAD_DATA,
     commands.FLASH_DOWNLOAD_DONE
@@ -45,6 +46,7 @@ const SYNC_FRAME = new Buffer([0x07, 0x07, 0x12, 0x20,
 
 const FLASH_BLOCK_SIZE = 0x400;
 const SUCCESS = new Buffer([0x00, 0x00]);
+const REQUIRED_SUCCESSFUL_SYNC_COUNT = 10;
 
 /**
  * An abstraction of talking to the ever so finicky ESP8266 ROM.
@@ -71,7 +73,6 @@ class RomComm {
         }
         this.board = new BoardFactory(this._port);
         this.config = config;
-        this.isInBootLoader = false;
     }
 
     bindPort() {
@@ -163,7 +164,7 @@ class RomComm {
             .then((response) => {
                 if (!ignoreResponse) {
                     log.info("Sync response completed!", response);
-                    this.isInBootLoader = true;
+                    this.board.isInBootLoader = true;
                 }
             });
     }
@@ -171,29 +172,14 @@ class RomComm {
     connect() {
         // Eventually responses calm down, but on initial contact, responses are not standardized.
         // This tries until break through is made.
-        return repeatPromise(5, () => {
-            if (this.isInBootLoader) {
-                log.info("In Bootloader not re-connecting");
-                return true;
-            } else {
-                return this._connectAttempt();
-            }
-        }).then(() => this.sync())
-        .then(() => this.isInBootLoader);
+        this._listenForSuccessfulSync();
+        return retryPromiseUntil(() => this._connectAttempt(), () => this.board.isInBootLoader);
     }
 
     _connectAttempt() {
         return this.board.resetIntoBootLoader()
                 .then(() => delay(100))
-                // And a 5x loop here
-                .then(() => repeatPromise(5, () => {
-                    if (this.isInBootLoader) {
-                        log.info("In Bootloader not syncing");
-                        return true;
-                    } else {
-                        return this._flushAndSync();
-                    }
-                }));
+                .then(() => retryPromiseUntil(() => this._flushAndSync(), () => this.board.isInBootLoader, 10));
     }
 
     _flushAndSync() {
@@ -209,6 +195,19 @@ class RomComm {
             }).then(() => this.sync(true));
     }
 
+    _listenForSuccessfulSync() {
+        let commandName = commandToKey(commands.SYNC_FRAME);
+        let successfulSyncs = 0;
+        this.in.on(commandName, (response) => {
+            successfulSyncs++;
+            if (successfulSyncs >= REQUIRED_SUCCESSFUL_SYNC_COUNT) {
+                log.info("Got enough successful syncs");
+                this.board.isInBootLoader = true;
+                this.in.removeAllListeners(commandName);
+            }
+        });
+    }
+
     /**
      * Send appropriate C struct header along with command as required
      * SEE:  https://github.com/igrr/esptool-ck/blob/master/espcomm/espcomm.h#L49
@@ -345,7 +344,7 @@ class RomComm {
         return this.sendCommand(commands.FLASH_DOWNLOAD_DONE, new Buffer(buffer))
             .then((result) => {
                 log.info("Received result", result);
-                this.isInBootLoader = false;
+                this.board.isInBootLoader = false;
             });
     }
 
@@ -361,13 +360,13 @@ class RomComm {
                 let sendHeader = this.headerPacketFor(command, data);
                 let message = Buffer.concat([sendHeader, data], sendHeader.length + data.length);
                 this.out.write(message, 'buffer', (err, res) => {
-                    delay(5).then(() => {
+                    delay(10).then(() => {
                         if (ignoreResponse) {
                             resolve("Response was ignored");
                         }
                         if (this.portIsOpen) {
                             this._port.drain((drainErr, results) => {
-                                log.info("Draining", drainErr, results);
+                                log.info("Draining after write", drainErr, results);
                             });
                         }
                     });

+ 27 - 15
back-end/utilities.js

@@ -11,26 +11,38 @@ const log = require("./logger");
 function delay(time) {
     return new Promise((resolve) => {
         log.info("Delaying for %d ms", time);
-        setTimeout(resolve, time);
+        setTimeout(() => resolve(time), time);
     });
 }
 
 /**
- * Repeats a promise a given number of times.
- * @param times. The number of times to repeat a given promise.
- * @param callback is a no parameter based function that returns a {Promise}.
+ * Repeats a promise until a condition is met, or `maxAttempts` have occurred
+ * @param callback This is a function that should return the promise to repeat
+ * @param checkFn A function that will run on each go, truthy values will stop the loop
+ * @param maxAttempts [OPTIONAL] Number of times this should loop.
  * @returns {Promise}
  */
-function repeatPromise(times, callback) {
-    let chain = Promise.resolve();
-    // Range just used for closure based loop
-    let range = new Array(times)
-        .fill(0)
-        .map((value, index) => index);
-    range.forEach(() => {
-        chain = chain.then(() => callback());
-    });
-    return chain;
+function retryPromiseUntil(callback, checkFn, maxAttempts) {
+    if (!callback.hasOwnProperty("attemptCount")) {
+        callback.attemptCount = 0;
+    }
+
+    let result = checkFn();
+    log.debug("Retrying promise...");
+    if (result) {
+        log.info("Check function returned", result);
+        return result;
+    }
+    callback.attemptCount++;
+    log.debug("Performing attempt", callback.attemptCount);
+    if (maxAttempts && callback.attemptCount > maxAttempts) {
+        log.warn("Max attempts reached exiting");
+        return;
+    }
+    // Recursively return the promise
+    return Promise.resolve()
+        .then(() => callback())
+        .then(() => retryPromiseUntil(callback, checkFn, maxAttempts));
 }
 
 /**
@@ -47,6 +59,6 @@ function promiseChain(promiseFunctions) {
 
 module.exports = {
     delay: delay,
-    repeatPromise: repeatPromise,
+    retryPromiseUntil: retryPromiseUntil,
     promiseChain: promiseChain
 };