Continuing work on huanyang/modbus split
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Thu, 22 Mar 2018 18:03:25 +0000 (11:03 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Mon, 26 Mar 2018 05:41:16 +0000 (22:41 -0700)
14 files changed:
CHANGELOG.md
src/avr/src/config.h
src/avr/src/huanyang.c
src/avr/src/huanyang.h
src/avr/src/modbus.c [new file with mode: 0644]
src/avr/src/modbus.h [new file with mode: 0644]
src/avr/src/pwm_spindle.h
src/avr/src/rtc.c
src/avr/src/usart.c
src/avr/src/usart.h
src/avr/src/vars.def
src/jade/templates/tool-view.jade
src/js/tool-view.js
src/resources/config-template.json

index ac11208494c2c7e8e36fddded8e2e3c0dabf2885..7e15bb52c555734aecdaebc835618b8979594cf1 100644 (file)
@@ -1,8 +1,9 @@
-Buildbotics CNC Controller Firmware Change Log
+Buildbotics CNC Controller Firmware Changelog
 ==============================================
 
 ## v0.3.21
  - Implemented M70-M73 modal state save/restore.
+ - Added support for modbus VFDs.
 
 ## v0.3.20
  - Eliminated drift caused by miscounting half microsteps.
@@ -166,4 +167,4 @@ Buildbotics CNC Controller Firmware Change Log
  - Start Web server eariler in case of Python coding errors
 
 
-Change log not maintained in previous versions.  See git commit log.
+Changelog not maintained in previous versions.  See git commit log.
index a8b64dee91bd5cb8d6916f2d250b3ddf4fcfdc77..648e499204da21fcf874ba0d6a6e2d4c2105ce57 100644 (file)
@@ -172,14 +172,17 @@ enum {
                        DRV8711_CTRL_EXSTALL_bm)
 
 
-// Huanyang settings
-#define HUANYANG_PORT          USARTD1
-#define HUANYANG_DRE_vect      USARTD1_DRE_vect
-#define HUANYANG_TXC_vect      USARTD1_TXC_vect
-#define HUANYANG_RXC_vect      USARTD1_RXC_vect
-#define HUANYANG_TIMEOUT       50 // ms. response timeout
-#define HUANYANG_RETRIES        4 // Number of retries before failure
-#define HUANYANG_ID             1 // Default ID
+// RS485 settings
+#define RS485_PORT             USARTD1
+#define RS485_DRE_vect         USARTD1_DRE_vect
+#define RS485_TXC_vect         USARTD1_TXC_vect
+#define RS485_RXC_vect         USARTD1_RXC_vect
+
+
+// Modbus settings
+#define MODBUS_TIMEOUT         50 // ms. response timeout
+#define MODBUS_RETRIES          4 // Number of retries before failure
+#define MODBUS_BUF_SIZE        64 // Max bytes in rx/tx buffers
 
 
 // Serial settings
index 547332a500f7738e7a8b24c49983602f55d2bf37..87d47a68c7f31542fc2d43b67095768a25eccb03 100644 (file)
 
 #include "huanyang.h"
 #include "config.h"
-#include "rtc.h"
+#include "modbus.h"
 #include "report.h"
-#include "pgmspace.h"
 
-#include <avr/io.h>
-#include <avr/interrupt.h>
-#include <util/crc16.h>
-
-#include <stdio.h>
 #include <string.h>
 #include <math.h>
 
+
 /*
-  Huanyang supposedly is not quite Modbus compliant.
+  Huanyang is not quite Modbus compliant.
 
   Message format is:
 
-    [id][cmd][length][data][checksum]
+    [id][func][length][data][checksum]
 
   Where:
 
      id       - 1-byte Peer ID
-     cmd      - 1-byte One of hy_cmd_t
+     func     - 1-byte One of hy_func_t
      length   - 1-byte Length data in bytes
      data     - length bytes - Command arguments
      checksum - 16-bit CRC: x^16 + x^15 + x^2 + 1 (0xa001) initial: 0xffff
@@ -58,7 +53,7 @@
 
 // See VFD manual pg56 3.1.3
 typedef enum {
-  HUANYANG_FUNC_READ = 1, // Use hy_func_addr_t
+  HUANYANG_FUNC_READ = 1, // Use hy_addr_t
   HUANYANG_FUNC_WRITE,    // ?
   HUANYANG_CTRL_WRITE,    // Use hy_ctrl_state_t
   HUANYANG_CTRL_READ,     // Use hy_ctrl_addr_t
@@ -66,7 +61,7 @@ typedef enum {
   HUANYANG_RESERVED_1,
   HUANYANG_RESERVED_2,
   HUANYANG_LOOP_TEST,
-} hy_cmd_t;
+} hy_func_t;
 
 
 // See VFD manual pg57 3.1.3.d
@@ -106,25 +101,16 @@ typedef enum {
 } hy_ctrl_status_t;
 
 
-typedef bool (*next_command_cb_t)(int index);
+static struct {
+  uint8_t id;
 
+  uint8_t state;
+  hy_func_t func;
+  uint8_t data[4];
+  uint8_t bytes;
+  uint8_t response;
 
-typedef struct {
-  uint8_t id;
-  bool debug;
-
-  next_command_cb_t next_command_cb;
-  uint8_t command_index;
-  uint8_t current_offset;
-  uint8_t command[8];
-  uint8_t command_length;
-  uint8_t response_length;
-  uint8_t response[10];
-  uint32_t last;
-  uint8_t retry;
-
-  uint8_t shutdown;
-  bool connected;
+  bool shutdown;
   bool changed;
   float speed;
 
@@ -138,424 +124,213 @@ typedef struct {
   uint16_t rated_rpm;
 
   uint8_t status;
-} hy_t;
-
-
-static hy_t ha = {0};
-
-
-#define CTRL_STATUS_RESPONSE(R) ((uint16_t)R[4] << 8 | R[5])
-#define FUNC_RESPONSE(R) (R[2] == 2 ? R[4] : ((uint16_t)R[4] << 8 | R[5]))
-
-
-static uint16_t _crc16(const uint8_t *buffer, unsigned length) {
-  uint16_t crc = 0xffff;
-
-  for (unsigned i = 0; i < length; i++)
-    crc = _crc16_update(crc, buffer[i]);
-
-  return crc;
-}
-
-
-static void _set_baud(uint16_t bsel, uint8_t bscale) {
-  HUANYANG_PORT.BAUDCTRLB = (uint8_t)((bscale << 4) | (bsel >> 8));
-  HUANYANG_PORT.BAUDCTRLA = bsel;
-}
-
-
-static void _set_write(bool x) {SET_PIN(RS485_RW_PIN, x);}
-
-
-static void _set_dre_interrupt(bool enable) {
-  if (enable) HUANYANG_PORT.CTRLA |= USART_DREINTLVL_MED_gc;
-  else HUANYANG_PORT.CTRLA &= ~USART_DREINTLVL_MED_gc;
-}
+} hy = {1}; // Default ID
 
 
-static void _set_txc_interrupt(bool enable) {
-  if (enable) HUANYANG_PORT.CTRLA |= USART_TXCINTLVL_MED_gc;
-  else HUANYANG_PORT.CTRLA &= ~USART_TXCINTLVL_MED_gc;
+static void _ctrl_write(hy_ctrl_state_t state) {
+  hy.func     = HUANYANG_CTRL_WRITE;
+  hy.bytes    = 2;
+  hy.response = 2;
+  hy.data[0]  = 1;
+  hy.data[1]  = state;
 }
 
 
-static void _set_rxc_interrupt(bool enable) {
-  if (enable) HUANYANG_PORT.CTRLA |= USART_RXCINTLVL_MED_gc;
-  else HUANYANG_PORT.CTRLA &= ~USART_RXCINTLVL_MED_gc;
+static void _ctrl_read(hy_ctrl_addr_t addr) {
+  hy.func     = HUANYANG_CTRL_READ;
+  hy.bytes    = 2;
+  hy.response = 4;
+  hy.data[0]  = 1;
+  hy.data[1]  = addr;
 }
 
 
-static void _set_command1(int code, uint8_t arg0) {
-  ha.command_length = 4;
-  ha.command[1] = code;
-  ha.command[2] = 1;
-  ha.command[3] = arg0;
+static void _freq_write(uint16_t freq) {
+  hy.func     = HUANYANG_FREQ_WRITE;
+  hy.bytes    = 3;
+  hy.response = 2;
+  hy.data[0]  = 2;
+  hy.data[1]  = freq >> 8;
+  hy.data[2]  = freq;
 }
 
 
-static void _set_command2(int code, uint8_t arg0, uint8_t arg1) {
-  ha.command_length = 5;
-  ha.command[1] = code;
-  ha.command[2] = 2;
-  ha.command[3] = arg0;
-  ha.command[4] = arg1;
+static void _func_read(uint16_t addr) {
+  hy.func     = HUANYANG_FUNC_READ;
+  hy.bytes    = 4;
+  hy.response = 4;
+  hy.data[0]  = 3;
+  hy.data[1]  = addr;
+  hy.data[2]  = addr >> 8;
+  hy.data[3]  = 0;
 }
 
 
-static void _set_command3(int code, uint8_t arg0, uint8_t arg1, uint8_t arg2) {
-  ha.command_length = 6;
-  ha.command[1] = code;
-  ha.command[2] = 3;
-  ha.command[3] = arg0;
-  ha.command[4] = arg1;
-  ha.command[5] = arg2;
-}
-
-
-static int _response_length(int code) {
-  switch (code) {
-  case HUANYANG_FUNC_READ:  return 8;
-  case HUANYANG_FUNC_WRITE: return 8;
-  case HUANYANG_CTRL_WRITE: return 6;
-  case HUANYANG_CTRL_READ:  return 8;
-  case HUANYANG_FREQ_WRITE: return 7;
-  default: return -1;
+static void _func_read_response(hy_addr_t addr, uint16_t value) {
+  switch (addr) {
+  case HY_PD005_MAX_FREQUENCY:         hy.max_freq  = value * 0.01; break;
+  case HY_PD011_FREQUENCY_LOWER_LIMIT: hy.min_freq  = value * 0.01; break;
+  case HY_PD144_RATED_MOTOR_RPM:       hy.rated_rpm = value;        break;
+  default: break;
   }
 }
 
 
-static bool _update(int index) {
-  // Read response
-  switch (index) {
-  case 1: ha.status = ha.response[5]; break;
-  case 2: ha.max_freq = FUNC_RESPONSE(ha.response) * 0.01; break;
-  case 3: ha.min_freq = FUNC_RESPONSE(ha.response) * 0.01; break;
-  case 4: ha.rated_rpm = FUNC_RESPONSE(ha.response); break;
+static void _ctrl_read_response(hy_ctrl_addr_t addr, uint16_t value) {
+  switch (addr) {
+  case HUANYANG_ACTUAL_FREQ:    hy.actual_freq    = value * 0.01; break;
+  case HUANYANG_ACTUAL_CURRENT: hy.actual_current = value * 0.01; break;
+  case HUANYANG_ACTUAL_RPM:     hy.actual_rpm     = value;        break;
+  case HUANYANG_TEMPERATURE:    hy.temperature    = value;        break;
   default: break;
   }
+}
 
-  // Setup next command
-  uint8_t var;
-  switch (index) {
-  case 0: { // Update direction
-    uint8_t state = HUANYANG_STOP;
-    if (!ha.shutdown) {
-      if (0 < ha.speed) state = HUANYANG_RUN;
-      else if (ha.speed < 0) state = HUANYANG_RUN | HUANYANG_REV_FWD;
-    }
-
-    _set_command1(HUANYANG_CTRL_WRITE, state);
-
-    return true;
-  }
-
-  case 1: var = HY_PD005_MAX_FREQUENCY; break;
-  case 2: var = HY_PD011_FREQUENCY_LOWER_LIMIT; break;
-  case 3: var = HY_PD144_RATED_MOTOR_RPM; break;
-
-  case 4: { // Update freqency
-    // Compute frequency in Hz
-    float freq = fabs(ha.speed * 50 / ha.rated_rpm);
-
-    // Clamp frequency
-    if (ha.max_freq < freq) freq = ha.max_freq;
-    if (freq < ha.min_freq) freq = ha.min_freq;
-    if (ha.shutdown) freq = 0;
-
-    // Frequency write command
-    uint16_t f = freq * 100;
-    _set_command2(HUANYANG_FREQ_WRITE, f >> 8, f);
 
-    if (1 < ha.shutdown) ha.shutdown--;
+static uint16_t _read_word(const uint8_t *data) {
+  return (uint16_t)data[0] << 8 | data[1];
+}
 
-    return true;
-  }
 
-  default:
-    report_request();
-    return false;
+static void _handle_response(hy_func_t func, const uint8_t *data) {
+  switch (func) {
+  case HUANYANG_FUNC_READ: {
+    _func_read_response((hy_addr_t)_read_word(data), _read_word(data + 2));
+    break;
   }
 
-  _set_command3(HUANYANG_FUNC_READ, var, 0, 0);
-
-  return true;
-}
-
+  case HUANYANG_FUNC_WRITE: break;
+  case HUANYANG_CTRL_WRITE: hy.status = _read_word(data); break;
 
-static bool _query_status(int index) {
-  // Read response
-  switch (index) {
-  case 1: ha.actual_freq = CTRL_STATUS_RESPONSE(ha.response) * 0.01; break;
-  case 2: ha.actual_current = CTRL_STATUS_RESPONSE(ha.response) * 0.1; break;
-  case 3: ha.actual_rpm = CTRL_STATUS_RESPONSE(ha.response); break;
-  case 4: ha.temperature = CTRL_STATUS_RESPONSE(ha.response); break;
-  default: break;
+  case HUANYANG_CTRL_READ: {
+    _ctrl_read_response((hy_ctrl_addr_t)_read_word(data), _read_word(data + 2));
+    break;
   }
 
-  // Setup next command
-  uint8_t var;
-  switch (index) {
-  case 0: var = HUANYANG_ACTUAL_FREQ; break;
-  case 1: var = HUANYANG_ACTUAL_CURRENT; break;
-  case 2: var = HUANYANG_ACTUAL_RPM; break;
-  case 3: var = HUANYANG_TEMPERATURE; break;
-  default:
-    report_request();
-    return false;
+  case HUANYANG_FREQ_WRITE: break;
+  default: break;
   }
-
-  _set_command1(HUANYANG_CTRL_READ, var);
-
-  return true;
 }
 
 
 static void _next_command();
 
 
-static void _next_state() {
-  if (ha.changed || ha.shutdown) {
-    ha.next_command_cb = _update;
-    ha.changed = false;
-
-  } else ha.next_command_cb = _query_status;
-
-  _next_command();
-}
-
-
-static bool _check_response() {
-  // Check CRC
-  uint16_t computed = _crc16(ha.response, ha.response_length - 2);
-  uint16_t expected = (ha.response[ha.response_length - 1] << 8) |
-    ha.response[ha.response_length - 2];
-
-  if (computed != expected) {
-    STATUS_WARNING(STAT_OK, "huanyang: invalid CRC, expected=0x%04u got=0x%04u",
-                   expected, computed);
-    return false;
-  }
-
-  // Check if response code matches the code we sent
-  if (ha.command[1] != ha.response[1]) {
-    STATUS_WARNING(STAT_OK,
-                   "huanyang: invalid function code, expected=%u got=%u",
-                   ha.command[2], ha.response[2]);
-    return false;
-  }
-
-  return true;
-}
-
-
 static void _reset(bool halt) {
-  _set_dre_interrupt(false);
-  _set_txc_interrupt(false);
-  _set_rxc_interrupt(false);
-  _set_write(true); // RS485 write mode
-
-  // Flush USART
-  uint8_t x = HUANYANG_PORT.DATA;
-  x = HUANYANG_PORT.STATUS;
-  x = x;
-
   // Save settings
-  uint8_t id = ha.id;
-  float speed = ha.speed;
-  bool debug = ha.debug;
+  uint8_t id = hy.id;
+  float speed = hy.speed;
 
   // Clear state
-  memset(&ha, 0, sizeof(ha));
+  memset(&hy, 0, sizeof(hy));
 
   // Restore settings
-  ha.id = id;
-  ha.speed = speed;
-  ha.debug = debug;
-  ha.changed = true;
+  hy.id = id;
+  hy.speed = speed;
+  hy.changed = true;
 
-  if (!halt) _next_state();
+  if (!halt) _next_command();
 }
 
 
-static void _next_command() {
-  if (ha.shutdown == 1) {
-    _reset(true);
-
-    // Disable USART
-    HUANYANG_PORT.CTRLA = 0;
-    HUANYANG_PORT.CTRLC = 0;
-    HUANYANG_PORT.CTRLB = 0;
-
-    // Float write pins
-    DIRCLR_PIN(RS485_DI_PIN);
-    DIRCLR_PIN(RS485_RW_PIN);
-
-    return;
-  }
-
-  if (ha.next_command_cb && ha.next_command_cb(ha.command_index++)) {
-    ha.response_length = _response_length(ha.command[1]);
-
-    ha.command[0] = ha.id;
+static void _modbus_cb(uint8_t slave, uint8_t func, uint8_t bytes,
+                       const uint8_t *data) {
+  if (data && bytes == *data + 1) {
+    _handle_response((hy_func_t)func, data + 1);
 
-    uint16_t crc = _crc16(ha.command, ha.command_length);
-    ha.command[ha.command_length++] = crc;
-    ha.command[ha.command_length++] = crc >> 8;
+    if (func == HUANYANG_CTRL_WRITE && hy.shutdown) {
+      _reset(true);
+      modbus_deinit();
+      return;
+    }
 
-    _set_dre_interrupt(true);
+    // Next command
+    if (++hy.state == 9) {
+      hy.state = 5;
+      report_request();
+    }
 
-    return;
+    // Update freq
+    if (hy.shutdown || (hy.changed && 4 < hy.state)) hy.state = 0;
   }
 
-  ha.command_index = 0;
-  _next_state();
-}
-
-
-static void _retry_command() {
-  ha.last = 0;
-  ha.current_offset = 0;
-  ha.retry++;
-
-  _set_write(true); // RS485 write mode
-
-  _set_txc_interrupt(false);
-  _set_rxc_interrupt(false);
-  _set_dre_interrupt(true);
-
-  if (ha.debug) STATUS_DEBUG("huanyang: retry %d", ha.retry);
+  _next_command();
 }
 
 
-// Data register empty interrupt
-ISR(HUANYANG_DRE_vect) {
-  HUANYANG_PORT.DATA = ha.command[ha.current_offset++];
+static void _next_command() {
+  switch (hy.state) {
+  case 0: { // Update direction
+    hy_ctrl_state_t state = HUANYANG_STOP;
+    if (!hy.shutdown) {
+      if (0 < hy.speed) state = HUANYANG_RUN;
+      else if (hy.speed < 0)
+        state = (hy_ctrl_state_t)(HUANYANG_RUN | HUANYANG_REV_FWD);
+    }
 
-  if (ha.current_offset == ha.command_length) {
-    _set_dre_interrupt(false);
-    _set_txc_interrupt(true);
-    ha.current_offset = 0;
+    _ctrl_write(state);
+    break;
   }
-}
 
+  case 1: _func_read(HY_PD005_MAX_FREQUENCY); break;
+  case 2: _func_read(HY_PD011_FREQUENCY_LOWER_LIMIT); break;
+  case 3: _func_read(HY_PD144_RATED_MOTOR_RPM); break;
 
-/// Transmit complete interrupt
-ISR(HUANYANG_TXC_vect) {
-  _set_txc_interrupt(false);
-  _set_rxc_interrupt(true);
-  _set_write(false); // RS485 read mode
-  ha.last = rtc_get_time(); // Set timeout timer
-}
-
-
-// Data received interrupt
-ISR(HUANYANG_RXC_vect) {
-  ha.response[ha.current_offset++] = HUANYANG_PORT.DATA;
+  case 4: { // Update freqency
+    // Compute frequency in Hz
+    float freq = fabs(hy.speed * 50 / hy.rated_rpm);
 
-  if (ha.current_offset == ha.response_length) {
-    _set_rxc_interrupt(false);
-    ha.current_offset = 0;
-    _set_write(true); // RS485 write mode
+    // Clamp frequency
+    if (hy.max_freq < freq) freq = hy.max_freq;
+    if (freq < hy.min_freq) freq = hy.min_freq;
 
-    if (_check_response())  {
-      ha.last = 0; // Clear timeout timer
-      ha.retry = 0; // Reset retry counter
-      ha.connected = true;
+    // Frequency write command
+    _freq_write(freq * 100);
+    hy.changed = false;
+    break;
+  }
 
-      _next_command();
-    }
+  case 5: _ctrl_read(HUANYANG_ACTUAL_FREQ);    break;
+  case 6: _ctrl_read(HUANYANG_ACTUAL_CURRENT); break;
+  case 7: _ctrl_read(HUANYANG_ACTUAL_RPM);     break;
+  case 8: _ctrl_read(HUANYANG_TEMPERATURE);    break;
   }
+
+  // Send command
+  modbus_func(hy.id, hy.func, hy.bytes, hy.data, hy.response, _modbus_cb);
 }
 
 
 void hy_init() {
-  PR.PRPD &= ~PR_USART1_bm; // Disable power reduction
-
-  DIRCLR_PIN(RS485_RO_PIN); // Input
-  OUTSET_PIN(RS485_DI_PIN); // High
-  DIRSET_PIN(RS485_DI_PIN); // Output
-  OUTSET_PIN(RS485_RW_PIN); // High
-  DIRSET_PIN(RS485_RW_PIN); // Output
-
-  _set_baud(3325, 0b1101); // 9600 @ 32MHz with 2x USART
-
-  // No parity, 8 data bits, 1 stop bit
-  HUANYANG_PORT.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc |
-    USART_CHSIZE_8BIT_gc;
-
-  // Configure receiver and transmitter
-  HUANYANG_PORT.CTRLB = USART_RXEN_bm | USART_TXEN_bm | USART_CLK2X_bm;
-
-  ha.id = HUANYANG_ID;
-  hy_reset();
+  modbus_init();
+  _reset(false);
 }
 
 
-void hy_deinit() {ha.shutdown = 5;}
+void hy_deinit() {hy.shutdown = true;}
 
 
 void hy_set(float speed) {
-  if (ha.speed != speed) {
-    if (ha.debug) STATUS_DEBUG("huanyang: speed=%0.2f", speed);
-    ha.speed = speed;
-    ha.changed = true;
-  }
-}
-
-
-void hy_reset() {_reset(false);}
-
-
-void hy_rtc_callback() {
-  if (ha.last && rtc_expired(ha.last + HUANYANG_TIMEOUT)) {
-    if (ha.retry < HUANYANG_RETRIES) _retry_command();
-    else {
-      if (ha.debug) STATUS_DEBUG("huanyang: timedout");
-
-      if (ha.debug && ha.current_offset) {
-        const uint8_t buf_len = 8 * 2 + 1;
-        char sent[buf_len];
-        char received[buf_len];
-
-        uint8_t i;
-        for (i = 0; i < ha.command_length; i++)
-          sprintf(sent + i * 2, "%02x", ha.command[i]);
-        sent[i * 2] = 0;
-
-        for (i = 0; i < ha.current_offset; i++)
-          sprintf(received + i * 2, "%02x", ha.response[i]);
-        received[i * 2] = 0;
-
-        STATUS_DEBUG("huanyang: sent 0x%s received 0x%s expected %u bytes",
-                     sent, received, ha.response_length);
-      }
-
-      // Try changing pin polarity
-      PINCTRL_PIN(RS485_RO_PIN) ^= PORT_INVEN_bm;
-      PINCTRL_PIN(RS485_DI_PIN) ^= PORT_INVEN_bm;
-
-      hy_reset();
-    }
+  if (hy.speed != speed) {
+    hy.speed = speed;
+    hy.changed = true;
   }
 }
 
 
 void hy_stop() {
   hy_set(0);
-  hy_reset();
+  _reset(false);
 }
 
 
-uint8_t get_hy_id() {return ha.id;}
-void set_hy_id(uint8_t value) {ha.id = value;}
-bool get_hy_debug() {return ha.debug;}
-void set_hy_debug(bool value) {ha.debug = value;}
-bool get_hy_connected() {return ha.connected;}
-float get_hy_freq() {return ha.actual_freq;}
-float get_hy_current() {return ha.actual_current;}
-uint16_t get_hy_rpm() {return ha.actual_rpm;}
-uint16_t get_hy_temp() {return ha.temperature;}
-float get_hy_max_freq() {return ha.max_freq;}
-float get_hy_min_freq() {return ha.min_freq;}
-uint16_t get_hy_rated_rpm() {return ha.rated_rpm;}
-float get_hy_status() {return ha.status;}
+uint8_t get_hy_id() {return hy.id;}
+void set_hy_id(uint8_t value) {hy.id = value;}
+float get_hy_freq() {return hy.actual_freq;}
+float get_hy_current() {return hy.actual_current;}
+uint16_t get_hy_rpm() {return hy.actual_rpm;}
+uint16_t get_hy_temp() {return hy.temperature;}
+float get_hy_max_freq() {return hy.max_freq;}
+float get_hy_min_freq() {return hy.min_freq;}
+uint16_t get_hy_rated_rpm() {return hy.rated_rpm;}
+float get_hy_status() {return hy.status;}
index ae0cfa79eb44ee2e2f107c6af0507c09ed9661c8..739199d533c655682c7831c21f0ad7de89733263 100644 (file)
 void hy_init();
 void hy_deinit();
 void hy_set(float speed);
-void hy_reset();
-void hy_rtc_callback();
 void hy_stop();
 
+
 /// See Huanyang VFD user manual
 typedef enum {
   HY_PD000_PARAMETER_LOCK,
@@ -223,4 +222,4 @@ typedef enum {
   HY_PD181_SOFTWARE_VERSION,
   HY_PD182_MANUFACTURE_DATE,
   HY_PD183_SERIAL_NO,
-} hy_func_addr_t;
+} hy_addr_t;
diff --git a/src/avr/src/modbus.c b/src/avr/src/modbus.c
new file mode 100644 (file)
index 0000000..6e3bee1
--- /dev/null
@@ -0,0 +1,330 @@
+/******************************************************************************\
+
+                 This file is part of the Buildbotics firmware.
+
+                   Copyright (c) 2015 - 2018, Buildbotics LLC
+                              All rights reserved.
+
+      This file ("the software") is free software: you can redistribute it
+      and/or modify it under the terms of the GNU General Public License,
+       version 2 as published by the Free Software Foundation. You should
+       have received a copy of the GNU General Public License, version 2
+      along with the software. If not, see <http://www.gnu.org/licenses/>.
+
+      The software is distributed in the hope that it will be useful, but
+           WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+                Lesser General Public License for more details.
+
+        You should have received a copy of the GNU Lesser General Public
+                 License along with the software.  If not, see
+                        <http://www.gnu.org/licenses/>.
+
+                 For information regarding this software email:
+                   "Joseph Coffland" <joseph@buildbotics.com>
+
+\******************************************************************************/
+
+#include "modbus.h"
+#include "usart.h"
+#include "status.h"
+#include "rtc.h"
+#include "config.h"
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/crc16.h>
+
+#include <string.h>
+#include <stdio.h>
+
+
+static struct {
+  bool debug;
+  uint8_t baud;
+
+  uint8_t bytes;
+  uint8_t command[MODBUS_BUF_SIZE];
+  uint8_t command_length;
+  uint8_t response[MODBUS_BUF_SIZE];
+  uint8_t response_length;
+
+  modbus_cb_t receive_cb;
+
+  uint32_t last;
+  uint8_t retry;
+  bool connected;
+  bool busy;
+} mb = {true, USART_BAUD_9600};
+
+
+static uint16_t _crc16(const uint8_t *buffer, unsigned length) {
+  uint16_t crc = 0xffff;
+
+  for (unsigned i = 0; i < length; i++)
+    crc = _crc16_update(crc, buffer[i]);
+
+  return crc;
+}
+
+
+static void _set_write(bool x) {SET_PIN(RS485_RW_PIN, x);}
+
+
+#define INTLVL_SET(REG, NAME, LEVEL)                        \
+  REG = ((REG) & ~NAME##INTLVL_gm) | NAME##INTLVL_##LEVEL##_gc
+
+
+#define INTLVL_ENABLE(REG, NAME, LEVEL, ENABLE)  do {   \
+    if (ENABLE) INTLVL_SET(REG, NAME, LEVEL);           \
+    else INTLVL_SET(REG, NAME, OFF);                    \
+  } while (0)
+
+
+static void _set_dre_interrupt(bool enable) {
+  INTLVL_ENABLE(RS485_PORT.CTRLA, USART_DRE, MED, enable);
+}
+
+
+static void _set_txc_interrupt(bool enable) {
+  INTLVL_ENABLE(RS485_PORT.CTRLA, USART_TXC, MED, enable);
+}
+
+
+static void _set_rxc_interrupt(bool enable) {
+  INTLVL_ENABLE(RS485_PORT.CTRLA, USART_RXC, MED, enable);
+}
+
+
+static bool _check_response() {
+  // Check CRC
+  uint16_t computed = _crc16(mb.response, mb.response_length - 2);
+  uint16_t expected = (mb.response[mb.response_length - 1] << 8) |
+    mb.response[mb.response_length - 2];
+
+  if (computed != expected) {
+    STATUS_WARNING(STAT_OK, "modbus: invalid CRC, expected=0x%04u got=0x%04u",
+                   expected, computed);
+    return false;
+  }
+
+  // Check that slave id matches
+  if (mb.command[0] != mb.response[0]) {
+    STATUS_WARNING(STAT_OK, "modbus: unexpected slave id, expected=%u got=%u",
+                   mb.command[0], mb.response[0]);
+    return false;
+  }
+
+  // Check that function code matches
+  if (mb.command[1] != mb.response[1]) {
+    STATUS_WARNING(STAT_OK, "modbus: invalid function code, expected=%u got=%u",
+                   mb.command[1], mb.response[1]);
+    return false;
+  }
+
+  return true;
+}
+
+
+static void _handle_response() {
+  if (!_check_response()) return;
+
+  mb.last = 0;  // Clear timeout timer
+  mb.retry = 0; // Reset retry counter
+
+  if (mb.receive_cb)
+    mb.receive_cb(mb.response[0], mb.response[1], mb.response_length - 4,
+                  mb.response + 2);
+
+  mb.connected = true;
+  mb.busy = false;
+}
+
+
+/// Data register empty interrupt
+ISR(RS485_DRE_vect) {
+  RS485_PORT.DATA = mb.command[mb.bytes++];
+
+  if (mb.bytes == mb.command_length) {
+    _set_dre_interrupt(false);
+    _set_txc_interrupt(true);
+    mb.bytes = 0;
+  }
+}
+
+
+/// Transmit complete interrupt
+ISR(RS485_TXC_vect) {
+  _set_txc_interrupt(false);
+  _set_rxc_interrupt(true);
+  _set_write(false);        // Switch to read mode
+  mb.last = rtc_get_time(); // Set timeout timer
+}
+
+
+/// Data received interrupt
+ISR(RS485_RXC_vect) {
+  mb.response[mb.bytes++] = RS485_PORT.DATA;
+
+  if (mb.bytes == mb.response_length) {
+    _set_rxc_interrupt(false);
+    _set_write(true); // Back to write mode
+    mb.bytes = 0;
+    _handle_response();
+  }
+}
+
+
+static void _reset() {
+  _set_dre_interrupt(false);
+  _set_txc_interrupt(false);
+  _set_rxc_interrupt(false);
+  _set_write(true); // RS485 write mode
+
+  // Flush USART
+  uint8_t x = RS485_PORT.DATA;
+  x = RS485_PORT.STATUS;
+  x = x;
+
+  // Save settings
+  bool debug = mb.debug;
+  uint8_t baud = mb.baud;
+
+  // Clear state
+  memset(&mb, 0, sizeof(mb));
+
+  // Restore settings
+  mb.debug = debug;
+  mb.baud = baud;
+}
+
+
+static void _retry() {
+  mb.last = 0;
+  mb.bytes = 0;
+  mb.retry++;
+
+  _set_write(true); // RS485 write mode
+
+  _set_txc_interrupt(false);
+  _set_rxc_interrupt(false);
+  _set_dre_interrupt(true);
+
+  if (mb.debug) STATUS_DEBUG("modbus: retry %d", mb.retry);
+}
+
+
+static void _timeout() {
+  if (mb.debug) STATUS_DEBUG("modbus: timedout");
+
+  // Try changing pin polarity
+  PINCTRL_PIN(RS485_RO_PIN) ^= PORT_INVEN_bm;
+  PINCTRL_PIN(RS485_DI_PIN) ^= PORT_INVEN_bm;
+
+  mb.retry = -1;
+  _retry();
+}
+
+
+void modbus_init() {
+  PR.PRPD &= ~PR_USART1_bm; // Disable power reduction
+
+  DIRCLR_PIN(RS485_RO_PIN); // Input
+  OUTSET_PIN(RS485_DI_PIN); // High
+  DIRSET_PIN(RS485_DI_PIN); // Output
+  OUTSET_PIN(RS485_RW_PIN); // High
+  DIRSET_PIN(RS485_RW_PIN); // Output
+
+  usart_set_port_baud(&RS485_PORT, mb.baud);
+
+  // No parity, 8 data bits, 1 stop bit
+  RS485_PORT.CTRLC = USART_CMODE_ASYNCHRONOUS_gc | USART_PMODE_DISABLED_gc |
+    USART_CHSIZE_8BIT_gc;
+
+  // Enable receiver and transmitter
+  RS485_PORT.CTRLB |= USART_RXEN_bm | USART_TXEN_bm;
+
+  // TODO remove this
+  PINCTRL_PIN(RS485_RO_PIN) ^= PORT_INVEN_bm;
+  PINCTRL_PIN(RS485_DI_PIN) ^= PORT_INVEN_bm;
+
+  _reset();
+}
+
+
+void modbus_deinit() {
+  _reset();
+
+  // Disable USART
+  RS485_PORT.CTRLB &= ~(USART_RXEN_bm | USART_TXEN_bm);
+
+  // Float write pins
+  DIRCLR_PIN(RS485_DI_PIN);
+  DIRCLR_PIN(RS485_RW_PIN);
+}
+
+
+bool modbus_busy() {return mb.busy;}
+
+
+void modbus_func(uint8_t slave, uint8_t func, uint8_t send, const uint8_t *data,
+                 uint8_t receive, modbus_cb_t receive_cb) {
+  ASSERT(send + 4 <= MODBUS_BUF_SIZE);
+  ASSERT(receive + 4 <= MODBUS_BUF_SIZE);
+
+  _reset();
+
+  mb.command[0] = slave;
+  mb.command[1] = func;
+  memcpy(mb.command + 2, data, send);
+  uint16_t crc = _crc16(mb.command, send + 2);
+  mb.command[send + 2] = crc;
+  mb.command[send + 3] = crc >> 8;
+
+  mb.command_length = send + 4;
+  mb.response_length = receive + 4;
+  mb.receive_cb = receive_cb;
+
+  _set_dre_interrupt(true);
+}
+
+
+void modbus_rtc_callback() {
+  if (mb.last && rtc_expired(mb.last + MODBUS_TIMEOUT)) {
+    if (mb.debug && mb.bytes) {
+      const uint8_t buf_len = 8 * 2 + 1;
+      char sent[buf_len];
+      char received[buf_len];
+
+      uint8_t i;
+      for (i = 0; i < mb.command_length; i++)
+        sprintf(sent + i * 2, "%02x", mb.command[i]);
+      sent[i * 2] = 0;
+
+      for (i = 0; i < mb.bytes; i++)
+        sprintf(received + i * 2, "%02x", mb.response[i]);
+      received[i * 2] = 0;
+
+      STATUS_DEBUG("modbus: sent 0x%s received 0x%s expected %u bytes",
+                   sent, received, mb.response_length);
+    }
+
+    if (mb.retry < MODBUS_RETRIES) _retry();
+    else _timeout();
+  }
+}
+
+
+// Variable callbacks
+bool get_modbus_debug() {return mb.debug;}
+void set_modbus_debug(bool value) {mb.debug = value;}
+uint8_t get_modbus_baud() {return mb.baud;}
+
+
+void set_modbus_baud(uint8_t baud) {
+  mb.baud = baud;
+  usart_set_port_baud(&RS485_PORT, baud);
+}
+
+
+bool get_modbus_connected() {return mb.connected;}
diff --git a/src/avr/src/modbus.h b/src/avr/src/modbus.h
new file mode 100644 (file)
index 0000000..802791e
--- /dev/null
@@ -0,0 +1,109 @@
+/******************************************************************************\
+
+                 This file is part of the Buildbotics firmware.
+
+                   Copyright (c) 2015 - 2018, Buildbotics LLC
+                              All rights reserved.
+
+      This file ("the software") is free software: you can redistribute it
+      and/or modify it under the terms of the GNU General Public License,
+       version 2 as published by the Free Software Foundation. You should
+       have received a copy of the GNU General Public License, version 2
+      along with the software. If not, see <http://www.gnu.org/licenses/>.
+
+      The software is distributed in the hope that it will be useful, but
+           WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+                Lesser General Public License for more details.
+
+        You should have received a copy of the GNU Lesser General Public
+                 License along with the software.  If not, see
+                        <http://www.gnu.org/licenses/>.
+
+                 For information regarding this software email:
+                   "Joseph Coffland" <joseph@buildbotics.com>
+
+\******************************************************************************/
+
+/******************************************************************************\
+
+  Modbus RTU command format is as follows:
+
+    Function 01: Read Coil Status
+    Function 02: Read Input Status
+
+           Send: [id][func][addr][count][crc]
+        Receive: [id][func][bytes][flags][crc]
+
+    Function 03: Read Holding Registers
+    Function 04: Read Input Registers
+
+           Send: [id][func][addr][count][crc]
+        Receive: [id][func][bytes][regs][crc]
+
+    Function 05: Force Single Coil
+    Function 06: Preset Single Register
+
+           Send: [id][func][addr][value][crc]
+        Receive: [id][func][addr][value][crc]
+
+    Function 15: Force Multiple Coils
+
+           Send: [id][func][addr][count][bytes][flags][crc]
+        Receive: [id][func][addr][count][crc]
+
+    Function 16: Preset Multiple Registers
+
+           Send: [id][func][addr][count][bytes][regs][crc]
+        Receive: [id][func][addr][count][crc]
+
+  Where:
+
+             id: 1-byte   Slave ID
+           func: 1-byte   Function code
+           addr: 2-byte   Data address
+          count: 2-byte   Address count
+          bytes: 1-byte   Number of bytes to follow
+          value: 2-byte   Value read or written
+           bits: n-bytes  Flags indicating on/off
+           regs: n-bytes  register values to write
+       checksum: 16-bit   CRC: x^16 + x^15 + x^2 + 1 (0x8005) initial: 0xffff
+
+\******************************************************************************/
+
+#pragma once
+
+
+#include <stdint.h>
+#include <stdbool.h>
+
+
+typedef enum {
+  MODBUS_READ_COILS        = 1,
+  MODBUS_READ_CONTACTS     = 2,
+  MODBUS_READ_OUTPUT_REG   = 3,
+  MODBUS_READ_INPUT_REG    = 4,
+  MODBUS_WRITE_COIL        = 5,
+  MODBUS_WRITE_OUTPUT_REG  = 6,
+  MODBUS_WRITE_COILS       = 15,
+  MODBUS_WRITE_OUTPUT_REGS = 16,
+} modbus_function_code_t;
+
+
+typedef enum {
+  MODBUS_COIL_BASE       = 0,
+  MODBUS_CONTACT_BASE    = 10000,
+  MODBUS_INPUT_REG_BASE  = 30000,
+  MODBUS_OUTPUT_REG_BASE = 40000,
+} modbus_base_addrs_t;
+
+
+typedef void (*modbus_cb_t)(uint8_t slave, uint8_t func, uint8_t bytes,
+                            const uint8_t *data);
+
+void modbus_init();
+void modbus_deinit();
+bool modbus_busy();
+void modbus_func(uint8_t slave, uint8_t func, uint8_t send, const uint8_t *data,
+                 uint8_t receive, modbus_cb_t receive_cb);
+void modbus_rtc_callback();
index 301694ed0b0482ff2ed380ec975fceb493468610..fe29f2812256c1169b6e825a162842c6f07a46ef 100644 (file)
@@ -27,8 +27,6 @@
 
 #pragma once
 
-#include "spindle.h"
-
 
 void pwm_spindle_init();
 void pwm_spindle_deinit();
index 702355a22495edfeacec41a732a8e051afab5aa1..1c7a779fc93261e4fef652c51c360081e50db3ab 100644 (file)
@@ -30,7 +30,7 @@
 #include "switch.h"
 #include "analog.h"
 #include "io.h"
-#include "huanyang.h"
+#include "modbus.h"
 #include "motor.h"
 #include "lcd.h"
 
@@ -51,7 +51,7 @@ ISR(RTC_OVF_vect) {
   switch_rtc_callback();
   analog_rtc_callback();
   io_rtc_callback();
-  hy_rtc_callback();
+  modbus_rtc_callback();
   if (!(ticks & 255)) motor_rtc_callback();
   wdt_reset();
 }
index 29017032e4c1fc2499d51c9a8f4a0bb227f5499f..55876a66d70fbf1d40ea798071e4cfc442c9536f 100644 (file)
@@ -117,7 +117,7 @@ void usart_init(void) {
     USART_CHSIZE_8BIT_gc;
 
   // Configure receiver and transmitter
-  SERIAL_PORT.CTRLB = USART_RXEN_bm | USART_TXEN_bm | USART_CLK2X_bm;
+  SERIAL_PORT.CTRLB |= USART_RXEN_bm | USART_TXEN_bm;
 
   PMIC.CTRL |= PMIC_HILVLEN_bm; // Interrupt level on
 
@@ -135,32 +135,38 @@ void usart_init(void) {
 }
 
 
-static void _set_baud(uint16_t bsel, uint8_t bscale) {
-  SERIAL_PORT.BAUDCTRLB = (uint8_t)((bscale << 4) | (bsel >> 8));
-  SERIAL_PORT.BAUDCTRLA = bsel;
+static void _set_baud(USART_t *port, uint16_t bsel, uint8_t bscale) {
+  port->BAUDCTRLB = (uint8_t)((bscale << 4) | (bsel >> 8));
+  port->BAUDCTRLA = bsel;
+  port->CTRLB |= USART_CLK2X_bm;
 }
 
 
-void usart_set_baud(int baud) {
+void usart_set_port_baud(USART_t *port, int baud) {
   // The BSEL / BSCALE values provided below assume a 32 Mhz clock
-  // Assumes CTRLB CLK2X bit (0x04) is set
+  // With CTRLB CLK2X is set
   // See http://www.avrcalc.elektronik-projekt.de/xmega/baud_rate_calculator
 
   switch (baud) {
-  case USART_BAUD_9600:    _set_baud(3325, 0b1101); break;
-  case USART_BAUD_19200:   _set_baud(3317, 0b1100); break;
-  case USART_BAUD_38400:   _set_baud(3301, 0b1011); break;
-  case USART_BAUD_57600:   _set_baud(1095, 0b1100); break;
-  case USART_BAUD_115200:  _set_baud(1079, 0b1011); break;
-  case USART_BAUD_230400:  _set_baud(1047, 0b1010); break;
-  case USART_BAUD_460800:  _set_baud(983,  0b1001); break;
-  case USART_BAUD_921600:  _set_baud(107,  0b1011); break;
-  case USART_BAUD_500000:  _set_baud(1,    0b0010); break;
-  case USART_BAUD_1000000: _set_baud(1,    0b0001); break;
+  case USART_BAUD_9600:    _set_baud(port, 3325, 0b1101); break;
+  case USART_BAUD_19200:   _set_baud(port, 3317, 0b1100); break;
+  case USART_BAUD_38400:   _set_baud(port, 3301, 0b1011); break;
+  case USART_BAUD_57600:   _set_baud(port, 1095, 0b1100); break;
+  case USART_BAUD_115200:  _set_baud(port, 1079, 0b1011); break;
+  case USART_BAUD_230400:  _set_baud(port, 1047, 0b1010); break;
+  case USART_BAUD_460800:  _set_baud(port, 983,  0b1001); break;
+  case USART_BAUD_921600:  _set_baud(port, 107,  0b1011); break;
+  case USART_BAUD_500000:  _set_baud(port, 1,    0b0010); break;
+  case USART_BAUD_1000000: _set_baud(port, 1,    0b0001); break;
   }
 }
 
 
+void usart_set_baud(int baud) {
+  usart_set_port_baud(&SERIAL_PORT, baud);
+}
+
+
 void usart_set(int flag, bool enable) {
   if (enable) usart_flags |= flag;
   else usart_flags &= ~flag;
index 9d5a272b53b34cd13dca8f4a7c15289fb2aef2a4..77a63494caf02365abc69fec215ce740b9a96ded 100644 (file)
 
 #pragma once
 
+#include <avr/io.h>
 
 #include <stdint.h>
 #include <stdbool.h>
 
+
 #define USART_TX_RING_BUF_SIZE 256
 #define USART_RX_RING_BUF_SIZE 256
 
@@ -56,6 +58,7 @@ enum {
 };
 
 void usart_init();
+void usart_set_port_baud(USART_t *port, int baud);
 void usart_set_baud(int baud);
 void usart_set(int flag, bool enable);
 bool usart_is_set(int flags);
index c96a0cbabda93c88540a2610df400795276637be..c2d324612069cb7751d2b0594e7c017b8a260b31 100644 (file)
@@ -94,32 +94,35 @@ VAR(pwm_freq,        sf, u16,   0,      1, 1, "Spindle PWM frequency in Hz")
 // PWM spindle
 VAR(pwm_invert,      pi, b8,    0,      1, 1, "Inverted spindle PWM")
 
+// Modbus spindle
+VAR(modbus_debug,     hb, b8,    0,      1, 1, "Modbus debugging")
+VAR(modbus_baud,      mb, u8,    0,      1, 1, "Modbus BAUD rate")
+VAR(modbus_connected, he, b8,    0,      0, 1, "Modbus connected")
+
 // Huanyang spindle
-VAR(hy_id,           hi, u8,    0,      1, 1, "Huanyang ID")
-VAR(hy_freq,         hz, f32,   0,      0, 1, "Huanyang actual freq")
-VAR(hy_current,      hc, f32,   0,      0, 1, "Huanyang actual current")
-VAR(hy_rpm,          hr, u16,   0,      0, 1, "Huanyang actual RPM")
-VAR(hy_temp,         ht, u16,   0,      0, 1, "Huanyang temperature")
-VAR(hy_max_freq,     hx, f32,   0,      0, 1, "Huanyang max freq")
-VAR(hy_min_freq,     hm, f32,   0,      0, 1, "Huanyang min freq")
-VAR(hy_rated_rpm,    hq, u16,   0,      0, 1, "Huanyang rated RPM")
-VAR(hy_status,       hs, u8,    0,      0, 1, "Huanyang status flags")
-VAR(hy_debug,        hb, b8,    0,      1, 1, "Huanyang debugging")
-VAR(hy_connected,    he, b8,    0,      0, 1, "Huanyang connected")
+VAR(hy_id,        hi, u8,    0,      1, 1, "Modbus ID")
+VAR(hy_freq,      hz, f32,   0,      0, 1, "Modbus actual freq")
+VAR(hy_current,   hc, f32,   0,      0, 1, "Modbus actual current")
+VAR(hy_rpm,       hr, u16,   0,      0, 1, "Modbus actual RPM")
+VAR(hy_temp,      ht, u16,   0,      0, 1, "Modbus temperature")
+VAR(hy_max_freq,  hx, f32,   0,      0, 1, "Modbus max freq")
+VAR(hy_min_freq,  hm, f32,   0,      0, 1, "Modbus min freq")
+VAR(hy_rated_rpm, hq, u16,   0,      0, 1, "Modbus rated RPM")
+VAR(hy_status,    hs, u8,    0,      0, 1, "Modbus status flags")
 
 // Machine state
 VAR(id,              id, u32,   0,      1, 1, "Last executed command ID")
-VAR(speed,           s,  f32,   0,      1, 1, "Current spindle speed")
+VAR(speed,            s, f32,   0,      1, 1, "Current spindle speed")
 VAR(feed_override,   fo, f32,   0,      1, 1, "Feed rate override")
 VAR(speed_override,  so, f32,   0,      1, 1, "Spindle speed override")
 VAR(optional_pause,  op, b8,    0,      1, 1, "Optional pause state")
 
 // System
-VAR(velocity,        v,  f32,   0,      0, 1, "Current velocity")
-VAR(acceleration,   ax,  f32,   0,      0, 1, "Current acceleration")
-VAR(jerk,            j,  f32,   0,      0, 1, "Current jerk")
-VAR(peak_vel,       pv,  f32,   0,      1, 1, "Peak velocity, set to clear")
-VAR(peak_accel,     pa,  f32,   0,      1, 1, "Peak acceleration, set to clear")
+VAR(velocity,         v, f32,   0,      0, 1, "Current velocity")
+VAR(acceleration,    ax, f32,   0,      0, 1, "Current acceleration")
+VAR(jerk,             j, f32,   0,      0, 1, "Current jerk")
+VAR(peak_vel,        pv, f32,   0,      1, 1, "Peak velocity, set to clear")
+VAR(peak_accel,      pa, f32,   0,      1, 1, "Peak acceleration, set to clear")
 VAR(hw_id,          hid, str,   0,      0, 1, "Hardware ID")
 VAR(estop,           es, b8,    0,      1, 1, "Emergency stop")
 VAR(estop_reason,    er, pstr,  0,      0, 1, "Emergency stop reason")
index c28debba48fa1e85b38cba0d897ee9dd2a7368af..d22e5d33ddc923e7ed36c6c2ed0f1b76a2044c57 100644 (file)
@@ -34,17 +34,25 @@ script#tool-view-template(type="text/x-template")
         templated-input(v-for="templ in template.tool", :name="$key",
           :model.sync="tool[$key]", :template="templ")
 
-    div(v-if="tool['spindle-type'] == 'PWM'")
+    div(v-if="get_type() == 'PWM'")
       h2 PWM Spindle
       form.pure-form.pure-form-aligned
         fieldset
           templated-input(v-for="templ in template['pwm-spindle']",
             :name="$key", :model.sync="pwmSpindle[$key]", :template="templ")
 
-    div(v-if="tool['spindle-type'] == 'HUANYANG'")
-      h2 Huanyang VFD Spindle
+    div(v-if="get_type() == 'HUANYANG'")
+      h2 Huanyang VFD
       form.pure-form.pure-form-aligned
         fieldset
           templated-input(v-for="templ in template['huanyang-spindle']",
             :name="$key", :model.sync="huanyangSpindle[$key]",
             :template="templ")
+
+    div(v-if="get_type() == 'MODBUS'")
+      h2 Modbus Compatiable VFD
+      form.pure-form.pure-form-aligned
+        fieldset
+          templated-input(v-for="templ in template['modbus-spindle']",
+            :name="$key", :model.sync="modbusSpindle[$key]",
+            :template="templ")
index 122babdc6e993061d2bef070b2d075d8e5c75f94..731942b671516a90d87b9574b8846489cb3c83fe 100644 (file)
@@ -37,7 +37,8 @@ module.exports = {
     return {
       tool: {},
       pwmSpindle: {},
-      huanyangSpindle: {}
+      huanyangSpindle: {},
+      modbusSpindle: {}
     }
   },
 
@@ -54,9 +55,24 @@ module.exports = {
 
 
   methods: {
+    get_type: function () {
+      return (this.tool['spindle-type'] || 'Disabled').toUpperCase();
+    },
+
+
+    update_tool: function (type) {
+      if (this.config.hasOwnProperty(type + '-spindle'))
+        this[type + 'Spindle'] = this.config[type + '-spindle'];
+
+      var template = this.template[type + '-spindle'];
+      for (var key in template)
+        if (!this[type + 'Spindle'].hasOwnProperty(key))
+          this.$set(type + 'Spindle["' + key + '"]', template[key].default);
+    },
+
+
     update: function () {
       Vue.nextTick(function () {
-        // Tool
         if (this.config.hasOwnProperty('tool'))
           this.tool = this.config.tool;
 
@@ -65,24 +81,10 @@ module.exports = {
           if (!this.tool.hasOwnProperty(key))
             this.$set('tool["' + key + '"]', template[key].default);
 
-        // PWM
-        if (this.config.hasOwnProperty('pwm-spindle'))
-          this.pwmSpindle = this.config['pwm-spindle'];
-
-        template = this.template['pwm-spindle'];
-        for (key in template)
-          if (!this.pwmSpindle.hasOwnProperty(key))
-            this.$set('pwmSpindle["' + key + '"]', template[key].default);
-
-        // Huanyang
-        if (this.config.hasOwnProperty('huanyang-spindle'))
-          this.huanyangSpindle = this.config['huanyang-spindle'];
-
-        template = this.template['huanyang-spindle'];
-        for (key in template)
-          if (!this.huanyangSpindle.hasOwnProperty(key))
-            this.$set('huanyangSpindle["' + key + '"]', template[key].default);
-      }.bind(this));
+        this.update_tool('pwm');
+        this.update_tool('huanyang');
+        this.update_tool('modbus');
+       }.bind(this));
     }
   }
 }
index 15623b465499bad9803c01f0df64da414e62b55d..c1bf508ba86e61e313e7785d4e43cb3be07443b7 100644 (file)
   "tool": {
     "spindle-type": {
       "type": "enum",
-      "values": ["DISABLED", "PWM", "HUANYANG"],
-      "default": "DISABLED",
+      "values": ["Disabled", "PWM", "Huanyang", "Modbus"],
+      "default": "Disabled",
       "code": "st"
     },
     "spin-reversed": {
     }
   },
 
+  "modbus-spindle": {
+    "modbus-id": {
+      "type": "int",
+      "default": "1",
+      "code": "bi"
+    }
+  },
+
   "pwm-spindle": {
     "max-spin": {
       "type": "float",