from labjack import ljm import time import datetime as dt import argparse import json import warnings def configure_thermocouple(handle, i): # configure analog inputs for thermocouple ljm.eWriteName(handle, "AIN{:.0f}_RESOLUTION_INDEX".format(i), 8) # 8 for highest resolution ljm.eWriteName(handle, "AIN{:.0f}_NEGATIVE_CH".format(i), i+1) # sets channel to differential ljm.eWriteName(handle, "AIN{:.0f}_RANGE".format(i), 0.01) # extended feature for thermocouples ljm.eWriteName(handle, "AIN{:.0f}_EF_INDEX".format(i), 24) # 24 for type T thermocouple ljm.eWriteName(handle, "AIN{:.0f}_EF_CONFIG_A".format(i), 2) # {0:K, 1:C, 2:F} ljm.eWriteName(handle, "AIN{:.0f}_EF_CONFIG_B".format(i), 16) # CJC modbus address = 60052 for screw terminals on labjack, 60050 for screw terminals on cb37 ljm.eWriteName(handle, "AIN{:.0f}_EF_CONFIG_D".format(i), 55.56) # slope of CJC ljm.eWriteName(handle, "AIN{:.0f}_EF_CONFIG_E".format(i), 255.37) # offset of CJC readName = f"AIN{i}_EF_READ_A" return readName def read_thermocouple(handle, readName): tF = ljm.eReadName(handle, readName) return tF def read_resistance(handle, ain_vout, vref=2.5, ref=100000): '''Read Analog voltages and interpret to resistance value handle : labjack handle object ain_vref : analog input channel for reference voltage ain_vout : analog input channel for measured out voltage ref : reference resistor value ''' vref = vref vout = ljm.eReadName(handle, f"AIN{ain_vout:.0f}") return ((2.5-vout)*ref)/vout #resistance value in ohms def configure_sdp810(handle, sda,scl): #resets sensor and sets instructions for averaging #soft reset all the sensors ljm.eWriteName(handle, "I2C_SDA_DIONUM", sda) # SDA pin ljm.eWriteName(handle, "I2C_SCL_DIONUM", scl) # SCL pin # send command to start continuous differential pressure measurement ljm.eWriteName(handle, "I2C_NUM_BYTES_TX", 2) # Set the number of bytes to transmit ljm.eWriteName(handle, "I2C_NUM_BYTES_RX", 0) # Set the number of bytes to receive aBytes = [0x00,0x06] #soft reset ljm.eWriteNameByteArray(handle, "I2C_DATA_TX", 2, aBytes) ljm.eWriteName(handle, "I2C_GO", 1) # Do the I2C communications. time.sleep(.1) aBytes = [0x3F, 0xF9] #stop continuous averaging ljm.eWriteNameByteArray(handle, "I2C_DATA_TX", 2, aBytes) ljm.eWriteName(handle, "I2C_GO", 1) # Do the I2C communications. time.sleep(.5) aBytes = [0x36, 0x15] #continuous averaging of differential pressure ljm.eWriteNameByteArray(handle, "I2C_DATA_TX", 2, aBytes) ljm.eWriteName(handle, "I2C_GO", 1) # Do the I2C communications. time.sleep(.02) def read_T9602(handle, sda, scl): # Configure labjack i2c bus to read our digital temp/rh sensors # Amphenol Telaire T9602 Humidity and Temperature Sensor ljm.eWriteName(handle, "I2C_SLAVE_ADDRESS", 0x28) # Slave Address of the I2C chip ljm.eWriteName(handle, "I2C_SDA_DIONUM", sda) # SDA pin ljm.eWriteName(handle, "I2C_SCL_DIONUM", scl) # SCL pin ljm.eWriteName(handle, "I2C_NUM_BYTES_TX", 0) # Set the number of bytes to transmit ljm.eWriteName(handle, "I2C_NUM_BYTES_RX", 4) # Set the number of bytes to receive ljm.eWriteName(handle, "I2C_GO", 1) # Do the I2C communications. data = ljm.eReadNameByteArray(handle, "I2C_DATA_RX", 4) rh = (((data[0] & 0x3F ) << 8) + data[1]) / 16384.0 * 100.0 tF = (((data[2] * 64) + (data[3] >> 2 )) / 16384.0 * 165.0 - 40.0) * 9/5 + 32 return tF, rh def read_spd810(handle, sda, scl, subtype="500Pa"): if subtype=="500Pa": scale_factor=60 elif subtype=="125Pa": scale_factor=240 ljm.eWriteName(handle, "I2C_SLAVE_ADDRESS", 0x25) # Slave Address of the I2C chip ljm.eWriteName(handle, "I2C_SDA_DIONUM", sda) # SDA pin ljm.eWriteName(handle, "I2C_SCL_DIONUM", scl) # SCL pin ljm.eWriteName(handle, "I2C_NUM_BYTES_TX", 0) # Set the number of bytes to transmit ljm.eWriteName(handle, "I2C_NUM_BYTES_RX", 9) # Set the number of bytes to receive ljm.eWriteName(handle, "I2C_GO", 1) # Do the I2C communications. reading = [0]*9 reading = ljm.eReadNameByteArray(handle, "I2C_DATA_RX", 9) acks = ljm.eReadName(handle, "I2C_ACKS") print(acks) temp_value=reading[3]+float(reading[4])/255 if temp_value>=0 and temp_value<=100: temperature=temp_value*255/200 #scale factor adjustment if temp_value<=256 and temp_value>=200: temperature=-(256-temp_value)*255/200 #scale factor adjustment pressure_value=reading[0]+float(reading[1])/255 if pressure_value>=0 and pressure_value<128: differential_pressure=pressure_value/(60/256) #scale factor adjustment for sdp810-500 for -125 use 240 elif pressure_value>128 and pressure_value<=256: differential_pressure=-(256-pressure_value)/(60/256) #scale factor adjustment elif pressure_value==128: differential_pressure=99999999 #Out of range return differential_pressure, temperature #pascals if __name__ == '__main__': dbx_token = 'xxxx' #dbx = dropbox.Dropbox(dbx_token) parser = argparse.ArgumentParser() parser.add_argument("--htz", type=float, help='Frequency to read sensors, in seconds.', default=1) parser.add_argument("--duration", type=int, help='Duration to log data, in seconds.', default=7) parser.add_argument("--write_htz", type=int, help='How often to save data to Dropbox, in seconds', default=60) parser.add_argument("--precision", type=int, help='Decimal precision to log data', default=1) parser.add_argument("--config", type=str, help='name of sensor configuration file', default='config') args = parser.parse_args() htz = args.htz limit = args.duration write_htz = args.write_htz precision = args.precision config_file = f"{args.config}.json" with open(config_file, 'r') as f: devices = json.load(f) #connect to devices in configuration file for device in devices: handle = ljm.openS(device["type"], "ANY", device["SN"]) device['handle'] = handle print(f"Connected to: Labjack {device['type']}: SN = {ljm.eReadName(handle, 'SERIAL_NUMBER'):.0f}") for device in devices: handle = device['handle'] # Global I2C configuration on Labjack # Options bits: bit0: Reset the I2C bus, bit1: Restart w/o stop, bit2: Disable clock stretching. ljm.eWriteName(handle, "I2C_OPTIONS", 0) ljm.eWriteName(handle, "I2C_SPEED_THROTTLE", 65516) # Speed throttle = 65516 (~100 kHz) #build column headings - first row of output datatable headings = ['timestamp'] #Configure Sensors for sensor in device['sensors']: if sensor['type'] == 'SPD810': configure_sdp810(handle, sensor['sda'], sensor['scl']) time.sleep(0.05) read_spd810(handle, sensor['sda'], sensor['scl']) headings.append(sensor['name'] + '_pa') headings.append(sensor['name'] + '_tC') elif sensor['type'] == 'T9602': headings.append(sensor['name'] + '_tF') headings.append(sensor['name'] + '_rh') elif sensor['type'] == 'TC': sensor['readName'] = configure_thermocouple(handle, sensor['AIN']) headings.append(sensor['name'] + '_tF') elif sensor['type'] == 'OHM': pass out = [] file_count = 0 # count of written data files total_records = 0 first = dt.datetime.now() step = dt.timedelta(seconds=htz) session_name = f'{first.timestamp()}' session_dir = f'/rheia-logger/{session_name}/' trigger = first + step while (total_records * htz) <= limit: start = dt.datetime.now() if start >= trigger: trigger += step #set up next trigger record = [start.strftime('%Y-%m-%d %H:%M:%S.%f')] for device in devices: handle = device['handle'] for sensor in device['sensors']: if sensor['type'] == 'SPD810': PA, tC = read_spd810(handle, sensor['sda'], sensor['scl']) record.append(f'{PA:.{precision}f}') record.append(f'{tC:.{precision}f}') elif sensor['type'] == 'T9602': try: TF, RH = read_T9602(handle, sensor['sda'], sensor['scl']) record.append(f'{TF:.{precision}f}') record.append(f'{RH:.{precision}f}') except: record.append(f'') record.append(f'') elif sensor['type'] == 'TC': TF = read_thermocouple(handle, sensor['readName']) record.append(f'{TF:.{precision}f}') elif sensor['type'] == 'OHM': ohms = read_resistance(handle, sensor['ain_vout'], ref=sensor['ref']) record.append(f'{ohms/1000:.2f}') else: warning.warn(f'Sensor type {sensor["type"]} not supported') total_records += 1 out.append(','.join(record)) print(out[-1]) # based on write frequency, upload data to dropbox if len(out) >= write_htz: upload_path = f'{session_dir}{file_count:0>5}.csv' upload_data = '\n'.join([','.join(headings)]+out).encode('utf-8') #dbx.files_upload(upload_data, upload_path) file_count+=1 out = [] # write remaining data upload_path = f'{session_dir}{file_count:0>5}.csv' upload_data = '\n'.join([','.join(headings)]+out).encode('utf-8') #dbx.files_upload(upload_data, upload_path) out = [] #dbx.close()