News:

Let's find out together what makes a PIC Tick!

Main Menu

Impliment I2C

Started by Mike, Apr 11, 2023, 09:26 AM

Previous topic - Next topic

Mike

Hi Guys.
I have recentlt purchased one od these Grove Water Level Sensor
My problem is that I have Never used I2C. Ive been reading up and experimenting to no avail.
I've noticed that most I2C devices have one Address, but this thing has 2 ?? Totally lost. ???

Can anyone help.
Thanks
Mike

RGV250

Hi,
Looking at the information supplied it is not that obvious but from what I can make out the low 10 pads/sensors go to the low address 0x77 and the top 10 go to the hight address 0x78. I am not sure what the UPD1 connections do but they do not appear to be used so I would ignore them.
If I were doing it (and as you are new to I2C) I would just work with the low sensor until you get reliable readings.
I would think there are some I2C samples in the samples folder so that is the place to look first.

Regards,
Bob

RGV250

Looking again at the schematic and the Arduino code it is 12 sensors for the high one and 8 for the low one.

Stephen Moss

This may not be entirely correct as it is from only a quick look, but as far as I can tell from the schemtaic and the Arduino example linked to from the page you provided it looks like it works something like this...
The I2C address of U1 is 0x78 from which you read the higher 12 sensor values (nearest the IC's)
The I2C adderss of U2 is 0x77 from which you read lower 8 sensor values (furthests from the IC's), you need two devices becasue there are 20 sensors and each device only has 12 analogue inputs, so a single device cannot access them all.

Which order the senors data comes in (lowest or highest first) from each IC is not clear, nor is the the output you get from them although from the video with the example code it would appear you get a value from 0 to 252 for each one.

The I2C sample files that come with the compiler along with the descriptions regarding I2C in the manual should help you to start reading the devices, just try getting it to work on one first, the sensor PCB has 4K7 pull up resistors on the SDA an SCL pins so you won't need to add any yourself.
The examples may only show reading and writing one byte, keep in mind that after sending the inital read command you will probably have to request all the bytes from each device before the stop command is sent otherwise you may only get the data from the first sensor each time if sending individual requests for each bytes.
Conseqently, you will have to consider how to store the data as it arrives, you could use a single Word variable to add them all together but storing them in a byte size array may be better as then you have the option to add them afterwards and/or look at individual data for each sensor depending on your needs.

That is about as much advice and I can give based in the available information.
Ignore the UPDI connection as from the information here it look like it is used from programming the devices after assembly, not for general day to day usuage of the sensor board.
 

RGV250

Hi,
This is something I have changed from an old program which should give you an idea, it may be wrong but from what I have briefly read I think it is ok.

Bob

' Include device configuration file
    Include "Amicus18.inc"        ' Configure the compiler to use the Amicus18 hardware (18F25K20, 64MHz) 

        Declare Slow_Bus On       '!!!! Might not be needed.
        Symbol SDA = PORTC.3 ' Alias the SDA (Data) line
        Symbol SCL = PORTC.4 ' Alias the SCL (Clock) line       
       
'You could use an array for this.       
        Dim Water_0 As Byte
        Dim Water_1 As Byte       
        Dim Water_2 As Byte
        Dim Water_3 As Byte
        Dim Water_4 As Byte       
        Dim Water_5 As Byte
        Dim Water_6 As Byte       
        Dim Water_7 As Byte     
        Dim Water_8 As Byte
        Dim Water_9 As Byte       
        Dim Water_10 As Byte
        Dim Water_11 As Byte
        Dim Water_12 As Byte       
        Dim Water_13 As Byte
        Dim Water_14 As Byte       
        Dim Water_15 As Byte   
        Dim Water_16 As Byte
        Dim Water_17 As Byte       
        Dim Water_18 As Byte
        Dim Water_19 As Byte
                                 
        Clear                   ' Clear ALL RAM locations

        DelayMS 100             ' Allow PIC to start up

'Set the device I2C address (Low 8 sensors)       
        Symbol GroveAddressLow = 0x77
'Set the device I2C address (High 12 sensors)       
        Symbol GroveAddressHigh = 0x78
       
MainLoop:
        I2CIn SDA, SCL, GroveAddressLow,0,[Water_0, Water_1, Water_2, Water_3, Water_4, Water_5, Water_6, Water_7] 'Read the Data for the low 8 sensors
        I2CIn SDA, SCL, GroveAddressHigh,0,[Water_8, Water_9, Water_10, Water_11, Water_12, Water_13, Water_14, Water_15, Water_16, Water_17, Water_18, Water_19] 'Read the Data for the high 12 sensors.
       
'Send the information to serial output.       
        HRSOut "Water_0: ", Dec Water_0,13           
        HRSOut "Water_1: ", Dec Water_1,13   
        HRSOut "Water_2: ", Dec Water_2,13           
        HRSOut "Water_3: ", Dec Water_3,13           
        HRSOut "Water_4: ", Dec Water_4,13           
        HRSOut "Water_5: ", Dec Water_5,13
        HRSOut "Water_6: ", Dec Water_6,13           
        HRSOut "Water_7: ", Dec Water_7,13
        HRSOut "Water_8: ", Dec Water_8,13           
        HRSOut "Water_9: ", Dec Water_9,13   
        HRSOut "Water_10: ", Dec Water_10,13           
        HRSOut "Water_11: ", Dec Water_11,13           
        HRSOut "Water_12: ", Dec Water_12,13           
        HRSOut "Water_13: ", Dec Water_13,13
        HRSOut "Water_14: ", Dec Water_14,13           
        HRSOut "Water_15: ", Dec Water_15,13
        HRSOut "Water_16: ", Dec Water_16,13           
        HRSOut "Water_17: ", Dec Water_17,13
        HRSOut "Water_18: ", Dec Water_18,13           
        HRSOut "Water_19: ", Dec Water_19,13                                     
        HRSOut " ",10,13       
       
        DelayMS 1000

        GoTo MainLoop

Mike

Wow guys I am impressed yet again with help from the forum. :)
I love it when I post something here before I go to bed and when I get up in the morning there are answers waiting for me.
I have most of the the day to play around with this device now. ;D

Thanks again
Mike

RGV250

Hi Mike,
If it doesn't work the address might need altering so don't panic. Some devices need the bits shifted so the first bit is read/write but I cannot remember how that goes at the moment.
I doubt if this is the case as it is not actually an I2C device but an Arduino programmed to respond to I2C requests.

Bob

Stephen Moss

@RGV250, although you could create an array using the method in your example it is a rather long hand way of doing it over the more ususal construct, ie....
'You could use an array for this.     
        Dim Water[20] As Byte  'Create a 20 element byte Array
                              
        Clear  Water            ' Clear the Array

        DelayMS 100            ' Allow PIC to start up

'Set the device I2C address (Low 8 sensors)     
        Symbol GroveAddressLow = 0x77
'Set the device I2C address (High 12 sensors)     
        Symbol GroveAddressHigh = 0x78
     
MainLoop:
        I2CIn SDA, SCL, GroveAddressLow,0,[Water[0], Water[1], Water[2], Water[3], Water[4], Water[5], Water[6], Water[7]] 'Read the Data for the low 8 sensors
        I2CIn SDA, SCL, GroveAddressHigh,0,[Water[8], Water[9], Water[10], Water[11], Water[12], Water[13], Water[14], Water[15], Water[16], Water[17], Water[18], Water_[19]] 'Read the Data for the high 12 sensors.

Am I missing something and thus there is a good reason for using the method in your example in this particular case (i.e the above does not work with I2Cin) or just your personal preference?

With the more typical construct you could then use a For-Next loop to output the data instead of 20 individualy instructions, i.e.
'Send the information to serial output.
Dim Array_Element as byte
Dim Lead_Text as String * 20

   For Array_Element = 0 to 19
      Lead_Text = "Water " + Str$(Dec Array_Element) + ": "     
      HRSOut Lead_Text, Dec Water[Array_Element],13           
   Next
                                 
        HRSOut " ",10,13


RGV250

Hi Stephen,
There main reason I did not do that is that Mike is shown on the forum as a newbie and I did not know his coding ability, hence the simplistic aproach which I think is a lot easier to follow if you are new to it. Also when I write code I tend to keep it as simple as possible and make it work first and then look at optimising it knowing that I can go back if it doesnt. Anyway, Mike has a couple of options to try now.

Bob

Mike

Hi Guys
I have been playing around with this for thepast few days without success. :(
Here is a couple of photos with this code.

'Program   : Water Sensor GLCD.1
'File Path : E:\DipTrace\My Projects\Water Control\Positron\Water Sensor GLCD.bas
'Author    : [select VIEW...EDITOR OPTIONS]
'Date      : 15/04/2023     Version 0.0.0.0
'Notes     : <Enter_Program_Notes>   

'****************************************************************************** 
'          PIC18F452:                 +----v----+
'                            MCLR/Vpp-[1      40]-B7/PGD
'                                  A0-[2      39]-B6/PGC
'                                  A1-[3      38]-B5
'                                  A2-[4      37]-B4
'                                  A3-[5      36]-B3
'                                  A4-[6      35]-B2/INT2
'                                  A5-[7      34]-B1/INT1
'                        LCD_RSPin E0-[8      33]-B0/INT0
'                       LCD_CS1Pin E1-[9      32]-+5V/Vdd
'                       LCD_CS1Pin E2-[10     31]-GND/Vss
'                             Vdd/+5V-[11     30]-D7 LCD_D7
'                             Vss/GND-[12     29]-D6 LCD_D6
'                           CLKI/OSC1-[13     28]-D5 LCD_D5
'                           CLKO/OSC2-[14     27]-D4 LCD_D4
'                        LCD_RWPin C0-[15     26]-C7/RX
'                        LCD_RSPin C1-[16     25]-C6/TX
'                                  C2-[17     24]-C5
'                                  C3-[18     23]-C4
'                           LCD_D0 D0-[19     22]-D3 LCD_D3
'                           LCD_D1 D1-[20     21]-D2 LCD_D2
'                                     +---------+
'******************************************************************************
Declare Reminders Off
;-------------------------------------------------------------------------------
;**** Added by Fuse Configurator ****
; Use the Fuse Configurator plug-in to change these settings

Device = 18F452

Config_Start
  OSC = HS ;HS oscillator
  OSCS = OFF ;Oscillator system clock switch option is disabled (main oscillator is source)
  PWRT = On ;PWRT enabled
  BOR = On ;Brown-out Reset enabled
  BORV = 20 ;VBOR set to 2.0V
  WDT = OFF ;WDT disabled (control is placed on the SWDTEN bit)
  WDTPS = 128 ;1:128
  CCP2MUX = On ;CCP2 input/output is multiplexed with RC1
  STVR = On ;Stack Full/Underflow will cause RESET
  LVP = OFF ;Low Voltage ICSP disabled
  Debug = OFF ;Background Debugger disabled. RB6 and RB7 configured as general purpose I/O pins.
  Cp0 = OFF ;Block 0 (000200-001FFFh) not code protected
  CP1 = OFF ;Block 1 (002000-003FFFh) not code protected
  CP2 = OFF ;Block 2 (004000-005FFFh) not code protected
  CP3 = OFF ;Block 3 (006000-007FFFh) not code protected
  CPB = OFF ;Boot Block (000000-0001FFh) not code protected
  CPD = OFF ;Data EEPROM not code protected
  WRT0 = OFF ;Block 0 (000200-001FFFh) not write protected
  WRT1 = OFF ;Block 1 (002000-003FFFh) not write protected
  WRT2 = OFF ;Block 2 (004000-005FFFh) not write protected
  WRT3 = OFF ;Block 3 (006000-007FFFh) not write protected
  WRTC = OFF ;Configuration registers (300000-3000FFh) not write protected
  WRTB = OFF ;Boot Block (000000-0001FFh) not write protected
  WRTD = OFF ;Data EEPROM not write protected
  EBTR0 = OFF ;Block 0 (000200-001FFFh) not protected from Table Reads executed in other blocks
  EBTR1 = OFF ;Block 1 (002000-003FFFh) not protected from Table Reads executed in other blocks
  EBTR2 = OFF ;Block 2 (004000-005FFFh) not protected from Table Reads executed in other blocks
  EBTR3 = OFF ;Block 3 (006000-007FFFh) not protected from Table Reads executed in other blocks
  EBTRB = OFF ;Boot Block (000000-0001FFh) not protected from Table Reads executed in other blocks
Config_End

;**** End of Fuse Configurator Settings ****
;-------------------------------------------------------------------------------
Declare Reminders On
Xtal   = 4
All_Digital TRUE
'******************************************************************************
 
'******************************
'Declares for a GRAPHIC LCD for the Proton board
'LCD Pin 1=CS1, 2=CS2, 3=GND, 4=Vcc, 5=Vo, 6=RS, 7=R/W, 8=EN, 9-16=B0-B7
'LCD Pin 17=RST(To +5V), 18=-Vout(To Contrast), 19=LEDA(N/C), 20= LEDK(N/C)
'******************************
'Set up the Graphic LCD            PIC Pin      LCD Pin
Declare LCD_DTPort   PORTD   '   19-22,27-30     9-16
Declare LCD_RWPin  = PORTC.0 '      15            7
Declare LCD_CS1Pin = PORTE.1 '       9            1
Declare LCD_CS2Pin = PORTE.2 '      10            2
Declare LCD_RSPin  = PORTC.1 '      16            6
Declare LCD_ENPin  = PORTE.0 '       8            8
Declare LCD_Type   = Graphic
Declare Internal_Font = On
Declare Font_Addr = 0
'******************************
'
'******************************************************************************
'        76543210
TRISA = %00000000      'TRISIO register. 1=I/P, 0=O/P. ALL set for O/P
TRISB = %00000000      'TRISIO register. 1=I/P, 0=O/P. ALL Set For O/P
TRISC = %00000000      'TRISIO register. 1=I/P, 0=O/P. ALL O/P, 3 is I/P
TRISD = %00010000      'TRISIO register. 1=I/P, 0=O/P. 0-3=I/P, 4-7=O/P
TRISE = %00000000      'TRISIO register. 1=I/P, 0=O/P. Bit 4 is a 0 to disable
'the Parallel Slave Port on PortD. 1=PSP, 0=I/O
'Declare PortB_Pullups = On
'******************************************************************************

        Declare Slow_Bus On       '!!!! Might not be needed.
        Symbol SDA = PORTC.4 ' Alias the SDA (Data) line. Pin 23.
        Symbol SCL = PORTC.3 ' Alias the SCL (Clock) line. Pin 18.
      
'You could use an array for this.      
        Dim Water_0 As Byte
        Dim Water_1 As Byte      
        Dim Water_2 As Byte
        Dim Water_3 As Byte
        Dim Water_4 As Byte      
        Dim Water_5 As Byte
        Dim Water_6 As Byte      
        Dim Water_7 As Byte    
        Dim Water_8 As Byte
        Dim Water_9 As Byte      
        Dim Water_10 As Byte
        Dim Water_11 As Byte
        Dim Water_12 As Byte      
        Dim Water_13 As Byte
        Dim Water_14 As Byte      
        Dim Water_15 As Byte  
        Dim Water_16 As Byte
        Dim Water_17 As Byte      
        Dim Water_18 As Byte
        Dim Water_19 As Byte
                                
        Clear                   ' Clear ALL RAM locations
        DelayMS 100             ' Allow PIC to start up
DelayMS 500            'LCD stabilise
Cls
 Print At 0,0, "Water Sensor.Bas"  'Program name and version
 Print At 1,0, "15/04/2023      "
DelayMS 1000

Cls
'Set the device I2C address (Low 8 sensors)      
        Symbol GroveAddressLow = 0x77
'Set the device I2C address (High 12 sensors)      
        Symbol GroveAddressHigh = 0x78
      
MainLoop:
'Read the Data for the low 8 sensors
        I2CIn SDA, SCL, GroveAddressLow,0,[Water_0, Water_1, Water_2, Water_3, Water_4, Water_5, Water_6, Water_7]
'Read the Data for the high 12 sensors.
        I2CIn SDA, SCL, GroveAddressHigh,0,[Water_8, Water_9, Water_10, Water_11, Water_12, Water_13, Water_14, Water_15, Water_16, Water_17, Water_18, Water_19]
      
'Send the information to GLCD.      
;The Low 8 Sensors.
        Print At 0,0, "Level "
        Print At 1,0, "Level "
        Print At 2,0, "Level "
        Print At 3,0, "Level "
        Print At 4,0, "Level "
        Print At 5,0, "Level "
        Print At 6,0, "Level "
        Print At 7,0, "Level "

        Print At 0,6, "00"
        Print At 1,6, "01"
        Print At 2,6, "02"
        Print At 3,6, "03"
        Print At 4,6, "04"
        Print At 5,6, "05"
        Print At 6,6, "06"
        Print At 7,6, "07"

        Print At 0,9, Dec Water_0
        Print At 1,9, Dec Water_1
        Print At 2,9, Dec Water_2
        Print At 3,9, Dec Water_3
        Print At 4,9, Dec Water_4
        Print At 5,9, Dec Water_5
        Print At 6,9, Dec Water_6
        Print At 7,9, Dec Water_7

        DelayMS 2000
;The High 12 Sensors.
        Print At 0,0, "Level "
        Print At 1,0, "Level "
        Print At 2,0, "Level "
        Print At 3,0, "Level "
        Print At 4,0, "Level "
        Print At 5,0, "Level "
        Print At 6,0, "Level "
        Print At 7,0, "Level "

        Print At 0,6, "08"
        Print At 1,6, "09"
        Print At 2,6, "10"
        Print At 3,6, "11"
        Print At 4,6, "12"
        Print At 5,6, "13"
        Print At 6,6, "14"
        Print At 7,6, "15"

        Print At 0,9, Dec Water_8
        Print At 1,9, Dec Water_9
        Print At 2,9, Dec Water_10
        Print At 3,9, Dec Water_11
        Print At 4,9, Dec Water_12
        Print At 5,9, Dec Water_13
        Print At 6,9, Dec Water_14
        Print At 7,9, Dec Water_15

        DelayMS 2000

        GoTo MainLoop

Include "Font.inc"         'Place at end of program for Graphic LCD

I have attached 6 photos. 4 are using the above code and the other 2 are using an Arduino UNO with the sample code from Grove.
As you can see the Basic code doesn't work whereas the Arduino code does.
I have had alook at the Arduino code but I doný know hardly anything about it.

On another topic Bob mentioned the fact that I am shown as a NEWBIE.
I have, in fact been using Proton since its inception as L.E.T. Basic from way back.
Today is my 73rd birthday so it would be nice to be known as maybe a Senior.
As you guys live in the UK it would still be my birthday because that is where I was born!!!

Anyway thanks for any help again.
Mike

John Lawton

'Newbie' is only related to the number of posts you've made. Keep going Mike and you'll be automatically 'upgraded' :)

Oh, and Happy Birthday!

Pepe

#11
could i add this


'Set the device I2C read address (Low 8 sensors)     
Symbol GroveRAddressLow = (0x77<<1) +1                '0xEF

'Set the device I2C read address (High 12 sensors)    '0xF1 
Symbol GroveRAddressHigh = (0x78<<1) +1

'Set the device I2C write address (Low 8 sensors)     
Symbol GroveWAddressLow = 0x77<<1                     '0xEE

'Set the device I2C write address (High 12 sensors)   '0xF0 
Symbol GroveWAddressHigh = 0x78<<1




and modify

I2CIn SDA, SCL, GroveRAddressLow,[Water_0, Water_1, Water_2, Water_3, Water_4, Water_5, Water_6, Water_7]
I2CIn SDA, SCL, GroveRAddressHigh,[Water_8, Water_9, Water_10, Water_11, Water_12, Water_13, Water_14, Water_15, Water_16, Water_17, Water_18, Water_19]
     

RGV250

#12
Hi Mike,
The Newbie is on the left of the web page.
I did mention in post 7 that if it did not work you might need to alter the address so instead of
GroveAddressLow / high = 0x77 / 0x78 try 0x9B / 0x9D.
This is adding the read bit(0) so 0x77 - 01001101 binary is 10011011 the address is shifted left and the read/write bit0 is set to one.

Regards,
Bob

Pepe

#13
for read
in binary 0x77 is 1110111 and shift left one bit is 11101111 0xEF
in binary 0x78 is 1111000 and shift left one bit is 11110001 0xF1
for write
in binary 0x77 is 1110111 and shift left one bit is 11101110 0xEE
in binary 0x78 is 1111000 and shift left one bit is 11110000 0xF0

try post #11

Stephen Moss

Quote from: Mike on Apr 17, 2023, 10:35 AMI have attached 6 photos. 4 are using the above code and the other 2 are using an Arduino UNO with the sample code from Grove.
As you can see the Basic code doesn't work whereas the Arduino code does.
Do you have any way of measuring the I2C clock speed?
It is possible that the Arduino code is using a 400KHz clock whereas the BASIC is using 100KHz or visa versa and thus there is a mismatch between the speed the sensor needs and the speed produced by the BASIC code and so that is why you are not getting any data back. 

RGV250

Hi Pepe,
That was strange, all I saw in post 11 was dim type as byte which did not make any sense, not sure why I did not see the other part.

Oops, I put 77 dec in the converter :-[

Hi Stephen,
It should not matter as the PIC is the master so controls the clock.

Bob

Pepe

according to the manual the address is optional

trastikata

#17
Hi,

as mentioned earlier for communication this sensor simply uses two ATtiny1616 MCU's configured as slave I2C, I can speculate that their firmware will emulate the Arduino I2C commands.

Therefore it might be necessary to break the I2C protocol to its basics in Positron and fully emulate the Arduino code.

Thus using Bstart-Bstop, BusOut-BusIn and BusAck-BusNack plus following the Arduino example code the number of requested bytes should be sent too.

'Read High sensor data
BStart
    BusOut AddressHighRead
    BusOut 12
BReStart
    For i = 0 To 11
        BusIn bTemp
        WaterH[i] = bTemp
        BusAck
    Next   
   
    BusNack
BStop

'Read Low sensor data
BStart
    BusOut AddressLowRead
    BusOut 8
BReStart
    For i = 0 To 7
        BusIn bTemp
        WaterL[i] = bTemp
        BusAck
    Next   
   
    BusNack
BStop

Declare SDA_Pin = PORTC.4
Declare SCL_Pin = PORTC.3

'GroveWAddressLow
Symbol AddressLowRead = 0xEF
'GroveWAddressHigh
Symbol AddressHighRead = 0xF1

'Holds High sensor data
Dim WaterH[12] As Byte
'Holds Low sensor data
Dim WaterL[8] As Byte

Dim i As Byte
Dim bTemp As Byte

RGV250

Hi Mike,
Have you got it sorted or at least get data back from the device, if not I will see if I can get one and have a go.

Bob

RGV250

Hi Mike,
If you have not got yours working with Positron try this. I have to say the Arduino code was pretty convoluted to do simple things, or at least simple things in Positron.
I am not sure why but I could not get it to work with I2Cin, I had to use HBusIn?, also It did not like reading in a loop.
Let me know if you have any problems and I will see what I can do.

Bob

'****************************************************************
'*  Name    : Water Sensor_1_2.BAS                              *
'*  Author  : Bobby Garrett                                     *
'*  Notice  : Copyright (c) 2023 Bobby Garrett                  *
'*          : All Rights Reserved                               *
'*  Date    : 29/04/2023                                        *
'*  Version : 1.2                                               *
'*  Notes   : Red / green LED's to show Status of readings.     *
'*          : ?? Appears to mess up if covered when powere up.  *
'*          : Uses 3.3v supply from the Amicus18 board.         *
'****************************************************************

' Include the device configuration file.
     Include "Amicus18.inc"        ' Configure the compiler to use the Amicus18 hardware (18F25K20, 64MHz)

' Optional LED's to show if the sensors are reading correctly.
' Will show RED if there is a zero / low value in the middle of high readings.
         Symbol RedLED = LATB.0
         Symbol GreenLED = LATB.1
         TRISB.0 = 0
         TRISB.1 = 0   
             
         Declare Slow_Bus On           ' Might not be needed but don't need the speed as only device on the BUS.
         Symbol SCL_Pin = PORTC.3      ' Alias the SCL (Clock) line (Yellow if using the Grove cable).
         Symbol SDA_Pin = PORTC.4      ' Alias the SDA (Data) line (White if using the Grove cable).

'Set the device I2C address (Low 8 sensors)
         Symbol LowAddress = 0x77
'Set the device I2C address (High 12 sensors)
         Symbol HighAddress = 0x78
         
         Dim SensorData[21] As Byte ' Create a 21 element Array for the sensor readings.
                                    ' Only 20 used, the 21st added as there may be an error in the validate routine [TempLoop + 1].
         Dim SensorData0 As Byte                           
         Dim SensorData1 As Byte                                     
                                   
         Dim ReadAddress As Byte
         Dim bAddress As Byte   
         Dim TempLoop As Byte      ' Temp variable for loop counts

         Dim SensorValueMin As Byte = 245 ' !!Possibly varies on water (filtered etc).
         Dim SensorValueMax As Byte = 255
         Dim Threshold As Byte = 100      ' Used for error count.
         Dim SensorsCovered As Byte       ' Number of sensors covered to calculate the percentage level.
         Dim WaterLevel As Byte           ' The actual percentage level.
         Dim ErrorCount As Byte
         
         Clear                            ' Clear ALL RAM locations
         DelayMS 100                      ' Allow PIC to start up

MainLoop:
         ReadSensor()                     ' Read the sensor data.
         ShowData()                       ' Send the sensor data to the serial com.
         ValidateData()

         DelayMS 1000
         GoTo MainLoop

' Read the low & high sensor data.
' Not sure why I have needed to do it like this but using a loop just does not appear to work.
' Also I2Cin does not??
' Test without the last delay(s) later.
Proc ReadSensor()
' Read the low 8 sensor elements.
         ReadAddress = (LowAddress << 1) + 1  ' Shift the address left and add the "read" bit.
' Read the low sensor data and store in array locations 0 - 7.
         HBusIn ReadAddress, 0, [SensorData[0]]
         DelayMS 10
         HBusIn ReadAddress, 1, [SensorData[1]]
         DelayMS 10
         HBusIn ReadAddress, 2, [SensorData[2]]
         DelayMS 10
         HBusIn ReadAddress, 3, [SensorData[3]]
         DelayMS 10 
         HBusIn ReadAddress, 4, [SensorData[4]]
         DelayMS 10
         HBusIn ReadAddress, 5, [SensorData[5]]
         DelayMS 10
         HBusIn ReadAddress, 6, [SensorData[6]]
         DelayMS 10
         HBusIn ReadAddress, 7, [SensorData[7]]
         DelayMS 10                            'Probably not needed but time is not critical here.

' Read the high 12 sensor elements.
         ReadAddress = (HighAddress << 1) + 1  ' Shift the address left and add the "read" bit.
' Read the high sensor data and store in array locations 8 - 19.
         HBusIn ReadAddress, 0, [SensorData[8]]
         DelayMS 10
         HBusIn ReadAddress, 1, [SensorData[9]]
         DelayMS 10
         HBusIn ReadAddress, 2, [SensorData[10]]
         DelayMS 10
         HBusIn ReadAddress, 3, [SensorData[11]]
         DelayMS 10 
         HBusIn ReadAddress, 4, [SensorData[12]]
         DelayMS 10
         HBusIn ReadAddress, 5, [SensorData[13]]
         DelayMS 10
         HBusIn ReadAddress, 6, [SensorData[14]]
         DelayMS 10
         HBusIn ReadAddress, 7, [SensorData[15]]
         DelayMS 10                 
         HBusIn ReadAddress, 8, [SensorData[16]]
         DelayMS 10
         HBusIn ReadAddress, 9, [SensorData[17]]
         DelayMS 10
         HBusIn ReadAddress, 10, [SensorData[18]]
         DelayMS 10
         HBusIn ReadAddress, 11, [SensorData[19]]
         DelayMS 10                            'Probably not needed but time is not critical here.
EndProc

' Send the RAW data to the serial com.
Proc ShowData()
         For TempLoop = 0 To 19
         HRSOut "Sensor Data - ", Dec TempLoop, " - ", Dec SensorData[TempLoop],13
         Next TempLoop

         HRSOut "***************************************************************",10,13
EndProc

Proc ValidateData()
' Validate the data, if there is a zero value and Then a higher value above it this is a possible error as this should not be possible.
' Calculate the percentage level by counting the number of consecutive high readings.
         SensorsCovered = 0                                          ' Clear the Sensor covered counter.
         For TempLoop = 0 To 19
         If SensorData[TempLoop] < Threshold Then GoTo ErrorCheck    ' There should not be any more positive readings now.         
         If SensorData[TempLoop] > Threshold Then Inc SensorsCovered ' Is the stored value greater than the threshold, yes so increment the counter.
         Next TempLoop

' Jump to here if there is a low reading as there should not be any high readings or it is a possible error.                 
ErrorCheck:
         ErrorCount = 0                                                        ' Clear the error counter.
         For TempLoop = 0 To 19
         If SensorData[TempLoop] < Threshold Then                              ' Is the stored value less than the threshold.         
         If SensorData[TempLoop + 1] > Threshold Then                          ' Yes so check if the next one is higher.
         Inc ErrorCount
         HRSOut "Sensor error - ", Dec TempLoop, " / ",Dec TempLoop + 1,10,13  ' Show the position where sensors give erroneous data.
         EndIf
         EndIf
         Next TempLoop

         HRSOut "Error count - ", Dec ErrorCount,10,13          ' Show the number of erroneous readings.     

' Only for optional status LED's.         
         If ErrorCount > 0 Then
         RedLED = 1
         GreenLED = 0
         Else
         RedLED = 0
         GreenLED = 1
         EndIf

         WaterLevel = SensorsCovered * 5                        'There are 20 sensors so multiply by 5 to get the percentage.
         
         HRSOut "Sensors covered - ", Dec SensorsCovered, 10,13 ' Show the number of sensors covered. !!!!!!!!! Only for debugging.
         HRSOut "Water level - ", Dec WaterLevel, "%", 10,13    ' Show the % value of the level.         
         HRSOut "***************************************************************",10,13  ' Separate the readings.       
EndProc