Translate

Friday 23 October 2015

Radford SC2 Control unit & pre-amplifier.

Remember the nice Radford STA15 I did a couple of months ago? (You can check it out here) Well, this is it's matching control unit & pre-amp.


The four ECC83s (12AX7), all check out well within limits, and still nicely matched after years of service. Those main smoothing caps also still performing well, and needed very little in the way of reforming.
Very little wrong with this , except it was full of those Wima caps that tend to short circuit without warning. They'll have to go. There's also a few 50uF 6V electrolytics that are also in poor shape. Noisy switches and pots are taken care of with a little cleaning.






 It's owner was saying that the recording output lacked bass. Indeed, mainly due to one cathode bypass being open circuit, and the other reading 7uF of it's original 50uF. Note the brown discolouring round the end cap. Let's get to work....














Replacement caps...












... and finally re-united and playing nicely together once more.

Tuesday 20 October 2015

Weller soldering iron add-on.

Now I'm a great fan of the Weller TCP "Magnastat" iron; robust, simple, great parts availability. Yes, there are better irons out there, but these are usually available secondhand for peanuts.

They are available in various wattages, and I have two, both 50W, one with a nice fat screwdriver type tip, for valve stuff and tagstrip etc, and a slender one, for more delicate work.

The tip has a temperature rating stamped on it's base. This contains a special magnet, which loses it's magnetism at a certain temperature (known as Curie temperature). This magnetism is used to operate a magnetic switch (like a reed switch, but a little more robust) to switch the element on and off, which heats the tip.

As bench space is limited, I only use one power supply, and got sick and tired of pulling one iron plug out of the power supply, and plugging the other in when I wanted to switch between irons. What was needed was a switch box....

... then I got a bit geeky (well, there's a surprise!). Let's have an led on the box, to show when the iron is heating. Useful to know if the iron is up to temperature, or if a fault has occurred (occasionally the switch or element fail , see here ) , so I devised a simple circuit to put an LED on when the iron is drawing current.



Simple. When the iron is drawing current, there is a small voltage dropped across R1, which causes the op-amp IC1A to attempt to drive it's output to rail. Now the current flowing in R1 is obviously AC, so the output is a 50Hz (or 60Hz, depending on your mains) waveform, but it's enough to light a red LED. The power supply for the op-amp is a crude supply derived from the 24VAC from the weller PSU. It's half-wave rectified by D1, and D3 and R2 Clamp the voltage peaks to 5.1V. It's smoothed by C1.

It's crude, horrible but works!

Monday 19 October 2015

Car subwoofer filter.

This is a bit of a random one, left over from the start of the year, but I thought I'd put it up anyway.

I got a new car earlier in the year (well, new to me!). It has a very nice radio in it, and the speakers aren't bad. It has DAB, USB , Bluetooth, and, after a long while looking for the slot, no CD! It also lacks a line output or remote line to switch on my after market sub-woofer and amplifier, but it does have an unused speaker output, so I knocked up this:



Now the issue is, the output from most head units is bridge-tied load, to get a bit more power out. This leaves the speaker lines floating at half supply (6V) to chassis. Never ground a speaker wire!

This is OK though, as we can use this to trigger our remote line.

One side of our speaker is connected via R10, to the gate of a small FET (Q1), this, in turn switches on Q2, which supplies 12V to our remote line, to trigger our amplifier on.

IC1A gives us a low-current half-rail power supply, we can use to reference our audio to, as we can only "swing" our audio above the chassis 0V, rather than swinging both ways, as we don't have a negative supply rail.

The speaker is also connected to a pad, formed by R4 and R9, which lowers the audio voltage to something more suitable to drive our amplifier. IC1B is simply a unity gain non-inverting buffer amplifier, and this feeds an adjustable low-pass filter, formed by the dual-gang pot R14, R15, C7, C8 and C9 around IC1C. R15 is an output level adjust, and finally IC1D is the output amplifier, with a gain of 2 to make up for the loss in the filter. R19 is there just to limit current. C10 blocks the DC content of the output (remember it's swinging around 6V, rather than chassis) and R20 references the audio back to the chassis.

S1 is used to switch the phase if the sub makes the mid-bass sound incorrect. R11 is a 10 ohm load resistor. Some head unit amplifiers may not like being run unloaded, so it's there to give some load. You might not need it, and it will certainly do no harm to the head unit if it's not there. Add it if you need to, but make sure it's well rated to dissipate the required power.

Thursday 15 October 2015

Vintage test gear - Heathkit Harmonic Distortion Analyzer IM-5258

A quick description of the Heathkit Harmonic Distortion Analyzer (Analyser!)

Great bit of vintage 70's 80's test gear.

I've tried a couple of times in my life to build a THD analyser, before I gave in and just bought one... I think I'd be OK today, but I have a reliable instrument, so it saves me the grief!

It takes the input signal, a sine wave, and buffers it, matches it for voltage with it's own internal oscillator, then notches out the input signal. What then remains is the harmonic distortion and noise. Simple to say, a little more complex to put into practice.

The Q-factor of the notch filter must be high enough so it doesn't notch out the 2nd harmonic (or 3rd, 4th, 5th etc) , which at audio frequencies can be difficult to achieve. The resultant output needs to be measured with a high bandwidth AC RMS meter, sensitive to microvolts, which can also be a tall order!

It's also tricky to keep the whole thing from oscillating.

Thankfully, I picked up this:




It's all discrete, not an op-amp in sight, and just works! It's also auto-tuning, but you still need to set the range and adjust the notch, so I'm not sure how "auto" it really is!

So, when we're designing an oscillator or amplifier, we can check how much distortion it really is producing, and where in the frequency spectrum it is! Useful stuff....

Here's a quick video of the thing in action...




Saturday 10 October 2015

Arduino Mains Monitor with SIM900A GSM messaging.

Picture the scene... You're away on holiday, sunning yourself in Spain (or if you're Spanish, enjoying the damp weather in the UK !) ... and thousands of miles away, some spurious minor electrical niggle in your house causes the RCD circuit breaker to open. You return 10 days later to find the contents of your freezer oozing their way across the kitchen floor. Not nice... You reset the breaker, and the power comes back on, showing no faults.

It happens from time to time in my house. I've tested each circuit, and every appliance to try and find the cause of the random tripping. The fault is not visible on any of my insulation readings, but, nevertheless it does happen. Sometimes not for years.... frustrating!

So what can we do about it? Fix the fault would be the easiest thing to do, but it's eluding me. How about monitoring the mains, and sending me a message, so if it does trip, I can reset the power without issue? Good plan...

Now the disclaimer:

WARNING. Do NOT try this at home. This project deals with mains voltages. A shock from the mains will hurt and can easily be fatal. Work safely. Use an RCD. Disconnect from the mains before making any adjustments. 
RESPECT IT'S AUTHORITY

I will not, under any circumstances, accept any liability if you decide to re-create this project for yourself.


Now that's got that out of the way...

So, we have a plan. Measure the mains voltage (and why not frequency at the same time?), and act conditionally on it's failure. We also need to get it to send us a message. We're going to need some form of uninterruptable power supply, so when the mains does go off, our micro will still be running, and we've got power enough to send the message. Here's the circuit:


I'll go through it step by step...

Power comes in via a fuse to two transformers. TR1, is a mains to 15V transformer, and is used as our power supply. It feeds a bridge rectifier, B1 and C1 and C2 are used to smooth and decouple the resultant DC. The DC is fed via D2 to a 7805 regulator, which provides the 5V for the Arduino and the SIM900  module. So that's fairly straightforward. The 7805 in this instance is a 2A part, as the SIM900A does require a fair amount of current to function.

(You could, and it would be safer to do so, build this unit using two wall warts, one, with, say at 15VDC, 3A output to replace TR1 & B1, and a second, with AC output in place of TR2. That way, all the tricky and potentially dangerous stuff with the mains is eliminated)

The rectified DC from B1 is also fed to two LM317 voltage regulators in series. The first, IC1, is configured as a 100mA constant current source, this is then used to feed the second regulator, IC2, which is configured as a conventional voltage regulator, in this case, R3 is adjusted to provide around 14V. This forms a 100mA constant current charger, with maximum voltage of 14V, which we can use to charge G1, which is an old 12V NiMH battery I happened to have kicking about. It's got plenty of capacity left, so will do nicely as our back up battery. S3 is a battery diconnect switch. Useful for resetting. (Why didn't I use an LM200, as it is capable of both current and voltage regulation in one package, instead of two LM317's? Because I didn't have one!)

