Our full technical support staff does not monitor this forum. If you need assistance from a member of our staff, please submit your question from the Ask a Question page.


Log in or register to post/reply in the forum.

I2C Sensor "Library"


--dd-- Oct 4, 2022 03:54 PM

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

 


bmcdanold Oct 17, 2022 06:58 PM

You just saved me a load of time trying to figure out how to interface a TMP117 with a CR1000x.  Many thanks!


--dd-- Oct 25, 2022 11:51 AM

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

 


JDavis Nov 1, 2022 06:40 PM

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.


isafir Mar 25, 2024 04:04 PM

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.

Log in or register to post/reply in the forum.