YATC - Yet Another Terrarium Controller

robtillaart:

I have two Terrariums so i need a Modular Controller to inependently Controll both.

Why not two UNO's in stead of one MEGA? Modular is modular :wink:

and simulate Sun Rising/ Falling

I discussed an algorithm here - http://arduino.cc/forum/index.php/topic,66426.0.html - might be useful

(you can also connect an LDR to the window and "read the local sunset/rise" :slight_smile:

Yes thats true but one othe requirement was cheap as possible :stuck_out_tongue:

Thanks for the Feedback i will take a look into the sunrise algorithm :slight_smile:

The next Part is the Programming Part... I want to use an SD Card to save the Settings and also to specify the "Switch Times" for each channel.(I updated the first Post with the new Hardware)
Additional i want to implement the functionality to programm the Controller over a Serial Interface in Step 1 and in Step 2 i want to replace the Serial Interface with an Bluetooth mate to configure the Controller from an Device.

Kind Regards
dafes

Edit:
I think i will add two additional Sensors. One Soil Moisture Sensor to verify if enough water in the watertank and one for the plant in the rainforest Terrarium. Has anybody a better idea for checking the water level in a Plastic Tank to prevent the pump for running dry? It should be cheap as possible :smiley:
The moisture sensor cost 5€.

Edit2: I ordered a Watersensor to apply into the Watertank.

Yes thats true but one othe requirement was cheap as possible

cheap comes at a price ...

Why not one uno, just count the number of lines you need to control. Note that the analog ports can be used as digital so an UNO has 19 IO lines

You need
input:

  • 1 line for all the DS18B20's
  • 2 lines water sensor
  • 2 lines moisture sensor
  • 2 I2C RTC

output:

  • 4 SD card
  • 2 Serial
  • 3 lines for the relays (use a shift register!)

16 lines so one UNO could do the job

robtillaart:

Yes thats true but one othe requirement was cheap as possible

cheap comes at a price ...

Why not one uno, just count the number of lines you need to control. Note that the analog ports can be used as digital so an UNO has 19 IO lines

You need
input:

  • 1 line for all the DS18B20's
  • 2 lines water sensor
  • 2 lines moisture sensor
  • 2 I2C RTC

output:

  • 4 SD card
  • 2 Serial
  • 3 lines for the relays (use a shift register!)

16 lines so one UNO could do the job

Yes thats right, but :slight_smile: i want a litle more space to expand.
I have an 433MHZ Sender and some 433MHZ switches so maybe i expand the Board to controll Lights over this or i want to add some Wirelesse Solution to connect it to my Network.
And just saw you forget the 5 Button pins :wink: Right, Left, Up, Down and my lovely select :slight_smile: So i have to use 21 Lines and i beleive uno
has only 17 ports because d0 and d1 are used for Serial Connection or do i miss something?

Regard
dafes

I have finished the button Pad to controll some basic settings... I´ve made it as small as possible so the bottom layer is a litle bit dirty soldered.

Hello,
today everything arrives and now i can start programming and soldering :slight_smile:

My Plan was to build up two additional Boards:

  • One Power Board to power the arduino and additional boards
  • One Sensor/LCD Board to connect everything with the Arduino

I will post Pictures when i´m finished. A big question at the moment is how to handle the 1Wire Sensor Adresses and assign them to the right Terrarium.
My idea was to create an Terrarium Object(struct) and have a List of Byte[8] Sensors but i´ve got some problems with it...

Has anybody another Idea?

At the first start the Controller should ask how many Terrariums do you have and maybe then quide the user through the configuration process. All Settings will be saved on a SD Card since i´m a litle bit worry about the read/write cycle limits in eeprom.
Feedback is desired...

Kind Regards
dafes

A big question at the moment is how to handle the 1Wire Sensor Adresses and assign them to the right Terrarium.

your idea looks good but I see no code you have tried sofar ...

robtillaart:

A big question at the moment is how to handle the 1Wire Sensor Adresses and assign them to the right Terrarium.

your idea looks good but I see no code you have tried sofar ...

Yes it is still pre Alpha :smiley:
But here is something:
I created the following Object

/*
Represents a Terrarium Object

*/


typedef struct
{
    String Name;
    byte* Sensor;

}Terrarium;

Created an Array of it

const int NumTerrariums = 2;

byte sensor1[8] = { 0x10, 0xB2, 0xA0, 0x54, 0x2, 0x8, 0x0, 0x4E };
byte sensor2[8] = { 0x10, 0x3F, 0x7D, 0x54, 0x2, 0x8, 0x0, 0x5C };

Terrarium Terrariums[NumTerrariums];

Add initialize it with the following Data:

void updateTerrariums {
  Terrariums[0].Name = "Terrarium1";
  Terrariums[0].Sensor =&sensor1[0];
  Terrariums[1].Name = "Terrarium2";
  Terrariums[1].Sensor = &sensor2[0];
}

I use the sensor first in the following DS1820 libfunction:

float GetTemperature(byte sensor[8])
{
  //CRC Check
  if (OneWire::crc8(sensor, 7) != sensor[7]) {
      Serial.println("CRC is not valid!");
      return 0;
  }
  // the first ROM byte indicates which chip
  switch (sensor[0]) {
    case 0x10:
      //Serial.println("  Chip = DS18S20");  // or old DS1820
      type_s = 1;
      break;
    case 0x28:
      //Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      //Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    default:
      Serial.println("Device is not a DS18x20 family device.");
      return 0;
  } 

  ds.reset();
  ds.select(sensor);
  ds.write(0x44,1);         // start conversion, with parasite power on at the end
  
  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.
  
  present = ds.reset();
  ds.select(sensor);    
  ds.write(0xBE);         // Read Scratchpad

  //Serial.print("  Data = ");
  //Serial.print(present,HEX);
  //Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    //Serial.print(data[i], HEX);
    //Serial.print(" ");
  }
  // convert the data to actual temperature
  unsigned int raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // count remain gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    if (cfg == 0x00) raw = raw << 3;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw << 2; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw << 1; // 11 bit res, 375 ms
    // default is 12 bit resolution, 750 ms conversion time
  }
  celsius = (float)raw / 16.0;
  return celsius;
}