So, when the mains is present, our battery is being charged. In the event of mains failure, D2 will stop conducting, and the supply will be seamlessly taken over by the battery, supplying current to the 7805 via D1. D3 prevents current flowing back into the charging circuit. The battery voltage is also sampled at the mid-point of the potential divider, R11 and R12, and fed to the Arduino A1 pin. T2, between the reference pin of IC2 and ground, is used to switch off the charging circuit momentarily, so we can measure the battery voltage, and not just the output of the charger. It's controlled by the Arduino A2 pin, configured as a digital output.

OK, so that's power and back up power sorted, so what about measuring the mains? TR2 is a small (3VA) 18V mains transformer. This feeds another bridge rectifier, B2, and a small smoothing network, formed by C7. The voltage developed across C7 is fed to a potential divider formed by R5 and R4, and the resultant voltage fed to Arduino A0. This voltage will be directly proportional to the mains input voltage on the primary of T2.... or will it? Whilst I was experimenting, I noticed the measurement wasn't linear. Adding a bit of loading, in the form of R15 to the secondary helped matters no end, although it was accurate enough over the range required for this to be left out, if required.

Also coupled to the secondary is our frequency measuring network. The AC is fed via coupling capacitor C8 to the base of a BC547, T1. This provides a 5 volt 50 Hz signal to the input of IC4A (a Schmitt trigger hex interter), this will square our pulses up. The output of IC4a is fed via a low pass filter, formed by R8 and C9 to the input of IC4B, the output of which is connected to the Arduino pin D8. We'll use this to measure the frequency of the mains. Tie all of the unused inputs of IC4 to ground.

On to the micro side of things...

The LCD is wired to the Arduino in the time-honoured fashion, except for the cathode of the backlight, which isn't connected straight to ground, but to the collector of T3. This allows us to control the backlight, using A4 of the Arduino configured as a digital output. There's a momentary switch coupled to A3, which is used as a "push to transmit" function, and a toggle switch, S2, connected to pin A5, which is used to stop the SIM900 sending messages. R9 is the LCD's contrast control.

The SIMR pin on the SIM900 module is connected to the Arduino's hardware serial Tx pin, and the SIMT pin is connected to the Rx pin.

The software.....

We're going to need something to measure our frequency. I did initially use PulseIn , but it's not that accurate, so I switched to using the most excellent FreqMeasure library, available from https://www.pjrc.com/teensy/td_libs_FreqMeasure.html . Now this has a drawback. It uses Int 1, and this is in conflict with the software serial library, which is why the SIM900 is connected to the hardware serial port of the Arduino. The issue here is the hardware port is also where the inbuilt USB interface sends/receives data, so you'll need to disconnect the SIM900 from the arduino whilst uploading the sketch, or doing any serial debugging. You can use SoftwareSerial during development, but be prepared for some unusual responses from FreqMeasure!

Ok, so the sketch.

There's a rake of variables set at the start. The variables to watch are the mains tolerances, these will need to be set for your local mains (They're currently set for UK mains spec.)

  float MainsMinV = 216.2; // This sets the lower limit for the mains voltage. Change this to suit your local voltage limit
  float MainsMaxV= 253; // Maximum voltage limit
  float BatteryMin=11.2; // Battery low limit
  float MainsMinF=49.5; // Minimum allowable mains frequency limit, change to suit local power
  float MainsMaxF=50.5; // Maximum allowable mains frequency limit.

