ODD CLOCK

QUICK PROJECT DESCRIPTION

PROJECT OBJECTIVE: Build a non-linear clock that displays time accurately in 5-minute steps.

HARDWARE REQUIREMENTS: Provided in the Mango Labs Mechatronics Maker Kit: Arduino-compatible board, USB cable, RTC module, 2 stepper motors, 2 stepper drivers, 10 male to male jumper cables, 16 male to female jumper cables, mini breadboard, power supply.

Additional hardware: computer, printer, cardboard (or foam-core, balsa wood or a thin, rigid board), glue gun, 4 3m stove bolts with nuts, small screwdriver, CR2032 coin battery, sanding paper (fine grain), epoxy glue. Optional: soldering iron, solder, fine solid-core wire.

SOFTWARE REQUIREMENTS: Arduino environment (or online editor). Download the RTC library here and the stepper library here. Unzip and paste the folder in the Arduino libraries folder. It’s in the same location as your default sketches. You can look it up under File/Preferences Sketchbook location.

KNOW-HOW REQUIREMENTS: Basic Arduino environment knowledge (learn more here). Basic electrical skills.

QUICK OVERVIEW

  1. Print out these templates.
  2. Use the templates to make the parts in the material of your choice.
  3. Mount the stepper motors and microswitches in their corresponding positions.
  4. Glue the clock hands to the stepper shafts.
  5. Connect everything as shown in the diagram.
  6. Install the RTC and access stepper libraries (if you tested the board with the Hello World example you have the libraries already installed).
  7. Compile and upload this sketch to your board.
  8. Connect your Arduino-compatible board to the power supply.
  9. Calibrate your homing by ensuring that the clock hands close the microswitches and modifying the values on lines 156 and 188 to compensate the position if your hands don’t home at 12’o clock.

PIN OUT DIAGRAM

This example is made of plywood. Depending on the material you choose, you may have to adjust some assembly guidelines.

Use the template to cut out the pieces. After cutting out all the pieces, I sanded them, taking special care with the clock hands. The minute hand was sanded down 1 mm thinner than the hour hand, since the hour hand must be able to pass over the minute hand.

The shaft and mounting holes, as well as recesses to counter-sink the screw heads to mount the stepper motors, are shown in the print-out.

Be careful to mount the minute stepper with the screw heads below the surface level. I used a warm blob of hot glue to level the motor and mount it parallel to the back of the clock without counter-sinking the shaft. Don’t over-tighten the screws.

I recommend to counter-sink the hour stepper motor shaft by carving away a hole with a small abrasive tool (I used a Dremel with a sanding attachment) to mount it flush with the back of the clock.

The vibration of the motors will loosen the nuts. Use cyanoacrylate.

At the other end of the network cable color coded wires will make connecting to the multiplexer easy. The moisture sensors have no polarity so it doesn’t matter at which side you put the striped or whole color wires.
If you want to use less than 16 moisture sensors change the smax variable to the number of sensors you will use.
Follow the diagram to wire the circuit and take special care checking that all the wiring that goes to mains is OK. Upload the sketch from here or copy the code below to your Arduino-compatible board. Time will automatically be set on the RTC board so be sure to have the coin battery already installed; this way, it won’t reset when you unplug it. Unplug the USB and plug the power supply to your Arduino-compatible board to perform a last check.
It’s a good idea to protect your electronics from water. A fast and cheap way to do it is to put everything in a plastic container. Connect your electrovalve to your water supply using a hose and a hose clamp or a washing machine hose. Pin your nails all around your garden, plug the12v power supply to your Arduino-compatible board, plug your electrovalve to the SSR controlled female plug, plug your SSR to mains and you should be done.
Don’t forget to check out the smart watering lab idea for more detailed instructions!

The system checks for normal conditions for every sensor at startup and will light the LED red if something is wrong (it will also send a message through the serial port indicating if there’s a problem in a specific sensor). Every 6 minutes it will check the input variables (and blink white) to decide if watering is needed.

Code

Code
//by Dr Mangus for Mango Labs
//expanded to use 16 humidity sensors
//around your garden

//inspired by
//Grady Hillhouse (March 2015) project

