tshannon
Posts: 2
Joined: Wed Sep 02, 2015 1:30 pm

Read signal of an RV Tank Sensor

Wed Sep 02, 2015 2:25 pm

My wife and I are in the middle of renovating an airstream travel trailer (more info here if you're curious: http://rumblestripramblings.com/), and one of the features I'd love to add during the reno would be to read our Fresh, Grey, and Black Water tanks with a Pi so I can display it on our TV in the trailer, or throw it up on a web page and display it on my phone, etc.

The sensor I'm planning on using is this: https://www.rvupgradestore.com/Seelevel ... /710es.htm.

It states that there is a micro-controller in the sensor, and it'll put out digital codes based on the sensed level. I don't know the voltage of the digital signal currently but I assume I can add resistors to get it to the right level for the GPIO.

Based on the admittedly, limited information I've provided, do you think this would be possible? Or are there some glaring inaccuracies in my assumptions?

Thanks,
Tim

scotty101
Posts: 4445
Joined: Fri Jun 08, 2012 6:03 pm

Re: Read signal of an RV Tank Sensor

Wed Sep 02, 2015 3:24 pm

Looks feasible but difficult to tell given that the protocol used by the sensors isn't mentioned. It looks like it might a some kind of one-wire protocol since it only has ground and signal connections.
You should ask the manufacturer for more details, I suspect that they only want you to connect this device to the level indicators they they manufacture.
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

tshannon
Posts: 2
Joined: Wed Sep 02, 2015 1:30 pm

Re: Read signal of an RV Tank Sensor

Wed Sep 02, 2015 3:51 pm

Yeah, I figure since it's single wire, and the protocol is described as a "set of codes", I may possibly be able to reverse engineer it. But that's a few big "ifs". Worse case scenario I can buy their panel, but a $150 panel that does one thing with data seems like a sad bargain compared to a Pi which will let me put the data wherever I want.
Tim

digiital
Posts: 4
Joined: Tue Oct 27, 2015 11:44 pm

Re: Read signal of an RV Tank Sensor

Wed Oct 28, 2015 12:18 am

That sensor reminds me of the following setup.
https://www.adafruit.com/products/1602

The 710ES has 8 points, the adafruit board has 8 points as well.

seu_dcmilan
Posts: 7
Joined: Tue Nov 03, 2015 5:18 am

Re: Read signal of an RV Tank Sensor

Thu Nov 05, 2015 5:16 am

In my experience, the signal may be connect to a rs-232. You just receive the bytes passively, maybe every 1sec or 10sec.

alfista2600
Posts: 34
Joined: Wed Jan 22, 2014 1:19 am

Re: Read signal of an RV Tank Sensor

Mon Apr 04, 2016 5:00 am

Scotty - did you ever end up doing anything with this? I'm thinking of doing the same thing. I saw another thread with someone planning on using a voltage meter running off the sensor to an arduino.

alfista2600
Posts: 34
Joined: Wed Jan 22, 2014 1:19 am

Re: Read signal of an RV Tank Sensor

Mon Apr 04, 2016 5:21 am

looking further into this, the sensors look much more complicated. I know nothing about reverse engineering whatever protocol. Too bad... have much of my automation wrapped around a pi and don't want to invest in this controller.

GiantLifeTinyRV
Posts: 1
Joined: Fri Mar 02, 2018 7:56 pm

Re: Read signal of an RV Tank Sensor

Fri Mar 02, 2018 8:00 pm

Has any one made progress on this. I'd also like to do this and tie it into my Google Home. For example, "Hey Google... What's the black tank level?". Has anyone contacted Garnet?

davidv851
Posts: 1
Joined: Wed Aug 22, 2018 1:15 am

Re: Read signal of an RV Tank Sensor

Wed Aug 22, 2018 1:21 am

I haven't done this _yet_ but there's a module that will allow you to interface up to 6 sensors via RS232 serial connection (https://www.garnetinstruments.com/produ ... interface/).

mydog8it
Posts: 1
Joined: Wed Sep 04, 2019 11:17 pm

Re: Read signal of an RV Tank Sensor

Wed Sep 04, 2019 11:31 pm

I was looking for a solution like this myself. I want to collect the data from the sensors and display it in a Splunk dashboard. I want to create alerts for high or low levels (depending on the tank type). I was looking at the See Level solution but don't think it will allow me to send the data outside of the system.

I took a look at the documentation on See Level's website and it looks like they are using Geotab/RV-C to "talk" to the sensors. Between the Geotab SDK/docs and the See Level docs this seems doable to me, am I missing something?

limeandcoconut
Posts: 2
Joined: Wed Mar 04, 2020 10:58 pm

Re: Read signal of an RV Tank Sensor

Wed Mar 04, 2020 10:59 pm

Has anyone made progress on this? I'm looking to start working on my tank sensors and this sounds like it would be a great solution.

jgrant911
Posts: 11
Joined: Thu Jun 04, 2020 6:02 pm

Re: Read signal of SeeLevel RV Tank Sensor

Thu Jun 04, 2020 6:50 pm

I just completed a monitor/control project in my RV using the SeeLevel sensors. My project was done using Arduino but the results are transportable.

I spent a fair amount of time reverse engineering the protocol and the electronics to be able to directly and reliably read the 3 tank sensors that I installed in my Sunseeker 2300. I have attached some of the documentation that I produced for my own records. Have a look and see if it makes sense. I would have added more but the size limitation on attachments made it difficult.

For anyone wanting to do a project like this I am willing to share details such as the code that I wrote to properly read the sensors including the checksum calculations that are used in the data stream. FYI, I also wrote code to access DS18B20 temperature sensors, DS3231 Real Time Clock, TriMetric Battery Monitor and EpEver Tracer Solar Charge Controller.

Jim G.
Attachments
Panel Front Layout3_LowRes.jpg
Panel Front Layout3_LowRes.jpg (146.72 KiB) Viewed 5080 times
SeeLevel Data Detail.jpg
SeeLevel Data Detail.jpg (244.92 KiB) Viewed 5080 times
SeeLevel Circuit.jpg
SeeLevel Circuit.jpg (240.73 KiB) Viewed 5080 times
Last edited by jgrant911 on Thu Feb 18, 2021 1:46 am, edited 1 time in total.

dgorman
Posts: 1
Joined: Fri Jun 12, 2020 5:21 am

Re: Read signal of an RV Tank Sensor

Fri Jun 12, 2020 5:24 am

Hi

I am very interested in the SeeLevel post above. I’ve instrumented Victron devices and my Weatherflow station and just need tank levels and have been trying to figure this out for a while! I have a Pi in my RV and it collects the data and I have my own dashboards. I’d love to see how this was implemented, the binary code signals (PWM?) isn’t something I’ve done with a Pi. Any additional info would be awesome!

jgrant911
Posts: 11
Joined: Thu Jun 04, 2020 6:02 pm

Re: Read signal of an RV Tank Sensor

Sat Jun 13, 2020 1:26 am

OK, glad someone is interested! It took me quite a while to reverse engineer the SeeLevel protocol and electronics so I hope others can use the results.

I have "played" with an RPi 3B+ that I picked up last year but as I mentioned before, all my work on this RV Info Panel was Arduino based because of the much more user friendly IDE and the auto restart functionality of the Arduinos (a real benefit in an RV when the power tends to be on and off quite a bit).

Also, remember that programming isn't my profession its just one of my hobbies. The only coding I did at university was FORTRAN so all of this "object oriented" stuff and anything to do with "array pointer de-referencing" I tend to avoid!

I have included 2 more pieces that are important to understanding the decoding of the data and how it is gathered, converted and displayed by my Arduino based system:

The first file is a Notepad++ excerpt of the code that actually reads the tank levels (this is from the Arduino Nano that I have doing temperature and tank level monitoring). I can't promise that I copied all the declarations into the file but you will find out if you get as far as running it! The file components are :

- Title blurb
- Declarations
- Main code segment that manages reading the level of 3 tanks and then converts the data to percent readings
- Subroutine readLevel() is passed a tank number and sends out 1-3 pulses to select a tank then waits for stores, and returns a 12 byte reply
- Subroutine readByte() reads and stores 8 bits and returns it as a single properly coded byte

If you review this info along with the previous pages showing the data format and the electrical interface you should get a good idea of how it all comes together. The major part of the Main code segment is spent figuring out what is currently the top non-zero segment of each tank tankFillSeg(), and then assigning fill percentages to it and all the lower segments then totaling them.

My setup is additionally complicated by the fact that I have shortened 2 of the sensors to properly fit my waste tanks which are long and flat. This is a design feature of the SeeLevel sensor to allow use on shorter tanks (you can also stack sensors to read taller tanks). So you can see 2 arrays that store calculated fill segment percentages for 8 segment (tankSegVol8[ ]) and 5 segment (tankSegVol5[ ]) tanks that are used in the calculations. My plan, when I have my RV in a location I can run the test, is to fill each tank at a known rate while collecting time-stamped fill data from the Nano. This will allow a more accurate determination of the tankSeg Fill numbers.

The checksum verification had me stumped for quite a while. The Checksum is basically a calculation of (MOD(byteSum, 256) -2). This works except when MOD(byteSum, 256) is 0 or 1. which results in an error (negative) when using byte math. The designer apparently uses byte 0 as some sort of rollover counter to keep track of each time the sum of the tank level segments (bytes 2 to 9) exceeds 255 and somehow incorporates this into the checksum verification. Someone with more smarts than me could probably look at the data and come up with an elegant solution to this. In order to avoid the checksum failure that occurs whenever (MOD(byteSum, 256) is 0 or 1 I just added 2 special cases to the verification logic to handle this.

The second is a transcript of data read from the Seelevel ES710 sensor on my test bench using a logic analyzer. I taped the sensor to a big bucket and used the SeeLevel 709 display panel to read the percent fill while gradually adding water to the bucket. You can get a good feel for how the first segment of the sensor "sees" the water rising then when the level gets to around 128 (9%) the second segment starts to register. This continues segment by segment until the sensor reads 100%.

Ok, that's about it for now. I am happy to answer questions but remember, being an old engineer I tend to get cranky when I get the sense that the people asking the questions haven't put much thought into the problem.

Cheers!

Jim

Code: Select all

// The part of my monitor program that reads and decodes the data from the SeeLevel sensors to provide a Fill Percent
// 
// Byte 0: read counter, byte 1: checksum, bytes 2-9: fill data, bytes 10-11: all zeros
// Bytes 2-9 are 8 bit representations of the "fill density" of the 8 sensor segments
//
// 3 tanks x 12 bytes:  
byte SeeLevels[3][12];  //Seelevel raw data storage:
byte lowLimit[3][8];    //May require zero/empty offset for SeeLevel calibration.
byte highLimit[3][8];   //May require zero/empty offset for SeeLevel calibration.
float tankLev[] = {0, 0, 0};      //Initialize tank levels to zero
byte tankArray[] = {90, 35, 25, 0};  //Test values: Fresh, Grey, Black; percent full with last byte reserved for crc
byte tankBaseSeg[] = {9, 6, 6};  //Base segment of bytes 11 (LSB) to 4 (MSB) of sensor strip (Black/Grey are cut down for shallow tank)

// It's important to note that a "full" segment does not mean a reading of 255. Usually a full segment reading only reaches around 125 
// before the next segment starts to register.
// Tank sensors were bench tested to determine variations in the sensor readings along the sensor strips and the following
// parameters were defined to help improve the accuracy of the calculations used to determine the measured tank level
// Use of each is explained further in the related code segments
byte tankFillSeg[] = {0, 0, 0};   //Current top segment of each tank with nonzero reading
float tankSegVol8[] = {18, 13, 11, 15, 11, 11, 11, 10};  //Measured approx. percent fill for each of 8 segments (top to bottom)
float tankSegVol5[] = {22, 21, 20, 19, 18};   //Measured percent fill for each of 5 segments on shallow tanks (top to bottom)
float baseSum;
int byteSum, checkSum;  //SeeLevel data integrity checks
float segVal, fltTFS;

// Main code segment that reads the 3 tank levels. (Lots of left-over print statements for debug of calculations)
    readLevel(tankNum);                // Read Tank levels 0, 1, 2 sequentially. Each Read returns 12 bytes.
    
	for (i = 0; i < 12; i++) {
      Serial.print(SeeLevels[tankNum][i]);  //Store 12 bytes of data in array
      Serial.print(' ');
    }
    Serial.println("----------");
	// Bytes 2 to 9 are an 8-bit code that represents  the fill in each segment of the tank, from bottom (9) to top (2)
    // Fresh tank uses all 8 segments of sensor strip, Grey and Black in my RV use only 5 segs, so decode must be adapted
    // Validate checksum (A checksum is contained in  byte1 (rollover count in byte0 is not used in calculations)
    byteSum = 0;                    
    for (i = 2; i <= 9; i++) {      
      byteSum = byteSum + SeeLevels[tankNum][i];  // Add all 8 data bytes together then ((remainder of byteSum/256) - 2) = checkSum.
    }
    // Verifying the checksum that comes with the SeeLevel data.
    // Remainder of (sum of all 8 data bytes)/256 - (checkSum + 2) should be zero
    checkSum = SeeLevels[tankNum][1];        //Get checksum from 2nd byte of read data
    Serial.print("byteSum % 256 - 2 = ");    
    Serial.print((byteSum % 256) - 2);
    Serial.print(" checkSum = ");
    Serial.println(checkSum);
    // Verify checkSum. Special cases to avoid negative result: if byteSum = 0 or 1, checksum will be 254 or 255 respectively
    if ((byteSum == 0 && checkSum == 254) || (byteSum == 1 && checkSum == 255) ||
        (byteSum % 256) > 0 and (byteSum % 256) == (checkSum + 2)) {                   
    // If data OK:
	// Calculation of total fill is kinda convoluted but it works:
      baseSum = 0;
      fltTFS = 0;
      segVal = 0;
      if (SeeLevels[tankNum][tankBaseSeg[tankNum]] == 0) {        // tankBaseSeg[] is 6 for tank 1 and 2 and 9 for tank 0
        tankFillSeg[tankNum] = tankBaseSeg[tankNum];              // If bottom (base) segment fill = 0, tank is empty so...
        tankLev[tankNum] = 0;
      }
      else {
        i = tankBaseSeg[tankNum];                                 // Data bytes are 2 to 9 or 2 to 6 depending on tank (MSB -> LSB)
        while (SeeLevels[tankNum][i] != 0 && i >= 2) {
          tankFillSeg[tankNum] = (i);                             // tankFillSeg[tankNum] will point to the top non-empty segment of tank[tankNum]
          --i;
        }
        if (tankNum == 0)  {                                      // Fresh water tank (tank 0) has 8 segment sensor
          for (i = 7; i > (tankFillSeg[tankNum] - 2); i--) {      // Add together the percent fill of each segment but the top one
            baseSum = baseSum + tankSegVol8[i];
          }
        }
        else {                                                    // Gray and Black tanks (tanks 1 and 2) have 5 segment sensorS
          for (i = 4; i > (tankFillSeg[tankNum] - 2); i--) {      // Add together the percent fill of each segment but the top one
            baseSum = baseSum + tankSegVol5[i];
          }
        }                                                          // Tank 0 (Fresh)has 8 segment sensor
        if (tankNum == 0) {                                        // Calculate the percent fill of the top non-zero segment(Fresh tank)
          fltTFS = SeeLevels[tankNum][tankFillSeg[tankNum]];       // convert partial tankFill to float to compare with typicsl "full" value
          segVal = (fltTFS / 125) * tankSegVol8[tankFillSeg[tankNum] - 2];    // 125 chosen to give close match to actual SeeLevel gauge
        }                                                          // Tanks 1 and 2(Grey and Black)have 5 segment sensors
        else {                                                     // Calculate the percent fill of the top non-zero segment(gray & black tanks)
          fltTFS = SeeLevels[tankNum][tankFillSeg[tankNum]];       // convert to float
          segVal = (fltTFS / 125) * tankSegVol5[tankFillSeg[tankNum] - 2];
        }
        tankLev[tankNum] = baseSum + segVal;                       // Add base fill and topseg fill together for total calculated tank fill
      }
	        //Serial.println(" ");
      Serial.print("BaseSeg = ");
      Serial.print(tankBaseSeg[tankNum]);
      Serial.print(" FillSeg = ");
      Serial.print(tankFillSeg[tankNum]);
      Serial.print(" FillSeg Level = ");
      Serial.print(SeeLevels[tankNum][tankFillSeg[tankNum]]);
      if (tankNum == 0) {
        Serial.print(" tankSegVol8 = ");
        Serial.println(tankSegVol8[tankFillSeg[tankNum] - 2]);
        //Serial.print(" tankSegVol8 = ");
        //Serial.println(tankSegVol8[tankFillSeg[tankNum] - 2]);
      }
      else {
        Serial.print(" tankSegVol5 = ");
        Serial.println(tankSegVol5[tankFillSeg[tankNum] - 2]);
      }
      Serial.print("baseSum = ");
      Serial.print(baseSum);
      Serial.print(" fltTFS = ");
      Serial.print(fltTFS);
      Serial.print(" segVal = ");
      Serial.print(segVal);

      tankLev[tankNum] = round(tankLev[tankNum]);
      if (tankLev[tankNum] > 100) tankLev[tankNum] = 100;  //Don't let tankLev exceed 100%
      Serial.print(" tankLev = ");
      Serial.println(tankLev[tankNum]);
      Serial.println("--------");

      //    Save limit values of 8 data bytes per sample for transfer to Mega
      //    Serial.print(SeeLevels[tankNum][0]);
      //    Serial.print(" ");
      //    Serial.print(SeeLevels[tankNum][1]);
      //Serial.println(" ");
      for (i = 2; i < 10; i++) {                     // Save and Print read values for verification
        if (SeeLevels[tankNum][i] > highLimit[tankNum][i - 2]) {
          highLimit[tankNum][i - 2] = SeeLevels[tankNum][i];
        }
        if (SeeLevels[tankNum][i] < lowLimit[tankNum][i - 2]) {
          lowLimit[tankNum][i - 2] = SeeLevels[tankNum][i];
        }
      }
	  
	  //Potential use of a zero/empty offset to provide better accuracy if tanks don't read empty when they are in fact empty
      Serial.println("Low-High Limit Check: ");
      for (i = 0; i < 8; i++) {
        Serial.print(lowLimit[tankNum][i]);
        Serial.print(" ");
        Serial.print(SeeLevels[tankNum][i + 2], HEX);
        Serial.print(" ");
        Serial.println(highLimit[tankNum][i]);
      }

      Serial.println(" ");

      
     
    }
    else {
      tankErr[tankNum] = ++tankErr[tankNum];      // increment error count for this tank
      dataUpdate = true;
    }
    if (tankNum != 2) {               // increment or reset tank number
      ++tankNum;
    }
    else {
      tankNum = 0;
    }
    tankPass = 1;
    //delay(200);

  


// Read individual tank levels
void readLevel(int t) {              // passed variable (t) is 0, 1 or 2          //Could use time check instead iof delay?
  digitalWrite(pinSLout, HIGH);     // Power the sensor line for 2.4 ms so tank levels can be read
  delayMicroseconds(2450);
  for (i = 0; i <= t; i++) {        // 1, 2 or 3 low pulses to select Fresh, Grey, Black tank
    digitalWrite(pinSLout, LOW);    // See RV Panel data file for protocol details
    delayMicroseconds(85);          // These settings give the 85 down/300 up (microsecond) pulse
    digitalWrite(pinSLout, HIGH);
    delayMicroseconds(290);
  }
  for (byteLoop = 0; byteLoop < 12; byteLoop++) {
    SeeLevels[t][byteLoop] = ~readByte();  // Populate 12 bytes with bitwise inverted readings (FF > 00)
  }
  delay(20);                    // Leave power on long enough to allow sensor to transmit data stream
  digitalWrite(pinSLout, LOW);  //Turn power off until next poll
}

byte readByte() {               //Function to read individual bytes from SeeLevel sensor
  result = 0;
  for (bitLoop = 0; bitLoop < 8; bitLoop++) {   // We need to populate byte from right to left,
    result = result << 1;                       // so shift right for each incoming bit
    pulseTime = (pulseIn(pinSLin, LOW));        // If needed, pulseIn() has optional 3rd arg: "timeout" in microseconds
    if (pulseTime  <= 26) {                     // "0" bit is low for about 43 microseconds, a "1" for about 13 microseconds
      thisBit = 1;                              // so 26 is the close to the decision point (tune for error improvement)
    }
    else  {
      thisBit = 0;
    }
    result |= thisBit;                          // compound bitwise OR succesive reads into shifted single byte
  }
  return result;
}

 
Attachments
SeeLevel Table P1.JPG
SeeLevel Table P1.JPG (167.42 KiB) Viewed 4940 times
SeeLevel Table P2.JPG
SeeLevel Table P2.JPG (185.58 KiB) Viewed 4940 times

jgrant911
Posts: 11
Joined: Thu Jun 04, 2020 6:02 pm

Read SeeLevel Sensor on an RV Tank Sensor - Update

Tue Jul 21, 2020 10:27 pm

So I finally have the entire Arduino Mega/Nano Touch Display project installed in my RV and I'm happy to say it works as expected. There is one useful piece of information I learned when reading the SeeLevel sensors on full size tanks (not a bucket on my workbench). The individual sensor segment readings actually range quite a bit higher on the full size tank.

If you refer to the Excel sheets that show my bench readings (above) you can see that the individual segments rarely read above 160 even when the fluid level is above that segment. In practice I have found that the segment readings approach 255 when the fluid level is at or above the top of a segment.

This didn't make much difference to my code:

I found one error where I neglected to do the modulo operation (line 2). and then where I was comparing the current top segment value to 125 to determine fullness, I change the comparison values to declared variables segLimit8 and segLimit6 and set these both to 250 so the comparison result would more accurately reflect the segment fill level about line 31).

Code: Select all

    // Verify checkSum. Special cases to avoid negative result: if byteSum = 0 or 1, checksum will be 254 or 255 respectively
    if (((byteSum % 256) == 0 && checkSum == 254) || ((byteSum % 256) == 1 && checkSum == 255) ||
        (byteSum % 256) > 0 and (byteSum % 256) == (checkSum + 2)) {                   
    // If data OK:
	// Calculation of total fill is kinda convoluted but it works:
      baseSum = 0;
      fltTFS = 0;
      segVal = 0;
      if (SeeLevels[tankNum][tankBaseSeg[tankNum]] == 0) {        // tankBaseSeg[] is 6 for tank 1 and 2 and 9 for tank 0
        tankFillSeg[tankNum] = tankBaseSeg[tankNum];              // If bottom (base) segment fill = 0, tank is empty so...
        tankLev[tankNum] = 0;
      }
      else {
        i = tankBaseSeg[tankNum];                                 // Data bytes are 2 to 9 or 2 to 6 depending on tank (MSB -> LSB)
        while (SeeLevels[tankNum][i] != 0 && i >= 2) {
          tankFillSeg[tankNum] = (i);                             // tankFillSeg[tankNum] will point to the top non-empty segment of tank[tankNum]
          --i;
        }
        if (tankNum == 0)  {                                      // Fresh water tank (tank 0) has 8 segment sensor
          for (i = 7; i > (tankFillSeg[tankNum] - 2); i--) {      // Add together the percent fill of each segment but the top one
            baseSum = baseSum + tankSegVol8[i];
          }
        }
        else {                                                    // Gray and Black tanks (tanks 1 and 2) have 5 segment sensorS
          for (i = 4; i > (tankFillSeg[tankNum] - 2); i--) {      // Add together the percent fill of each segment but the top one
            baseSum = baseSum + tankSegVol5[i];
          }
        }                                                          // Tank 0 (Fresh)has 8 segment sensor
        if (tankNum == 0) {                                        // Calculate the percent fill of the top non-zero segment(Fresh tank)
          fltTFS = SeeLevels[tankNum][tankFillSeg[tankNum]];       // convert partial tankFill to float to compare with typicsl "full" value
          segVal = (fltTFS / segLimit8) * tankSegVol8[tankFillSeg[tankNum] - 2];    // 125 chosen to give close match to actual SeeLevel gauge
        }                                                          // Tanks 1 and 2(Grey and Black)have 5 segment sensors
        else {                                                     // Calculate the percent fill of the top non-zero segment(gray & black tanks)
          fltTFS = SeeLevels[tankNum][tankFillSeg[tankNum]];       // convert to float
          segVal = (fltTFS / segLimit5[/ * tankSegVol5[tankFillSeg[tankNum] - 2];
        }
        tankLev[tankNum] = baseSum + segVal;                       // Add base fill and topseg fill together for total calculated tank fill
      }
I will be "field testing" the system on a 2 week camping trip coming up and I will follow up with any other changes that I make.

Jim G.

Wurlitzer28
Posts: 22
Joined: Fri Jan 02, 2015 4:41 pm

Re: Read signal of an RV Tank Sensor

Tue Jul 28, 2020 5:32 pm

jgrant911 this is EXACTLY what I am looking for. The tank level sensors in my Forest River Georgetown are useless as they tend to show levels much higher than actual.

Right now the RPi in the RV is only for a media center but I'd gladly purchase a new RPi-4 for such a system. My target display would be the phone as if my phone fails it would be immediately replaced. When some proprietary display/control panel fails replacement is not so easy.

Hope you are still working on this as the RV world needs such a system.

While I was quite adept at writing Assembly for an 8 core true multitasking Microcontroller and a very good VB 6.0 programmer, my "C" skills are not as good but this would be a great project to sharpen those skills.

jgrant911
Posts: 11
Joined: Thu Jun 04, 2020 6:02 pm

Re: Read signal of an RV Tank Sensor

Mon Aug 10, 2020 11:41 pm

Wurlitzer28 ,

Hope you have success with your project!

Just got back from a 2 week trip in our RV where I "field tested" my information panel. It worked very well but I did notice a couple of minor bugs that I will have to take care of. If you are using RPi and your phone as a display you can probably avoid a lot of the screen formatting I had to do on my general purpose touch display. Are you using Blynk or something similar?

One thing that exists now that wasn't available when I started my project is a SeeLevel panel with a RV-C CAN bus interface that you could interface directly to the RPi. A lot simpler than doing all the data crunching on raw sensor inputs. https://www.rvupgradestore.com/Garnet-T ... rvc-pm.htm

BTW, one difficulty with using the SeeLevel sensors is actually getting access to the sides of the tanks to attached the adhesive sensor strips. Sometimes hard to get to because of the way the campers/RVs are put together. This is one thing you will want to check out before you order parts. If you check out the link you can find lots of helpful details. In particular once you know the height of the tallest side of each tank, you can determine the exact sensors that you need.

Cheers!

Kaadk
Posts: 48
Joined: Fri Aug 28, 2020 3:39 pm

Re: Read signal of an RV Tank Sensor

Fri Aug 28, 2020 3:53 pm

Jgrant911

I’m still early looking into alternative sensors for a project I want to build for the tanks in our RV (it’s going to be an automated dumping solution that’s different than what’s commercially available) and was hoping you can shed some light on a bit of research I haven’t been able to tell.

My concern is what do the sensors register when you’re running the black tank flush? I figure with a few 12v powered valves, I want set up a one touch ‘dump’ scenario that drains the black, runs the flush then drains the gray and galley tanks. I’ve got the concept planned out, but it relies on having accurate tank level readings that aren’t complicated by the act of the flush spraying the interior walls of the tank.

Before I sink any money into getting the various parts (those sensors can be expensive) I was hoping that if you have a flush on your Sunseeker, you could give me some insight into how well the sensors are able to handle the flush running?

Thanks,
Mike

jgrant911
Posts: 11
Joined: Thu Jun 04, 2020 6:02 pm

Re: Read signal of an RV Tank Sensor

Sat Nov 14, 2020 7:38 pm

Mike,

Sorry to be so long responding. I've been tied up with other projects and haven't been checking this forum.

I don't have any data that would be particularly useful for your specific application. In my opinion the SeeLevel sensors are quite accurate both when the tank levels are steady and when they are changing (during a fill or empty cycle they track the level accurately).

In my setup I have no need for a high tracking rate on the levels so I only read the tanks every 5-10 seconds depending on what other higher priority tasks are running. My guess is that the accuracy during a flush cycle would still be good unless the sprayer is placing a large amount of water directly on the area of the tank where the sensor strip is mounted. I don't know the deep technical detail of their design but it is based on a measurement of density in the area behind the sensor.

As I mentioned in an earlier post, the manufacturer has not published any information on how to interpret the data provided by the sensors. Only by a lot of data capture on a test bench setup and many iterations of code was I able to come up with a way to get reasonable reading from the sensor data. With the work that I've done as a starting point it is likely possible to verify the accuracy of the readings in your scenario if you were to do some similar bench testing.

Good luck!

Jim

daddyben1
Posts: 3
Joined: Fri Dec 04, 2020 6:04 am

Re: Read signal of an RV Tank Sensor

Fri Dec 04, 2020 6:36 am

hi Jim , this is exactly what i have been looking for, kudos to you for going through all the time and effort. I also am using arduino IDE and of course the nano chip, in your code( read bytes) pulseTime = (pulseIn(pinSLin, LOW)); is pinSLin D5 ? and also would pinSLout be D6 ?
thank you...

jgrant911
Posts: 11
Joined: Thu Jun 04, 2020 6:02 pm

Re: Read signal of an RV Tank Sensor

Fri Dec 04, 2020 4:14 pm

Yes, the Input (D5) and Output (D6) correspond to the circuit connections on the interface circuit diagram that I posted earlier.

Jim

Kaadk
Posts: 48
Joined: Fri Aug 28, 2020 3:39 pm

Re: Read signal of an RV Tank Sensor

Fri Dec 04, 2020 5:45 pm

Thanks for the info Jim. I've also been tied up with several other endeavors on the go, so I haven't had the time to get back to my tinkering, but when I do, I'll be back here checking this info out again.

Basically, I'm trying to see if I can build a system, where, when I'm at a dump station, it dumps the black, then pumps the grey through the black's tank flusher to help rinse the black out, instead of just closing the black and dumping the grey. Right now, I'm only rinsing the black when I'm on full hookups, so we've been booking full hookups for 60% or so of our trips. If I get it working right I won't be as dependent on full hookups as often to keep my tanks clean.

For this project, I figure I'll need a water pump (seperate from the onboard one of course), a handfull of valves, some powered gate valves for dumping (man those things are expensive... so, maybe some selenoids instead attached to the existing gate valves) and I need some sensors to validate the amount of liquid in each tank while everything runs so that when the grey gets down to about 10 gallons left, it closes the black valve, then at 5 gallons in the black it shuts down the pump, and opens the grey's dump valve for one final rinse of the lines/hoses. Hence the need for accurate(ish) measurements while the flusher is running.

jgrant911
Posts: 11
Joined: Thu Jun 04, 2020 6:02 pm

Re: Read signal of an RV Tank Sensor

Tue Dec 08, 2020 7:35 am

Sounds like an ambitious project. Even if it turns out to be impractical you will learn lots! One of my earlier RV projects was a digital level gauge that would tell me how many blocks I needed to put under each wheel to get the RV level when setting up. I put together the hardware, wrote the code and got it working but it's still sitting in my project drawer because it would have been too difficult to mount it and calibrate it and by that time I was pretty good at just eyeballing the block requirements.
Leveler2.jpg
Leveler2.jpg (220.7 KiB) Viewed 3635 times

Anyway, if I were in your shoes, my first step would be to confirm that the tank monitors will do the job:

1. Get the quantity of SeeLevel strips that you need and adapt the code to read them.
2. Confirm on a test bench that your code can interpret the tank levels accurately (maybe use 5 gal. water jugs).
3. Gather data from the RV on the emptying rate of the tanks at various valve openings (time from full to empty/tank capacity).
4. Gather data on the bench by collecting measurements as the water jugs are draining to see if the system can track similar emptying rates.

Once you have the basics sorted out you will need to verify that the system can gather the data and perform the required valve actuations in real time. This will probably require the specs for whatever valve actuator you want to use and how quickly they can respond to commands.

I'll be interested to hear your progress.

Cheers!

Jim

daddyben1
Posts: 3
Joined: Fri Dec 04, 2020 6:04 am

Re: Read signal of an RV Tank Sensor

Mon Jan 04, 2021 7:18 am

hi jim thank you for such a great reverse engineering job ! i have completed my sys all working fine except some of the tanks are little off acording to my bench readings. think i may need to do some tweeking , where would be the best place to start in the code ? sensors are reading fine but on my fresh tank when it is 100% full im only getting a reading of 90%, i cant really move the sensor any further down its a 6 segment one and i dont want to cut one seg off..
any tweek ideas ?

johnny

jgrant911
Posts: 11
Joined: Thu Jun 04, 2020 6:02 pm

Re: Read signal of an RV Tank Sensor

Mon Jan 04, 2021 11:59 pm

johnny,

Seems like you are making good progress. I will try to explain my method of "calibrating" my tank levels.

In my code I defined arrays for each tank that contained values for the approximate percent of total volume that would be present for each "full" segment of the tank:

float tankSegVol8[] = {12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5}; //Measured approx. percent fill for each of 8 segments (top to bottom)
float tankSegVol5[] = {22, 21, 20, 19, 18}; //Measured percent fill for each of 5 segments on wedge shaped tanks (top to bottom)

tankSegVol8[ ] is for my fresh water which is a standard rectangular tank and is tall enough to fit a full 8 segment sensor.
tankSegVol5[] is for my grey and black tanks which are long (~7') narrow (~16") and shallow (~8") and have a bit of a wedge shape. Because of the short height they can only fit a cut down 5 segment sensor.

The percentages are based on some testing and guesswork . I hope to to verify the accuracy of these numbers by monitoring the actual fill data from the Arduino during complete fill and empty cycles, using a known fill rate, but I have not done this yet.

The code I posted earlier has been updated a bit so I have re-posted the relevant section here:

Code: Select all

    ++tankReads;
    readLevel(tankNum);                // Read Tank levels 0, 1, 2 sequentially. Returns 12 bytes per tank
    for (i = 0; i < 12; i++) {
      Serial.print(SeeLevels[tankNum][i]);
      Serial.print(' ');
    }
    Serial.println("----------");

    // Validate checksum (A checksum is contained in  byte1, rollover count in byte0)
    byteSum = 0;                                         // Add all 8 data bytes together. Remainder of byteSum/256 = checkSum + 2.
    for (i = 2; i <= 9; i++) {                           // If byteSum = 0 or 1, checksum will be 254 or 255 respectively
      byteSum = byteSum + SeeLevels[tankNum][i];
    }
    // Verifying the checksum that comes with the SeeLevel data.
    // Remainder of (sum of all 8 data bytes)/256 - (checkSum + 2) should be zero
    checkSum = SeeLevels[tankNum][1];        //Get checksum from 2nd byte of read data
    
    //    Serial.print("byteSum % 256 - 2 = ");
    //    Serial.print((byteSum % 256) - 2);
    //   Serial.print(" checkSum = ");
    //    Serial.println(checkSum);

    if (((byteSum % 256) == 0 && checkSum == 254) || ((byteSum % 256) == 1 && checkSum == 255) ||
        (byteSum % 256) > 2 and (byteSum % 256) == (checkSum + 2)) {                   //If data OK:

      // Bytes 2 to 9 are an 8-bit code that represents  the fill in each segment of the tank, from top to bottom
      // Fresh tank uses all 8 segments of sensor strip, Grey and Black use only 5, so decode must be adapted
      
      baseSum = 0;
      fltTFS = 0;
      segVal = 0;
      if (SeeLevels[tankNum][tankBaseSeg[tankNum]] == 0) {        // tankBaseSeg[] is 6 for tank 1 and 2 and 9 for tank 0
        tankFillSeg[tankNum] = tankBaseSeg[tankNum];              // If bottom (base) segment fill = 0, tank is empty
        tankLev[tankNum] = 0;
      }
      else {
        i = tankBaseSeg[tankNum];                                                   // Data bytes are 2 to 9 or 2 to 6 depending on tank (MSB -> LSB)
        while (SeeLevels[tankNum][i] != 0 && i >= 2) {
          tankFillSeg[tankNum] = (i);                                                   // Will point to the top non-empty segment of tank[tankNum]
          --i;
        }
        if (tankNum == 0)  {                                                                 // Fresh water tank has 8 segment sensor
          for (i = 7; i > (tankFillSeg[tankNum] - 2); i--) {                     // Add together the percent fill of each segment but the top one
            baseSum = baseSum + tankSegVol8[i];
          }
        }
        else {                                                    // Gray and Black tanks have 5 segment sensor
          for (i = 4; i > (tankFillSeg[tankNum] - 2); i--) {                      // Add together the percent fill of each segment but the top one
            baseSum = baseSum + tankSegVol5[i];
          }
       }                                                                                                     
       
        if (tankNum == 0) {                                                                     // Tank 2 (Fresh)has 8 segment sensor
                                                                                                               // Calculate the percent fill of the top non-zero segment(fresh tank)
          fltTFS = SeeLevels[tankNum][tankFillSeg[tankNum]];        // convert partial tankFill to float to compare with typical "full" value
          segVal = (fltTFS / segLimit8) * tankSegVol8[tankFillSeg[tankNum] - 2];    // segLimit chosen to give close match to actual SeeLevel gauge
        }              
                                                                                                              // Tanks 0 and 1(Black and Grey)have 5 segment sensors
        else {                                                                                             // Calculate the percent fill of the top non-zero segment(gray & black tanks)
          fltTFS = SeeLevels[tankNum][tankFillSeg[tankNum]];        //convert to float
          segVal = (fltTFS / segLimit5) * tankSegVol5[tankFillSeg[tankNum] - 2];
        }
        tankLev[tankNum] = baseSum + segVal;                                // Add base fill and topseg fill together for total calculated tank fill
      }
      /*    //Serial.println(" ");
            Serial.print("BaseSeg = ");
            Serial.print(tankBaseSeg[tankNum]);
            Serial.print(" FillSeg = ");
            Serial.print(tankFillSeg[tankNum]);
            Serial.print(" FillSeg Level = ");
            Serial.print(SeeLevels[tankNum][tankFillSeg[tankNum]]);

      if (tankNum == 0) {
        Serial.print(" tankSegVol8 = ");
        Serial.println(tankSegVol8[tankFillSeg[tankNum] - 2]);
        //Serial.print(" tankSegVol8 = ");
        //Serial.println(tankSegVol8[tankFillSeg[tankNum] - 2]);
      }
      else {
        Serial.print(" tankSegVol5 = ");
        Serial.println(tankSegVol5[tankFillSeg[tankNum] - 2]);
      }
      Serial.print("baseSum = ");
      Serial.print(baseSum);
      Serial.print(" fltTFS = ");
      Serial.print(fltTFS);
      Serial.print(" segVal = ");
      Serial.print(segVal);
      */
      tankLev[tankNum] = round(tankLev[tankNum]);
      Serial.print("Raw tankLev = ");
      Serial.println(tankLev[tankNum]);      
      Serial.println("--------");
      if (tankLev[tankNum] > 100) tankLev[tankNum] = 100;  //Don't let tankLev exceed 100%
I also added a couple of variables that help with the tweeking:
byte segLimit8 = 240;
byte segLimit5 = 240;

You can see that when I read the data for each tank I look for the highest segment with a non-zero reading. I then assume all segments below this are full and sum the percent values for all of these. The next step is where the "tweeking" comes in.

To calculate the value for the top segment I convert to float then divide the data reading (fltTFS) by the maximum expected value (segLimit) and multiply that by the percent assigned to that segment (tankSegVol5[tankFillSeg[tankNum]). The -2 is just to align the array index offset between the 12 byte tankFillSeg[ ] raw data storage (first significant data at byte 2) and the 5 or 8 byte tankSegVol[ ] (first significant data at byte 0).

Now I add this to the previous sum for the lower segments and get the total fill.

You would be totally correct to notice that this is probably not going to give 100% accurate results! That is why it would be very useful to do the field test I mentioned and calibrate the actual fill volume against the data readings to come up with a better set of data for:

segLimit8 = 240,
segLimit5 = 240,
tankSegVol8[] = {12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5, 12.5} and
tankSegVol5[] = {22, 21, 20, 19, 18}.

I also would consider changing the segLimit variables from a single value to an array with a value for each segment.

You will also be likely to find that the real world readings will be different from your bench readings due to different tanks and changes in the position of the sensor strips. That's where I did a bit of fine tuning to the 4 variables above.

Of course the whole thing depends on how accurate you need to be. For my purposes I am happy just to know when I am approaching full on the waste tanks or empty on the fresh tank and this setup does that far better than the crappy LED indicators that the RV manufacturers typically provide.

Hope this is helpful.

Good luck!

Jim

Return to “Advanced users”