################################################################################ # # # 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 . # # # # 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 # # . # # # # For information regarding this software email: # # "Joseph Coffland" # # # ################################################################################ import bbctrl import bbctrl.Cmd as Cmd # Must match regs in pwr firmware TEMP_REG = 0 VIN_REG = 1 VOUT_REG = 2 MOTOR_REG = 3 LOAD1_REG = 4 LOAD2_REG = 5 VDD_REG = 6 FLAGS_REG = 7 VERSION_REG = 8 # Must be kept in sync with pwr firmware UNDER_VOLTAGE_FLAG = 1 << 0 OVER_VOLTAGE_FLAG = 1 << 1 OVER_CURRENT_FLAG = 1 << 2 SENSE_ERROR_FLAG = 1 << 3 SHUNT_OVERLOAD_FLAG = 1 << 4 MOTOR_OVERLOAD_FLAG = 1 << 5 LOAD1_SHUTDOWN_FLAG = 1 << 6 LOAD2_SHUTDOWN_FLAG = 1 << 7 MOTOR_UNDER_VOLTAGE_FLAG = 1 << 8 MOTOR_VOLTAGE_SENSE_ERROR_FLAG = 1 << 9 MOTOR_CURRENT_SENSE_ERROR_FLAG = 1 << 10 LOAD1_SENSE_ERROR_FLAG = 1 << 11 LOAD2_SENSE_ERROR_FLAG = 1 << 12 VDD_CURRENT_SENSE_ERROR_FLAG = 1 << 13 POWER_SHUTDOWN_FLAG = 1 << 14 SHUNT_ERROR_FLAG = 1 << 15 reg_names = 'temp vin vout motor load1 load2 vdd pwr_flags pwr_version'.split() class Pwr(): def __init__(self, ctrl): self.ctrl = ctrl self.log = ctrl.log.get('Pwr') self.i2c_addr = ctrl.args.pwr_addr self.regs = [-1] * 9 self.lcd_page = ctrl.lcd.add_new_page() self.failures = 0 self._update_cb(False) def check_fault(self, var, status): status = bool(status) if not self.ctrl.state.has(var) or status != self.ctrl.state.get(var): self.ctrl.state.set(var, status) if status: return True return False def check_faults(self): flags = self.regs[FLAGS_REG] if self.check_fault('under_voltage', flags & UNDER_VOLTAGE_FLAG): self.log.warning('Device under voltage') if self.check_fault('over_voltage', flags & OVER_VOLTAGE_FLAG): self.log.warning('Device over voltage') if self.check_fault('over_current', flags & OVER_CURRENT_FLAG): self.log.warning('Device total current limit exceeded') if self.check_fault('sense_error', flags & SENSE_ERROR_FLAG): self.log.warning('Power sense error') if self.check_fault('shunt_overload', flags & SHUNT_OVERLOAD_FLAG): self.log.warning('Power shunt overload') if self.check_fault('motor_overload', flags & MOTOR_OVERLOAD_FLAG): self.log.warning('Motor power overload') if self.check_fault('load1_shutdown', flags & LOAD1_SHUTDOWN_FLAG): self.log.warning('Load 1 over temperature shutdown') if self.check_fault('load2_shutdown', flags & LOAD2_SHUTDOWN_FLAG): self.log.warning('Load 2 over temperature shutdown') if self.check_fault('motor_under_voltage', flags & MOTOR_UNDER_VOLTAGE_FLAG): self.log.warning('Motor under voltage') if self.check_fault('motor_voltage_sense_error', flags & MOTOR_VOLTAGE_SENSE_ERROR_FLAG): self.log.warning('Motor voltage sense error') if self.check_fault('motor_current_sense_error', flags & MOTOR_CURRENT_SENSE_ERROR_FLAG): self.log.warning('Motor current sense error') if self.check_fault('load1_sense_error', flags & LOAD1_SENSE_ERROR_FLAG): self.log.warning('Load1 sense error') if self.check_fault('load2_sense_error', flags & LOAD2_SENSE_ERROR_FLAG): self.log.warning('Load2 sense error') if self.check_fault('vdd_current_sense_error', flags & VDD_CURRENT_SENSE_ERROR_FLAG): self.log.warning('Vdd current sense error') if self.check_fault('power_shutdown', flags & POWER_SHUTDOWN_FLAG): self.log.warning('Power shutdown') self.ctrl.mach.i2c_command(Cmd.SHUTDOWN) if self.check_fault('shunt_error', flags & SHUNT_ERROR_FLAG): self.log.warning('Shunt error') def _update_cb(self, now = True): if now: self._update() self.ctrl.ioloop.call_later(1, self._update_cb) def _update(self): update = {} try: for i in range(len(self.regs)): value = self.ctrl.i2c.read_word(self.i2c_addr + i) if value is None: return # Handle lack of i2c port if i == TEMP_REG: value -= 273 elif i == FLAGS_REG or i == VERSION_REG: pass else: value /= 100.0 key = reg_names[i] self.ctrl.state.set(key, value) if self.regs[i] != value: update[key] = value self.regs[i] = value if i == FLAGS_REG: self.check_faults() except Exception as e: if i < 6: # Older pwr firmware does not have regs > 5 self.failures += 1 msg = 'Pwr communication failed at reg %d: %s' % (i, e) if self.failures != 5: self.log.info(msg) else: self.log.warning(msg) self.failures = 0 return self.lcd_page.text('%3dC Tmp' % self.regs[TEMP_REG], 0, 0) self.lcd_page.text('%5.1fV In' % self.regs[VIN_REG], 0, 1) self.lcd_page.text('%5.1fV Out' % self.regs[VOUT_REG], 0, 2) self.lcd_page.text(' %04x Flg' % self.regs[FLAGS_REG], 0, 3) self.lcd_page.text('%5.1fA Mot' % self.regs[MOTOR_REG], 10, 0) self.lcd_page.text('%5.1fA Ld1' % self.regs[LOAD1_REG], 10, 1) self.lcd_page.text('%5.1fA Ld2' % self.regs[LOAD2_REG], 10, 2) self.lcd_page.text('%5.1fA Vdd' % self.regs[VDD_REG], 10, 3) if len(update): self.ctrl.state.update(update) self.failures = 0