Go Down

Topic: Greenhouse controller v0.4 (Read 1 time) previous topic - next topic

Mullet

Feb 12, 2010, 08:33 pm Last Edit: Feb 12, 2010, 08:39 pm by Mulletsoda Reason: 1
This is my first project with the arduino, and I know, I jumped right in.  This is a pretty big project for a first.  I feel like each small part is a hundred sub projects.  It's not nearly complete, but I have a working partial version right now, and I figured I might as well put it out there, see if I get any feedback.  My documentation is pretty sloppy, but I do put effort into documenting it, so you should be able to see what's going on pretty well.  Again, very much a work in progress.  At the moment it's fairly limited in capability, here's some stats:

3 outputs (ULN2003 to 12v relay to 110v relay): a water pump, artificial sunlight and a green LED worklight.  (Green light does not interrupt the darkness requirement flowering photosensitive plants have)

2 analog inputs: temperature sensors, LM34DZ

2 digital inputs : regular momentary NO pushbuttons

http://mulletsoda.seedtheplanet.com/marvin.jpg

http://code.google.com/p/atmega328growcontroller/    <~~ More detailed info

Quote

#include "OutletTimer.h"
#include "DigitalInputter.h"
#include "PrintPJRClcd.h"

/*Initialize outlets, OutletTimer(pinNumber, InitialMode, InitialTimeSec) */
OutletTimer MainLightOutletTimer(10,0,300);// Main light
OutletTimer MainVegPumpOutletTimer(9,1,60);//Water pumps
OutletTimer GreenWorkLightOutletTimer(8,4,0);//work lights

/* Initialize digital inputs DigitalInputter(int pinNumber) */
DigitalInputter WorkLightButton(7);
DigitalInputter PumpToggleButton(6);


/* Open LCD library on serial port */
PrintPJRClcd LCD = PrintPJRClcd(); 

/* Initialize analog inputs */
int AnalogRezTemp = 0;
int AnalogAirTemp = 0;
#define AnalogRezTempPin 0
#define AnalogAirTempPin 1

/* Variables for time watching */
unsigned long previousMillis = 0;        // will store last time millis
unsigned long curMillis = 0;
int timeSinceLast = 0;

/* floats for calculating time */
float MainLightTimeFloat;
unsigned char LoopsAround = 0;
void setup()
{

  /*   Set outlet on and off times  */
  MainLightOutletTimer.OnDuration = 64800; //main lights, 64800 = 18 hours
  MainLightOutletTimer.OffDuration = 21600; // 21600 = 6 hours
  MainVegPumpOutletTimer.OnDuration = 60;//veg pump
  MainVegPumpOutletTimer.OffDuration = 600;
  GreenWorkLightOutletTimer.OnDuration = 0;
  GreenWorkLightOutletTimer.OffDuration = 0;
  Serial.begin(19200);
  delay(1000);
  LCD.RedrawScreen();

}

void loop()
{

  /* Check inputs */
  if (WorkLightButton.CheckPin())   // if work light button has been pushed
  { 
    if (GreenWorkLightOutletTimer.CurrentMode==4) GreenWorkLightOutletTimer.ChangeMode(5); // if current mode is perm off, turn it perm on
    else if (GreenWorkLightOutletTimer.CurrentMode==5) GreenWorkLightOutletTimer.ChangeMode(4);
  };      
  if (PumpToggleButton.CheckPin()) //if pump button has been pushed
  {
    switch(MainVegPumpOutletTimer.CurrentMode) //what's the veg pump at now?
    {
    case 0: //off, normal operation
      MainVegPumpOutletTimer.ChangeMode(1);
      break;
    case 1: //on, normal operation
      MainVegPumpOutletTimer.ChangeMode(4);
      break;
    case 4: // perm off
      MainVegPumpOutletTimer.ChangeMode(5);
      break;
    case 5: // perm on
      MainVegPumpOutletTimer.ChangeMode(0);                  
      break;
    };
  };



  /*measure time past since last loop and increment down */
  curMillis = millis();
  if (curMillis < previousMillis) previousMillis = 0.0; //roll over check
  timeSinceLast = curMillis- previousMillis;
  previousMillis = curMillis;

  /* Analog inputs */
  AnalogRezTemp = analogRead(AnalogRezTempPin);
  AnalogAirTemp = analogRead(AnalogAirTempPin);

  /* Print info to screen */
  //LCD.RedrawScreen();
  LCD.PrintData(0,int(AnalogAirTemp/2.1));
  LCD.PrintData(1,int(AnalogRezTemp/2.1));
  LCD.PrintData(2,MainVegPumpOutletTimer.CurrentMode);
  LCD.PrintData(3,(MainVegPumpOutletTimer.TimeLeft/1000));
  LCD.PrintData(4, MainLightOutletTimer.CurrentMode);
  MainLightTimeFloat = ((long(MainLightOutletTimer.TimeLeft)/1000)/60);
  LCD.PrintData(5, int(MainLightTimeFloat));
  LCD.PrintData(6, GreenWorkLightOutletTimer.CurrentMode);
  /* Pass Time Since Last loop to each outlet */

  MainLightOutletTimer.ElapseTime(timeSinceLast);
  MainVegPumpOutletTimer.ElapseTime(timeSinceLast);
  GreenWorkLightOutletTimer.ElapseTime(timeSinceLast);


  LoopsAround++;
  if(LoopsAround==42) LCD.RedrawScreen();
}