You will also need to change line 260 :
      Serial.println("AT + CMGS = \"+44xxxxxxxxxx\"");// recipient's mobile number, in international format
If your mains is not ~240v, you will also need to change the scaling factor in the software , line 286, so the voltage reads correctly. Currently the mains voltage is scaled so 250V is equal to 5V at our micro. If, say, you're on 120V mains, and want the voltage to top out at , say 130V, then the scaling will be 130/5 = 26. It's currently set to 51, so 5V on our analogue port reads as 255V :
  ACVoltage = (sensorValue * (5.0 / 1023.0))*51;

Adjust R4 to get the mains calibration correct.

So, here's the complete sketch:


If you're using a SIM900A module outside of Asia, you may run into difficulties, as I did. There's some brief notes I made here : http://andydoz.blogspot.co.uk/2015/10/sim900-and-sim900a-module-signal.html and a link to a website which contains detailed instructions on sorting out the firmware to make the unit work.
Here's some pictures of my unit:



BTW. Sainsbury's is my service provider!









Electrical Safety is paramount. Here is the incoming mains earth (ground) securely tied to the chassis of my metal case. 

Friday 2 October 2015

SIM900 (and SIM900A) module signal strength.




I've been working on a mains monitor project for a while, and I've wanted to interface it to a mobile phone module, so it will send me data. I'll publish the finished thing up soon, but , in the meantime, reading forums etc, people seem to have a couple of problems.








1) Getting the oh-so-very-cheap SIM900A module to work.

2) Getting a signal strength report from the thing.




The first problem appears to be that the SIM900A module, which I purchased for less than a tenner from eBay, doesn't work outside Asia. Yep. It certainly doesn't work here in the UK. But, never fear, there's a website with all the info to help us out. http://amichalec.net/2014/08/sim900a-fixed-for-europe/ which, after a little fiddling about, had provided a firmware cure. Now I have used my little FTDI module to flash the firmware on my unit at 57,600 Baud. The website claims you need to set the baudrate of your device to 460800 baud, which mine won't support. It worked fine at 57,600, however. I did have to wait a little while (about 20 mins) for the process to complete though.
The second problem seems to bug a lot of users. I searched in vain for a ready-to-go library to solve all my woes, but couldn't find one, so here's a little routine to get the signal strength out of the unit, so you can do with it what you will...
#include <SoftwareSerial.h>
SoftwareSerial SIM900 (6,7); // SIM900 connected to pins 6 & 7 (pin 6 to SIMT and pin 7 to SIMR)
char Reply[200]; // Serial buffer length
int number = 43; // msg length
int Signal; // Signal strength as reported
int temp1; // 4 temporary integers, used to extract the data from the incoming serial.
int temp2;
int temp3;
int temp4;
int BER; // Bit error rate
int SignaldBm; //Signal in dBm
void setup() { SIM900.
{
SIM900.begin(9600); // Start serial comms with module
Serial.begin(57600); // Start serial comms with PC
}
void GetStatus() { SIM900.
SIM900.write("AT+CMGF=1\r"); //set GSM to text mode
delay (150); SIM900.
SIM900.write("AT+CSQ\r"); //Send command for signal report
delay (200);
while(SIM900.available() >0 ) { //wait for responce
for(int i=0; i<44; i++) //read character into array from serial port
Reply[i] = SIM900.read(); } Reply[199] =
}
Reply[199] = '/0'; temp1=Reply[31]-


temp1=Reply[31]-'0'; //convert relevant characters from array into integers
temp2=Reply[32]-'0'; temp3=Reply[33]-
temp3=Reply[33]-'0'; temp4=Reply[34]-
temp4=Reply[34]-'0';

if (temp1 == -48) { //if temp1 is -48 then do it again as the data is not valid (yet)
GetStatus();
}

if (temp3 == -4) { // use temp3 to determine where in the array the relevent integers are (-4 is a ",")
Signal= temp2+ temp1*10; // calculate signal if the first digit is a multiple of 10
BER = temp4;
}

else{ Signal= temp1;
Signal= temp1; //calculate signal if the first digit is not a multiple of 10
BER = temp3;
}

if ( Signal == 99) { // if our signal is 99, IE no signal condition , the return a signal of -1
Signal = -1;
}


SignaldBm = Signal*2 -113; // calculate dBm for geeks like me.
Serial.print ("Signal: "); //output stats to serial interface.
Serial.println (Signal);
Serial.print (SignaldBm);
Serial.println ("dBm");
Serial.print ("BER: ");
Serial.println (BER); }

}
void loop() { GetStatus (); }
{
GetStatus ();
}
There , I hope that eases the pain for a few users! Have fun, and check back soon for the mains monitor!







Sunday 27 September 2015

Top Gear Fuzz

This guitar pedal came in for repair, and to add a DC socket on. (Jeremy Clarkson not included, thankfully)




I couldn't find a diagram anywhere, so for future reference, and for the common good, here's the schematic:


TR1 was U/S in my example, although, in a similar manner to the Trio amp (here), had two junctions, and tested OK on the transistor tester!

Amazing two BC109's and a slack handful of bits and pieces, can fetch so much money on eBay!

Thursday 24 September 2015

A milestone ....

Amazing to reach 10,000 page views!!!


Thanks to all the readers everywhere!

Wednesday 23 September 2015

Minor DHT22 Dehumidifier controller update.

Winter's coming again.

Looking at the recent data from the dehumidifier controller (last version update was here.) I'm concerned that it's possible for the dehumidifier to run when it's damn cold up there.

Two reasons why this is daft.

1) The outlet pipe from the dehumidifier may be frozen, resulting in a broken pipe and possible flood
2) There's no point in trying to condense water from the air when it's too cold.

