Shahariar
Published © CC BY

Solar Powered Wireless Water Level Sensing for Remote Tank

Go to bathroom with the peace of mind that there is enough water on the water tank, no more shampoo head situation

BeginnerShowcase (no instructions)5 hours1,161
Solar Powered Wireless Water Level Sensing for Remote Tank

Things used in this project

Hardware components

Beetle - The Smallest Arduino
DFRobot Beetle - The Smallest Arduino
×1
Arduino UNO
Arduino UNO
×1
Li-Ion Battery 1000mAh
Li-Ion Battery 1000mAh
×1
Development Kit Accessory, Solar Cell
Development Kit Accessory, Solar Cell
×1
433.92 MHz Transmitter
×1
433.92 MHz Receive
×1
5 mm LED: Red
5 mm LED: Red
×1
5 mm LED: Green
5 mm LED: Green
×1
5 mm LED: Yellow
5 mm LED: Yellow
×1
Resistor 100k ohm
Resistor 100k ohm
×1
Through Hole Resistor, 20 kohm
Through Hole Resistor, 20 kohm
×1
Buzzer
Buzzer
×1
SparkFun Power Cell - LiPo Charger/Booster
SparkFun Power Cell - LiPo Charger/Booster
×1
Linear Regulator (7805)
Linear Regulator (7805)
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

433 Receive

txv 433 sch

Code

main.c

C/C++
transmitter code
// For periodic nap and Power saving
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>

// For 433Mhz transmission with some kind of protocol
#include <RH_ASK.h>
#include <SPI.h> 


#define TRANSMISSION_ACTIVE_LED 13
#define FLOAT_SENSOR_UPPER A4
#define FLOAT_SENSOR_LOWER A5
#define LIPO_BATTERY_SENSE A1

RH_ASK rf433_driver(128); // 128 bit/s over the air (speed) 
volatile unsigned int seconds = 0;


void setup()
{
    rf433_driver.init();    // init the wireless transmission drive
    delay(500);
    dickson_booster_init(); // generate 12v for transmitter from 4.2v lipo
    setup_watchdog(9);      // every 8 seconds watch dog barks
    analogReference(INTERNAL); // 1.1v analog reference for batt monitor
    pinMode(TRANSMISSION_ACTIVE_LED, OUTPUT);
    pinMode(FLOAT_SENSOR_UPPER, INPUT_PULLUP);
    pinMode(FLOAT_SENSOR_LOWER, INPUT_PULLUP);
}

void loop()
{

 // system will go to slumber to save power now !
 system_sleep () ;
 sleep_disable();                     
 
 // the following if block will run every 3.3 minutes once
 if (seconds >=200)
 {
   seconds = 0; 
 
 
    power_up_transmitter();         
    digitalWrite(TRANSMISSION_ACTIVE_LED,HIGH);
    delay(500);
    digitalWrite(TRANSMISSION_ACTIVE_LED,LOW);
    delay(500);
    
     delay(500);
  // make a data packet that will hold battery voltage info
     char  valbuff[4];
     int temp = analogRead(LIPO_BATTERY_SENSE);
     valbuff [0] = 'B';
     valbuff [1] = temp;
     valbuff [2] = temp>>8;
     valbuff [3] = '/0';
  // send battery voltage level info packet over 433 MHz link
    char *msg0 = valbuff;
    digitalWrite(TRANSMISSION_ACTIVE_LED,HIGH);
    rf433_driver.send((uint8_t *)msg0, strlen(msg0));
    rf433_driver.waitPacketSent();
    delay(100);
    digitalWrite(TRANSMISSION_ACTIVE_LED,LOW);
    delay(2000);
  
  // read the float sensors and send water level data 3 times over 433 MHz
  for(int x=0;x<3;x++)
  {
        // send bottom float sensor data
////////////////////////////////////////////
    if (digitalRead(FLOAT_SENSOR_UPPER) && digitalRead(FLOAT_SENSOR_LOWER))
    {
     char *msg1 = ">80%";
         rf433_driver.send((uint8_t *)msg1, strlen(msg1));
    digitalWrite(TRANSMISSION_ACTIVE_LED,HIGH);
    rf433_driver.waitPacketSent();
    delay(200);
    digitalWrite(TRANSMISSION_ACTIVE_LED,LOW);
    delay(200);

    }
/////////////////////////////////////////////    
    if ((!digitalRead(FLOAT_SENSOR_UPPER)) && digitalRead(FLOAT_SENSOR_LOWER))
    {
     char *msg2 = "~50%";
         rf433_driver.send((uint8_t *)msg2, strlen(msg2));
    digitalWrite(TRANSMISSION_ACTIVE_LED,HIGH);
    rf433_driver.waitPacketSent();
    delay(200);
    digitalWrite(TRANSMISSION_ACTIVE_LED,LOW);
    delay(200);

    }
//////////////////////////////////////////////
 if ((!digitalRead(FLOAT_SENSOR_UPPER)) && !(digitalRead(FLOAT_SENSOR_LOWER)))
       {
     char *msg3 = "<20%";
         rf433_driver.send((uint8_t *)msg3, strlen(msg3));
    digitalWrite(TRANSMISSION_ACTIVE_LED,HIGH);
    rf433_driver.waitPacketSent();
    delay(200);
    digitalWrite(TRANSMISSION_ACTIVE_LED,LOW);
    delay(200);

    }
//////////////////////////////////////////////  
if (digitalRead(FLOAT_SENSOR_UPPER) && !(digitalRead(FLOAT_SENSOR_LOWER)))
        {
     char *msg4 = "ERR";
         rf433_driver.send((uint8_t *)msg4, strlen(msg4));
    digitalWrite(TRANSMISSION_ACTIVE_LED,HIGH);
    rf433_driver.waitPacketSent();
    delay(200);
    digitalWrite(TRANSMISSION_ACTIVE_LED,LOW);
    delay(200);

    }
    
//////////////////////////////////////////////  

  }
  } 
  // turn off dickson voltage booster after transmission is end
    power_down_transmitter();
    

} // end of void loop

