I have started to put together a 'library' to read I2C sensors with a Campbell Cr1000X. My thought was that we could collect and share code for more sensor here.
The sensors I have code for, and have tested, is so far these:
TCA9548A I2C Multiplexer: https://www.adafruit.com/product/2717
TMP117 ±0.1°C High Accuracy I2C Temperature Sensor: https://www.adafruit.com/product/4821
SHT31-D Sensirion - Temperature & Humidity Sensor: https://www.adafruit.com/product/2857
Keller LD series pressure sensors: https://download.keller-druck.com/api/download/2LfcGMzMbeHdjFbyUd5DWA/en/2021-07.pdf
'Test program to read sensors with the I2C "Library" '(c) 2022 john.hulth@geo.uio.no 'Wiring 'VDD on external 3.3 volt (dc/dc on SW12-1) 'C1 Clock SCL 'C2 Data SDA 'Pull up resistors 2.2 kOhm between C1 and 3V3 'Pull up resistors 2.2 kOhm between C2 and 3V3 SequentialMode 'Use SequentialMode 'Declare Constants ConstTable Station_Constants Const ScanInterval = 30 'Scan Interval Const ScanUnit = Sec 'Scan Unit Const TFInterval = 60 'Table File Interval Const TFUnit = min 'Table File Unit Const I2C_Sensors = True 'Use I2C Sensors Const TCA_MUX = True 'TCA9548A 1-to-8 I2C Multiplexer Const TMP117 = True 'TMP117 I2C Precision Temperature Sensor Const SHT3X = True 'SHT3X I2C Temperature and Humidity Sensor Const KellerLD = True 'Keller LD Pressure sensor #If I2C_Sensors Const I2C_port = C1 'C1 -> SCL, C2 -> SDA Const I2C_BaudRate =100000 Const I2C_volt = 2 '1 -> 5V, 2 -> 3V3 Const I2C_Delay = 20 'mSec #EndIf #If TCA_MUX Const TCA_adress = &H70 #EndIf #If TMP117 Const TMP_adress = &H48 #EndIf #If SHT3X Const SHT_adress = &H44 Const HeatDelay As Long = 0 'mSec the heater will be on before measurement #EndIf #If KellerLD Const KellerLD_adress = &H40 #EndIf EndConstTable 'Declare include files #If I2C_Sensors Include "CPU:I2C_Sensors.CR1X" 'Must be after declaring constants #EndIf 'Declare Variables #If TCA_MUX Public SelectMux_OK As Boolean #EndIf #If TMP117 Public ReadTMP_OK As Boolean #EndIf #If SHT3X Public ReadSHT_OK As Boolean #EndIf #If KellerLD Public ReadKellerLD_OK As Boolean #EndIf DataTable (Test,1,-1) 'Set table size to # of records, or -1 to autoallocate. DataInterval (0,ScanInterval,ScanUnit,10) TableFile ("CRD:Test_",8,-1,0,TFInterval,TFUnit,0,0) #If TCA_MUX Sample (1,SelectMux_OK,Boolean) #EndIf #If TMP117 Sample (1,ReadTMP_OK,Boolean) Sample (1,TMP_Temp,IEEE4) #EndIf #If SHT3X Sample (1,ReadSHT_OK,Boolean) Sample (1,SHT_Temp,IEEE4) Sample (1,SHT_RH,IEEE4) #EndIf #If KellerLD Sample (1,ReadKellerLD_OK,Boolean) Sample (1,KellerLD_P,IEEE4) Sample (1,KellerLD_T,IEEE4) #EndIf EndTable 'Main Program BeginProg #If I2C_Sensors PortPairConfig(I2C_port,I2C_volt) I2COpen(I2C_port,I2C_BaudRate) #EndIf Scan (ScanInterval,ScanUnit,0,0) SW12 (SW12_1,1 ) #If TCA_MUX SelectMux_OK = SelectMux(6) 'Select channel 0-7 #EndIf #If TMP117 ReadTMP_OK = ReadTMP() 'Read TMP117 Temperature #EndIf #If SHT3X ReadSHT_OK = ReadSHT() 'Read SHT3X Temperature and Humidity #EndIf #If KellerLD ReadKellerLD_OK = ReadKellerLD() 'Read Keller LD Pressure and Temperature #EndIf SW12 (SW12_1,0) CallTable Test NextScan EndProg
Include file: "I2C_Sensors.CR1X"
'I2C "Library" '(c) 2022 john.hulth@geo.uio.no 'Function to select MUX channel #If TCA_MUX Function SelectMux (i As Long) As Boolean Dim j As Long Dim Mux_Selected As Long j = 1 << (i + 24) 'Move bit to first Byte plus selected Mux channel 'Set up for I2C I2COpen(I2C_port,I2C_BaudRate) Delay(0,I2C_Delay,mSec) 'Select channel I2CWrite (I2C_port, TCA_adress, j, 1, &H03) Delay(0,I2C_Delay,mSec) I2CRead (I2C_port, TCA_adress, Mux_Selected, 1, &H03) If (i >= 0 AND i <= 7 AND Mux_Selected = j) Then Return True Else Return false EndIf EndFunction #EndIf 'Function to read TMP117 sensor #If TMP117 Function ReadTMP () As Boolean Const ConfigReg = &H010CC000 '0x01 Config Reg and 0x0CC0 One shot, 32 average, Conversion cycling time 500 ms Const TempResReg = &H0000000 '0x00 Temperature result register Dim dest As Long ' is able to read up to 4 bytes Dim ConfigRegBack As Long Public TMP_Temp : Units TMP_Temp = °C 'Final temperature 'Set up for I2C I2COpen(I2C_port,I2C_BaudRate) Delay(0,I2C_Delay,mSec) 'set up sensor I2CWrite (I2C_port, TMP_adress, ConfigReg, 3, &H03) '0x0CC0 One shot, 32 average, Conversion cycling time 500 ms Delay (1,I2C_Delay,mSec) I2CRead (I2C_port, TMP_adress, dest, 2, &H03) dest = dest >> 8 ConfigRegBack = dest 'Gives back the settings (0x0CC0) 'Read temperature I2CWrite (I2C_port, TMP_adress, TempResReg, 1, &H03) Delay (1,500,mSec)'500 ms Conversion time needed for 0x0CC0 I2CRead (I2C_port, TMP_adress, dest, 2, &H03) dest = dest >> 16 TMP_Temp = dest * 0.0078125 If (ConfigRegBack AND &H00ffff00) = (ConfigReg AND &H00ffff00) Return True Else Return false EndIf EndFunction #EndIf 'Function to read SHT3X sensor #If SHT3X Function ReadSHT () As Boolean Const DReg As Long = &H24000000 'Measurement command for single shot data aquisition Const StatReg As Long = &HF32D0000 'Get status register Const HeatOn As Long = &H306D0000 'Turn heater on Const HeatOff As Long = &H30660000 'Turn heater on Dim statusOn As Long Dim statusOff As Long Dim dest(2) As Long' is able to read up to 8 bytes Dim ta0 As Long ' raw data Dim rh0 As Long Public SHT_Temp As Float : Units SHT_Temp = °C 'Final temperature Public SHT_RH As Float : Units SHT_RH = % 'Final relative humidity 'Set up for I2C I2COpen(I2C_port,I2C_BaudRate) Delay(0,I2C_Delay,mSec) 'Read Temperature (before turning on the heater to avoid internal heating, can be 0.35-0.5 °C) I2CWrite (I2C_port, SHT_adress, DReg, 2,&H03) Delay (1,20,mSec)'Needed I2CRead (I2C_port, SHT_adress, dest, 6, &H03) MoveBytes(ta0, 2, dest ,0, 2) SHT_Temp = -45 + 175 * ta0 / 65535 'Turn heater on I2CWrite (I2C_port, SHT_adress, HeatOn, 2,&H03) I2CWrite (I2C_port, SHT_adress,StatReg, 2,&H03) I2CRead (I2C_port, SHT_adress, statusOn, 2, &H03) statusOn = (statusOn >> (16+13)) AND &H00000001 'Heater status is in bit 13 Delay (1,HeatDelay,mSec) 'Time to keep heater on before measurement 'Read Humidity I2CWrite (I2C_port, SHT_adress, DReg, 2,&H03) Delay (1,20,mSec)'Needed I2CRead (I2C_port, SHT_adress, dest, 6, &H03) MoveBytes(rh0, 2, dest, 3, 2) SHT_RH = 100 * rh0 / 65535 'Turn heater of I2CWrite (I2C_port, SHT_adress, HeatOff, 2,&H03) I2CWrite (I2C_port, SHT_adress,StatReg, 2,&H03) I2CRead (I2C_port, SHT_adress, statusOff, 2, &H03) statusOff = (statusOff >> (16+13)) AND &H00000001 'Heater status is in bit 13 If (SHT_Temp <> 130 OR SHT_RH <> 100) AND statusOn = 1 AND statusOff = 0 Return True Else Return False EndIf EndFunction #EndIf 'Function to read Keller LD sensor #If KellerLD Function ReadKellerLD () As Boolean Dim ScaleReg As Long 'Scale0 to scale 4 register adress, 0x12 to 0x16 Const MeasReg = &HAC000000 '0xAC Request Measurement (must be 4 bytes 0xAC 0x00 0x00 0x00!!!) Dim i As Long Dim status(6) As Long 'Sensor status msg Dim scaling(5) As Long 'Raw scaling Dim dest(2) As Long 'is able to read up to 8 bytes Dim mode As Long '0 vented gauge, 1 Sealed gauge, 2 absolute, 3 not defined Dim P_mode As Float Dim year As Long 'Production date Dim month As Long Dim days As Long Dim scaling12 As Long 'Raw P_min Dim scaling34 As Long 'Raw P_max Dim P_min As Float ': Units P_min = bar 'P_min should be retrived in program !!! Dim P_max As Float ': Units P_max = bar 'P_max should be retrived in program !!! Dim P0 As Long 'Raw pressure Dim T0 As Long 'Raw temperature Public KellerLD_P As Float : Units KellerLD_P = bar 'final pressure Public KellerLD_T As Float : Units KellerLD_T = °C 'final temperature 12 bit 'Set up for I2C I2COpen(I2C_port,I2C_BaudRate) Delay(0,I2C_Delay,mSec) 'Read Scaling 0 to 4 For i = 0 To 4 Step 1 ScaleReg = (i + &H12) << 24 I2CWrite (I2C_port, KellerLD_adress, ScaleReg, 1,&H03) '0xAC Request Measurement (must be 4 bytes 0xAC 0x00 0x00 0x00!!!) Delay (1,2,mSec)'>0.6ms needed to read memmory I2CRead (I2C_port, KellerLD_adress, scaling(i+1), 3, &H03) 'Read 3 bytes [STATUS, MS_word, LS_word] Delay (1,2,mSec) MoveBytes(status(i+1),3,scaling(i+1),0,1) 'Get status message scaling(i+1) = (scaling(i+1) >> 8) AND &H0000FFFF 'removes status msg Next 'Handel Scaling0 mode = scaling(0+1) AND &H00000003 year = ((scaling(0+1) AND &H00007800) >> 11) + 2010 month = (scaling(0+1) AND &H00000780) >> 7 days = (scaling(0+1) AND &H0000007C) >> 2 'Handle P-mode pressure offset (to vacuum pressure) If (mode = 0) 'PA mode, Vented Gauge. Zero at atmospheric pressure P_mode = 1.01325 ElseIf (mode = 1) 'PR mode, Sealed Gauge. Zero at 1.0 bar P_mode = 1.0 ElseIf (mode = 2) 'PAA mode, Absolute. Zero at vacuum P_mode = 0.0 Else 'Undefined mode P_mode = 0.0 EndIf 'Handel Scaling1 and Scaling2, P_min scaling12 = (scaling(1+1) << 16) + scaling(2+1) MoveBytes (P_min,0,scaling12,0,4) 'MoveBytes converts from Long to Float 'Handel Scaling3 and Scaling4, P_max scaling34 = (scaling(3+1) << 16) + scaling(4+1) MoveBytes (P_max,0,scaling34,0,4) 'MoveBytes converts from Long to Float 'Read pressure and temperature I2CWrite (I2C_port, KellerLD_adress, MeasReg, 1,&H03) '0xAC Request Measurement (must be 4 bytes 0xAC 0x00 0x00 0x00!!!) Delay (1,20,mSec)'>8 ms needed for measurement 0xAC I2CRead (I2C_port, KellerLD_adress, dest, 5, &H05) 'Read 5 bytes [STATUS, P_MSB, P_LSB, T_MSB, T_LSB] MoveBytes(status(6),3,dest,0,1) MoveBytes(P0,2,dest,1,2) MoveBytes(T0,2,dest,3,2) 'Calculate pressure and temperature KellerLD_P = (P0-16384)*(P_max-P_min)/32768 + P_min + P_mode KellerLD_T = (T0-384)*0.003125-50 '16 bit 'T = ((T0>>4)-24)*0.05-50 '12 bit If (status(1) = 64) AND (status(2) = 64) AND (status(3) = 64) AND (status(4) = 64) AND (status(5) = 64) AND (status(6) = 64) Return True Else Return False EndIf EndFunction #EndIf
You just saved me a load of time trying to figure out how to interface a TMP117 with a CR1000x. Many thanks!
Added the SHT40/41/45 sensors for temperature and humidity: https://www.adafruit.com/product/4885
'Test program to read sensors with the I2C "Library" '(c) 2022 john.hulth@geo.uio.no 'Wiring 'VDD on external 3.3 volt (dc/dc on SW12-1) 'C1 Clock SCL 'C2 Data SDA 'Pull up resistors 2.2 kOhm between C1 and 3V3 'Pull up resistors 2.2 kOhm between C2 and 3V3 SequentialMode 'Use SequentialMode 'Declare Constants ConstTable Station_Constants Const ScanInterval = 20 'Scan Interval Const ScanUnit = Sec 'Scan Unit Const TFInterval = 60 'Table File Interval Const TFUnit = min 'Table File Unit Const I2C_Sensors = True 'Use I2C Sensors Const SHT4X = True 'SHT40/41/45 I2C Temperature and Humidity Sensor #If I2C_Sensors Const I2C_port = C1 'C1 -> SCL, C2 -> SDA Const I2C_BaudRate =100000 Const I2C_volt = 2 '1 -> 5V, 2 -> 3V3 Const I2C_Delay = 20 'mSec #EndIf #If SHT4X Const SHT4X_adress = &H44 Const SHT4X_Heat As Boolean = False 'The heater will be on before humidity measurement (20mW for 1s) max duty cycle recommended is 5% #EndIf EndConstTable 'Declare include files #If I2C_Sensors Include "CPU:I2C_Sensors.CR1X" 'Must be after declaring constants #EndIf 'Declare Variables Public PTemp, Batt_volt #If SHT4X Public ReadSHT4X_OK As Boolean #EndIf DataTable (Test,1,-1) 'Set table size to # of records, or -1 to autoallocate. DataInterval (0,ScanInterval,ScanUnit,10) TableFile ("CRD:Test_",8,-1,0,TFInterval,TFUnit,0,0) Minimum (1,Batt_volt,FP2,False,False) Sample (1,PTemp,FP2) #If SHT4X Sample (1,ReadSHT4X_OK,Boolean) Sample (1,SHT4X_Temp,IEEE4) Sample (1,SHT4X_RH,IEEE4) #EndIf EndTable 'Main Program BeginProg #If I2C_Sensors PortPairConfig(I2C_port,I2C_volt) I2COpen(I2C_port,I2C_BaudRate) #EndIf Scan (ScanInterval,ScanUnit,0,0) PanelTemp (PTemp,15000) Battery (Batt_volt) SW12 (SW12_1,1 ) #If SHT4X ReadSHT4X_OK = ReadSHT4X() 'Read SHT4X Temperature and Humidity #EndIf SW12 (SW12_1,0) CallTable Test NextScan EndProg
'Function to read SHT4X sensor #If SHT4X Function ReadSHT4X () As Boolean Const SHT4X_HighPrecCmd As Long = &HFD000000 'Measure T & RH with high precision (high repeatability) Const SHT4X_HeatCmd As Long = &H1E000000 'Activate lowest heater power & high precis. meas. (typ. 20mW @ 3.3V) for 1s Dim dest(2) As Long' is able to read up to 8 bytes Dim ta0 As Long ' raw data Dim rh0 As Long Public SHT4X_Temp As Float : Units SHT4X_Temp = °C 'Final temperature Public SHT4X_RH As Float : Units SHT4X_RH = % 'Final relative humidity 'Set up for I2C I2COpen(I2C_port,I2C_BaudRate) Delay(0,I2C_Delay,mSec) 'Read Temperature (before turning on the heater to avoid internal heating, can be 0.35-0.5 °C) I2CWrite (I2C_port, SHT4X_adress, SHT4X_HighPrecCmd, 2,&H03) Delay (1,10,mSec)'Needed I2CRead (I2C_port, SHT4X_adress, dest, 6, &H03) MoveBytes(ta0, 2, dest ,0, 2) SHT4X_Temp = -45 + 175 * ta0 / 65535 'Do a new meassurement for Humidity if heating is used If SHT4X_Heat I2CWrite (I2C_port, SHT4X_adress, SHT4X_HeatCmd, 2,&H03) Delay (1,1100,mSec)'Needed I2CRead (I2C_port, SHT4X_adress, dest, 6, &H03) EndIf MoveBytes(rh0, 2, dest, 3, 2) SHT4X_RH = -6 + (125 * rh0 / 65535) If (SHT4X_Temp <> 130 OR SHT4X_RH <> 119) Return True Else Return False EndIf EndFunction #EndIf
A tip I have is to start testing with a low bitrate. Some sensors specify a minimum they can tolerate. Low bitrates are forgiving of wiring quality. For high bit rates, you may need short, fine gauge, shielded wires.
Hello,
This is a great resurce to create. very useful. I have a question on using I2C to control servo motors. I'm trying to use this servo controller board from Adafruit https://learn.adafruit.com/16-channel-pwm-servo-driver to control 8 servo motors on channels 0 to 7 on the board, how to use I2C in CRBasic to send the signal to each motor individually. I would appreciate your help on this. thank you.