- Suppress warning missing config.json warning after config reset.
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Mon, 26 Feb 2018 22:59:25 +0000 (14:59 -0800)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Mon, 26 Feb 2018 22:59:25 +0000 (14:59 -0800)
 - Fixed EStop reboot loop.
 - Removed AVR unexpected reboot error.

CHANGELOG.md
package.json
src/avr/src/estop.c
src/avr/src/hardware.c
src/avr/src/hardware.h
src/avr/src/io.c
src/avr/src/switch.c
src/py/bbctrl/Cmd.py
src/py/bbctrl/Comm.py
src/py/bbctrl/Config.py
src/py/bbctrl/Planner.py

index 120a64bb328ab36bd75e430e0d34c2ef6899f5a4..13e69b6ffa7176648c37bae2b509d4a4cfb39306 100644 (file)
@@ -1,6 +1,11 @@
 Buildbotics CNC Controller Firmware Change Log
 ==============================================
 
+## v0.3.15
+ - Suppress warning missing config.json warning after config reset.
+ - Fixed EStop reboot loop.
+ - Removed AVR unexpected reboot error.
+
 ## v0.3.14
  - Fixed: Config fails silently after web disconnect #112
  - Always reload the page after a disconnect.
index b5c255ddb0ca41660bddcf53e83bc9b0dae1c512..208b4d83ae7c02cec48b168a71067a7f92de74db 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "bbctrl",
-  "version": "0.3.14",
+  "version": "0.3.15",
   "homepage": "http://buildbotics.com/",
   "repository": "https://github.com/buildbotics/bbctrl-firmware",
   "license": "GPL-3.0+",
index 08ca346486e78d15c55fc0b8da2acb8a8703f066..c9335600a1d19f99a4b14616528fd7978ce8bd4f 100644 (file)
@@ -45,7 +45,7 @@ typedef struct {
 } estop_t;
 
 
-static estop_t estop = {0};
+static estop_t estop = {false};
 
 static uint16_t estop_reason_eeprom EEMEM;
 