But i think i missed something with the pointers... I come from the c# world so i dont know the point(er)...:slight_smile:

BTW: Look what i received from China :slight_smile: And so nicely wrapped

Hi everybody,
i rebuilded some Boards to be a litle more modular.
I have created a new power board out of two pin connectors and changed the "normal" pin header to Connectors.
I will post some pictures tonight.
On Programming side there is not much progress. I have defined all Pins and at the moment i work on a small menu. I also try to find a good solution to switch the Lights timebased on and off.... I think i will create delay loop of 5 minutes in the main loop and call a function to verify if something to do on timer or temperature basis.

void loop
{
switch(Time)
{
case "12:00":
 Light.On(T1.S2);
break;
}
deleay(300000);
}

Hello,
On Hardware Side:
i have rebuilded some Boards to replace Pin Headers with Connectors. I also ordered many Connectors to connect the additional Sensos to sthe Systems.
I ordered some 3,5mm Audio Jacks to connect the sensors and 25m Shielded 4 lines Wire.
On Software Side:
I defined all IO Pins to match the Sensors and everything. At the moment i have only a small test setup to test all Boards and Sensors.
In the next weeks i will write a small menu and the "scheduler". I think i will use 5m delay loop like:

void loop()
{
time = GetTime();
switch(time)
{
 case(12):
  if(!Switch.Status(Terrarium1,Switch1)) {
   Switch.On(Terrarium1,Switch1); }
 break;
}
delay(30000);
}

Or is it better to use a Timer based Interrupt?

String Name;

Strong advice to remove the String usage form your sketch and replace it with a char array as the String class has "destructor" problems ..

Or is it better to use a Timer based Interrupt?

Don't use delay either => check the Blink without delay example how to schedule functionality in your code

robtillaart:

String Name;

Strong advice to remove the String usage form your sketch and replace it with a char array as the String class has "destructor" problems ..

Or is it better to use a Timer based Interrupt?

Don't use delay either => check the Blink without delay example how to schedule functionality in your code

Thank you for the feedback. I wil avoid using String and try the millis thing.
I have just finished the build up and everything is now connected.
Now i can concentrate my time to programming a running beta.

Keep us informed of your progress!

robtillaart:
Keep us informed of your progress!

Sure :slight_smile:

Here is the loop Code:

void loop() {
//Display
 if(SettingsScreen) { DisplaySettings(); }
 else { NormalView(); }
 
 //Timing
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis > interval) 
  {
    // save the last time you run the function 
    previousMillis = currentMillis;
    CheckForSomethingtoDo();   
  }
}

and here is the "CheckForSomethingtoDo" function:

void CheckForSomethingtoDo()
{
  //DBEUG To Verify if Function runs
  if(DEBUG) {Serial.println("DEBUG: CheckForSomethingtoDo"); }
  _CheckForSomethingtoDoTerrarium1();
  _CheckForSomethingtoDoTerrarium2();
}

