Translate

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...


Sunday 23 August 2015

Soundlab CPA 100 Mobile power amp, and surprising output matching.

Now, I needed a mobile amp, 100 watt-ish for a project. Didn't I have something in the loft? Yep, a cheap-and-cheerful Soundlab CPA 100. 75 watts allegedly. That'll do. It had a label on it. "Been reverse poled" ... ugh. Let's have a look.

I wasn't holding out much hope really, the fuse was intact. but I was expecting a rake of burnt out resistors, maybe some PCB tracks blown open...

I was, however presently surprised, just a handful of caps had popped their bases, as a result of being effectively connected backwards!   

















Anyway, a few cursory checks, and on with the power. Great. It works. Left it running for a few mins, driving a few watts into a dummy load, when I started thinking... why two transformers?


Now normally an automotive power amp needs a few more volts to drive a larger amount of power into a 4 ohm load... Ohms law applies here again, but we're dealing with a sine wave (for RMS watts)

For example, a "proper" class A only amp will swing 6 volts into our 4 ohm load ( a little less in reality, due to the losses in the transistor), so ohms law :

Power = ((Epeak/1.414) x 2) / R where E-peak is the maximum voltage of the sine wave (it will be less than the supply in reality due to transistor losses)
= (6/1.414) x 2 / 4
=4.2 watts

A push pull amp , will swing our full 12 volts into our load, giving ~18 watts. 

There are other amp topologies, such as Class H "Rail switchers" which will give more, but the limit is usually around 50 watts.

Now normally, our big automotive power amp has a step-up switched mode supply, which generates a nice +/- supply of , say 40 volts, so it can swing 80 volts into our load... mucho power ! As long as the supply is up to it! You don't get something for nothing, so the current consumed at 12 volts from the battery would rise proportionally. If the amp with +/- 40 volt supply actually exists, it will generate about 200 watts in a 4 ohm load, and will need 200 watts (plus a bit for losses in the power supply) from our 12 volt source (That's 16 2/3 amps) In Bridge-tied load, we're looking at more like 750 watts.

So, back to our little Soundlab. Are the transformers a power supply? Nope. So how does it generate more power than it's push pull design allows for?

Those transformers are output transformers. They lower the impedance the output stage sees from the speaker. So , to swing enough current from a 12 volt supply into our load, the load needs to be small.

Let's work it out...

Power = (Epeak/1.414)x2  / Resistance , so 
Resistance = (Epeak/1.414) x 2/Power
= (12/1.414)x2/75 
= 0.226 Ohms.

So our two output transformers change our 4 ohm load to a 0.226 ohm load, which allows more current to be drawn from the output stage, and more voltage to swing into the load.

There's some quite heavy negative feedback from the speaker side to tame an otherwise cheap amplifier into giving reasonable performance, and it doesn't make 75 watts RMS, but more like 75 watts when bridged, so will probably give me sufficient power for my needs. The plus side to having a transformer coupled output is if the output transistors should fail, there will never be DC on the speaker!

Radford STA-15 repairs & renovations. A quick note on biassing.

Telephone rang, a lovely chap called John. 

"I've got a Radford, one of the valves has gone, can you take a look?"

Of course. The trouble is, John lives in Scotland, and posting anything with valves in always worries me, but never fear, UPS did a stunning job and all arrived safely.

It's a rather lovely STA-15, made in 1962.


Ah yes, the disclaimer.....

WARNING. Do NOT try this at home. This amplifier calls for some high HT voltages. The main HT rail is some 390 VDC. It will floor you or kill you if you come into contact with it. It will not warn you, blow a fuse, nor pull your safety trip. It may not give you a second chance. RESPECT IT'S AUTHORITY

There are exposed 240VAC mains connections inside the enclosure. 

I will not, under any circumstances, accept any liability if you decide to repair an amplifier for yourself.

Having got that out of the way....


Fairly obvious straight away which valve had failed. Apparently, it had glowed brightly and the fizzed. It's gone down to air. It was one of a pair of Mullard EL34's. Pity, but it's twin read a bit sad on the Avo tester, so was probably due a change anyway. Interestingly enough, the other pair, which read low, but not within the realms of working OK, were Zaerix valves. I never really understand people mixing valves brands between channels, I mean we want the thing to be balanced really. Nothing for it but a new set of valves. A phone call to the lovely people at hotroxuk.com saw a new matched set of four Russian made Tung-Sol on the way. (No connection with Hot Rox, other than a delighted customer)