///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////

// drives diode-capacitor dickson charge pump to
// generate 12v from 4v lipo to power the transmitter

void power_up_transmitter()
{
 analogWrite(6,127);
 delay(200); 
}

// turns off charge pump to save power
void power_down_transmitter()
{
  
 analogWrite(6,0);
 delay(200);
}

// init pwm 1khz on pin 6 to drive charge pump
void dickson_booster_init()
{
  pinMode(6,OUTPUT);
}


// Puts MCU into Sleep// 
////////////////////////

void system_sleep(void) 
{

  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();
  sleep_mode();                        // System sleeps here
  sleep_disable();                     // System continues execution here when watchdog timed out 
}


// watchdog periodically wakes up mcu from sleep to do something 
// or go back to sleep if nothing needs to be done !

void setup_watchdog(int i) 
{
  // 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec

  uint8_t bb;
  if (i > 9 ) i=9;
  bb=i & 7;
  if (i > 7) bb|= (1<<5);
  bb|= (1<<WDCE);

  MCUSR &= ~(1<<WDRF);
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  WDTCSR = bb;
  WDTCSR |= _BV(WDIE);
}

// keep a rough track of time in watchdog ISR

ISR(WDT_vect)
{
sleep_disable();
seconds = seconds+8; // update every 4 second
}

main.c

C/C++
receiver code
// library for OLED, 433MHz Radio-head protocol

#include "U8glib.h"
#include <RH_ASK.h>
#include <SPI.h> // Not actualy used but needed to compile

RH_ASK rf433_driver(128); // 128 bit/s data rate OTA 


U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NONE);   // SCL = 2  SDA = 3

// UI buttons (only one is used for now)
#define BUTTON_DEC    A2
#define BUTTON_INC     1
#define BUTTON_SEL    A0

//#define RF_433_IN   11 (radio head library will use pin 11 by default)

// indication LEDs
#define LOW_LEVEL_LEDS_RED   A1
#define MID_LEVEL_LEDS_YELLOW   9
#define HIGH_LEVEL_LEDS_GREEN   0
#define BUZZER_ALARM 10
#define BLUE_LED      13


long last_millis;
bool selector = 0;
int battvolt = 0;
float vbatt =0.0;

    uint8_t buf[12];
    uint8_t buflen = sizeof(buf);

    uint8_t lvl[12];
    uint8_t lvllen = sizeof(lvl);

    uint8_t bat[12];
    uint8_t batlen = sizeof(bat);


void setup(void)
{
 
// u8g.setRot90();        // change oled display orientation

 // set GPIOs for buttons and LED
 
 pinMode(BUTTON_DEC, INPUT_PULLUP);
 pinMode(BUTTON_INC, INPUT_PULLUP);
 pinMode(BUTTON_SEL, INPUT_PULLUP);

 pinMode(BLUE_LED, OUTPUT);
 pinMode(LOW_LEVEL_LEDS_RED, OUTPUT);
 pinMode(MID_LEVEL_LEDS_YELLOW, OUTPUT);
 pinMode(HIGH_LEVEL_LEDS_GREEN, OUTPUT);
 
 pinMode(BUZZER_ALARM, OUTPUT);

 digitalWrite(LOW_LEVEL_LEDS_RED, 1);
 digitalWrite(MID_LEVEL_LEDS_YELLOW, 1);
 digitalWrite(HIGH_LEVEL_LEDS_GREEN, 1);
 digitalWrite(BUZZER_ALARM, 0);

  
 
 delay(500);
 
 // init 433 MHz Radio head driver for receiver
 rf433_driver.init();
  
}