void _CheckForSomethingtoDoTerrarium1()
{
  //Temperature
  TemperatureControlT1();
}
void _CheckForSomethingtoDoTerrarium2()
{
  //Temperature
  TemperatureControlT2();
}

//Snakes
void TemperatureControlT1()
{
  DateTime now = RTC.now();
  //Sommer
  if(now.month() >= T1_SommerStart && now.month() <  T1_WinterStart)
  {
   if(DEBUG) { Serial.println("Debug: Sommer"); }
   if(GetTemperature(sensor1) >= T1_TTS)
   {  
     if(DEBUG) {Serial.print("Debug:T1 "); Serial.print(GetTemperature(sensor1)); Serial.println(" SwitchOff");}
     SwitchOff(1, 1);
   }
   else
   {
     SwitchOn(1,1);
   }   
  }
  //Winter
  else
  {
    if(DEBUG) { Serial.println("Debug: Winter"); }
    
    if(GetTemperature(sensor1) >= T1_TTW)
    {
      
      if(DEBUG) {Serial.print("Debug:T1 "); Serial.print(GetTemperature(sensor1)); Serial.println(" SwitchOff");}
      SwitchOff(1, 1);
    }
    else
    {
      SwitchOn(1,1);
    }
  }  
}


void TemperatureControlT2()
{

}

In the main class i have the following "Configuration Section":

//---------------------------------Configuration-------------------------------------------------
const boolean DEBUG = true;

//Terrarium 1 
//Target Temperature Sommer
const int T1_TTS = 26;
//Target Temperature Winter
const int T1_TTW = 26;
//Sommer
const int T1_SommerStart = 5;
//Winter
const int T1_WinterStart = 10;
//Terrarium 2

//---------------------------------End-Configuration---------------------------------------------

In future i will configure this variables over a lcd and buttons and a config file on a sd card.

Hi there.
I think you also need a great enclosure to fit your arduino, buttons, lcd, etc.
Take a look at this topic: http://arduino.cc/forum/index.php/topic,141660.0.html
Let me know if you decided to get one, we will customize size and cut needed windows for you. I think a cherry wooden one will look pretty nice with terrarium.

These names are a clue that you can refactor: TemperatureControlT1, TemperatureControlT2. Consider making these a single function and pass the specific terrarium parameters, either as individual arguments or as a struct (or pointer to one) so that the same code can serve many terrarium instances. Alternatively, you could go full OO and just build a class that controls a terrarium - never thought I'd use that word so many times in a single post :wink:

wildbill:
These names are a clue that you can refactor: TemperatureControlT1, TemperatureControlT2. Consider making these a single function and pass the specific terrarium parameters, either as individual arguments or as a struct (or pointer to one) so that the same code can serve many terrarium instances. Alternatively, you could go full OO and just build a class that controls a terrarium - never thought I'd use that word so many times in a single post :wink:

Yes i thought about it. I thought also about a Struct Terrarium object with its Sensors. In the first step i want to build it as simple as possible to get the job done :slight_smile:

I have replaced the RTC with an GPS Clock and added some additional "timers"

long Seconds30Interval = 30000;  //30 Secons //Intervall in miliseconds
long previous30SecondsMillis = 0;        // will store last time CheckForSomethingtoDo was running
long Minute1Interval = 60000;  //1min //Intervall in miliseconds
long previous1MinuteMillis = 0;        // will store last time CheckForSomethingtoDo was running
long Minute5Interval = 300000;  //5min //Intervall in miliseconds
long previous5MinuteMillis = 0;        // will store last time CheckForSomethingtoDo was running
long Hour1Interval = 3600000;  //1hour //Intervall in miliseconds
long previous1HourMillis = 0;        // will store last time CheckForSomethingtoDo was running

This will give mne some freedom.
At the moment i stuck at the Day Night things:
I created a function which Adjusts the DayNightRhythm according the month:

void SetDayRhythm_T2()
{
  if(GPS.month == 0){
    if(DEBUG) {Serial.println("Debug:SetDayRhythm_T2 GPS.month == 0");}
    return;
  }
  
  if(DEBUG) {Serial.print("DEBUG: SetDayRhythm_T2 Month:");Serial.println(GPS.month,DEC); }
  
  switch(GPS.month)
  {
    case 1:
      T2_DayStart = 915; //09:15
      T2_DayEnd = 2015; //20:15
      T2_SpotStart = 140; //14:00
      T2_SpotEnd = 200; //20:00
    break;
.....
....
....
    case 12:
      T2_DayStart = 100; //10:00
      T2_DayEnd = 200; //20:15
      T2_SpotStart = 1600; //16:00
      T2_SpotEnd = 1700; //17:00
    break;
  }
}