// Improved smart watering
/* Measure humidity in  16 places (or less) of your garden
 * and calulate the average. If some point is to dry turn on a flag
 * and water even if average is above setpoint.
   (tested with IDE 1.8.5 under Windows 10)
   
  ARDUINO conected to:
   Thermistor
   ¯¯¯¯¯¯¯¯¯¯
   +5V □---------□                      Range  : -25°C to 125°C
                10kΩ = Rw               R_nom  :  10kΩ bei 25°C
   A1  o---------o----------○           T_nom  :           25°C
                         Thermistor     ß-Koeff:         3950
   GND ■---------■----------■           R ~ 1/T
   
  Multiplexer
   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ _________________
   A0  o-----------o Z
   D2  o-----------o EN
   D4  o-----------o S0
   D5  o-----------o S1
   D6 o-----------o S2
   D7 o-----------o S3     
   +5V □-----------□ Vcc     
   GND ■-----------■ GND     
                   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    Every output of the multiplexer(Y0...Y15) is connecteed to one nail 
    and the other nail is connected to 5V. Keep distance even between all nails
    (30mm).
      RGB LED
   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ _________________
   D9  o-----------o R
   D10  o-----------o G   
   D11 o-----------o B     
   GND ■-----------■ -    
                   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

    RTC
   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ _________________
   A4  o-----------o SDA
   A5  o-----------o SCL 
   +5V □-----------□ Vcc     
   GND ■-----------■ GND     
                   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
     SSR
   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ _________________
   D3  o-----------o CH1  
   +5V □-----------□ DC+     
   GND ■-----------■ DC-     
                   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯                
*/


#include 
#include 
#include "RTClib.h"
#include "MUX74HC4067.h"

//create a clock instance
RTC_DS1307 rtc;

// Creates a MUX74HC4067 instance
// 1st argument is the Arduino PIN to which the EN pin connects
// 2nd-5th arguments are the Arduino PINs to which the S0-S3 pins connect
MUX74HC4067 mux(2, 4, 5, 6, 7);


#define smax 16             // number of moisture sensors you will install
#define LOG_INTERVAL 360000 //milliseconds between entries (6 minutes = 360000)
// I/O declarations
#define THERMISTORPIN A1   
#define LEDPinRed  9
#define LEDPinGreen 10
#define LEDPinBlue 11
#define ssrPin 3

int wateringTime = 600000; //Set the watering time (10 min for a start)

const float wateringThreshold = 65; //Value in humidity % below which the garden gets watered
// (estimated, based on soil resistivity)

//Thermistor declarations 
// resistance at 25 degrees C
#define THERMISTORNOMINAL 10000      
// temp. for nominal resistance (almost always 25 C)
#define TEMPERATURENOMINAL 25   
// how many samples to take and average, more takes longer
// but is more 'smooth'
#define NUMSAMPLES 10
// The beta coefficient of the thermistor (usually 3000-4000)
#define BCOEFFICIENT 3950
// the value of the 'other' resistor
#define SERIESRESISTOR 10050    
 
int samples[NUMSAMPLES];

float Temp = 0; //Scaled value of soil temp (degrees C)
float soilMoistureRaw = 0; //Raw analog input of soil moisture sensor (Ain)
float soilMoisture = 0; //Scaled value of volumetric water content in soil (percent)
float humidity = 0; //Relative humidity (%)
int state = 0;
bool watering = false;
bool wateredToday = false;
DateTime now;


void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
  
  // red LED indicates error
  digitalWrite(LEDPinRed, HIGH);
  
  while(1);
}

void setup() {
  
  //Initialize serial connection
  Serial.begin(9600); //Just for testing
  
  
  pinMode(LEDPinRed, OUTPUT); //LED red pin
  pinMode(LEDPinGreen, OUTPUT); //LED green pin
  pinMode(LEDPinBlue, OUTPUT); //LED blue pin
  pinMode(ssrPin, OUTPUT); //solenoid pin
  digitalWrite(ssrPin, HIGH); //Make sure the valve is off (inverted logic)
 

  // Configures how the SIG pin will be interfaced
  // e.g. The SIG pin connects to PIN A0 on the Arduino,
  //      and PIN A0 is a analog input
  mux.signalPin(A0, INPUT, ANALOG);
  
  //Establish connection with real time clock
  Wire.begin();
  if (!rtc.begin()) {
        error("RTC failed com");
  }
  
  //Set the time and date on the real time clock if necessary
  if (! rtc.isrunning()) {
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

// Check if thermistor is inside normal range
Temp = readTemp();
delay(20);
if ((Temp > 45) || (Temp < -25)){
    error("temp sensor out of range");
}

//Check if humidity sensor is inside normal range
for (byte i = 0; i < smax; ++i) { // Reads moisture sensors to smax. Returns a value from 0 to 1023 soilMoistureRaw = mux.read(i); if ((soilMoistureRaw> 1000) || (soilMoistureRaw < 0)){
    error("humidity sensor out of range");
    
    delay(5);
  }
} 

  now = rtc.now();
    
}