Quote

/*
 *  A library for dealing with digital inputs *  Created by MulletSoda, 2010
 *  With love!
 */

#ifndef DigitalInputter_h
#define DigitalInputter_h

#include "WProgram.h"

class DigitalInputter
{
public:
  //public variables:
  int PinNumber;
  int _PinNumber;
  unsigned long _currentTime;
  unsigned long _deBounceTimeDone;
  boolean _deBounce;
  void DeBouncer();

  //public functions:
  DigitalInputter(int _PinNumber);
  boolean CheckPin();
private:
  //private variables
  //private functions
};

#endif




Quote

#include "WProgram.h"
#include "DigitalInputter.h"


DigitalInputter::DigitalInputter(int PinNumber)
{
  //initialize
  _PinNumber = PinNumber;
  pinMode(_PinNumber, INPUT);
  //digitalWrite(_PinNumber, HIGH);//turn on pull up resistor
  _deBounceTimeDone = 0;
}

boolean DigitalInputter::CheckPin()
{
  _currentTime = millis(); //what is the current time?

  //debounce and check
  if (!_deBounce)  //if deBounce has not been activated
    if (digitalRead(_PinNumber)==1){//read pin, if it returns activated then
      {
        _deBounce = true;
        _deBounceTimeDone =500 + _currentTime; // <*******************************************  Will this bypass the rollover problem????
        //turn on debounce, set time over, return true
        return true;
      };
    };
  if (_deBounce)  //if deBounce has been activated
  {
    if (_deBounceTimeDone <= _currentTime) _deBounce = false; //if enough time has passed
  };
  return false;
}

void DigitalInputter::DeBouncer()
{
}


Mullet

Quote

/*
 *  A timer for controlling outlets
 *  Created by MulletSoda, 2010
 *  With love!
 */

#ifndef OutletTimer_h
#define OutletTimer_h

#include "WProgram.h"

class OutletTimer
{
public: //publicly accessable variables and functions

  int pinNumber; //0 - 255, pin number of outlet
  unsigned long MaintTime; //   Maintainence mode time, turns on/off for a set period of time then returns to normal functioning
  int CurrentMode; //the current mode of this outlet
  unsigned long TimeLeft; //milliseconds until mode change
  unsigned long OffDuration; //Time outlet remains off
  unsigned long OnDuration; //Time outlet remains on

  /*  Functions available to the public */

  OutletTimer(int _pinNumber, int _InitialMode, unsigned int _InitialTimeSec);
  void ElapseTime(int TimeSinceLast); //De-increment time remaining
  void ChangeMode(int NewMode); //force a mode change
private: //private, localized variables and functions
  int _pinNumber;
  int _InitialMode;
  int _InitialTimeSec;

};

#endif





Quote

#include "WProgram.h"
#include "OutletTimer.h"

OutletTimer::OutletTimer(int _pinNumber, int _InitialMode, unsigned int _InitialTimeSec)
{
  /*  This should do everything needed when the controller first starts up
   *  Initialize pin, set pin to whatever, set on time, set off time, set timeleft, set mode,
   *  
   */
  pinNumber=_pinNumber;
  pinMode(pinNumber, OUTPUT);
  digitalWrite(pinNumber, _InitialMode);
  TimeLeft = _InitialTimeSec * 1000.0;          //set TimeLeft
  CurrentMode = int(_InitialMode);
}

void OutletTimer::ElapseTime(int TimeSinceLast)
{
  //increment time down, this includes check for 0 time and TimeIsUp

  if (TimeLeft > 1000.0) TimeLeft-=TimeSinceLast;  //time slips away yet again
  if (TimeLeft <= 1000.0 && TimeLeft != 0.0) //if TimeIsUp but not disabled
  {
    switch (CurrentMode) //what's the current mode we're switching from?
    {
      case (0): //outlet off, normal op: turn mode to on, time to OnTime, turn pin high
      ChangeMode(1);
      break;
      case (1): //outlet on, normal op : mode off, time to OffTime, pin low
      ChangeMode(0);
      break; 
      case (2): //maint off time up, return to normal operation: turn mode to on, time to OnTime, turn pin high
      ChangeMode(1);
      break;
      case (3): //maint on time up, return normal op : turn mode off, time to OffTime, pin low
      ChangeMode(0);
      break; 
      case (4): //perm off, should NEVER get here
      break;
      case (5): //perm on, should NEVER get here
      break; 

    }
    //for some reason I needed a delay here before, if there are errors, try adding 2ms
  }
}