So I've added a simple few lines to prevent the dehumidifier running if it's cold


    if ( Temp <=1 ) { // Frost protection. If it's less than 1 deg - don't run.
      DewFlag = false;
      Timer = 0;
    }

I've also decided to include a maximum value of humidity, set to 65%. I'd noticed some high humidity peaks, and the worry is, as the temperature falls at night, the water will condense, so now the dehumidifier is set to run if this level is exceeded.

  // Dew detect loop. If the dewTrip point is reached, or the maintenance timer is 129600, or the humidity is greater than 65%, start the timer running and set the Dew flag
    if ( Temp <= DewTrip || Timer2 == 129600 || Humidity >= 65 ) {
      DewFlag = true;
      Timer = 1;    
      Timer2 = 0;  // reset maintenance timer 
    } 
    else {
      DewFlag = false;
    }



So the whole code now looks like this:




// Wireless dehumidifier Controller
// 
// Copyright Andy Doswell 24th May 2014 modified Dec 2014. 
// PURPOSE: DHT22 sensor with LCD Display and output for controlling dehumidifier.
// Data send via virtualwire and 433MHZ link to remote display
// Dehumidifier is controlled by a relay attached to pin 7, defined in rlyPin.
// DHT22 sensor is connected to 8, as defined in #define
// LCD is a Hitachi HD44780 or compatible, attached thus:
// LCD RS pin to digital pin 12
// LCD Enable pin to digital pin 11
// LCD D4 pin to digital pin 5
// LCD D5 pin to digital pin 4
// LCD D6 pin to digital pin 3
// LCD D7 pin to digital pin 2
// RW to GND
// V0 Held high (this may or may not work with your LCD, usual practice is to connect this to a 10K pot between +5 and GND)
// TX connected to pin 10
// Modified to transmit every 198 seconds, and to switch dehumidifier on when Dewpoint is within 5 Degrees of actual temperature
// Also brings dehumidifer on every 3 days regardless of temperature or humidity.
// You'll need the wonderful dht library by Rob Tillaart from here : https://github.com/RobTillaart/Arduino/tree/master/libraries/DHTlib 
// and the equally wonderful Virtualwire library from here :https://www.pjrc.com/teensy/td_libs_VirtualWire.html
// 21 Sep 2015 - 65% humidity upper limit added
//             - Frost protection added

// reference: http://en.wikipedia.org/wiki/Dew_point
double dewPointFast(double celsius, double humidity)
{
 double a = 17.271;
 double b = 237.7;
 double temp = (a * celsius) / (b + celsius) + log(humidity*0.01);
 double Td = (b * temp) / (a - temp);
 return Td;
}

#include <VirtualWire.h>
#include <VirtualWire_Config.h>

#include <dht.h>


dht DHT;

#define DHT22_PIN 8

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// define constants & global variables
const int rlyPin = 7; //defines pin the relay is connected to. relay is active low
const int TimerLength = 2700;// number of seconds in an 90 mins / 2 - this sets the minimum time the dehumidifier will remain on for.
int Timer = 0; //timer is off to start
boolean DewFlag = false; // This is the dew flag. Off as default.
boolean OutputStatus = false; //Output status is off as default
int chk;
int Temp;
int Dew;
int Humidity;
int DewTrip;
float Timer2 = 0; //maintenance timer - used to run the compressor every ~3 days


// Data structure set up for Transmission

struct TXData
{
  int TX_ID;
  int Temp;
  int Humidity;
  int Dew;
  boolean Output;
};
  

void setup()
{
  lcd.begin(16, 2); // defines the LCD as a 16 x 2
  pinMode (rlyPin, OUTPUT); // sets our relay pin
  digitalWrite (rlyPin, HIGH); // sets the relay off for default condition.
  
  // Virtualwire setup
  vw_set_tx_pin(10); // TX pin set to 10
  vw_set_rx_pin(9); // RX pin set to a spare pin, even though we don't use it. If left as default it interferes with LCD operation.
  vw_setup(300); //sets virtualwire for a tx rate of 300 bits per second
  

}

void loop()
{
  for(int i = 0; i < 100 ; i++) { //transmit only every 99 loops (198 seconds)
    chk = DHT.read22(DHT22_PIN); // these 4 lines get data from the sensor
    Dew = dewPointFast(DHT.temperature, DHT.humidity);
    Temp = (DHT.temperature);
    Humidity = (DHT.humidity);
    DewTrip= Dew + 5; // Brings the dehumidifier on 5 deg C before the dew point. 
  
    // writes information about the system to the LCD
    lcd.clear ();
    lcd.print("Humidity:");
    lcd.print(Humidity);
    lcd.print("%");
    lcd.setCursor(0, 1);
    lcd.print(Temp);
    lcd.print((char)0xDF);
    lcd.print("C");
    lcd.setCursor(6, 1);
    lcd.print("Dew:");
    lcd.print(Dew);
    lcd.print((char)0xDF);
    lcd.print("C");
   
  // Dew detect loop. If the dewTrip point is reached, or the maintenance timer is 129600, or the humidity is greater than 65%, start the timer running and set the Dew flag
    if ( Temp <= DewTrip || Timer2 == 129600 || Humidity >= 65 ) {
      DewFlag = true;
      Timer = 1;    
      Timer2 = 0;  // reset maintenance timer 
    } 
    else {
      DewFlag = false;
    }
    
    if ( Temp <=1 ) { // Frost protection. If it's less than 1 deg - don't run.
      DewFlag = false;
      Timer = 0;
    }
    
  
    if (Timer >= TimerLength and DewFlag == false) { // If the timer has expired and there's no dew, switch the dehumidifier off.
      Timer = 0;
      digitalWrite (rlyPin, HIGH);
   
    }

    if (Timer !=0) {                // If the timer is running, switch the dehumidifier on , and write On to the lcd.
      digitalWrite (rlyPin, LOW);
      lcd.setCursor (13,0);
      lcd.print (" On");
      OutputStatus = true;
      Timer++;
    }
  
    else {
      lcd.setCursor (13,0);
      lcd.print ("off");
      OutputStatus = false;
    }
  
 delay (2000);
 Timer2 ++;
  }
  


  
struct TXData payload; //Loads the Data struct with the payload data
lcd.clear ();
lcd.print ("Transmitting");
  
payload.TX_ID=10;
payload.Temp = Temp;
payload.Humidity = Humidity;
payload.Dew = Dew;
payload.Output = OutputStatus;
vw_send((uint8_t *)&payload, sizeof(payload)); //Transmits the struct
vw_wait_tx(); //waits for the TX to complete

}  
  