void loop()
{
 
 // check if there are valid data in receiver buffer
 
    if (rf433_driver.recv(buf, &buflen)) // Non-blocking
    {
      int i;
    last_millis = millis();
    transmission_blinky(); // indicates some valid data just received
    }
///////////////////////////////////    
    // separate data from buffer
    // extract battery voltage info from received data
    // if data array starts with 'B', then following bytes contain 
    // adc info of battery on the transmitter end
    if((char*)buf[0] == 'B')
    {
     byte lowbyte = byte(buf[1]);
     byte highbyte = byte(buf[2]);
     battvolt = lowbyte + (highbyte <<8);
     vbatt = ((10+2)/2)*(1.07*battvolt/1023); //1M//200k voltage div
   
    }
    
    // if it is not battery voltage info then it is float sensor info
    else
    {
      for(int i=0; i<=4;i++)
      {
      lvl[i] = buf [i];
      }
    }

    
    // refresh oled every 10 sec to avoid pixel burnout from 
    // static info that rarely changes with time
    // also beep alarm only on lowest water level 
   
    if (((millis()/1000)%10) == 0)
    {
    clear_display_message();
    // beep alarm if water level lowest
    if((char*)lvl[1]=='2')
    {digitalWrite(BUZZER_ALARM,1);}
    delay(100);
    digitalWrite(BUZZER_ALARM,0);
    delay(300);
    u8g.setColorIndex(1);    
    }

    // takes button press from user and 
    // switches between vbatt and water level info
    if(!digitalRead(BUTTON_SEL))
    {
      selector = !(selector);
      delay(100);
    }

    if (selector == 0)
    {
      update_display_message();
    }
    else
    {
      update_display_vbatt();
    }
 // Show water level on Red/Green/Yellow LEDs
 water_level_LEDS();
 
} // end of loop

///////////////////////////////////
///control and display functions///
///////////////////////////////////

// updates oled with the latest water level data

void update_display_message(void)
{
    u8g.firstPage();  
  do {
    u8g.setFont(u8g_font_10x20); 
    u8g.setPrintPos(0,13 );
    u8g.print("water lv:");
    u8g.setPrintPos(89,13 );
    u8g.print((char*)lvl);

    u8g.setFont(u8g_font_6x13); 
   u8g.setPrintPos(0,25 );
    u8g.print("last updated :");
   u8g.setPrintPos(87,25 );
    u8g.print((millis()-last_millis)/60000);
   u8g.setPrintPos(104,25 );
    u8g.print("min");    
     
  } while( u8g.nextPage() );
delay(10);

}

// updates oled with battery voltage info
void update_display_vbatt(void)
{
    u8g.firstPage();  
  do {
    u8g.setFont(u8g_font_10x20); 
    u8g.setPrintPos(0,13 );
    u8g.print("V_batt");
    u8g.setPrintPos(80,13 );
    u8g.print(vbatt);

    u8g.setFont(u8g_font_6x13); 
   u8g.setPrintPos(0,25 );
    u8g.print("last updated :");
   u8g.setPrintPos(86,25 );
    u8g.print((millis()-last_millis)/60000);
   u8g.setPrintPos(103,25 );
    u8g.print("min");    
     
  } while( u8g.nextPage() );
delay(10);

}

// blackens the oled to refresh
void clear_display_message(void)
{
    u8g.firstPage();  
  do {
    u8g.setColorIndex(0);
    u8g.drawBox(1, 1, 32, 128); 
  } while( u8g.nextPage() );
delay(10);

}

// indicates a valid transmission data received
void transmission_blinky(void)
{
  digitalWrite(BLUE_LED,HIGH);
  delay(25);  
  digitalWrite(BLUE_LED,LOW);
  delay(25);

}

// helps determine water level quickly 
void water_level_LEDS(void)
{
  if ((char*)lvl[1]=='8')
  {
   digitalWrite(LOW_LEVEL_LEDS_RED, 1);
   digitalWrite(MID_LEVEL_LEDS_YELLOW, 1);
   digitalWrite(HIGH_LEVEL_LEDS_GREEN, 0);
  }

  
  else if ((char*)lvl[1]=='5')
  {
   digitalWrite(LOW_LEVEL_LEDS_RED, 1);
   digitalWrite(MID_LEVEL_LEDS_YELLOW, 0);
   digitalWrite(HIGH_LEVEL_LEDS_GREEN, 1);
  }
  
  else if ((char*)lvl[1]=='2')
  {
   digitalWrite(LOW_LEVEL_LEDS_RED, 0);
   digitalWrite(MID_LEVEL_LEDS_YELLOW, 1);
   digitalWrite(HIGH_LEVEL_LEDS_GREEN, 1);
  }
  else
  {
   digitalWrite(LOW_LEVEL_LEDS_RED, 1);
   digitalWrite(MID_LEVEL_LEDS_YELLOW, 1);
   digitalWrite(HIGH_LEVEL_LEDS_GREEN, 1);
  }
  
}

Credits

Shahariar
75 projects • 270 followers
"What Kills a 'Great life' is a 'Good Life', which is Living a Life Inside While Loop"
Contact

Comments

Please log in or sign up to comment.