def __init__(self, output='json'): # TODO: make safe for MicroPython, leave here for now in Conda from collections import deque from math import sin, pi from meerkat.data import CSVWriter, JSONWriter # data bus placeholder self.bus = None self.bus_addr = None # what kind of data output to file self.output = output # types of verbose printing self.verbose = False self.verbose_data = False # thread safe deque for sharing and plotting self.q_maxlen = 300 self.q = deque(maxlen=self.q_maxlen) self.q_prefill_zeros = False # realtime/stream options self.go = False self.unlimited = False self.max_samples = 1000 # information about this device self.device = DeviceData('Software Test') self.device.description = 'Dummy data for software testing' self.device.urls = None self.device.manufacturer = None self.device.version_hw = None self.device.version_sw = None self.device.accuracy = None self.device.precision = None self.device.bus = None self.device.state = 'Test Not Running' self.device.active = False self.device.error = None self.device.dtype = None self.device.calibration_date = None # data writer if self.output == 'csv': self.writer = CSVWriter('Software Test') #self.writer.device = self.device.values() elif self.output == 'json': self.writer = JSONWriter('Software Test') self.writer.header = ['index', 'degrees', 'amplitude'] self.writer.device = self.device.values() # example data of one 360 degree, -1 to 1 sine wave self._deg = [n for n in range(360)] self._amp = [sin(d * (pi / 180.0)) for d in self._deg] self._test_data = list(zip(self._deg, self._amp))
def __init__(self, bus_n, bus_addr=0x0F, output='csv'): # i2c bus self.bus = I2C(bus_n=bus_n, bus_addr=bus_addr) # PWM clock frequency self.frequency = 31372 # speed can be -255 to 255 self.motor_1_speed = 0 self.motor_2_speed = 0 # direction of DC motor self.direction = "" # driver mode, 'dc' or 'step' self.mode = "dc" # phase of the motor being used, only applies in stepper mode self.phase = None # step code initialize self.step_codes_cw = None self.step_codes_ccw = None # motor phase steps, default to 2 phase self.set_phase(phase=2) # number of steps commanded self.steps = 0 # running total steps, for positioning microsteps self.step_count = 0 # information about this device self.device = DeviceData('Grove Motor Driver') self.device.description = ('I2C DC and stepper motor controller') self.device.urls = 'http://wiki.seeedstudio.com/Grove-I2C_Motor_Driver_V1.3/' self.device.active = None self.device.error = None self.device.bus = repr(self.bus) self.device.manufacturer = 'Seeed Studio' self.device.version_hw = '1.3' self.device.version_sw = '1.0' self.device.accuracy = None self.device.precision = '1 microstep' self.device.calibration_date = None # data recording method self.writer_output = output self.csv_writer = CSVWriter(self.device.name, time_format='std_time_ms') self.csv_writer.device = self.device.values() self.csv_writer.header = ['description', 'freq', 'm1_speed', 'm2_speed', 'm_direction', 'mode', 'phase', 'steps'] self.json_writer = JSONWriter(self.device.name, time_format='std_time_ms') self.json_writer.device = self.device.values() self.json_writer.header = self.csv_writer.header
def __init__(self, bus_n, bus_addr=0x18, output='csv'): # i2c bus self.bus = I2C(bus_n=bus_n, bus_addr=bus_addr) self.state_mapper = {0: "closed", 1: "open"} # information about this device self.device = DeviceData("Qwiic Relay") self.device.description = ("Sparkfun Single Pole Double Throw Relay") self.device.urls = "https://learn.sparkfun.com/tutorials/qwiic-single-relay-hookup-guide/all" self.device.active = None self.device.error = None self.device.bus = repr(self.bus) self.device.manufacturer = "Sparkfun" self.device.version_hw = "1" self.device.version_sw = "1" self.application = "test" del self.device.accuracy del self.device.precision del self.device.dtype # data recording method self.writer_output = output self.csv_writer = CSVWriter("Qwiic Relay", time_format='std_time_ms') self.csv_writer.device = self.device.__dict__ self.csv_writer.header = ["sample_id", "state"] self.json_writer = JSONWriter("Qwiic Relay", time_format='std_time_ms') self.json_writer.device = self.device.__dict__ self.json_writer.header = ["sample_id", "state"]
def __init__(self, bus_n, bus_addr, output='csv'): """Initialize worker device on i2c bus. Parameters ---------- bus_n : int, i2c bus number on Controller bus_addr : int, i2c bus number of this Worker device """ # i2c bus self.bus = I2C(bus_n=bus_n, bus_addr=bus_addr) # time to wait for conversions to finish self.short_delay = 0.3 # seconds for regular commands self.long_delay = 1.5 # seconds for readings self.cal_delay = 3.0 # seconds for calibrations # information about this device self.device = DeviceData('Atlas_Base') self.device.description = ('') self.device.urls = 'www.atlas-scientific.com' self.device.active = None self.device.error = None self.device.bus = repr(self.bus) self.device.manufacturer = 'Atlas Scientific' self.device.version_hw = '1.0' self.device.version_sw = '1.0' self.device.accuracy = None self.device.precision = 'Varies' self.device.calibration_date = None """ # data recording method if output == 'csv': self.writer = CSVWriter('Atlas_Base', time_format='std_time_ms') elif output == 'json': self.writer = JSONWriter('Atlas_Base', time_format='std_time_ms') else: pass # holder for another writer or change in default self.writer.header = ['description', 'sample_n', 'not_set'] self.writer.device = self.device.values() """ # data recording information self.sample_id = None # data recording method self.writer_output = output self.csv_writer = CSVWriter("Atlas_Base", time_format='std_time_ms') self.csv_writer.device = self.device.__dict__ self.csv_writer.header = ['description', 'sample_n', 'not_set'] self.json_writer = JSONWriter("Atlas_Base", time_format='std_time_ms') self.json_writer.device = self.device.__dict__ self.json_writer.header = self.csv_writer.header
def __init__(self, bus_n, bus_addr=0x68, output='csv'): # i2c bus self.bus = I2C(bus_n=bus_n, bus_addr=bus_addr) # Wake up the MPU-6050 since it starts in sleep mode # by toggling bit6 from 1 to 0, see pg 40 of RM-MPU-6000A-00 v4.2 self.bus.write_register_8bit(self.PWR_MGMT_1, 0x00) # information about this device self.device = DeviceData('MPU-6050') self.device.description = ('TDK InvenSense Gyro & Accelerometer') self.device.urls = 'https://www.invensense.com/products/motion-tracking/6-axis/mpu-6050/' self.device.active = None self.device.error = None self.device.bus = repr(self.bus) self.device.manufacturer = 'TDK' self.device.version_hw = '0.1' self.device.version_sw = '0.1' self.device.gyro_accuracy = '+/-3%, +/-2% cross axis' self.device.gyro_precision = '16bit' self.device.gyro_noise = '0.05 deg/s-rms' self.device.accel_accuracy = '+/-0.5%, +/-2 cross axis' self.device.accel_precision = '16bit' self.device.accel_noise = 'PSD 400 ug / Hz**1/2' self.device.calibration_date = None ''' # data recording method if output == 'csv': self.writer = CSVWriter('MPU-6050', time_format='std_time_ms') self.writer.header = ['description', 'sample_n', 'arange', 'grange', 'ax', 'ay', 'az', 'gx', 'gy', 'gz', 'temp_C'] elif output == 'json': self.writer = JSONWriter('MPU-6050', time_format='std_time_ms') else: pass # holder for another writer or change in default self.writer.device = self.device.values() ''' # data recording information self.sample_id = None # data recording method self.writer_output = output self.csv_writer = CSVWriter("MPU-6050", time_format='std_time_ms') self.csv_writer.device = self.device.__dict__ self.csv_writer.header = [ 'description', 'sample_n', 'ax', 'ay', 'az', 'gx', 'gy', 'gz' ] self.json_writer = JSONWriter("MCP9808", time_format='std_time_ms') self.json_writer.device = self.device.__dict__ self.json_writer.header = self.csv_writer.header
def __init__(self, bus_n, bus_addr=0x18, output='csv'): """Initialize worker device on i2c bus. Parameters ---------- bus_n : int, i2c bus number on Controller bus_addr : int, i2c bus number of this Worker device """ # i2c bus self.bus = I2C(bus_n=bus_n, bus_addr=bus_addr) # register values and defaults # TODO: do the constant values need to be specified? self.reg_map = { 'config': REG_CONFIG, 'upper_temp': REG_UPPER_TEMP, 'lower_temp': REG_LOWER_TEMP, 'crit_temp': REG_CRIT_TEMP, 'ambient': REG_AMBIENT_TEMP, 'manufacturer': REG_MANUF_ID, 'device_id': REG_DEVICE_ID } self.alert_critial = None self.alert_upper = None self.alert_lower = None self.manufacturer_id = None self.device_id = None self.revision = None # information about this device self.device = DeviceData('MCP9808') self.device.description = ( '+/-0.5 degrees Celcius ' + 'maximum accuracy digital temperature sensor') self.device.urls = 'https://www.microchip.com/datasheet/MCP9808' self.device.active = None self.device.error = None self.device.bus = repr(self.bus) self.device.manufacturer = 'Microchip' self.device.version_hw = '0.1' self.device.version_sw = '0.1' self.device.accuracy = '+/-0.25 (typical) C' self.device.precision = '0.0625 C maximum' self.device.units = 'Degrees Celcius' self.device.calibration_date = None # data recording information self.sample_id = None # data recording method self.writer_output = output self.csv_writer = CSVWriter("MCP9808", time_format='std_time_ms') self.csv_writer.device = self.device.__dict__ self.csv_writer.header = ['description', 'sample_n', 'temperature'] self.json_writer = JSONWriter("MCP9808", time_format='std_time_ms') self.json_writer.device = self.device.__dict__ self.json_writer.header = ['description', 'sample_n', 'temperature']
class TestDevice(Base): """Non-hardware test class""" def __init__(self, output='json'): # TODO: make safe for MicroPython, leave here for now in Conda from collections import deque from math import sin, pi from meerkat.data import CSVWriter, JSONWriter # data bus placeholder self.bus = None self.bus_addr = None # what kind of data output to file self.output = output # types of verbose printing self.verbose = False self.verbose_data = False # thread safe deque for sharing and plotting self.q_maxlen = 300 self.q = deque(maxlen=self.q_maxlen) self.q_prefill_zeros = False # realtime/stream options self.go = False self.unlimited = False self.max_samples = 1000 # information about this device self.device = DeviceData('Software Test') self.device.description = 'Dummy data for software testing' self.device.urls = None self.device.manufacturer = None self.device.version_hw = None self.device.version_sw = None self.device.accuracy = None self.device.precision = None self.device.bus = None self.device.state = 'Test Not Running' self.device.active = False self.device.error = None self.device.dtype = None self.device.calibration_date = None # data writer if self.output == 'csv': self.writer = CSVWriter('Software Test') #self.writer.device = self.device.values() elif self.output == 'json': self.writer = JSONWriter('Software Test') self.writer.header = ['index', 'degrees', 'amplitude'] self.writer.device = self.device.values() # example data of one 360 degree, -1 to 1 sine wave self._deg = [n for n in range(360)] self._amp = [sin(d * (pi / 180.0)) for d in self._deg] self._test_data = list(zip(self._deg, self._amp)) @staticmethod def _cycle(iterable): """Copied from Python 3.7 itertools.cycle example""" saved = [] for element in iterable: yield element saved.append(element) while saved: for element in saved: yield element def run(self, delay=0, index='count'): """Run data collection""" # TODO: make safe for MicroPython, leave here for now in Conda from time import sleep, time, ctime if self.verbose: print('Test Started') # used in non-unlimited acquisition count = 0 self.q.clear() if self.q_prefill_zeros: for _ in range(self.q_maxlen): self.q.append((0, 0)) if index == 'time': def get_index(): return time() elif index == 'ctime': def get_index(): return ctime() else: def get_index(): return count for d, a in self._cycle(self._test_data): if not self.go: if self.verbose: print('Test Stopped') break self.device.state = 'Test run() method' i = get_index() data = [i, d, a] if self.output is not None: self.writer.write(data) q_out = self.writer.stream(data) self.q.append(q_out) if self.verbose_data: print(q_out) if not self.unlimited: count += 1 if count == self.max_samples: self.go = False sleep(delay)
def __init__(self, bus_n, bus_addr=0x48, output='csv'): """Initialize worker device on i2c bus. Parameters ---------- bus_n : int, i2c bus number on Controller bus_addr : int, i2c bus number of this Worker device """ # i2c bus self.bus = I2C(bus_n=bus_n, bus_addr=bus_addr) # time to wait for conversion to finish self.delay = 0.009 # units = seconds # register values and defaults self.conversion_value = 40000 # higher than any conversion result self.config_value = None # 0x8583 default self.lo_thres_value = None # 0x8000 default self.hi_thres_value = None # 0x7fff default self.reg_map = { 'conversion': 0b00, 'config': 0b01, 'lo_thresh': 0b10, 'hi_thresh': 0b11 } # config register attributes and chip defaults self.comp_que_value = 0b11 self.comp_lat_value = 0b0 self.comp_pol_value = 0b0 self.comp_mode_value = 0b0 self.dr_value = 0b100 self.mode_value = 0b1 self.pga_value = 0b010 self.mux_value = 0b000 self.os_value = 0b0 # voltage measurement self.pga_float = -999 self.volts = -999 # attribute converters self.str_mux = { '01': 0b000, '03': 0b001, '13': 0b010, '23': 0b011, '0G': 0b100, '1G': 0b101, '2G': 0b110, '3G': 0b111 } self.bin_mux = {v: k for k, v in self.str_mux.items()} self.str_pga = { '6.144': 0b000, '4.096': 0b001, '2.048': 0b010, '1.024': 0b011, '0.512': 0b100, '0.256': 0b101 } self.bin_pga = {v: float(k) for k, v in self.str_pga.items()} self.str_mode = {'continuous': 0b0, 'single': 0b1} self.bin_mode = {v: k for k, v in self.str_mode.items()} self.str_data_rate = { 8: 0b000, 16: 0b001, 32: 0b010, 64: 0b011, 128: 0b100, 250: 0b101, 475: 0b110, 860: 0b111 } self.bin_data_rate = {v: k for k, v in self.str_data_rate.items()} self.str_comp_mode = {'trad': 0b0, 'window': 0b1} self.bin_comp_mode = {v: k for k, v in self.str_comp_mode.items()} self.str_comp_pol = {'1': 0b00, '2': 0b01, '3': 0b10, 'off': 0b11} self.bin_comp_pol = {v: k for k, v in self.str_comp_pol.items()} self.str_comp_lat = {'off': 0b0, 'on': 0b1} self.bin_comp_lat = {v: k for k, v in self.str_comp_lat.items()} self.str_comp_que = {'1': 0b00, '2': 0b01, '3': 0b10, 'off': 0b11} self.bin_comp_que = {v: k for k, v in self.str_comp_que.items()} # information about this device self.device = DeviceData('ADS1115') self.device.description = ('Texas Instruments 16-bit 860SPS' + ' 4-Ch Delta-Sigma ADC with PGA') self.device.urls = 'www.ti.com/product/ADS1115' self.device.active = None self.device.error = None self.device.bus = repr(self.bus) self.device.manufacturer = 'Texas Instruments' self.device.version_hw = '1.0' self.device.version_sw = '1.0' self.device.accuracy = None self.device.precision = '16bit' self.device.calibration_date = None # current settings of this device self.device.pga_gain = self.pga_float ''' # data recording method if output == 'csv': self.writer = CSVWriter('ADS1115', time_format='std_time_ms') self.writer.header = ['description', 'sample_n', 'mux', 'voltage'] elif output == 'json': self.writer = JSONWriter('ADS1115', time_format='std_time_ms') else: pass # holder for another writer or change in default self.writer.device = self.device.values() # data recording information self.sample_id = None ''' # data recording information self.sample_id = None # data recording method self.writer_output = output self.csv_writer = CSVWriter("ADS1115", time_format='std_time_ms') self.csv_writer.device = self.device.__dict__ self.csv_writer.header = ['description', 'sample_n', 'mux', 'voltage'] self.json_writer = JSONWriter("ADS1115", time_format='std_time_ms') self.json_writer.device = self.device.__dict__ self.json_writer.header = self.csv_writer.header # initialize class attributes from device registry self.get_config()
class GroveMotor: def __init__(self, bus_n, bus_addr=0x0F, output='csv'): # i2c bus self.bus = I2C(bus_n=bus_n, bus_addr=bus_addr) # PWM clock frequency self.frequency = 31372 # speed can be -255 to 255 self.motor_1_speed = 0 self.motor_2_speed = 0 # direction of DC motor self.direction = "" # driver mode, 'dc' or 'step' self.mode = "dc" # phase of the motor being used, only applies in stepper mode self.phase = None # step code initialize self.step_codes_cw = None self.step_codes_ccw = None # motor phase steps, default to 2 phase self.set_phase(phase=2) # number of steps commanded self.steps = 0 # running total steps, for positioning microsteps self.step_count = 0 # information about this device self.device = DeviceData('Grove Motor Driver') self.device.description = ('I2C DC and stepper motor controller') self.device.urls = 'http://wiki.seeedstudio.com/Grove-I2C_Motor_Driver_V1.3/' self.device.active = None self.device.error = None self.device.bus = repr(self.bus) self.device.manufacturer = 'Seeed Studio' self.device.version_hw = '1.3' self.device.version_sw = '1.0' self.device.accuracy = None self.device.precision = '1 microstep' self.device.calibration_date = None # data recording method self.writer_output = output self.csv_writer = CSVWriter(self.device.name, time_format='std_time_ms') self.csv_writer.device = self.device.values() self.csv_writer.header = ['description', 'freq', 'm1_speed', 'm2_speed', 'm_direction', 'mode', 'phase', 'steps'] self.json_writer = JSONWriter(self.device.name, time_format='std_time_ms') self.json_writer.device = self.device.values() self.json_writer.header = self.csv_writer.header def get_info(self): pid = self.bus.read_register_16bit(0x00) return pid def set_phase(self, phase=2): """Set the phase of the stepper motor being used. Defaults to a 2 phase. Parameters ---------- phase : int, either 2 or 4 for the phase of the motor design """ if phase == 4: # 4 phase motor self.step_codes_cw = [0b0001, 0b0011, 0b0010, 0b0110, 0b0100, 0b1100, 0b1000, 0b1001] self.step_codes_ccw = [0b1000, 0b1100, 0b0100, 0b0110, 0b0010, 0b0011, 0b0001, 0b1001] self.phase = 4 else: # default to 2 phase motor self.step_codes_cw = [0b0001, 0b0101, 0b0100, 0b0110, 0b0010, 0b1010, 0b1000, 0b1001] self.step_codes_ccw = [0b1000, 0b1010, 0b0010, 0b0110, 0b0100, 0b0101, 0b0001, 0b1001] self.phase = 2 def set_frequency(self, f_Hz=31372): """Set the frequency of the PWM cycle where cycle length = 510 system_clock = 16MHz Available frequencies in Hz: 31372, 3921, 490, 122, 30 Parameters ---------- f_Hz : int, frequencey in Hertz (Hz). Default is 31372 """ freq_mapper = {31372: 0x01, 3921: 0x02, 490: 0x03, 122: 0x04, 30: 0x05} self.frequency = freq_mapper[f_Hz] self.bus.write_n_bytes([0x84, self.frequency, 0x01]) def set_speed(self, motor_id, motor_speed): """Set the speed (duty cycle) of motor Parameters ---------- motor_id : int, either 1 or 2 where 1 = J1 and 2 = J5 on the board motor_speed : int, between -255 and 255 where > 0 is clockwise """ assert (motor_speed > -256) & (motor_speed < 256) if motor_id == 1: self.motor_1_speed = motor_speed if motor_id == 2: self.motor_2_speed = motor_speed self.bus.write_n_bytes([0x82, self.motor_1_speed, self.motor_2_speed]) def stop(self, motor_id=None): """Stop one or both motors. Alias for set_speed(motor_id, motor_speed=0). Parameters ---------- motor_id : int, (optional) 1 or 2 where 1 = J1 and 2 = J5 on the board. If not set, defaults to stopping both. """ if (motor_id == 1) or (motor_id == 2): self.set_speed(motor_id=motor_id, motor_speed=0) else: self.set_speed(motor_id=1, motor_speed=0) self.set_speed(motor_id=2, motor_speed=0) def set_direction(self, code='cw'): """Set the direction of one or both motors in dc mode. Accepted string command codes are: 'cw' : clockwise 'ccw' : counter clockwise 'm1m2cw' : motor 1 and motor 2 clockwise 'm1m2cc' : motor 1 and motor 2 counter clockwise 'm1cw_m2ccw' : motor 1 clockwise and motor 2 counter clockwise 'm1ccw_m2cw' : motor 1 counter clockwise and motor 2 clockwise Parameters ---------- code : str, command code, default is 'cw' """ direction_mapper = {'cw': 0x0a, 'ccw': 0x05, 'm1m2cw': 0x0a, 'm1m2cc': 0x05, 'm1cw_m2ccw': 0x06, 'm1ccw_m2cw': 0x09} self.direction = direction_mapper[code] self.bus.write_n_bytes([0xaa, self.direction, 0x01]) def set_mode(self, mode_type="dc"): if (mode_type == "full_step") or (mode_type == "micro_step"): self.set_speed(motor_id=1, motor_speed=255) self.set_speed(motor_id=2, motor_speed=255) self.mode = mode_type else: self.mode = "dc" def step_full(self, steps, delay=0.0001, verbose=False): """Stepper motor motion in full steps (four steps per step commanded) Parameters ---------- steps : int, number of motor steps where positive numbers are clockwise negative numbers are counter clockwise delay : float, seconds to delay between step commands, default is 0.0001 seconds which smooths operation on the pi verbose : bool, print debug statements """ self.steps = steps if steps > 0: step_codes = self.step_codes_cw self.direction = "step_cw" if steps < 0: step_codes = self.step_codes_ccw self.direction = "step_ccw" if steps == 0: self.stop() return self.set_mode(mode_type="full_step") # set speed to 255, i.e. full current for _ in range(abs(steps)): for sc in step_codes: if verbose: print("{0:04b}".format(sc)) self.bus.write_n_bytes([0xaa, sc, 0x01]) time.sleep(delay) self.stop() # set speed to 0, i.e. current off def step_micro(self, steps, delay=0.0001, verbose=False): """Stepper motor motion in micro steps (one step per step commanded) Parameters ---------- steps : int, number of motor steps where positive numbers are clockwise negative numbers are counter clockwise delay : float, seconds to delay between step commands, default is 0.0001 seconds which smooths operation on the pi verbose : bool, print debug statements """ self.steps = steps if steps > 0: step_codes = self.step_codes_cw self.direction = "step_cw" if steps < 0: step_codes = self.step_codes_ccw self.direction = "step_ccw" if steps == 0: self.stop() return self.set_mode(mode_type="micro_step") # set speed to 255, i.e. full current for _ in range(abs(steps)): if verbose: print("n >>", _) ix = self.step_count * 2 if verbose: print("ix >> ", ix) for sc in step_codes[ix:ix + 2]: if verbose: print("bin >> {0:04b}".format(sc)) self.bus.write_n_bytes([0xaa, sc, 0x01]) time.sleep(delay) self.step_count = (self.step_count + 1) % 4 self.stop() # set speed to 0, i.e. current off def get(self, description='no_description'): """Get formatted output. Parameters ---------- description : char, description of data sample collected Returns ------- data : list, data that will be saved to disk with self.write containing: description : str """ return [description, self.frequency, self.motor_1_speed, self.motor_2_speed, self.direction, self.mode, self.phase, self.steps] def publish(self, description='no_description'): """Format output and save to file, formatted as either .csv or .json. Parameters ---------- description : char, description of data sample collected Returns ------- None, writes to disk the following data: description : str, description of sample """ return self.json_writer.publish(self.get(description=description)) def write(self, description='no_description'): """Format output and save to file, formatted as either .csv or .json. Parameters ---------- description : char, description of data sample collected Returns ------- None, writes to disk the following data: description : str, description of sample """ wr = {"csv": self.csv_writer, "json": self.json_writer}[self.writer_output] wr.write(self.get(description=description))