Tuesday 25 August 2015

Request from Raminda Subhashana - Arduino astronomical clock controller with over-ride! De-luxe!

Well, I've not always got time for this sort of thing, but one of my readers, Raminda has sent a request in...

Hi, Nice project Just checked it is working, perfect appreciate that. One thing I need to add Button to off light manually in Auto mode, but it should switch on automatically next day. please help me to add it. Thanks. on Pond Pump Controller Update!

Thanks Raminda, a great idea. Sometimes it's difficult to fathom things out, especially when you are trying to learn, and don't have anyone to ask.

OK, so it looks like Raminda is using my Pond pump controller to switch on and off some lights during the night. Great idea.

So, we'll need to add a push button in to switch our over-ride on. A1 isn't doing anything, so we'll hook up a momentary push button (S1) to pin A1 and GND. That's all the hardware we'll need to modify (Note I've made some amendments to the diagram from previous)



Then , we need to think about the code. We'll need to define a flag to control the status of our over-ride. We'll set it to false to start with,

// Over-Ride feature flag for Raminda Subhashana
boolean OverRideFlag = false;

OK. Now we need to tell the arduino what we need pin A1 to be doing... Add this line to the setup..




    pinMode(A1,INPUT);// Over ride push button connected to A1 (and GND)
    digitalWrite(A1,HIGH);


This tells the arduino that A1 is an input, and we want to pull it high when the button is open circuit.

Now in the Loop we need to check the status of the button.

       buttonstate = digitalRead(A1);// check to see if our override button is pressed, if it is, set the flag
       if (buttonstate == LOW ) {
         OverRideFlag=true;
       }        

Here we set the temporary variable buttonstate (also used elsewhere) to the value of A1, if it's low, we set our OverRideFlag to be true.