void OutletTimer::ChangeMode(int NewMode)
{
  //mode change, newMode can be used to pass button presses, too, by using higher NewMode values
  switch (NewMode)
  {
  case 0: //off
    digitalWrite(pinNumber, LOW);
    CurrentMode = 0;
    TimeLeft = OffDuration * 1000.0;
    break;
  case 1: //on
    digitalWrite(pinNumber, HIGH);
    CurrentMode = 1;
    TimeLeft = OnDuration * 1000.0;
    break;
  case 2://off maint
    digitalWrite(pinNumber, LOW);
    CurrentMode = 2;
    TimeLeft=MaintTime * 1000.0;
    break;
  case 3://on maint
    CurrentMode = 3;
    digitalWrite(pinNumber, HIGH);
    TimeLeft = MaintTime * 1000.0;
    break;
  case 4://perm off
    digitalWrite(pinNumber, LOW);
    CurrentMode = 4;
    TimeLeft = 0.0;
    break;
  case 5://perm on
    digitalWrite(pinNumber, HIGH);
    CurrentMode = 5;
    TimeLeft = 0.0;
    break;
  default: //other
    digitalWrite(pinNumber, LOW);
    /*
            *  This will be used to deal with button presses later.  Example :
                  * Button0 gets pressed, can be sent as NewMode==50, then a switch (CurrentMode)
                  * {case 0: //toggle mode to on; case 1: //toggle mode to off}
                  */
    break;

  }
}

Mullet

Quote

/*
 *  A library for printing to a 8x20 LCD, obtained from PJRC
 *  Created by MulletSoda, 2010
 *  With love!
 */

#ifndef PrintPJRClcd_h
#define PrintPJRClcd_h

#include "WProgram.h"

class PrintPJRClcd
{

public:
  /* Variables */
  int blank;
  /* Functions */
  PrintPJRClcd(); //initialize, clear screen, set up all descriptive info
  void PrintData(int type,unsigned long datas);//type is set to location, datas is info to be printed
  void RedrawScreen();
private:
  /* Variables */
  int _row; //Cursor row position, 1 - 8
  int _column; //Cursor column position, 1 - 20
  /* Functions */
  void PositionCursor(int _row, int _cursor); //position cursor
};


#endif



Quote

#include "WProgram.h"
#include "PrintPJRClcd.h"

PrintPJRClcd::PrintPJRClcd()
{

};

void PrintPJRClcd::RedrawScreen()
{
  PositionCursor(1,1);
  Serial.print("Air T:   F      My Robot");
  PositionCursor(2,1);
  Serial.print("Rez1 T:   F       Friend");
  PositionCursor(4,1);
  Serial.print("Pump1:    :      seconds");
  PositionCursor(3,1);
  Serial.print("                        ");
  PositionCursor(5,1);
  Serial.print("                        ");
  PositionCursor(6,1);
  Serial.print("Worklight:               ");
  PositionCursor(7,1);
  Serial.print("            Min left     ");
  PositionCursor(8,1);
  Serial.print("                   ");

};

void PrintPJRClcd::PositionCursor(int _row, int _column)
{
  Serial.write(0x5C);
  Serial.write(0x42);
  Serial.write(byte(_column+31));
  Serial.write(byte(_row+31));
};

void PrintPJRClcd::PrintData(int type, unsigned long datas)
{

  switch (type) //where does this data go?
  {
  case 0: //Air temp
    PositionCursor(1,8);
    Serial.print(datas);
    break;
  case 1: //Rez temp
    PositionCursor(2,9);
    Serial.print(datas);
    break;
  case 2: //Pump1 on or off
    PositionCursor(4,8);
    if(datas==0) Serial.print("Off");
    else if(datas==1) Serial.print("On ");
  case 3: // Minutes left on Pump1
    PositionCursor(4,13);
    Serial.print(datas);
    break;
  case 4: //Day or Night
    PositionCursor(7,1);
    if(datas==0) Serial.print("Night");
    else if(datas==1) Serial.print(" Day ");
    break;
  case 5: // Min left till change
    PositionCursor(7,7);
    Serial.print(datas);
    break;
  case 6: //worklight status
    PositionCursor(6,12);
    if(datas==4) Serial.print("Off  ");
    if(datas==5) Serial.print("On   ");
    break;
  case 7: //debug
    PositionCursor(5,2);
    Serial.print(datas);
    break;
  };
};


johnny72

Are you growing in soil?

If you're interested I can post the homebrewed moisture sensor I built.

Mullet

Nope, hydroponic, but post it anyway.  Maybe somebody else can use it.

I'm interested in what people think of the code.  

Hexteque

Looks pretty good!  

I am doing a similar project soon.   I may use your code for a little bit of reference... I am new to Arduino.

Go Up