void loop() {
   int haverage = 0; 
   int data = 0;
   bool dryflag = 0;
  //delay software
  delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL));
  
  //Three white blinks means start of new cycle
  setColourRgb(75, 90, 100);
  delay(500);
  setColourRgb(0, 0, 0);
  delay(150);
  setColourRgb(75, 90, 100);
  delay(150);
  setColourRgb(0, 0, 0);
  delay(150);
  setColourRgb(75, 90, 100);
  delay(150);
  setColourRgb(0, 0, 0);
  
  //Reset wateredToday variable if it's a new day
  if (!(now.day()==rtc.now().day())) {
    wateredToday = false;
  }
  
  now = rtc.now();
  
  Serial.print(now.year(), DEC);
  Serial.print("/");
  Serial.print(now.month(), DEC);
  Serial.print("/");
  Serial.print(now.day(), DEC);
  Serial.print(" ");
  Serial.print(now.hour(), DEC);
  Serial.print(":");
  Serial.print(now.minute(), DEC);
  Serial.print(":");
  Serial.print(now.second(), DEC);
  Serial.print(", ");

  
  //Collect Variables
  Temp = readTemp();
  delay(20);
  
  for (byte i = 0; i < smax; ++i)
  {
    // Reads moisture sensors to smax. Returns a value from 0 to 1023 and maps to
    //real max and min on the soil. Generates a flag (dryflag) if any point is very dry
    data = mux.read(i);

    Serial.print("moisture at point ");
    Serial.print(i);
    Serial.print(" is at ");
    Serial.print(map(data, 150, 950, 0, 1000)/10);
    Serial.println("%");
    haverage = haverage + data;
    if (data < 150){ dryflag = 1; } delay(5); } haverage = haverage / 10; haverage = haverage / smax; Serial.print("average moisture is:"); Serial.print(haverage); Serial.println("%"); Serial.println(); delay(20); //Soil Water Content soilMoisture = haverage; Serial.print(Temp); Serial.println("*C, "); // LED display based on soil humidity if (soilMoisture > 24 && soilMoisture < 50)
      setColourRgb(200, 200, 0); //yellow
      else if (soilMoisture < 25 || dryflag == 1) setColourRgb(255, 0, 0); //red else setColourRgb(0, 100, 0); // watering RULES //extreme dryness at any point if ((dryflag == 1) && (Temp > 10)) {
    //water the garden
    digitalWrite(ssrPin, LOW);
        Serial.print("Extreme dryness watering");
        setColourRgb(0, 0, 255);  //blue
        wateringTime = wateringTime * 1.5;  //extend the watering time 
    delay(wateringTime);
    
    digitalWrite(ssrPin, HIGH);
  }
  else {
    state = 0;
  }

  //normal conditions
  if ((soilMoisture < wateringThreshold) && (now.hour() > 8) && (now.hour() < 11) && (Temp > 4) && (wateredToday = false)) {
    //water the garden
    digitalWrite(ssrPin, LOW);
        Serial.print("Normal watering");
        setColourRgb(0, 0, 255);  //blue
    delay(wateringTime);
    
    digitalWrite(ssrPin, HIGH);
    wateredToday = true;  //record that we're watering
  }
  else {
    state = 0;
  }
  
//extreme heat
if ((soilMoisture < wateringThreshold) && (now.hour() > 10) && (now.hour() < 19) && (Temp > 27)) {
    //water the garden
    digitalWrite(ssrPin, LOW);
        Serial.print("Extreme heat watering");
        setColourRgb(0, 0, 255);  //blue
        wateringTime = wateringTime / 2;  //half the watering time 
    delay(wateringTime);
    
    digitalWrite(ssrPin, HIGH);
  }
  else {
    state = 0;
  }
    
//protecting plants against freezing
if ((soilMoisture < wateringThreshold) && (now.hour() > 18) && (now.hour() < 22) && (Temp > 4) && (Temp < 9)) {
    //water the garden
    digitalWrite(ssrPin, LOW);
        Serial.print("Cold protection watering");
        setColourRgb(0, 0, 255);  //blue
        wateringTime = wateringTime / 2;  //half the watering time 
    delay(wateringTime);
    
    digitalWrite(ssrPin, HIGH);
  }
  else {
    state = 0;
  }

switch (state) {
    case 0:
       Serial.print("Not watering");
      break;
     }
  
    Serial.println();
  delay(50);
  }


float readTemp(){
   uint8_t i;
  float average;
   // take N samples in a row, with a slight delay
  for (i=0; i< NUMSAMPLES; i++) { 
   samples[i] = analogRead(THERMISTORPIN);
   delay(3);
  }
  // average all the samples out
  average = 0;
  for (i=0; i< NUMSAMPLES; i++) {
     average += samples[i];
  }
  average /= NUMSAMPLES;
  
  // convert the value to resistance
  average = 1023 / average - 1;
  average = SERIESRESISTOR / average;
  
  float steinhart;
  steinhart = average / THERMISTORNOMINAL;     // (R/Ro)
  steinhart = log(steinhart);                  // ln(R/Ro)
  steinhart /= BCOEFFICIENT;                   // 1/B * ln(R/Ro)
  steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
  steinhart = 1.0 / steinhart;                 // Invert
  steinhart -= 273.15;                         // convert to C
     
    return steinhart;   
}

void setColourRgb(unsigned int red, unsigned int green, unsigned int blue) {
  analogWrite(LEDPinRed, red);
  analogWrite(LEDPinGreen, green);
  analogWrite(LEDPinBlue, blue);
 }


[/c]