I2C for LCD

IMG_20141108_161103A cheap I2C expander aimed at LCD’s is available from a multitude of sources such as eBay or DealExtreme (http://www.dx.com/p/lcd1602-adapter-board-w-iic-i2c-interface-black-works-with-official-arduino-boards-216865). The pin-out from this interface matches the pin-out from most Hitachi based LCD’s and therefore can easily be connected, just make sure that the pin’s are not connected in reverse.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Gnd Vcc Vlcd RS RW E D0 D1 d2 D3 D4 D5 D6 D7 Anod Cathode

After downloading and installing the library from F. Malpartida, use the following program to test

/*
P4 R/S
P5 RW
P6 E
P0 D4
P1 D5
P2 D6
P3 D7

A4=SDA 
A5=SCL
*/
#include  <Wire.h>
#include  <LiquidCrystal_I2C.h>
#define BACKLIGHT_PIN     3
#define I2C_ADDR 0x27 
LiquidCrystal_I2C lcd(I2C_ADDR, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
// Create a set of new characters
const uint8_t charBitmap[][8] = {
   { 0xc, 0x12, 0x12, 0xc, 0, 0, 0, 0 },
   { 0x6, 0x9, 0x9, 0x6, 0, 0, 0, 0 },
   { 0x0, 0x6, 0x9, 0x9, 0x6, 0, 0, 0x0 },
   { 0x0, 0xc, 0x12, 0x12, 0xc, 0, 0, 0x0 },
   { 0x0, 0x0, 0xc, 0x12, 0x12, 0xc, 0, 0x0 },
   { 0x0, 0x0, 0x6, 0x9, 0x9, 0x6, 0, 0x0 },
   { 0x0, 0x0, 0x0, 0x6, 0x9, 0x9, 0x6, 0x0 },
   { 0x0, 0x0, 0x0, 0xc, 0x12, 0x12, 0xc, 0x0 }
   
};

void setup()
{
   int charBitmapSize = (sizeof(charBitmap ) / sizeof (charBitmap[0]));

    
  lcd.begin(16,2);               // initialize the lcd 
// Switch off the backlight
noBacklight()

   for ( int i = 0; i < charBitmapSize; i++ )
   {
      lcd.createChar ( i, (uint8_t *)charBitmap[i] );
   }

  lcd.home ();                   // go home
  lcd.print("Hello, ARDUINO ");  
  lcd.setCursor ( 0, 1 );        // go to the next line
  lcd.print (" FORUM - fm   ");
  delay ( 1000 );
}

void loop()
{
   lcd.home ();
   // Do a little animation by writing to the same location
   for ( int i = 0; i < 2; i++ )
   {
      for ( int j = 0; j < 16; j++ )
      {
         lcd.print (char(random(7)));
      }
      lcd.setCursor ( 0, 1 );
   }
   delay (200);
}

The standard address on the I2C bus for this model is 27H. In case you need/want to change that, you need to connect the A1-A3 lines to ground: either 1, 2 or all 3 of them. The table below  shows what adresses correspond with what  connections. You will find solderpads  of these addresses on the board.
pcf8574end

Advertisement

Arduino for greenhouse or growbox

greenhouse2Let me start with saying it is not really an ‘Arduino’ but an Atmega 328 with Arduino bootloader.
After doing several ‘garduino’ projects, i wanted to build something that I could use to either run a greenhouse outside, or a growbox inside the house, for veggies and stuff during the winter.

The circuit contains various elements, that I will discuss:
Most will probably recognize the ‘barebones arduino’ basically the atmega 328, a crystal, 2 capacitors and a reset circuit.
K1: is a header that will feed into 4 relays that have the following functions:
Switch on the irrigation, switch on a lamp, switch on a heater, switch on a fan
K2: is an FTDI header for uploading programs to the Atmega.
K3: is a header to connect a DHT11 moisture and temperature sensor. Those only need 3 connectors, but I had 2 with different pin lay-outs, hence the 4 prong  connector. R11 is a pull up resistor
K4: is the IIC header  that connects to an LCD
K5: is a connector  to connect an RTC module. The Led and resistors are optional… just in case you would want a led flashing every second.
CN 1 and 2 are to connect a soil moisture sensor. In order to save the sensor being broken down by electrolysis quickly, The transistor is there to switch off current through the sensor.
The Zenerdiodes  are there to protect the Atmega against high voltages that may build up on a long wire.
There are 2 switches tat can be used  freely. Currently I use the pushbutton to select a different reading on the LCD and the throw switch is still unused.
The level Switch  is a switch that closes when the waterlevel in the reservoir is too low. It is a simple float switch
The LDR can determine if it is day or night if no RTC is used. ofcourse for inside use it is not that usefull

Software:

/* meet de vochtigheid
 als minder dan ingestelde waarde, pomp aan
 meet temperatuur
 als lager dan ingestelde waarde, verwarming aan
 meet humidity
 als hoger dan ingestelde waarde zet ventilator aan
 ventilator ook aan snachts
 http://www.quickgrow.com/gardening_articles/fans_air_movement.html
 LCD commands:
 lcd.setBacklight(LED_OFF);
 lcd.noBacklight();
 lcd.clear();
 lcd.home();
 
 
 */
/*-----( Import needed libraries )-----*/
#include 
#include  //Malpertida
#include "RTClib.h"   //Adafruit
//DHT11 instellingen
#include 
dht DHT;
/*-----( Declare objects )-----*/
// set the LCD address to 0x27 for a 20 chars 4 line display
// Set the pins on the I2C chip used for LCD connections:
//                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
/*-----(Declare RTC objects )------*/
RTC_DS1307 RTC; //declare onject

/*-----( Declare pins )------*/
byte moisturePin=0; //read soil mositure
byte levelPin= 1; //analogue Pin1  set level for  irrigation
byte humidPin=2;  //Physical pin4  Humidity sensor
byte lightPin=5; //Switches on light
byte fanPin=6; //Switches fan
byte pumpPin =7;  //Switches pump
byte hotPin= 8; // Switches heater
byte buzPin=9;  // Switches buzzer
byte emptyPin=10;  //digital  Pin10  guards waterlevel
byte spikePin=12;  //Digital  Pin12  -> Extra for intermittent switching of spike
byte LDRPin=3;// analog pin for LDR
byte PushButton=4;   // PushButton
byte SwitchButton=3;// Make Switch
byte push=0;
byte sButton=0;
#define DHT11_PIN humidPin
/* Variable setting   */
unsigned int moist=0;    //will contain the soil moisture level
unsigned int irrigate=0;   //sets level for irrigation in case no potmeter
byte level=0;
int c;// contains the temperature reading
// these constants won't change:
const int sensorMin = 40;      // LDR minimum, discovered through experiment
const int sensorMax = 1012;    // LDR maximum, discovered through experiment
byte light;  // value for LDR reading
// Create a set of new characters

const uint8_t charBitmap[][8] = {

  { 
    0xc, 0x12, 0x12, 0xc, 0, 0, 0, 0     }
  ,

  { 
    0x6, 0x9, 0x9, 0x6, 0, 0, 0, 0     }
  ,

  { 
    0x0, 0x6, 0x9, 0x9, 0x6, 0, 0, 0x0     }
  ,

  { 
    0x0, 0xc, 0x12, 0x12, 0xc, 0, 0, 0x0     }
  ,

  { 
    0x0, 0x0, 0xc, 0x12, 0x12, 0xc, 0, 0x0     }
  ,

  { 
    0x0, 0x0, 0x6, 0x9, 0x9, 0x6, 0, 0x0     }
  ,

  { 
    0x0, 0x4, 0xE, 0x15, 0x4, 0x4, 0x4, 0x0     }
  ,

  { 
    0x4,0x4,0x4, 0x4, 0x15, 0xE,0x4,0x0     }
};
/// -------- end creation--------------
//The following function, "setup", must always be present
void setup()
{  
  Serial.begin(115200);
  //upload defined characters to LCD

  //---------------end upload----------------
  
  pinMode(levelPin,INPUT);  // set level
  pinMode(humidPin,INPUT);  // measures humidity
  pinMode(emptyPin,INPUT);  // measures reservoir
  //digitalWrite(emptyPin, HIGH);       // turn on pullup resistors
  pinMode(SwitchButton, INPUT);   // make Switch
  pinMode(PushButton, INPUT);     // PushButton
  pinMode(spikePin,OUTPUT); // for alternative supply to spikes
  pinMode(pumpPin,OUTPUT);  // Output for Relay
  pinMode(fanPin, OUTPUT);  // Output for fan
  pinMode(hotPin, OUTPUT);  // Output for heater
  pinMode(lightPin, OUTPUT);// Output for light
  pinMode(buzPin, OUTPUT);  // Output for buzzer
  digitalWrite(pumpPin, LOW);// Pump off
  digitalWrite(spikePin, LOW);// moisture sensor off
  digitalWrite(fanPin,LOW);  // fan Off
  digitalWrite(hotPin,LOW);   // heater off
  digitalWrite(lightPin, LOW); // light Off
  digitalWrite(buzPin, LOW); // buzzer off
  /* Now LCD */
  lcd.begin(16,2);         // initialize the lcd for 20 chars 4 lines, turn on backlight
  int charBitmapSize = (sizeof(charBitmap ) / sizeof (charBitmap[0]));
  for ( int i = 0; i < charBitmapSize; i++ )
  {
    lcd.createChar ( i, (uint8_t *)charBitmap[i] );
  }
  lcd.backlight();   
  // Print a message to the LCD.
  lcd.setCursor(0, 0);
  lcd.print("Greenhouse");
  // ------- Quick 2 blinks of backlight  -------------
  flash(2); 
  // ------- Quick buzz--------------------------------
 // buzz(1);
  Wire.begin(); //needed for RTC, not for LCD
  RTC.begin();
  /* Set the date / time to the time this program was compiled.
   Comment this OUT, AND upload, to let the clock just run.  */
  //  RTC.adjust(DateTime(__DATE__, __TIME__));

}
/*----------------------------(end setup )---------------------*/

void loop() 
{
  DateTime now = RTC.now();  //Get the current data
 // Serial.print("The year is ");
 // Serial.print(now.year(), DEC);
 // Serial.print(" Month = ");
 // Serial.print(now.month(), DEC);
 // Serial.print(" Day = ");
 // Serial.print(now.day(), DEC);
 // Serial.print(" Time = ");
 // Serial.print(now.hour(), DEC);
 // Serial.print(':');
 // Serial.print(now.minute(), DEC);
 // Serial.print(':');
 // Serial.print(now.second(), DEC);
 // Serial.println();
  //------------end of RTC
  // ---------- 1. Check if there is enough water -------------  
  // check if there is water  
  level=  digitalRead(emptyPin);
  Serial.print(level);
  if (level==0) {
    digitalWrite(buzPin, HIGH);
    delay (50);
    digitalWrite(buzPin, LOW);
    delay(500);
  }
  //------------2. Read the soil moisture content/switch pump----------------
  /*
First read the level set with P1 on the levelPin and store that in 'irrigate'
   */
  irrigate=sample(levelPin);
  /*

   Then we read the soil humidity sensor.
   We'll first have to set the spikePin to HIGH, in case that is used to feed the sensor. 
   After the reading we set it back) 
   If the value read ('moist') is smaller than what is considered dry ('irrigate') 
   then the pump should be switched on for a specific time. 
   This is done by indicating a higher treshhold for switching the pump off
   */
  digitalWrite(spikePin, HIGH);// put voltage on the  humidity sensor
  delay(100); //wait a short while
  moist=sample(moisturePin);  //read soil humiditySensor
  //
  digitalWrite(spikePin, LOW);
  // level=  digitalRead(emptyPin);// just read again to make sure
  if (moist <= irrigate) digitalWrite(pumpPin, level);
   if (moist >= irrigate+5) digitalWrite(pumpPin, LOW); // prevents  Jitter


  //-------------3. Read the DHT11 humidity/temp sensor-----------------


  // now we measure temperature and air humidity
  // READ DATA
  // Serial.print("DHT11, \t");
  int chk = DHT.read11(DHT11_PIN);
  switch (chk)
  {
  case DHTLIB_OK:  
    //	Serial.print("OK,\t"); 
    break;
  case DHTLIB_ERROR_CHECKSUM: 
    Serial.print("Checksum error,\t"); 
    break;
  case DHTLIB_ERROR_TIMEOUT: 
    Serial.print("Time out error,\t"); 
    break;
  default: 
    Serial.print("Unknown error,\t"); 
    break;
  }

  //-------------4. Read  LDR ----------------------------------------
  light=Map(LDRPin);
  
   /* ------------------Actions -------------------*/

  //-------------5.  DISPLAY DATA ------------------------------------
  // Serial.print(DHT.humidity,1);
  // Serial.print(",\t \t");
  //Serial.println(DHT.temperature,0);
  /*   Display data on LCD  */
  push=digitalRead(PushButton);
  //Serial.print("Push.   : ");
  // Serial.println(push);
  if (push==1) // pushbutton not pressed
  {
    lcd.clear();
    lcd.setCursor(0, 0);  //set cursor on first line (line 0)
    lcd.print("Temp.   : ");
    lcd.print((float)DHT.temperature, 0);
    lcd.print (char(1));  // prints degree sign
    lcd.print("C");
    lcd.print(" ");
    lcd.print (char(7-level));
    Serial.print(level);
    //  Serial.print("Temp. (oC): ");
    //  Serial.println((float)DHT.temperature,2);
    lcd.setCursor(0,1);  //set cursor on 2nd line
    lcd.print("Humidity: ");
    lcd.print((float)DHT.humidity, 0);
    lcd.print("%");
    lcd.print("  ");
    // Serial.print("Humidity (%): ");
    //Serial.println((float)DHT.humidity, 2);  
    delay(1000);  // wait for one second and then print the  soilmoisture
    lcd.setCursor(0,1);
    lcd.print ("Irr. Level: ");
    lcd.print(irrigate);
    lcd.setCursor(0,0);
    lcd.print("Moisture: ");
    lcd.print(moist); 
    lcd.print("   ");

  }
  if (push==0)   // pushbutton pressed
  {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("licht: ");
    lcd.print(light);
    lcd.print("          ");
    lcd.setCursor(0,1);
    lcd.print("licht niv.: ");
    lcd.print(analogRead(LDRPin));
    //buzz(1);
  }

 

  // ---------------5. Action on temperature ------
 
  c=(DHT.temperature);
  if (c<=20)     //Serial.println(c);
   {
// switch on heating     
digitalWrite(hotPin,HIGH);   
}   else   {
     digitalWrite(hotPin,LOW);   
}  
 //--------------6. Action on Humidity -----------  
 if (DHT.humidity >=50)
  {
    // switch on fan
    digitalWrite(fanPin, HIGH);
  }
  else
  {
    digitalWrite(fanPin,LOW);
  }
  delay(1000);
  //end dht
  //end loop
}
//-------  End of Main program-----------
//
//-------- Start of functions------------
int sample(int z)
/* This function will read the Pin 'z' 5 times and take an average.
 Afterwards it will be mapped to 8 bits by dividing by 4
 Could ofcourse immediately divided by 20 
 */
{
  byte i;
  int sval = 0;
  for (i = 0; i < 5; i++){    
    sval = sval + analogRead(z);// sensor on analog pin 'z'
  }
  //sval = sval / 5;    // average
  //sval = sval / 4;    // scale to 8 bits (0 - 255)
  sval=sval / 20;
  return sval;
}
//------------- Flash the backlight a number of times--------
void flash(byte y)
{
  byte j;
  for (j=0; j<y;j++)
  {
    lcd.backlight();
    delay(250);
    lcd.noBacklight();
    delay(250);
  }
  lcd.backlight(); // finish with backlight on  
  return;
}
// This function will sound the buzzer "u" times
void buzz(byte u)
{
  byte k;
  for (k=0; k<u;k++)
  {
    digitalWrite(buzPin, HIGH);
    delay(200);
    digitalWrite(buzPin, LOW);
    delay(200);
  }
  return;
}
// This function will blink an LED a number of times for a specific duration
void ledblink(int times, int lengthms, int pinnum){
  for (int x=0; x<times;x++){
     digitalWrite(pinnum, HIGH);
     delay (lengthms);
     digitalWrite(pinnum, LOW);
     delay(lengthms);
   }
 }
 // This function maps the LDR reading into nrs 0-3
 int Map(byte sens) {
  // read the sensor:
   int sensorReading = analogRead(sens);
   // map the sensor range to a range of four options:
   int range = map(sensorReading, sensorMin, sensorMax, 0, 3);
  // do something different depending on the
  // range value:
  switch (range) {
  case 0:    // 
  //  Serial.println("dark");
    lcd.backlight();
    break;
  case 1:    // 
   // Serial.println("dim");
    lcd.backlight();
    break;
  case 2:    // 
  //  Serial.println("medium");
    lcd.noBacklight();
    break;
  case 3:    // 
  //  Serial.println("bright");
    lcd.noBacklight();
    break;
  }
  return range;
}