So, why had the Mullard expired? Switching on with the valves removed, showed the offending valve had a positive voltage on it's grid, with respect to the cathode. Not good. That's equivalent to you holding the throttle flat down of your car for hours... The valve's anode current would have risen, and the anode itself probably glowed bright red trying to dissipate all that heat, followed by the glass envelope failing due to the heat. Sadly, it's an all too common a fault. The capacitor which couples the audio to the grid, after the phase splitter had broken down, and was passing DC. Nothing for it , but a re-cap.

The Radford has a lovely symmetrical design.
In this picture I've nearly finished re-capping the left hand channel...








... and there's both sides done. Caps are Vishay and Rifa, they're reliable and are well thought of in Audiophile circles.








I checked the other valves for faults, and emission, and all was found to be in good order. I also did some checks on the values of resistors, especially anode, cathode and screen. Everything was good, and always better than 5% tolerance. Nice!

Now to check the biassing....

There's a lot of BS out there about biassing, some of it comes from manufacturers and importers, some of it folklore, most of it wrong or downright stupid. We need to bias out output stage to get our anode current to less than the maximum plate dissipation of our valve. So we have an EL34. It's maximum plate dissipation is 20 watts. Anything more and we're in trouble, and run the risk of premature failure, the anode glowing red etc etc. Nasty. Don't do it.

So we need to know two things. The current flowing into the anode, and the voltage between the anode and the cathode (not, as some suggest between the anode and ground). That's all the valve "sees" and is all we need to do our calculations.

To make this easy, please welcome the Doz "Bias-o-rama" ... It's an octal socket and an octal plug. All the pins are connected together, except the anode and cathode, which are connected by 1 ohm resistors. There's some flying leads across these resistors. Now measuring the voltage across the resistors, will give us the current flowing in mA. Great.
So, first off, install the bias-o-rama, and valves into the amp. Plug in speakers.


Right, switch on and wait for the valves to warm. It's worth keeping an eye on things here, just in case the bias is miles out, or there's a fault, and our valve starts to red-plate. If it does, switch it off quickly.

Now that everything's ok, measure the voltage between the anode and cathode. In this case a very civilised 390 V. Now measure the voltage across the anode resistor on the "Bias-o-rama"... That gives us the current in mA flowing though the Anode. As these valves are from a matched set, both currents should be within a couple of mA of each other....








Yep , they're good (Thanks HotRox!)


So we have 390v and about 50 mA flowing. Simple ohm's law tells us power = voltage x current, so
390 x 0.050 = 19.5 watts. Less than our maximum allowable 20 watts. (I will point out, this was taken after I'd adjusted the bias, which on the Radford involves changing resistors in the grid feed circuit, there's no pot or anything helpful like that)

Biassing too cool (not enough current flowing) will show up as cross-over distortion.

Some biassing voodoo calls for measurement of the cathode current. I'm not sure why, as this also includes any current flowing into the screen too. Less accurate, but probably near enough. I suppose the cathode is always at a lower voltage, so it's safer.

OK, so now we're happy with the bias, leave the amp running for 15 mins and check again. Still good? (It should be, although I once had a set of clear top 6550's that drifted badly, they went back!)
Right, now go to a darkened room, and play the amp loud for 15 mins. Look carefully at the large grey anode. If there's any signs of it glowing red, or little spots of glowing, it's still biassed too hot. Go and do it again!

So there it is, on test in the living room, much to the wife's delight. Playing the excellent CD "I will be a pilgrim" by Arch Garrison (http://www.archgarrison.co.uk/) , a blend of acoustic guitar and philcordia , some nice folky bits with a psychadelic air to it.



A post script...


My apologies for the poor focus, the infra red from the tubes stops the auto-focus working on the camera (it works by infra-red!), and this was a long exposure. I hope you can see the blue glow around the holes in the anode. This is caused by electrons hitting the glass and causing it to fluoresce. This is normal. If a tube gets "gassy" a similar blue glow can be seen, but very much more pronounced and time for a new valve!