+ - Respect offsets in canned cycle moves. #219
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Fri, 14 Jun 2019 16:51:05 +0000 (09:51 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Fri, 14 Jun 2019 16:51:05 +0000 (09:51 -0700)
+ - Fixed G53 warning.
+ - Fixed delayed offset update after M2 or M30 end of program.
+ - Handle multiple consecutive config resets correctly.
+ - Fixed log CPU usage problem introduced in v0.4.6.
+ - Show RPi temp on indicators page.
+ - Show red thermometer if RPi temp exceeds 80°C.
+ - Enforce 6A per motor channel peak current limit.
+ - Adjust config values above max or below min instead of resetting to default.

18 files changed:
CHANGELOG.md
package.json
scripts/install.sh
scripts/update-bbctrl
scripts/upgrade-bbctrl
src/avr/src/config.h
src/avr/src/drv8711.c
src/avr/src/vars.def
src/bbserial/bbserial.c
src/js/indicators.js
src/pug/index.pug
src/pug/templates/admin-general-view.pug
src/pug/templates/indicators.pug
src/py/bbctrl/Config.py
src/py/bbctrl/Log.py
src/py/bbctrl/MonitorTemp.py
src/py/bbctrl/__init__.py
src/resources/config-template.json

index c97aa735b76c168e95500846c4bf5998771a6eb3..c70a282ff66a227cafbfa295e18698d29ea50d95 100644 (file)
@@ -1,6 +1,10 @@
 Buildbotics CNC Controller Firmware Changelog
 =============================================
 
+## v0.4.9
+ - Enforce 6A per motor channel peak current limit.
+ - Adjust config values above max or below min instead of resetting to default.
+
 ## v0.4.8
  - Fixed log rotating.
  - Use systemd serivce instead of init.d.
@@ -11,6 +15,13 @@ Buildbotics CNC Controller Firmware Changelog
  - Rewrote RPi serial driver.
  - Automatically scale max CPU speed to reduce RPi temp.
  - Disable USB camera if RPi temperature above 80°C, back on at 75°C.
+ - Respect offsets in canned cycle moves.  #219
+ - Fixed G53 warning.
+ - Fixed delayed offset update after M2 or M30 end of program.
+ - Handle multiple consecutive config resets correctly.
+ - Fixed log CPU usage problem introduced in v0.4.6.
+ - Show RPi temp on indicators page.
+ - Show red thermometer if RPi temp exceeds 80°C.
 
 ## v0.4.7
  - Fix homing switch to motor channel mapping with non-standard axis order.
index 326139fa965972097be9401f556f8466227ac841..958ad19c11ff43e7a012cf9593e74188a09e7b77 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "bbctrl",
-  "version": "0.4.8",
+  "version": "0.4.9",
   "homepage": "http://buildbotics.com/",
   "repository": "https://github.com/buildbotics/bbctrl-firmware",
   "license": "GPL-3.0+",
index 86f796e686723f7106737c7f3fec82fb0ec0c27d..eeacf8ff6168b7b0a1604b863f78c402da9ba133 100755 (executable)
@@ -29,7 +29,7 @@ function update_config_txt() {
 
 
 if $UPDATE_PY; then
-    service bbctrl stop
+    systemctl stop bbctrl
 
     # Update service
     rm -f /etc/init.d/bbctrl
@@ -130,11 +130,14 @@ cp scripts/rc.local /etc/
 if $UPDATE_PY; then
     rm -rf /usr/local/lib/python*/dist-packages/bbctrl-*
     ./setup.py install --force
-    service bbctrl start
+    service bbctrl restart
 fi
 
 sync
 
 if $REBOOT; then
+    echo "Rebooting"
     reboot
 fi
+
+echo "Install complete"
index 739203bb9340c5df7289d24965fad302e1c006ee..06433edd62b2459f85a8bdb4b36c3ae631cf24ca 100755 (executable)
@@ -7,13 +7,16 @@ if [ ! -e "$UPDATE" ]; then
   exit 1
 fi
 
+systemctl stop bbctrl
+
 rm -rf /tmp/update
 mkdir /tmp/update
 cd /tmp/update
 
+LOG=/var/log/bbctrl.$(date +%Y%m%d-%H%M%S).install
 tar xf "$UPDATE"
 cd *
-./scripts/install.sh "$*"
+./scripts/install.sh "$*" 2>&1 > $LOG
 
 cd -
 rm -rf /tmp/update $UPDATE
index 44454f2496e6830b0de2851c8f71d65b6ed199be..babd550933c91461b724173c4896df69c24cb068 100755 (executable)
@@ -5,22 +5,18 @@
 
     VERSION=$(curl -s https://buildbotics.com/bbctrl/latest.txt)
     PKG_NAME=bbctrl-$VERSION
-    PKG=$PKG_NAME.tar.bz2
-    PKG_URL=https://buildbotics.com/bbctrl/$PKG
+    PKG=/var/lib/bbctrl/firmware/update.tar.bz2
+    PKG_URL=https://buildbotics.com/bbctrl/$PKG_NAME.tar.bz2
 
     logger Installing bbctrl firmware $VERSION
 
+    cd /var/lib/bbctrl
+    mkdir -p firmware
+
     echo Downloading $PKG_URL
     curl -s $PKG_URL > $PKG
 
-    echo Unpacking $PKG
-    tar xf $PKG
-
-    echo Installing $PKG
-    (cd $PKG_NAME; ./scripts/install.sh)
-
-    echo Cleaning up
-    rm -rf $PKG_NAME $PKG
+    /usr/local/bin/update-bbctrl
 
     echo Success
 
index 512b3f37d8d3f9596a4a32599c2aa8b6f5dc6c8e..68df3849cbc7a1a2f04b37f42b48ef8993f26c35 100644 (file)
@@ -214,7 +214,7 @@ enum {
 #define MIN_VELOCITY             10            // mm/min
 #define CURRENT_SENSE_RESISTOR   0.05          // ohms
 #define CURRENT_SENSE_REF        2.75          // volts
-#define MAX_CURRENT              10            // amps
+#define MAX_CURRENT                          // amps
 #define MAX_IDLE_CURRENT         2             // amps
 #define VELOCITY_MULTIPLIER      1000.0
 #define ACCEL_MULTIPLIER         1000000.0
index 18a8fde9752adc9ee8a950efc16971b636a701d8..ca7a8f635af08d277722c0aa2efa306032d16235 100644 (file)
@@ -397,8 +397,8 @@ float get_drive_current(int driver) {
 
 
 void set_drive_current(int driver, float value) {
-  if (driver < 0 || DRIVERS <= driver || value < 0 || MAX_CURRENT < value)
-    return;
+  if (driver < 0 || DRIVERS <= driver || value < 0) return;
+  if (MAX_CURRENT < value) value = MAX_CURRENT;
   _current_set(&drivers[driver].drive, value);
 }
 
index 45dda839610f3a5f9629b00aaa04f31a69c8629b..cfac5c99d454ba3cbd3908903a6cd95551d649fe 100644 (file)
@@ -25,9 +25,9 @@
 
 \******************************************************************************/
 
-#define AXES_LABEL   "xyzabc"
+#define   AXES_LABEL "xyzabc"
 #define MOTORS_LABEL "0123"
-#define OUTS_LABEL   "ed12ft"
+#define   OUTS_LABEL "ed12ft"
 #define ANALOG_LABEL "12"
 #define VFDREG_LABEL "0123456789abcdefghijklmnopqrstuv"
 
index 8a5745f94a9cbd59880fa8c6f9d829a3c14fe925..0c00d5df1845d3f039a5905d9417fb184e1bb1a4 100644 (file)
 #include <linux/poll.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
+#include <linux/tty.h>
 #include <asm/ioctls.h>
+#include <asm/termios.h>
 
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Joseph Coffland");
 MODULE_DESCRIPTION("Buildbotics controller serial port driver");
-MODULE_VERSION("0.2");
+MODULE_VERSION("0.3");
 
 
-#define DEVICE_NAME "ttyBB0"
+#define DEVICE_NAME "ttyAMA0"
 #define BUF_SIZE (1 << 16)
 
 
-enum {
-  REG_DR,
-  REG_ST_DMAWM,
-  REG_ST_TIMEOUT,
-  REG_FR,
-  REG_LCRH_RX,
-  REG_LCRH_TX,
-  REG_IBRD,
-  REG_FBRD,
-  REG_CR,
-  REG_IFLS,
-  REG_IMSC,
-  REG_RIS,
-  REG_MIS,
-  REG_ICR,
-  REG_DMACR,
-  REG_ST_XFCR,
-  REG_ST_XON1,
-  REG_ST_XON2,
-  REG_ST_XOFF1,
-  REG_ST_XOFF2,
-  REG_ST_ITCR,
-  REG_ST_ITIP,
-  REG_ST_ABCR,
-  REG_ST_ABIMSC,
-  REG_ARRAY_SIZE,
-};
-
+#define UART01x_LCRH_WLEN_bm 0x60
 
-static u16 _offsets[REG_ARRAY_SIZE] = {
-  [REG_DR]      = UART01x_DR,
-  [REG_FR]      = UART01x_FR,
-  [REG_LCRH_RX] = UART011_LCRH,
-  [REG_LCRH_TX] = UART011_LCRH,
-  [REG_IBRD]    = UART011_IBRD,
-  [REG_FBRD]    = UART011_FBRD,
-  [REG_CR]      = UART011_CR,
-  [REG_IFLS]    = UART011_IFLS,
-  [REG_IMSC]    = UART011_IMSC,
-  [REG_RIS]     = UART011_RIS,
-  [REG_MIS]     = UART011_MIS,
-  [REG_ICR]     = UART011_ICR,
-  [REG_DMACR]   = UART011_DMACR,
-};
 
+static int debug = 0;
+module_param(debug, int, 0660);
 
-static int _debug = 0;
 
 struct ring_buf {
   unsigned char *buf;
@@ -110,14 +71,11 @@ static struct {
   spinlock_t            lock;
   unsigned              open;
   unsigned char __iomem *base;
-  wait_queue_head_t     wait;
-  unsigned long         req_events;
+  wait_queue_head_t     read_wait;
+  wait_queue_head_t     write_wait;
   unsigned              irq;
   unsigned              im;             // interrupt mask
 
-  unsigned              rx_bytes;
-  unsigned              tx_bytes;
-
   unsigned              brk_errs;
   unsigned              parity_errs;
   unsigned              frame_errs;
@@ -126,6 +84,7 @@ static struct {
   int                   major;
   struct class          *class;
   struct device         *dev;
+  struct ktermios       term;
 } _port;
 
 
@@ -137,70 +96,55 @@ static struct {
 #define RING_BUF_PUSH(BUF, C)                   \
   do {                                          \
     (BUF).buf[(BUF).head] = C;                  \
+    mb();                                       \
     RING_BUF_INC(BUF, head);                    \
   } while (0)
 
 #define RING_BUF_POKE(BUF) (BUF).buf[(BUF).head]
 #define RING_BUF_PEEK(BUF) (BUF).buf[(BUF).tail]
-
-#define RING_BUF_MOVE(SRC, DST)                 \
-  do {                                          \
-    RING_BUF_PUSH(DST, RING_BUF_PEEK(SRC));     \
-    RING_BUF_POP(SRC);                          \
-  } while (0)
-
-
 #define RING_BUF_SPACE(BUF) ((((BUF).tail) - ((BUF).head + 1)) & (BUF_SIZE - 1))
 #define RING_BUF_FILL(BUF) ((((BUF).head) - ((BUF).tail)) & (BUF_SIZE - 1))
+#define RING_BUF_CLEAR(BUF) do {(BUF).head = (BUF).tail = 0;} while (0)
 
 
-static unsigned _read(unsigned reg) {
-  return readw_relaxed(_port.base + _offsets[reg]);
-}
+static unsigned _read(unsigned reg) {return readw_relaxed(_port.base + reg);}
 
 
 static void _write(unsigned val, unsigned reg) {
-  writew_relaxed(val, _port.base + _offsets[reg]);
+  writew_relaxed(val, _port.base + reg);
 }
 
 
-static unsigned _tx_chars(void) {
-  unsigned bytes = 0;
+static void _tx_chars(void) {
   unsigned fill = RING_BUF_FILL(_port.tx_buf);
 
-  for (int i = 0; i < fill; i++) {
+  while (fill--) {
     // Check if UART FIFO full
-    if (_read(REG_FR) & UART01x_FR_TXFF) break;
+    if (_read(UART01x_FR) & UART01x_FR_TXFF) break;
 
-    _write(RING_BUF_PEEK(_port.tx_buf), REG_DR);
-    RING_BUF_POP(_port.tx_buf);
+    _write(RING_BUF_PEEK(_port.tx_buf), UART01x_DR);
     mb();
-    _port.tx_bytes++;
-    bytes++;
+    RING_BUF_POP(_port.tx_buf);
   }
 
   // Stop TX when buffer is empty
   if (!RING_BUF_FILL(_port.tx_buf)) {
     _port.im &= ~UART011_TXIM;
-    _write(_port.im, REG_IMSC);
+    _write(_port.im, UART011_IMSC);
   }
-
-  return bytes;
 }
 
 
-static unsigned _rx_chars(void) {
-  unsigned bytes = 0;
+static void _rx_chars(void) {
   unsigned space = RING_BUF_SPACE(_port.rx_buf);
 
-  for (int i = 0; i < space; i++) {
+  while (space--) {
     // Check if UART FIFO empty
-    unsigned status = _read(REG_FR);
+    unsigned status = _read(UART01x_FR);
     if (status & UART01x_FR_RXFE) break;
 
     // Read char from FIFO and update status
-    unsigned ch = _read(REG_DR);
-    _port.rx_bytes++;
+    unsigned ch = _read(UART01x_DR);
 
     // Record errors
     if (ch & UART011_DR_BE) _port.brk_errs++;
@@ -210,45 +154,193 @@ static unsigned _rx_chars(void) {
 
     // Queue char
     RING_BUF_PUSH(_port.rx_buf, ch);
-    bytes++;
   }
 
   // Stop RX interrupts when buffer is full
   if (!RING_BUF_SPACE(_port.rx_buf)) {
-    _port.im &= ~(UART011_RXIM | UART011_RTIM | UART011_FEIM | UART011_PEIM |
-                  UART011_BEIM | UART011_OEIM);
-    _write(_port.im, REG_IMSC);
+    _port.im &= ~(UART011_RXIM | UART011_RTIM);
+    _write(_port.im, UART011_IMSC);
   }
+}
+
+
+static int _read_status(void) {
+  int status = 0;
+
+  unsigned fr = _read(UART01x_FR);
+  unsigned cr = _read(UART011_CR);
 
-  return bytes;
+  if (fr & UART01x_FR_DSR) status |= TIOCM_LE;  // DSR (data set ready)
+  if (cr & UART011_CR_DTR) status |= TIOCM_DTR; // DTR (data terminal ready)
+  if (cr & UART011_CR_RTS) status |= TIOCM_RTS; // RTS (request to send)
+  // TODO What is TIOCM_ST - Secondary TXD (transmit)?
+  // TODO What is TIOCM_SR - Secondary RXD (receive)?
+  if (fr & UART01x_FR_CTS) status |= TIOCM_CTS; // CTS (clear to send)
+  if (fr & UART01x_FR_DCD) status |= TIOCM_CD;  // DCD (data carrier detect)
+  if (fr & UART011_FR_RI)  status |= TIOCM_RI;  // RI  (ring)
+  if (fr & UART01x_FR_DSR) status |= TIOCM_DSR; // DSR (data set ready)
+
+  if (debug) printk(KERN_INFO "bbserial: _read_status() = %d\n", status);
+
+  return status;
 }
 
 
-static irqreturn_t _interrupt(int irq, void *id) {
+static void _write_status(int status) {
+  if (debug) printk(KERN_INFO "bbserial: _write_status() = %d\n", status);
+
   unsigned long flags;
   spin_lock_irqsave(&_port.lock, flags);
 
-  unsigned rBytes = 0;
-  unsigned wBytes = 0;
+  unsigned cr = _read(UART011_CR);
+
+  // DTR (data terminal ready)
+  if (status & TIOCM_DTR) cr |= UART011_CR_DTR;
+  else cr &= ~UART011_CR_DTR;
+
+  // RTS (request to send)
+  if (status & TIOCM_RTS) cr |= UART011_CR_RTS;
+  else cr &= ~UART011_CR_RTS;
+
+  _write(cr, UART011_CR);
+
+  spin_unlock_irqrestore(&_port.lock, flags);
+}
+
 
-  for (int pass = 0; pass < 256; pass++) {
-    unsigned status = _read(REG_MIS);
-    if (!status) break;
+static struct ktermios *_get_term(void) {
+  unsigned lcrh = _read(UART011_LCRH);
+  unsigned cr = _read(UART011_CR);
 
-    // Clear interrupt status
-    _write(status, REG_ICR);
+  // Baud rate
+  unsigned brd = _read(UART011_IBRD) << 6 | _read(UART011_FBRD);
+  speed_t baud = clk_get_rate(_port.clk) * 4 / brd;
+  tty_termios_encode_baud_rate(&_port.term, baud, baud);
 
-    // Read and/or write
-    if (status & (UART011_RTIS | UART011_RXIS)) rBytes += _rx_chars();
-    if (status & UART011_TXIS) wBytes += _tx_chars();
+  // Data bits
+  unsigned cflag;
+  switch (lcrh & UART01x_LCRH_WLEN_bm) {
+  case UART01x_LCRH_WLEN_5: cflag = CS5; break;
+  case UART01x_LCRH_WLEN_6: cflag = CS6; break;
+  case UART01x_LCRH_WLEN_7: cflag = CS7; break;
+  default: cflag = CS8; break;
   }
 
+  // Stop bits
+  if (lcrh & UART01x_LCRH_STP2) cflag |= CSTOPB;
+
+  // Parity
+  if (lcrh & UART01x_LCRH_PEN) {
+    cflag |= PARENB;
+
+    if (!(UART01x_LCRH_EPS & lcrh)) cflag |= PARODD;
+    if (UART011_LCRH_SPS & lcrh) cflag |= CMSPAR;
+  }
+
+  // Hardware flow control
+  if (cr & UART011_CR_CTSEN) cflag |= CRTSCTS;
+
+  _port.term.c_cflag = cflag;
+
+  return &_port.term;
+}
+
+
+static void _set_baud(speed_t baud) {
+  if (debug) printk(KERN_INFO "bbserial: baud=%d\n", baud);
+
+  unsigned brd = clk_get_rate(_port.clk) * 16 / baud;
+
+  if ((brd & 3) == 3) brd = (brd >> 2) + 1; // Round up
+  else brd >>= 2;
+
+  _write(brd & 0x3f, UART011_FBRD);
+  _write(brd >> 6,   UART011_IBRD);
+}
+
+
+static int _set_term(struct ktermios *term) {
+  unsigned lcrh = UART01x_LCRH_FEN; // Enable FIFOs
+  unsigned cflag = term->c_cflag;
+
+  // Data bits
+  switch (cflag & CSIZE) {
+  case CS5: lcrh |= UART01x_LCRH_WLEN_5; break;
+  case CS6: lcrh |= UART01x_LCRH_WLEN_6; break;
+  case CS7: lcrh |= UART01x_LCRH_WLEN_7; break;
+  default:  lcrh |= UART01x_LCRH_WLEN_8; break;
+  }
+
+  // Stop bits
+  if (cflag & CSTOPB) lcrh |= UART01x_LCRH_STP2;
+
+  // Parity
+  if (cflag & PARENB) {
+    lcrh |= UART01x_LCRH_PEN;
+
+    if (!(cflag & PARODD)) lcrh |= UART01x_LCRH_EPS;
+    if (cflag & CMSPAR) lcrh |= UART011_LCRH_SPS;
+  }
+
+  // Get baud rate
+  speed_t baud = tty_termios_baud_rate(term);
+
+  // Set
+  unsigned long flags;
+  spin_lock_irqsave(&_port.lock, flags);
+
+  // Hardware flow control
+  unsigned cr = _read(UART011_CR);
+  if (cflag & CRTSCTS) cr |= UART011_CR_CTSEN;
+
+  _write(0, UART011_CR);      // Disable
+  _set_baud(baud);            // Baud
+  _write(lcrh, UART011_LCRH); // Must be after baud
+  _write(cr, UART011_CR);     // Enable
+
+  spin_unlock_irqrestore(&_port.lock, flags);
+
+  return 0;
+}
+
+
+static void _flush_input(void) {
+  unsigned long flags;
+  spin_lock_irqsave(&_port.lock, flags);
+
+  RING_BUF_CLEAR(_port.rx_buf);
+
+  spin_unlock_irqrestore(&_port.lock, flags);
+}
+
+
+static void _flush_output(void) {
+  unsigned long flags;
+  spin_lock_irqsave(&_port.lock, flags);
+
+  RING_BUF_CLEAR(_port.tx_buf);
+
+  spin_unlock_irqrestore(&_port.lock, flags);
+}
+
+
+static irqreturn_t _interrupt(int irq, void *id) {
+  unsigned long flags;
+  spin_lock_irqsave(&_port.lock, flags);
+
+  // Read and/or write
+  unsigned status = _read(UART011_MIS);
+  if (status & (UART011_RTIS | UART011_RXIS)) _rx_chars();
+  if (status & UART011_TXIS) _tx_chars();
+
+  unsigned txSpace = RING_BUF_SPACE(_port.tx_buf);
+  unsigned rxFill  = RING_BUF_FILL(_port.rx_buf);
+
   spin_unlock_irqrestore(&_port.lock, flags);
 
   // Notify pollers
-  if ((rBytes && (_port.req_events & POLLIN)) ||
-      (wBytes && (_port.req_events & POLLOUT)))
-    wake_up_interruptible(&_port.wait);
+  if (rxFill)  wake_up_interruptible_poll(&_port.read_wait,  POLLIN);
+  if (txSpace) wake_up_interruptible_poll(&_port.write_wait, POLLOUT);
 
   return IRQ_HANDLED;
 }
@@ -259,26 +351,32 @@ static void _enable_tx(void) {
   spin_lock_irqsave(&_port.lock, flags);
 
   _port.im |= UART011_TXIM;
-  _write(_port.im, REG_IMSC);
+  _write(_port.im, UART011_IMSC);
   _tx_chars(); // Must prime the pump
 
   spin_unlock_irqrestore(&_port.lock, flags);
 }
 
 
+static int _tx_enabled(void) {return _port.im & UART011_TXIM;}
+
+
 static void _enable_rx(void) {
   unsigned long flags;
   spin_lock_irqsave(&_port.lock, flags);
 
   _port.im |= UART011_RTIM | UART011_RXIM;
-  _write(_port.im, REG_IMSC);
+  _write(_port.im, UART011_IMSC);
 
   spin_unlock_irqrestore(&_port.lock, flags);
 }
 
 
+static int _rx_enabled(void) {return _port.im & (UART011_RTIM | UART011_RXIM);}
+
+
 static int _dev_open(struct inode *inodep, struct file *filep) {
-  if (_debug) printk(KERN_INFO "bbserial: open()\n");
+  if (debug) printk(KERN_INFO "bbserial: open()\n");
   if (_port.open) return -EBUSY;
   _port.open = 1;
   return 0;
@@ -287,41 +385,37 @@ static int _dev_open(struct inode *inodep, struct file *filep) {
 
 static ssize_t _dev_read(struct file *filep, char *buffer, size_t len,
                          loff_t *offset) {
-  if (_debug) printk(KERN_INFO "bbserial: read() len=%d\n", len);
+  if (debug) printk(KERN_INFO "bbserial: read() len=%d overruns=%d\n", len,
+                    _port.overruns);
+
   ssize_t bytes = 0;
 
-  // TODO read whole blocks
-  while (len && RING_BUF_FILL(_port.rx_buf)) {
+  while (bytes < len && RING_BUF_FILL(_port.rx_buf)) {
     put_user(RING_BUF_PEEK(_port.rx_buf), buffer++);
     RING_BUF_POP(_port.rx_buf);
-    len--;
     bytes++;
+    if (!_rx_enabled()) _enable_rx();
   }
 
-  if (bytes) _enable_rx();
-
   return bytes ? bytes : -EAGAIN;
 }
 
 
 static ssize_t _dev_write(struct file *filep, const char *buffer, size_t len,
-                         loff_t *offset) {
-  if (_debug)
+                          loff_t *offset) {
+  if (debug)
     printk(KERN_INFO "bbserial: write() len=%d tx=%d rx=%d\n",
            len, RING_BUF_FILL(_port.tx_buf), RING_BUF_FILL(_port.rx_buf));
 
   ssize_t bytes = 0;
 
-  // TODO write whole blocks
-  while (len && RING_BUF_SPACE(_port.tx_buf)) {
+  while (bytes < len && RING_BUF_SPACE(_port.tx_buf)) {
     get_user(RING_BUF_POKE(_port.tx_buf), buffer++);
     RING_BUF_INC(_port.tx_buf, head);
-    len--;
     bytes++;
+    if (!_tx_enabled()) _enable_tx();
   }
 
-  if (bytes) _enable_tx();
-
   return bytes ? bytes : -EAGAIN;
 }
 
@@ -334,34 +428,76 @@ static int _dev_release(struct inode *inodep, struct file *filep) {
 
 
 static unsigned _dev_poll(struct file *file, poll_table *wait) {
-  if (_debug) printk(KERN_INFO "bbserial: poll(tx=%d rx=%d)\n",
-                     RING_BUF_FILL(_port.tx_buf), RING_BUF_FILL(_port.rx_buf));
+  if (debug) {
+    unsigned events = poll_requested_events(wait);
+    printk(KERN_INFO "bbserial: poll(in=%s, out=%s)\n",
+           (events & POLLIN)  ? "true" : "false",
+           (events & POLLOUT) ? "true" : "false");
+  }
 
-  _port.req_events = poll_requested_events(wait);
-  poll_wait(file, &_port.wait, wait);
-  _port.req_events = 0;
+  poll_wait(file, &_port.read_wait,  wait);
+  poll_wait(file, &_port.write_wait, wait);
 
   unsigned ret = 0;
-  if (RING_BUF_SPACE(_port.tx_buf)) ret |= POLLOUT | POLLWRNORM;
   if (RING_BUF_FILL(_port.rx_buf))  ret |= POLLIN  | POLLRDNORM;
+  if (RING_BUF_SPACE(_port.tx_buf)) ret |= POLLOUT | POLLWRNORM;
+
+  if (debug) printk(KERN_INFO "bbserial: tx=%d rx=%d\n",
+                     RING_BUF_FILL(_port.tx_buf), RING_BUF_FILL(_port.rx_buf));
 
   return ret;
 }
 
 
 static long _dev_ioctl(struct file *file, unsigned cmd, unsigned long arg) {
-  if (_debug) printk(KERN_INFO "bbserial: ioctl() cmd=%d arg=%lu\n", cmd, arg);
+  if (debug)
+    printk(KERN_INFO "bbserial: ioctl() cmd=0x%04x arg=%lu\n", cmd, arg);
 
   int __user *ptr = (int __user *)arg;
+  int status;
 
   switch (cmd) {
-  case TCGETS:   // TODO Get serial port settings
-  case TCSETS:   // TODO Set serial port settings
-  case TIOCMBIS: // TODO Set modem control state
+  case TCGETS: { // Get serial port settings
+    struct ktermios *term = _get_term();
+    if (copy_to_user((void __user *)arg, &term, sizeof(struct termios)))
+      return -EFAULT;
     return 0;
+  }
+
+  case TCSETS: { // Set serial port settings
+    struct ktermios term;
+    if (copy_from_user(&term, (void __user *)arg, sizeof(struct termios)))
+      return -EFAULT;
+    return _set_term(&term);
+  }
+
+  case TIOCMGET: // Get status of modem bits
+    put_user(_read_status(), ptr);
+    return 0;
+
+  case TIOCMSET: // Set status of modem bits
+    get_user(status, ptr);
+    _write_status(status);
+    return 0;
+
+  case TIOCMBIC: // Clear indicated modem bits
+    get_user(status, ptr);
+    _write_status(~status & _read_status());
+    return 0;
+
+  case TIOCMBIS: // Set indicated modem bits
+    get_user(status, ptr);
+    _write_status(status | _read_status());
+    return 0;
+
+  case TCFLSH: // Flush
+    if (arg == TCIFLUSH || arg == TCIOFLUSH) _flush_input();
+    if (arg == TCOFLUSH || arg == TCIOFLUSH) _flush_output();
+    return 0;
+
+  case TIOCINQ:  return put_user(RING_BUF_FILL(_port.rx_buf), ptr);
+  case TIOCOUTQ: return put_user(RING_BUF_FILL(_port.tx_buf), ptr);
 
-  case TCFLSH: return 0;
-  case FIONREAD: return put_user(RING_BUF_FILL(_port.rx_buf), ptr);
   default: return -ENOIOCTLCMD;
   }
 
@@ -381,7 +517,7 @@ static struct file_operations _ops = {
 
 
 static int _probe(struct amba_device *dev, const struct amba_id *id) {
-  if (_debug) printk(KERN_INFO "bbserial: probing\n");
+  if (debug) printk(KERN_INFO "bbserial: probing\n");
 
   // Allocate buffers
   _port.tx_buf.buf = devm_kzalloc(&dev->dev, BUF_SIZE, GFP_KERNEL);
@@ -392,7 +528,7 @@ static int _probe(struct amba_device *dev, const struct amba_id *id) {
   _port.base = devm_ioremap_resource(&dev->dev, &dev->res);
   if (IS_ERR(_port.base)) {
     dev_err(&dev->dev, "bbserial: failed to map IO memory\n");
-   return PTR_ERR(_port.base);
+    return PTR_ERR(_port.base);
   }
 
   // Get and enable clock
@@ -409,33 +545,28 @@ static int _probe(struct amba_device *dev, const struct amba_id *id) {
   }
 
   // Disable UART and mask interrupts
-  _write(0, REG_CR);
-  _write(0, REG_IMSC);
+  _write(0, UART011_CR);
+  _write(0, UART011_IMSC);
 
-  // Set baud rate
-  const unsigned baud = 230400;
-  unsigned brd = clk_get_rate(_port.clk) * 16 / baud;
-  if ((brd & 3) == 3) brd = (brd >> 2) + 1;
-  else brd >>= 2;
-  _write(brd & 0x3f, REG_FBRD);
-  _write(brd >> 6,   REG_IBRD);
+  // Set default baud rate
+  _set_baud(38400);
 
-  // N81 & enable FIFOs
-  _write(UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN, REG_LCRH_RX);
+  // N81 & enable FIFOs, must be after baud
+  _write(UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN, UART011_LCRH);
 
   // Enable, TX, RX, RTS, DTR & CTS
   unsigned cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE |
     UART011_CR_RTS | UART011_CR_DTR | UART011_CR_CTSEN;
-  _write(cr, REG_CR);
+  _write(cr, UART011_CR);
 
   // Set interrupt FIFO trigger levels
-  _write(UART011_IFLS_RX2_8 | UART011_IFLS_TX6_8, REG_IFLS);
+  _write(UART011_IFLS_RX2_8 | UART011_IFLS_TX6_8, UART011_IFLS);
 
   // Clear pending interrupts
-  _write(0x7ff, REG_ICR);
+  _write(0x7ff, UART011_ICR);
 
   // Enable read interrupts
-  _port.im = _read(REG_IMSC);
+  _port.im = 0;
   _enable_rx();
 
   // Allocate IRQ
@@ -461,7 +592,7 @@ static int _probe(struct amba_device *dev, const struct amba_id *id) {
   if (IS_ERR(_port.class)) {
     unregister_chrdev(_port.major, DEVICE_NAME);
     clk_disable_unprepare(_port.clk);
-    dev_err(&dev->dev, "bbserial: ailed to register device class\n");
+    dev_err(&dev->dev, "bbserial: failed to register device class\n");
     return PTR_ERR(_port.class);
   }
 
@@ -480,17 +611,17 @@ static int _probe(struct amba_device *dev, const struct amba_id *id) {
 
 
 static int _remove(struct amba_device *dev) {
-  if (_debug) printk(KERN_INFO "bbserial: removing\n");
+  if (debug) printk(KERN_INFO "bbserial: removing\n");
 
   unsigned long flags;
   spin_lock_irqsave(&_port.lock, flags);
 
   // Mask and clear interrupts
-  _write(0, REG_IMSC);
-  _write(0x7ff, REG_ICR);
+  _write(0, UART011_IMSC);
+  _write(0x7ff, UART011_ICR);
 
   // Disable UART
-  _write(0, REG_CR);
+  _write(0, UART011_CR);
 
   spin_unlock_irqrestore(&_port.lock, flags);
 
@@ -542,7 +673,8 @@ static int __init bbserial_init(void) {
   spin_lock_init(&_port.lock);
 
   // Init wait queues
-  init_waitqueue_head(&_port.wait);
+  init_waitqueue_head(&_port.read_wait);
+  init_waitqueue_head(&_port.write_wait);
 
   return amba_driver_register(&_driver);
 }
index 058667372a70cb2ae3be314b9cb7837a59208909..0285784fee677b9cbb46b47a56a7af2f2e91ff37 100644 (file)
@@ -80,17 +80,17 @@ module.exports = {
     },
 
 
-    motor_fault_class: function (motor, fault) {
+    motor_fault_class: function (motor, bit) {
       if (typeof motor == 'undefined') {
         var status = this.state['fa'];
         if (typeof status == 'undefined') return 'fa-question';
         return 'fa-thumbs-' + (status ? 'down error' : 'up success')
       }
 
-      var status = this.state[motor + 'ds'];
-      if (typeof status == 'undefined') return 'fa-question';
-      return status.indexOf(fault) == -1 ? 'fa-thumbs-up success' :
-        'fa-thumbs-down error';
+      var flags = this.state[motor + 'df'];
+      if (typeof flags == 'undefined') return 'fa-question';
+      return (flags & (1 << bit)) ? 'fa-thumbs-down error' :
+        'fa-thumbs-up success';
     },
 
 
index f960c0c197e5e8e8db5bf5178dd3118424606cd7..34e56b5f43918c28473f5bd995dd5384e7dd416f 100644 (file)
@@ -95,6 +95,9 @@ html(lang="en")
               .title
                 span.left Build
                 span.right botics
+                .fa.fa-thermometer-full(class="error",
+                  v-if="80 <= state.rpi_temp",
+                  title="Raspberry Pi temperature too high.")
               .subtitle
                 | CNC Controller #[b {{state.demo ? 'Demo ' : ''}}]
                 | v{{config.version}}
index 37040dc595a1cfde95911acf7977d0afe238897b..cbcfa240d67a821a4db1aba85684830b8178f909 100644 (file)
@@ -52,7 +52,7 @@ script#admin-general-view-template(type="text/x-template")
     button.pure-button.pure-button-primary(@click="confirmReset = true") Reset
     message(:show.sync="confirmReset")
       h3(slot="header") Reset to default configuration?
-      p(slot="body") All configuration changes will be lost.
+      p(slot="body") Non-network configuration changes will be lost.
       div(slot="footer")
         button.pure-button(@click="confirmReset = false") Cancel
         button.pure-button.button-success(@click="reset") OK
index 05697327684bb6ad295658d9fdae8a4ce41bf265..70821c55fa098df7bc6866ffdfbb312a474d6bce 100644 (file)
@@ -191,17 +191,17 @@ script#indicators-template(type="text/x-template")
 
       tr(v-for="motor in [0, 1, 2, 3]")
         td {{motor}}
-        td: .fa(:class="motor_fault_class(motor, 'temp')",
+        td: .fa(:class="motor_fault_class(motor, 0)",
           title="Overtemperature fault")
-        td: .fa(:class="motor_fault_class(motor, 'current a')",
+        td: .fa(:class="motor_fault_class(motor, 1)",
           title="Overcurrent motor channel A")
-        td: .fa(:class="motor_fault_class(motor, 'fault a')",
+        td: .fa(:class="motor_fault_class(motor, 3)",
           title="Predriver fault motor channel A")
-        td: .fa(:class="motor_fault_class(motor, 'current b')",
+        td: .fa(:class="motor_fault_class(motor, 2)",
           title="Overcurrent motor channel B")
-        td: .fa(:class="motor_fault_class(motor, 'fault b')",
+        td: .fa(:class="motor_fault_class(motor, 4)",
           title="Predriver fault motor channel B")
-        td: .fa(:class="motor_fault_class(motor, 'comm')",
+        td: .fa(:class="motor_fault_class(motor, 8)",
           title="Driver communication failure")
         td(:title="'Reset motor ' + motor + ' flags'")
           .fa.fa-eraser(@click="motor_reset(motor)")
@@ -221,8 +221,8 @@ script#indicators-template(type="text/x-template")
         td {{state.motor | fixed 2}} A
         th Motor
         th.separator
-        td {{state.temp | fixed 0}} ℃
-        th Temp
+        td {{state.vdd | fixed 1}} V
+        th Low-side
 
       tr
         td {{state.load1 | fixed 2}} A
@@ -231,6 +231,14 @@ script#indicators-template(type="text/x-template")
         td {{state.load2 | fixed 2}} A
         th Load 2
 
+      tr
+        td {{state.temp | fixed 0}} ℃
+        th Temp
+        th.separator
+        td(:class="{'error': 80 <= state.rpi_temp}")
+          | {{state.rpi_temp | fixed 0}} ℃
+        th RPi Temp
+
       tr
         td {{state['1ai'] | percent 0}} A
         th Analog 1
index 68004fc2ce3fef1bb13ce9d00e45d731ea31cb2f..0034cfe07c435ae61485780d69641beb84c4e588 100644 (file)
@@ -94,9 +94,7 @@ class Config(object):
         except:
             return False
 
-        if (('min' in template and value < template['min']) or
-            ('max' in template and template['max'] < value) or
-            ('values' in template and value not in template['values'])):
+        if 'values' in template and value not in template['values']:
             return False
 
         return True
@@ -108,6 +106,12 @@ class Config(object):
                 not self._valid_value(template, config[name])):
                 config[name] = template['default']
 
+            elif 'max' in template and template['max'] < config[name]:
+                config[name] = template['max']
+
+            elif 'min' in template and config[name] < template['min']:
+                config[name] = template['min']
+
             if template['type'] == 'list':
                 config = config[name]
 
@@ -182,7 +186,7 @@ class Config(object):
 
 
     def reset(self):
-        os.unlink('config.json')
+        if os.path.exists('config.json'): os.unlink('config.json')
         self.reload()
         self.ctrl.preplanner.invalidate_all()
 
index 090be3648b069a6041af697962cffeffcd588d98..6bcfbb3ad9f65f3237186fce19a37c2aa03264ca 100644 (file)
@@ -27,6 +27,7 @@
 
 import os
 import sys
+import io
 import datetime
 import traceback
 import pkg_resources
@@ -43,6 +44,10 @@ ERROR    = 3
 def get_level_name(level): return 'debug info warning error'.split()[level]
 
 
+# Get this file's name
+_srcfile = os.path.normcase(get_level_name.__code__.co_filename)
+
+
 class Logger(object):
     def __init__(self, log, name, level):
         self.log = log
@@ -54,13 +59,29 @@ class Logger(object):
     def _enabled(self, level): return self.level <= level and level <= ERROR
 
 
+    def _find_caller(self):
+        f = sys._getframe()
+        if f is not None: f = f.f_back
+
+        while hasattr(f, 'f_code'):
+            co = f.f_code
+
+            filename = os.path.normcase(co.co_filename)
+            if filename == _srcfile:
+                f = f.f_back
+                continue
+
+            return co.co_filename, f.f_lineno, co.co_name
+
+        return '(unknown file)', 0, '(unknown function)'
+
+
     def _log(self, level, msg, *args, **kwargs):
         if not self._enabled(level): return
 
         if not 'where' in kwargs:
-            caller = getframeinfo(stack()[2][0])
-            kwargs['where'] = '%s:%d' % (
-                os.path.basename(caller.filename), caller.lineno)
+            filename, line, func = self._find_caller()
+            kwargs['where'] = '%s:%d' % (os.path.basename(filename), line)
 
         if len(args): msg %= args
 
index 839b2e641fc3e80d9c33324f498bb03b9fc8f62d..0d1501d33a9f09f7022a7c1644992312d226d77b 100644 (file)
@@ -42,9 +42,9 @@ class MonitorTemp(object):
     def __init__(self, app):
         self.app = app
 
-        ctrl = app.get_ctrl()
-        self.log = ctrl.log.get('Mon')
-        self.ioloop = ctrl.ioloop
+        self.ctrl = app.get_ctrl()
+        self.log = self.ctrl.log.get('Mon')
+        self.ioloop = self.ctrl.ioloop
 
         self.last_temp_warn = 0
         self.temp_thresh = 80
@@ -94,6 +94,7 @@ class MonitorTemp(object):
         try:
             temp = read_temp()
 
+            self.ctrl.state.set('rpi_temp', temp)
             self.scale_cpu(temp)
             self.update_camera(temp)
             self.log_warnings(temp)
index 3f54b1937227b79d27a884421cbb187f94bebacb..bf6dd2427d4ffa3fca84cac32072578cf4d4ee16 100644 (file)
@@ -126,7 +126,7 @@ def parse_args():
                         type = int, help = 'HTTP port')
     parser.add_argument('-a', '--addr', metavar = 'IP', default = '0.0.0.0',
                         help = 'HTTP address to bind')
-    parser.add_argument('-s', '--serial', default = '/dev/ttyBB0',
+    parser.add_argument('-s', '--serial', default = '/dev/ttyAMA0',
                         help = 'Serial device')
     parser.add_argument('-b', '--baud', default = 230400, type = int,
                         help = 'Serial baud rate')
index 591e18a8ff45a490c6b1f481374e7d9c1f74428f..0b988b5a570629dcacb598bee198d1aca86ba745 100644 (file)
@@ -34,7 +34,7 @@
         "drive-current": {
           "type": "float",
           "min": 0,
-          "max": 8,
+          "max": 6,
           "unit": "amps",
           "default": 1.5,
           "code": "dc"