This is a litle dirty but it does the job. In the 30seconds function i got an Verify if its time to Start/Stop a Lamp or Spot

void _CheckForSomethingtoDoTerrarium1_30Seconds()
{
  String time = String(GPS.hour)+=String(GPS.minute);
  if(DEBUG) {Serial.println("Debug: _CheckForSomethingtoDoTerrarium1_30Seconds Time: ");Serial.println(time);  }
  
  if(time == String(T2_DayStart))
  {
    if(DEBUG) {Serial.print("Debug: T2_DayStart Time: ");Serial.println(time);  }
    return;
  }

  if(time == String(T2_DayEnd))
  {
    if(DEBUG) {Serial.println("Debug: T2_DayEnd Time: ");Serial.println(time); }
    return;
  }

  if(time == String(T2_SpotStart))
  {
    if(DEBUG) {Serial.println("Debug: T2_SpotStart Time: ");Serial.println(time);  }
    return;
  }

  if(time == String(T2_SpotEnd))
  {
    if(DEBUG) {Serial.println("Debug: T2_SpotEnd Time: ");Serial.println(time);  }
    return;
  }

}

Yes i use strings.. i will replace them a soon as possible :slight_smile: this is just a habit from c#.

Anyway has anybody a solution for a dynamic Terrarium struct which includes a "array" or "list" of sensors(a sensor is an 8 byte array)

byte sensor1[8] = { 0x10, 0xB2, 0xA0, 0x54, 0x2, 0x8, 0x0, 0x4E };

How can i walk thorugh such a array of sensors?
Edit: Is the String Problem still present? I have read some post that this was prior 1.0:
http://code.google.com/p/arduino/issues/detail?id=468&q=string&sort=-id

robtillaart:

I have two Terrariums so i need a Modular Controller to inependently Controll both.

Why not two UNO's in stead of one MEGA? Modular is modular :wink:

and simulate Sun Rising/ Falling

I discussed an algorithm here - http://arduino.cc/forum/index.php/topic,66426.0.html - might be useful

(you can also connect an LDR to the window and "read the local sunset/rise" :slight_smile:

Hi,
i have read the Post and created a function according to this Post. the Location is Sydney (Because of UpsideDown i switched December with June to match the local Time.
Lat:
-33,52,0
Long:
-151,13,0

Sunries @23 June = 7:00 = 60 * 7 + 0 =420
Sunrise @23 dec = 4:42 = 60*4 + 42 = = 282
average = (=420+282)/2 = 351
delta = =420- 282= 138 => divide by 2 makes 69

Sunset @23 Dec 7:06PM = 60 * 19 + 6 = 1146
Sunset @23 June 4:54PM = 60*16 + 54 = 1014
average = (1146+1014)/2 = 1080
delta = 1146 - 1014= 132 => divide by 2 makes 66

void getRiseSet()
{
  float common = 69 * cos( ((month-1)*30.5+dayOfMonth + 8 ) * 0.017214206) ;  // replace division with faster multiplication
  sunrise = 351 + common;
  sunset = 1080 - common;    
}

Formula looks good.
Run it in a loop from day 1 to day 365 and check it against some website: - Sun & moon times today, Sydney, New South Wales, Australia -

salut a tous,

Je suis a la recherche de développeur pour un projet opensource similaire, Nous somme déjà 3 sur ce projet.
Ce projet consiste a rendre accessible aux plus novices les fonctionnalités du rapberry pi et arduino combinés dans le but de gérer des systêmes domotiques annexes tel qu'une ruche, un jardin, un aquarium, une cave a vin, une champignonnière, un terrarium, une serre, enfin presque tout enfaite.

Une interface graphique seras accessible depuis partout. Un systême de scénarios configurable permettra la régulation ainsi que l'alerte mail et sms du systême.

Le But étant de pouvoir partager depuis un site communautaire des données créer par la communauté dans un esprit de DRY (don't repeat yourself), pour éviter que chacun écrive sont petit morceau de code dans sont coin alors qu'il pourrais le rendre accessible a toute une communauté. Quel gains de temps pour toute une communauté!!!.

n’hésitez pas à nous rejoindre, on ne mort pas lol!!! les projets open sources ont besoin de vous!!

https://ardui-farm.sonetin.com/