@@ -105,6 +105,10 @@ void estop_trigger(stat_t reason) {
 
 
 void estop_clear() {
+  // It is important that we don't clear the estop if it's not set because
+  // it can cause a reboot loop.
+  if (!estop.triggered) return;
+
   // Check if estop switch is set
   if (switch_is_active(SW_ESTOP)) {
     if (_get_reason() != STAT_ESTOP_SWITCH) _set_reason(STAT_ESTOP_SWITCH);
index 6f67d56c4612aea78f53b2a22dbba2b5a1ecba1f..ee81ff7a45d76de74dd2758e3bb463467f132b3a 100644 (file)
@@ -127,7 +127,7 @@ void hardware_init() {
 void hw_request_hard_reset() {hw.hard_reset = true;}
 
 
-void hw_hard_reset() {
+static void _hard_reset() {
   usart_flush();
   cli();
   CCP = CCP_IOREG_gc;
@@ -137,12 +137,13 @@ void hw_hard_reset() {
 
 /// Controller's rest handler
 void hw_reset_handler() {
-  if (hw.hard_reset) {
-    while (!usart_tx_empty() || !eeprom_is_ready())
-      continue;
+  if (!hw.hard_reset) return;
 
-    hw_hard_reset();
-  }
+  // Flush serial port and wait for EEPROM writes to finish
+  while (!usart_tx_empty() || !eeprom_is_ready())
+    continue;
+
+  _hard_reset();
 }
 
 
index 3bdef8965b8f3b8f2f9552a4fd1126117c3bc422..fdda57ef52a38eb7d8cd4a517080f55dd768f92c 100644 (file)
@@ -34,7 +34,6 @@
 
 void hardware_init();
 void hw_request_hard_reset();
-void hw_hard_reset();
 void hw_reset_handler();
 
 uint8_t hw_disable_watchdog();
index e581efb06232f15f9549b7d21cb9fe5f0bec8cdd..db2325be29cd6365690dc591338095036b63d495 100644 (file)
@@ -84,19 +84,19 @@ stat_t command_input(char *cmd) {
   cmd++; // Skip command
 
   // Analog or digital
-  if (cmd[0] == 'd') input_cmd.digital = true;
-  else if (cmd[0] == 'a') input_cmd.digital = false;
+  if (*cmd == 'd') input_cmd.digital = true;
+  else if (*cmd == 'a') input_cmd.digital = false;
   else return STAT_INVALID_ARGUMENTS;
   cmd++;
 
   // Port index
-  if (!isdigit(cmd[1])) return STAT_INVALID_ARGUMENTS;
-  input_cmd.port = cmd[1] - '0';
+  if (!isdigit(*cmd)) return STAT_INVALID_ARGUMENTS;
+  input_cmd.port = *cmd - '0';
   cmd++;
 
   // Mode
-  if (!isdigit(cmd[2])) return STAT_INVALID_ARGUMENTS;
-  input_cmd.mode = cmd[2] - '0';
+  if (!isdigit(*cmd)) return STAT_INVALID_ARGUMENTS;
+  input_cmd.mode = *cmd - '0';
   if (INPUT_LOW < input_cmd.mode) return STAT_INVALID_ARGUMENTS;
   cmd++;
 
index aac99e25e8b014ec9f4b5bccf757fc98076fd4c8..40e3a8815394b2352152a138bdb53a747df86638 100644 (file)
@@ -38,6 +38,7 @@ typedef struct {
   switch_callback_t cb;
   bool state;
   int8_t debounce;
+  bool initialized;
 } switch_t;
 
 
@@ -83,9 +84,11 @@ void switch_rtc_callback() {
     // Debounce switch
     bool state = IN_PIN(s->pin);
     if (state == s->state) s->debounce = 0;
-    else if (++s->debounce == SWITCH_DEBOUNCE) {
+    else if ((state && ++s->debounce == SWITCH_DEBOUNCE) ||
+             (!state && --s->debounce == -SWITCH_DEBOUNCE)) {
       s->state = state;
       s->debounce = 0;
+      s->initialized = true;
       if (s->cb) s->cb(i, switch_is_active(i));
     }
   }
@@ -95,6 +98,8 @@ void switch_rtc_callback() {
 bool switch_is_active(switch_id_t sw) {
   if (sw < 0 || num_switches <= sw) return false;
 
+  if (!switches[sw].initialized) return false;
+
   // NOTE, switch inputs are active lo
   switch (switches[sw].type) {
   case SW_DISABLED: break; // A disabled switch cannot be active
index e7b167edc855ab86a19eeb1b06e965d89680c611..438e1f27a1481be223c26a66019e205d6a858ebc 100644 (file)
@@ -103,19 +103,19 @@ def speed(speed): return '#s=:' + encode_float(speed)
 
 
 def input(port, mode, timeout):
-    type = 'd'
-    index = 0
-    m = 0
-
-    if port == 'digital-in-0': digital, index = 'd', 0
-    if port == 'digital-in-1': digital, index = 'd', 1
-    if port == 'digital-in-2': digital, index = 'd', 2
-    if port == 'digital-in-3': digital, index = 'd', 3
-    if port == 'analog-in-0':  digital, index = 'a', 0
-    if port == 'analog-in-1':  digital, index = 'a', 1
-    if port == 'analog-in-2':  digital, index = 'a', 2
-    if port == 'analog-in-3':  digital, index = 'a', 3
-
+    type, index, m = 'd', 0, 0
+
+    # Analog/digital & port index
+    if port == 'digital-in-0': type, index = 'd', 0
+    if port == 'digital-in-1': type, index = 'd', 1
+    if port == 'digital-in-2': type, index = 'd', 2
+    if port == 'digital-in-3': type, index = 'd', 3
+    if port == 'analog-in-0':  type, index = 'a', 0
+    if port == 'analog-in-1':  type, index = 'a', 1
+    if port == 'analog-in-2':  type, index = 'a', 2
+    if port == 'analog-in-3':  type, index = 'a', 3
+
+    # Mode
     if mode == 'immediate': m = 0
     if mode == 'rise':      m = 1
     if mode == 'fall':      m = 2
index 4d98f3ad989ec657fad892a4e69d7da0623cc496..1c611113863b037ea13564fa57ba3f1bca445807 100644 (file)
@@ -47,7 +47,6 @@ class Comm():
         self.queue = deque()
         self.in_buf = ''
         self.command = None
-        self.reboot_expected = True
 
         try:
             self.sp = serial.Serial(ctrl.args.serial, ctrl.args.baud,
@@ -192,13 +191,11 @@ class Comm():
 
                 if 'variables' in msg:
                     self._update_vars(msg)
-                    self.reboot_expected = False
 
                 elif 'msg' in msg: self._log_msg(msg)
 
                 elif 'firmware' in msg:
-                    if self.reboot_expected: log.info('AVR firmware rebooted')
-                    else: log.error('Unexpected AVR firmware reboot')
+                    log.info('AVR firmware rebooted')
                     self.connect()
 
                 else: self.ctrl.state.update(msg)
@@ -220,7 +217,6 @@ class Comm():
     def clear(self):
         if self.ctrl.state.get('xx', '') == 'ESTOPPED':
             self.i2c_command(Cmd.CLEAR)
-            self.reboot_expected = True
 
 
     def pause(self, optional = False):
@@ -228,9 +224,7 @@ class Comm():
         self.i2c_command(Cmd.PAUSE, byte = data)
 
 
-    def reboot(self):
-        self.queue_command(Cmd.REBOOT)
-        self.reboot_expected = True
+    def reboot(self): self.queue_command(Cmd.REBOOT)
 
 
     def connect(self):
index d6c972eb679fe36c3e02a01f5e02e250fbda322b..f42fef1f1b7ba893fabab3a8241d3a0af49ba491 100644 (file)
@@ -30,6 +30,7 @@ import json
 import logging
 import pkg_resources
 import subprocess
+import copy
 
 import bbctrl
 
@@ -82,7 +83,9 @@ class Config(object):
 
     def load(self):
         try:
-            config = self.load_path('config.json')
+            if os.path.exists('config.json'):
+                config = self.load_path('config.json')
+            else: config = copy.deepcopy(default_config)
 
             try:
                 self.upgrade(config)
index 4e96ab4c2564258d3c8cc805d9d69f6890e5560b..368db4c49e23dfdc708973e4a85ed3228725b409 100644 (file)
@@ -182,6 +182,8 @@ class Planner():
 
 
     def __encode(self, block):
+        log.info('Cmd:' + json.dumps(block))
+
         type = block['type']
 
         if type == 'line':
@@ -237,27 +239,6 @@ class Planner():
         self.setq.clear()
 
 
-    def stop(self):
-        self.planner.stop()
-        self.lastID = 0
-        self.setq.clear()
-
-
-    def restart(self):
-        state = self.ctrl.state
-        id = state.get('id')
-
-        position = {}
-        for axis in 'xyzabc':
-            if state.has(axis + 'p'):
-                position[axis] = state.get(axis + 'p')
-
-        log.info('Planner restart: %d %s' % (id, json.dumps(position)))
-        self.planner.restart(id, position)
-        if self.planner.is_synchronizing(): self.planner.synchronize(1)
-        # TODO if these calls fail we get stuck in a loop
-
-
     def mdi(self, cmd):
         log.info('MDI:' + cmd)
         self.planner.load_string(cmd, self._get_config(True))
@@ -268,6 +249,36 @@ class Planner():
         self.planner.load(path, self._get_config(False))
 
 
+    def stop(self):
+        try:
+            self.planner.stop()
+            self.lastID = 0
+            self.setq.clear()
+
+        except Exception as e:
+            log.exception(e)
+            self.reset()
+
+
+    def restart(self):
+        try:
+            state = self.ctrl.state
+            id = state.get('id')
+
+            position = {}
+            for axis in 'xyzabc':
+                if state.has(axis + 'p'):
+                    position[axis] = state.get(axis + 'p')
+
+            log.info('Planner restart: %d %s' % (id, json.dumps(position)))
+            self.planner.restart(id, position)
+            if self.planner.is_synchronizing(): self.planner.synchronize(1)
+
+        except Exception as e:
+            log.exception(e)
+            self.reset()
+
+
     def has_move(self): return self.planner.has_more()
 
 
@@ -279,5 +290,5 @@ class Planner():
                 if cmd is not None: return cmd
 
         except Exception as e:
-            self.reset()
             log.exception(e)
+            self.reset()