Finally we need to check the status of the flag, when we come to set the status of the relay, so we need to modify two lines of code...

      if (TimeMins >= Sunrise && TimeMins <=Sunset+60 && tempC>=2 && OverRideFlag==false) { //If it's after sunrise and before sunset + 1 hour, and it's not frozen, and our override isn't set, switch our relay on

I think the comment there is self-explanitory!

We also need to reset the flag once the sun comes up again:
    // Reset Override flag if the sun is up!
    if (TimeMins == Sunrise) {
      OverRideFlag = false;
    }

There's no need to specifically set the relay off, as this is taken care of in the else statement.

So, there you go Raminda, but I have a concern. What if we change our mind? What happens if we want to un-overide it?!?!

ahh... OK , we can change the code slightly to flip the status of the flag everytime the button is pressed.. ON-OFF-ON-OFF  etc . This might suffer with switch bounce, in which case we'll need a slight delay, but it worked ok with me (because of the delay in the loop).

       buttonstate = digitalRead(A1);// check to see if our override button is pressed, if it is, change the status of the flag
       if (buttonstate == LOW ) {
         OverRideFlag = ~OverRideFlag;
       }       
The ~ sign means NOT and is a boolean operator. So our flag is now NOT what it was before, so flips from true to false, or false to true!
The whole code should now look like this:
// Dawn & Dusk controller with frost protection. A special revision for Raminda Subhashana to control some lights.
// 5th December 2014. Modified 17th April 2015. Modified 25 August 2015
// (C) A.G.Doswell 2014 & 2015
// License: The MIT License (See full license at the bottom of this file)
//
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
//
// Designed to control a relay connected to pin A3. Pin goes low during daylight hours and high during night. Relay uses active low, so is
// "On" during the day. This is connected to the fountain pump in my garden.
//
// Time is set using a rotary encoder with integral push button. The Encoder is connected to interrupt pins D2 & D3 (and GND), 
// and the push button to pin analogue 0 (and GND)
// The RTC is connections are: Analogue pin 4 to SDA. Connect analogue pin 5 to SCL.
// A 2 x 16 LCD display is connected as follows (NOTE. This is NOT conventional, as interrupt pins are required for the encoder)
//  Arduino LCD  
//  D4      DB7
//  D5      DB6
//  D6      DB5
//  D7      DB4
//  D12     RS
//  D13     E
// 
// In this revision, there is a Dallas 18B20 Temperature sensor connected to pin 8, enabling frost protection. This is pulled up to +5volts via a 4.7K resistor.
//
// Use: Pressing and holding the button will enter the clock set mode (on release of the button). Clock is set using the rotary encoder. 
// The clock must be set to UTC.
// Pressing and releasing the button quickly will display the current sun rise and sun set times. Pressing the button again will enter the mode select menu. 
// Modes are AUTO: On when the sun rises, off when it sets.
//           AUTO EXTEND : same as above but goes off 1 hour after sunet (nice for summer evenings!) 
//           ON: Permanently ON
//           OFF: Permanently OFF (Who'd have guessed it?)
//
// Change the LATTITUDE and LONGITUDE constant to your location.
// Use the address finder from http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html to find the address of your temperature sensor and enter it where required in DeviceAddress.
// 28th Dec 2014 - Slowed the rate at which the temperature is requested down to once per minute. It has improved the clock display, and solved an issue whereby the temp sensor wasn't always ready to send data, and hung up.
// 10th Jan 2015 - Altered the method of updating the temp to something simpler and more sensible!
// 17th April 2015 - Stored Mode settings in EEPROM
//
// Be sure to visit my website at http://andydoz.blogspot.com

#include <Wire.h>
#include "RTClib.h" // from https://github.com/adafruit/RTClib
#include <LiquidCrystal.h>
#include <Encoder.h> // from http://www.pjrc.com/teensy/td_libs_Encoder.html
#include <TimeLord.h> // from http://swfltek.com/arduino/timelord.html. When adding it to your IDE, rename the file, removing the "-depreciated" 
#include <OneWire.h> // from http://playground.arduino.cc/Learning/OneWire
#include <DallasTemperature.h> // from http://www.hacktronics.com/code/DallasTemperature.zip. When adding this to your IDE, ensure the .h and .cpp files are in the top directory of the library.
#include <EEPROM.h> 

#define ONE_WIRE_BUS 8 // Data wire from temp sensor is plugged into pin 8 on the Arduino
OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices
DeviceAddress outsideThermometer = { 0x28, 0x1A, 0x1A, 0x3E, 0x06, 0x00, 0x00,0xC7 }; // use the address finder from http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html to find the address of your device.
RTC_DS1307 RTC; // Tells the RTC library that we're using a DS1307 RTC
Encoder knob(2, 3); //encoder connected to pins 2 and 3 (and ground)
LiquidCrystal lcd(12, 13, 7, 6, 5, 4); // I used an odd pin combination because I need pin 2 and 3 for the interrupts.
//the variables provide the holding values for the set clock routine
int setyeartemp; 
int setmonthtemp;
int setdaytemp;
int sethourstemp;
int setminstemp;
int setsecs = 0;
int maxday; // maximum number of days in the given month
int TimeMins = 0; // number of seconds since midnight
int TimerMode = 2; //mode 0=Off 1=On 2=Auto
int TimeOut = 10;
int TimeOutCounter;

// These variables are for the push button routine
int buttonstate = 0; //flag to see if the button has been pressed, used internal on the subroutine only
int pushlengthset = 3000; // value for a long push in mS
int pushlength = pushlengthset; // set default pushlength
int pushstart = 0;// sets default push value for the button going low
int pushstop = 0;// sets the default value for when the button goes back high

int knobval; // value for the rotation of the knob
boolean buttonflag = false; // default value for the button flag
float tempC; // Temperature

// Over-Ride feature flag for Raminda Subhashana
boolean OverRideFlag = false;


const int TIMEZONE = 0; //UTC
const float LATITUDE = 20.00, LONGITUDE = -2.00;  // set YOUR position here 
int Sunrise, Sunset; //sunrise and sunset expressed as minute of day (0-1439)
TimeLord myLord; // TimeLord Object, Global variable
byte sunTime[]  = {0, 0, 0, 1, 1, 13}; // 17 Oct 2013
int SunriseHour, SunriseMin, SunsetHour, SunsetMin; //Variables used to make a decent display of our sunset and sunrise time.
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature.

void setup () {
    //Serial.begin(57600); //start debug serial interface
    Wire.begin(); //start I2C interface
    RTC.begin(); //start RTC interface
    lcd.begin(16,2); //Start LCD (defined as 16 x 2 characters)
    lcd.clear(); 
    pinMode(A0,INPUT);//push button on encoder connected to A0 (and GND)
    digitalWrite(A0,HIGH); //Pull A0 high
    pinMode(A1,INPUT);// Over ride push button connected to A1 (and GND)
    digitalWrite(A1,HIGH);
    pinMode(A3,OUTPUT); //Relay connected to A3
    digitalWrite (A3, HIGH); //sets relay off (default condition)
    sensors.begin();
    sensors.setResolution(outsideThermometer, 12); // set the resolution to 12 bits (why not?!)
    
    //Checks to see if the RTC is runnning, and if not, sets the time to the time this sketch was compiled, also sets default mode to 2 and writes to EEPROM
    DateTime now = RTC.now();
    lcd.clear();
    lcd.print("Waiting for RTC");
    delay (60000);// wait a minute for the RTC to sort itself out.
    if (! RTC.isrunning()) {
    RTC.adjust(DateTime(__DATE__, __TIME__));
    EEPROM.update (0,0); // set the auto mode as 0 (off) for default
    }
  
 
   lcd.print("Raminda Subhashana");
   lcd.setCursor(0, 1);
   lcd.print("Light Controller");
   delay (2000);
   lcd.clear();
   lcd.print("Version 1.32");
   lcd.setCursor(0, 1);
   lcd.print("(C) A.G.Doswell ");
   delay (5000);
   lcd.clear();
    //Timelord initialisation
    myLord.TimeZone(TIMEZONE * 60);
    myLord.Position(LATITUDE, LONGITUDE);
    CalcSun ();
    TimerMode = EEPROM.read (0); // Read TimerMode from EEPROM
    delay (1000);
}
           
void printTemperature(DeviceAddress deviceAddress)
{
  lcd.setCursor (9,1);
  tempC = sensors.getTempC(deviceAddress);
  if (tempC == -127.00) {
    lcd.print("Err");
  } else {
    
    lcd.print(tempC);
    lcd.print((char)0xDF);
    lcd.print("C ");
   
  }
}


void loop () {
    

  
    DateTime now = RTC.now(); //get time from RTC
    //Display current time
    lcd.setCursor (0,0);
    lcd.print(now.day(), DEC);
    lcd.print('/');
    lcd.print(now.month());
    lcd.print('/');
    lcd.print(now.year(), DEC);
    lcd.print(" ");
    lcd.setCursor (0,1);
    lcd.print(now.hour(), DEC);
    lcd.print(':');
    if (now.minute() <10) 
      {
        lcd.print("0");
      }
    lcd.print(now.minute(), DEC);
    lcd.print(':');
    if (now.second() <10) 
      {
        lcd.print("0");
      }
    lcd.print(now.second());
    lcd.print(" ");
    
    //current time in minutes since midnight (used to check against sunrise/sunset easily)
    TimeMins = (now.hour() * 60) + now.minute();
   
    
    //Get and display temp every minute
    if (now.second() == 7) {
      sensors.requestTemperatures(); // Request temperature
      delay (249);
      printTemperature(outsideThermometer); // display on lcd.
     
    }
    
    // Calculate sun times once a day at a minute past midnight
    if (TimeMins == 1) {
      lcd.clear();
      CalcSun ();
    }
    
    // Reset Override flag if the sun is up!
    if (TimeMins == Sunrise) {
      OverRideFlag = false;
    }
         
    
    if (TimerMode ==2) {
      if (TimeMins >= Sunrise && TimeMins <=Sunset-1 && tempC>=2 && OverRideFlag==false) { //If it's after sunrise and before sunset, and it's not frozen, and our override isn't set, switch our relay on
          digitalWrite (A3, LOW);
          lcd.setCursor (13,0);
          lcd.print ("On ");
        }
        else {  //otherwise switch it off
          digitalWrite (A3, HIGH);
          lcd.setCursor (13,0);
          lcd.print ("Off");
        }
      }
       if (TimerMode ==3) {
      if (TimeMins >= Sunrise && TimeMins <=Sunset+60 && tempC>=2 && OverRideFlag==false) { //If it's after sunrise and before sunset + 1 hour, and it's not frozen, and our override isn't set, switch our relay on
          digitalWrite (A3, LOW);
          lcd.setCursor (13,0);
          lcd.print ("On ");
        }
        else {  //otherwise switch it off
          digitalWrite (A3, HIGH);
          lcd.setCursor (13,0);
          lcd.print ("Off");
        }
      }
       if (TimerMode ==0) { // Off
         digitalWrite (A3, HIGH);
         lcd.setCursor (13,0);
         lcd.print ("Off");
       }
     
       if (TimerMode ==1 && tempC>=2) { // TimerMode is On, but it's not frozen
         digitalWrite (A3, LOW);
         lcd.setCursor (13,0);
         lcd.print ("On ");
       }
       
       buttonstate = digitalRead(A1);// check to see if our override button is pressed, if it is, change the status of the flag
       if (buttonstate == LOW ) {
         OverRideFlag = ~OverRideFlag;
       }        

    
    pushlength = pushlengthset;
    pushlength = getpushlength ();
    delay (10);
    
    if (pushlength <pushlengthset) {
      lcd.clear ();
      ShortPush ();   
      lcd.clear ();
    }
    
       
       //This runs the setclock routine if the knob is pushed for a long time
       if (pushlength >pushlengthset) {
         lcd.clear();
         DateTime now = RTC.now();
         setyeartemp=now.year(),DEC;
         setmonthtemp=now.month(),DEC;
         setdaytemp=now.day(),DEC;
         sethourstemp=now.hour(),DEC;
         setminstemp=now.minute(),DEC;
         setclock();
         pushlength = pushlengthset;
       };
}

//sets the clock
void setclock (){
   setyear ();
   lcd.clear ();
   setmonth ();
   lcd.clear ();
   setday ();
   lcd.clear ();
   sethours ();
   lcd.clear ();
   setmins ();
   lcd.clear();
   RTC.adjust(DateTime(setyeartemp,setmonthtemp,setdaytemp,sethourstemp,setminstemp,setsecs)); //set the DS1307 RTC
   CalcSun (); //refresh the sunrise and sunset times
   delay (1000);
   
}

// subroutine to return the length of the button push.
int getpushlength () {
  buttonstate = digitalRead(A0);  
       if(buttonstate == LOW && buttonflag==false) {     
              pushstart = millis();
              buttonflag = true;
          };
          
       if (buttonstate == HIGH && buttonflag==true) {
         pushstop = millis ();
         pushlength = pushstop - pushstart;
         buttonflag = false;
       };
       return pushlength;
}
// The following subroutines set the individual clock parameters
int setyear () {
   lcd.setCursor (0,0);
    lcd.print ("Set Year");
    pushlength = pushlengthset;
    pushlength = getpushlength ();
    if (pushlength != pushlengthset) {
      return setyeartemp;
    }

    lcd.setCursor (0,1);
    knob.write(0);
    delay (50);
    knobval=knob.read();
    if (knobval < -1) { //bit of software de-bounce
      knobval = -1;
    }
    if (knobval > 1) {
      knobval = 1;
    }
    setyeartemp=setyeartemp + knobval;
    if (setyeartemp < 2014) { //Year can't be older than currently, it's not a time machine.
      setyeartemp = 2014;
    }
    lcd.print (setyeartemp);
    lcd.print("  "); 
    setyear();
}
  
int setmonth () {

   lcd.setCursor (0,0);
    lcd.print ("Set Month");
    pushlength = pushlengthset;
    pushlength = getpushlength ();
    if (pushlength != pushlengthset) {
      return setmonthtemp;
    }

    lcd.setCursor (0,1);
    knob.write(0);
    delay (50);
    knobval=knob.read();
    if (knobval < -1) {
      knobval = -1;
    }
    if (knobval > 1) {
      knobval = 1;
    }
    setmonthtemp=setmonthtemp + knobval;
    if (setmonthtemp < 1) {// month must be between 1 and 12
      setmonthtemp = 1;
    }
    if (setmonthtemp > 12) {
      setmonthtemp=12;
    }
    lcd.print (setmonthtemp);
    lcd.print("  "); 
    setmonth();
}

int setday () {
  if (setmonthtemp == 4 || setmonthtemp == 5 || setmonthtemp == 9 || setmonthtemp == 11) { //30 days hath September, April June and November
    maxday = 30;
  }
  else {
  maxday = 31; //... all the others have 31
  }
  if (setmonthtemp ==2 && setyeartemp % 4 ==0) { //... Except February alone, and that has 28 days clear, and 29 in a leap year.
    maxday = 29;
  }
  if (setmonthtemp ==2 && setyeartemp % 4 !=0) {
    maxday = 28;
  }
  
   lcd.setCursor (0,0);
    lcd.print ("Set Day");
    pushlength = pushlengthset;
    pushlength = getpushlength ();
    if (pushlength != pushlengthset) {
      return setdaytemp;
    }

    lcd.setCursor (0,1);
    knob.write(0);
    delay (50);
    knobval=knob.read();
    if (knobval < -1) {
      knobval = -1;
    }
    if (knobval > 1) {
      knobval = 1;
    }
    setdaytemp=setdaytemp+ knobval;
    if (setdaytemp < 1) {
      setdaytemp = 1;
    }
    if (setdaytemp > maxday) {
      setdaytemp = maxday;
    }
    lcd.print (setdaytemp);
    lcd.print("  "); 
    setday();
}

int sethours () {
    lcd.setCursor (0,0);
    lcd.print ("Set Hours");
    pushlength = pushlengthset;
    pushlength = getpushlength ();
    if (pushlength != pushlengthset) {
      return sethourstemp;
    }

    lcd.setCursor (0,1);
    knob.write(0);
    delay (50);
    knobval=knob.read();
    if (knobval < -1) {
      knobval = -1;
    }
    if (knobval > 1) {
      knobval = 1;
    }
    sethourstemp=sethourstemp + knobval;
    if (sethourstemp < 1) {
      sethourstemp = 1;
    }
    if (sethourstemp > 23) {
      sethourstemp=23;
    }
    lcd.print (sethourstemp);
    lcd.print("  "); 
    sethours();
}

int setmins () {

   lcd.setCursor (0,0);
    lcd.print ("Set Mins");
    pushlength = pushlengthset;
    pushlength = getpushlength ();
    if (pushlength != pushlengthset) {
      return setminstemp;
    }

    lcd.setCursor (0,1);
    knob.write(0);
    delay (50);
    knobval=knob.read();
    if (knobval < -1) {
      knobval = -1;
    }
    if (knobval > 1) {
      knobval = 1;
    }
    setminstemp=setminstemp + knobval;
    if (setminstemp < 0) {
      setminstemp = 0;
    }
    if (setminstemp > 59) {
      setminstemp=59;
    }
    lcd.print (setminstemp);
    lcd.print("  "); 
    setmins();
}

int setmode () { //Sets the mode of the timer. Auto, On or Off

    lcd.setCursor (0,0);
    lcd.print ("Set Mode");
    pushlength = pushlengthset;
    pushlength = getpushlength ();
    if (pushlength != pushlengthset) {
      EEPROM.update (0,TimerMode); //write the mode setting to EEPROM
      return TimerMode;
    }

    lcd.setCursor (0,1);
    knob.write(0);
    delay (50);
    knobval=knob.read();
    if (knobval < -1) {
      knobval = -1;
    }
    if (knobval > 1) {
      knobval = 1;
    }
    TimerMode=TimerMode + knobval;
    if (TimerMode < 0) {
      TimerMode = 0;
    }
    if (TimerMode > 3) {
      TimerMode=3;
    }
    if (TimerMode == 0) {
    lcd.print("Off        ");
    lcd.print("  "); 
    }
    if (TimerMode == 1) {
    lcd.print("On         ");
    lcd.print(" "); 
    }
    if (TimerMode == 2) {
    lcd.print("Auto       ");
    lcd.print("  "); 
    }
    if (TimerMode == 3) {
    lcd.print("Auto Extend");
    lcd.print("  "); 
    }
    setmode ();
}

int CalcSun () { //Calculates the Sunrise and Sunset times
    DateTime now = RTC.now();
    sunTime[3] = now.day(); // Give Timelord the current date
    sunTime[4] = now.month();
    sunTime[5] = now.year();
    myLord.SunRise(sunTime); // Computes Sun Rise.
    Sunrise = sunTime[2] * 60 + sunTime[1]; // Sunrise returned in minutes past midnight
    SunriseHour = sunTime[2];
    SunriseMin = sunTime [1];
    sunTime[3] = now.day(); // Uses the Time library to give Timelord the current date
    sunTime[4] = now.month();
    sunTime[5] = now.year();
    myLord.SunSet(sunTime); // Computes Sun Set.
    Sunset = sunTime[2] * 60 + sunTime[1]; // Sunset returned in minutes past midnight
    SunsetHour = sunTime[2];
    SunsetMin = sunTime [1];
}

void ShortPush () {
  //This displays the calculated sunrise and sunset times when the knob is pushed for a short time.
for (long Counter = 0; Counter < 604 ; Counter ++) { //returns to the main loop if it's been run 604 times 
                                                     //(don't ask me why I've set 604,it seemed like a good number)
  lcd.setCursor (0,0);
  lcd.print ("Sunrise ");
  lcd.print (SunriseHour);
  lcd.print (":");
  if (SunriseMin <10) 
     {
     lcd.print("0");
     }
  lcd.print (SunriseMin);
  lcd.setCursor (0,1);
  lcd.print ("Sunset ");
  lcd.print (SunsetHour);
  lcd.print (":"); 
    if (SunsetMin <10) 
     {
     lcd.print("0");
     }
  lcd.print (SunsetMin);        

    
  //If the knob is pushed again, enter the mode set menu
  pushlength = pushlengthset;
  pushlength = getpushlength ();
  if (pushlength != pushlengthset) {
    lcd.clear ();
    TimerMode = setmode ();

  }
  
}

}
/*
 * Copyright (c) 2015 Andrew Doswell
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHOR(S) OR COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */


Hope this helps! Have fun and check back soon!

PS. Some users are having trouble with contact bounce... Add C1/C2, R5 & R6...