Sunday, March 18, 2012

Power Led drivers

R2 determines the current.
Calculations:
- LED current is approximately equal to: 0.5 / R2
- R3 power: the power dissipated by the resistor is approximately: 0.25 / R2.
choose a resistor value at least 2x the power calculated so the resistor does not get burning hot. so for 700mA LED current: R2 = 0.5 / 0.7 = 0.71 ohms. closest standard resistor is 0.75 ohms. R2 power = 0.25 / 0.71 = 0.35 watts. we'll need at least a 1/2 watt rated resistor.
led-driver3

The design of this LED driver is very simple and probably can be found in many variations on the interenet: T2 operates in linear mode and acts as a variable resistor that controls the current through the LED’s. R1 pulls-up the gate of T2 so that T2 starts up in its conductive state.
As the current begins to flow through the LED’s, T2, and R2, the voltage drop across R2 increases until it is high enough to switch on T1, and in so doing starts to pull the gate of T2 to ground.
This causes T2 to increase its resistance, which decreases current flow through itself, the LED’s, and R2, which decreases the voltage drop across R2, which causes T1 to let T2's gate float back toward positive supply, thus increasing the current flow. Within a few milliseconds, the current flow stabilizes around a specific set point determined by the value of R2. The required supply voltage overhead is only about 0,6-1.2 volts, less than half of what for instance an LM317 regulator circuit requires.

As the value of R2 is based on the transition emitter-base voltage of T1, calculating it is easy. Most conventional silicon general-purpose NPN transistors switch fully at around 0.7VDC and start to transition from off to on at 0.56-0.58VDC. Assuming a transition voltage of 0.58VDC and a target load current of 320mA, the math is as follows (Ohms law):

R = V / I
R = 0.58VDC / 0.320A
R = 1.8 Ohms

Calculating the power dissipation for R2 under normal operation is done by Watt's Law:

P = V * I
P = 0.58VDC * 0.320A
P = 0.185 Watt

In this case a quarter-watt resistor will work but a one-watt would have a safety margin of double. Using a design with an LM317 would lead to a much greater loss. 

Choosing the input voltage is simple. In the example above I have used 3 LED’s, so you would have to total the forward voltage of those and add about 1.2 Volts:

For example:
Supply voltage for red LEDs=3*2.5V+1.2V=8.7V
Supply voltage for blue LEDs=3*3.8V+1.2V=12,6V

Perhaps you could even get away with an excess voltage of 0.6 instead of 1.2 Volts

The circuit has a disadvantage: It tries to push as much current through the load as it can as long as the voltage across R2 doesn't exceed what starts to turn T1 on. So, for increased fault tolerance, the power rating for R2 could be selected based on the worst-case scenario of the maximum amount of available current flow in the event of a failure: a short across the LED’s as well as T2. For that, simply calculate dropping the supply power (voltage times current) across R1. It's generally cheaper to simply use a fuse rated for just above the desired current set point, though, as a power supply providing 1A @ 12VDC would require a 15-watt resistor for R2 and this would be far more costly than a simple fuse.

Dissipation through the MOSFET under operating conditions is calculated the same way, only the voltage used in the calculation should be the supply voltage so that the heatsink selection will account for the worst-case scenario of a dead-shorted load at the target current. In the above example, assuming a 12VDC supply and 320mA current limit, the math works out as follows:

P = V * I
P = 12VDC * 0.750A
P = 3.84 watts

The IRF520 can handle 9.2A at 100VDC, and can dissipate 60 watts, so only a modest heatsink might be necessary. 
To calculate dissipation through T2 with an accidentally shorted load, simply calculate the maximum supply power that T2 will see. If the supply is unregulated and capable of large current flows in short time spans (e.g., batteries), again a fuse would be wise as a last-ditch protective measure against catastrophic failures by blowing the circuit open so it cannot try to short the supply. If the set current is 320mA, a 500mA fuse in series with this circuit would be a wise, and relatively inexpensive, addition - all the more so for portable projects using lithium batteries, given how these like to catch fire (and for nonrechargeable lithium primaries, explode) when shorted.
The only limiting factors on load current and supply voltage relate to the components - using a suitable resistor for R2, this circuit can easily regulate up to a few amps of current in as little as one square inch of board space. R1 is not too critical: 1Megaohm will work and I also have seen 10k for R1 and 33Ohm for R2 driving 3 LED’s at a 12 V supply. A good way to calculate the value of R1 is: R1= (Vcc - 1.4)/(I/20)

If you need precision, the easiest way to get it is to build a test circuit with a fixed supply (12VDC is a great value, but use the desired or intended supply voltage if there's a need to be precise), a dead short for a load, and a 100 ohm resistor for R2. This will set the current limit to about 6mA. Once the circuit is powered up, measure the drop across R2 to determine the exact transition voltage for T2, and calculate a specific value for R2 for your current requirements based on the math described earlier. 

led-driver4PWM
The extra parts for PWM support include T3, R3. Since T3 is a PNP transistor and R4 biases its base to ground, T3 starts up turned on and pulls T1's gate to ground regardless of what T2 is doing, and this forces both T1 and T2 to turn off. R3 limits current draw to the PWM signal source. Provide a positive voltage greater than ~0.7VDC to T3 through R3, and T3 turns off, which allows the rest of the regulator to function as described above. In this manner a PWM signal can be used to vary the brightness of a load of power LEDs, or a simple on-off switch effect can be implemented by merely pulling T3's base to positive supply with a few milliamperes of current. In this manner it would be possible to use the circuit as a self-current-limiting switch that only needs a tiny little low-current button as its actuator even though the load could be an amp or more. 

For LEDs, this circuit is  very simple and wastes very little power if the source and load voltages are kept close to each other. Ideally this circuit works most efficiently with the supply being right at 1.5VDC above the ideal load voltage at the set current. The power source should be able to supply the required amount of current.

If the circuit is used for other loads than LED’s there will be some ripple depending on the type of load being powered, especially during power-up, but decoupling capacitors could be used to compensate for this. The circuit can be driven directly from any logic circuit (even at 3.3VDC).

The circuit can drive resistive, inductive, capacitive, or mixed loads. Capacitive loads may well experience slower charge times, as the circuit will clamp the charge current. Inductive loads should be paralleled with a reverse-polarity diode as is common with e.g. relais (and in some cases, also a proper snubber capacitor) to suppress any back-EMF from the load when power is disconnected so that the voltage spike won't damage the MOSFET. (Most power MOSFETs include integrated protection diodes, but this should never be relied upon as the sole protection mechanism.) 

A simple LED driver with a 2N3055
led-driver5
Even simpler
If you do not have a MOSFET, but you happen to maybe still have an old trusty 2N3055 (or similar like the TIP series) workhorse laying around, the following circuit is for you:

The current is determined by R2 and is:
I =0.8/R2

The maximum value of I in this driver is 10A. .
DC-power supply 9V-15V DC 

R2 for 1W LED -2.7ohms 1W
R2 for 3W LED -1.5-ohms 1W
R2 for 5W LED -0.6-ohms or 2 x 1.2-ohms/1W in parallel

Tuesday, February 28, 2012

Timer interrupts

Does your program seem like it’s trying to do too much at once? Are you using a lot of delay() or while() loops that are holding other things up? If so, your project is a good candidate to use timers. In this tutorial, we’ll discuss AVR and Arduino timers and how to use them to write better code.
In our prior article, we covered interrupt basics and how to use external interrupts that are triggered by a pin change or similar event. Check it out if you’re looking to brush up on interrupts in general.
This chapter moves on to timer interrupts and talks about their applications in Arduino projects or custom AVR circuits. Almost all Arduino boards are powered by AVR 8-bit processors, so to experience the full power of timers you’ll use the same techniques no matter which platform you’re on. Here’s the tutorial’s table of contents:

What is a timer?

You’re probably familiar with the general concept of a timer: something used to measure a given time interval. In microcontrollers, the idea is the same. You can set a timer to trigger an interrupt at a certain point in the future. When that point arrives, you can use the interrupt as an alert, run different code, or change a pin output. Think of it as an alarm clock for your processor.
The beauty of timers is that just like external interrupts, they run asynchronously, or independently from your main program. Rather than running a loop or repeatedly calling millis(), you can let a timer do that work for you while your code does other things.
For example, say you’re building a security robot. As it roams the halls, you want it to blink an LED every two seconds to let potential intruders know they’ll be vaporized if they make a wrong move. Using normal code techniques, you’d have to set a variable with the next time the LED should blink, then check constantly to see if that time had arrived. With a timer interrupt, you can set up the interrupt, then turn on the timer. Your LED will blink perfectly on cue, even while your main program executes its complicated terminateVillian() routine.

How do timers work?

Timers work by incrementing a counter variable, also known as a counter register. The counter register can count to a certain value, depending on its size. The timer increments this counter one step at a time until it reaches its maximum value, at which point the counter overflows, and resets back to zero. The timer normally sets a flag bit to let you know an overflow has occurred. You can check this flag manually, or you can also have the timer trigger an interrupt as soon as the flag is set. Like any other interrupt, you can specify an Interrupt Service Routine (ISR) to run code of your choice when the timer overflows. The ISR will reset the overflow flag behind the scenes, so using interrupts is usually your best option for simplicity and speed.
In order to increment the counter value at regular intervals, the timer must have access to a clock source.  The clock source generates a consistent repeating signal.  Every time the timer detects this signal, it increases its counter by one.
Because timers are dependent on the clock source, the smallest measurable unit of time will be the period of this clock.  For example, if we provide a 1 MHz clock signal to a timer, we can calculate our timer resolution (or timer period) as follows:
T = timer period, f = clock frequency

T = 1 / f
T = 1 / 1 MHz = 1 / 10^6 Hz
T = (1 * 10^-6) s


Our timer resolution is one millionth of a second. You can see how even relatively slow processors can break time into very small chunks using this method.


You can also supply an external clock source for use with timers, but in most cases the chip’s internal clock is used as the clock source. This means that your minimum timer resolution will be based on your processor speed (either 8 or 16 MHz for most 8-bit AVRs).


Types of timers

If you’re using any of the standard Arduino variants or an 8-bit AVR chip, you have several timers at your disposal. In this tutorial, we’ll assume you’re using a board powered by the AVR ATmega168 or ATmega328. This includes the Arduino Uno, Duemilanove, Mini, any of Sparkfun’s Pro series, and many similar designs. You can use the same techniques on other AVR processors like those in the Arduino Mega or Mega 2560, you’ll just have to adjust your pinout and check the datasheet for any differences in the details.


The ATmega168 and ATmega328 have three timers: Timer0, Timer1, and Timer2. They also have a watchdog timer, which can be used as a safeguard or a software reset mechanism. However, we don’t recommend messing with the watchdog timer until you get comfortable with the basics. Here are a few details about each timer:
Timer0
Timer0 is an 8-bit timer, meaning its counter register can record a maximum value of 255 (the same as an unsigned 8-bit byte). Timer0 is used by native Arduino timing functions such as delay() and millis(), so you Arduino users shouldn’t mess with it unless you’re comfortable with the consequences.
Timer1
Timer1 is a 16-bit timer, with a maximum counter value of 65535 (an unsigned 16-bit integer). The Arduino Servo library uses this timer, so be aware if you use it in your projects.
Timer2
Timer2 is an 8-bit timer that is very similar to Timer0. It is utilized by the Arduino tone() function.
Timer3, TIMER4, TIMER5

The AVR ATmega1280 and ATmega2560 (found in the Arduino Mega variants) have an additional three timers.  These are all 16-bit timers, and function similarly to Timer1.


Configuring and running the timer

In order to use these timers, we need to set them up, then make them start running.  To do this, we’ll use built-in registers on the AVR chip that store timer settings.  Each timer has a number of registers that do various things.  Two of these registers hold setup values, and are called TCCRxA and TCCRxB, where x is the timer number (TCCR1A and TCCR1B, etc.).  TCCR stands for Timer/Counter Control Register.  Each register holds 8 bits, and each bit stores a configuration value.  Here are the details, taken from the ATmega328 datasheet:

timerregisters

To start using our timer, the most important settings are the last three bits in TCCR1B, CS12, CS11, and CS10.  These dictate the timer clock setting.  By setting these bits in various combinations, we can tell the timer to run at different speeds.  Here’s the relevant table from the datasheet:

Clock Select bit description
CS12 CS11 CS10 Description
0 0 0 No clock source (Timer/Counter stopped)
0 0 1 clki/o/1 (No prescaling)
0 1 0 clki/o/8 (From Prescaler)
0 1 1 clki/o/64 (From Prescaler)
1 0 0 clki/o/256 (From Prescaler)
1 0 1 clki/o/1024 (From Prescaler)
1 1 0 External clock source on T1 pin. Clock on falling edge
1 1 1 External clock source on T1 pin. Clock on rising edge

By default, these bits are set to zero.  Let’s use a simple example, and say that we want to have Timer1 run at clock speed, with one count per clock cycle.  When it overflows, we’ll run an Interrupt Service Routine (ISR) that toggles a LED tied to pin 2 on or off. We’ll write Arduino code for this example, though we’ll use avr-libc routines wherever they don’t make things overly complicated. AVR pros can adapt as they see fit.

First, we initialize the timer:

// avr-libc library includes

#include <avr/io.h>

#include <avr/interrupt.h>

#define LEDPIN 2

void setup()

{

pinMode(LEDPIN, OUTPUT);

// initialize Timer1

cli();             // disable global interrupts

TCCR1A = 0;        // set entire TCCR1A register to 0

TCCR1B = 0;

// enable Timer1 overflow interrupt:

TIMSK1 = (1 << TOIE1);

// Set CS10 bit so timer runs at clock speed:

TCCR1B |= (1 << CS10);

// enable global interrupts:

sei();

}

You’ll notice that we used a new register, TIMSK1. This is the Timer/Counter1 Interrupt Mask Register. It controls which interrupts the timer can trigger. Setting the TOIE1 bit tells the timer to trigger an interrupt when the timer overflows. We can also set other bits to trigger other interrupts. More on that later.

Once we set the CS10 bit, the timer is running, and since we’ve enabled an overflow interrupt, it will call the ISR(TIMER1_OVF_vect) whenever the timer overflows.

Next, we can define the ISR:

ISR(TIMER1_OVF_vect)

{

digitalWrite(LEDPIN, !digitalRead(LEDPIN));

}

Now we’re free to define our loop() and our LED will toggle on and off regardless of what’s happening in the main program. To turn the timer off, we can set TCCR1B = 0 at any time.

However, let’s think about how this will work. Using the code we’ve written, how fast will our LED blink?

We’ve set Timer1 to interrupt on an overflow, and let’s assume we’re using an ATmega328 with a 16MHz clock. Since Timer1 is 16 bits, it can hold a maximum value of (2^16 – 1), or 65535. At 16MHz, we’ll go through one clock cycle every 1/(16*10^6) seconds, or 6.25e-8 s. That means 65535 timer counts will elapse in (65535 * 6.25e-8s) and our ISR will trigger in, oh… about 0.0041 seconds. Then again and again, every four thousandths of a second after that. Oops. At this rate, we probably won’t even be able to detect blinking. If anything, we’ve created an extremely fast PWM signal for the LED that’s running at a 50% duty cycle, so it may appear to be constantly on but dimmer than normal. An experiment like this shows the amazing power of microprocessors – even an inexpensive 8-bit chip can process information far faster than we can detect.


Timer prescaling and CTC

Luckily, the good engineers at Atmel thought of this problem, and included some options. It turns out you can also set the timer to use a prescaler, which allows you to divide your clock signal by various powers of two, thereby increasing your timer period.  For example, let’s say we’d rather have our LED blink at one second intervals. Going back to the TCCR1B register, we can use the three CS bits to set a better timer resolution.  If we set CS10 and CS12 using TCCR1B |= (1 << CS10); and TCCR1B |= (1 << CS12);, we divide our clock source by 1024. This gives us a timer resolution of 1/(16*10^6 / 1024), or 6.4e-5 seconds. Now the timer will overflow every (65535 * 6.4e-5s), or 4.194s. Hm, too long. What can we do?

It turns out there’s another mode of operation for AVR timers. This mode is called Clear Timer on Compare Match, or CTC. Instead of counting until an overflow occurs, the timer compares its count to a value that was previously stored in a register. When the count matches that value, the timer can either set a flag or trigger an interrupt, just like the overflow case.

To use CTC, let’s start by figuring out how many counts we need to get to our one second interval. Assuming we keep the 1024 prescaler as before, we’ll calculate as follows:

(target time) = (timer resolution) * (# timer counts + 1)

and rearrange to get

(# timer counts + 1) = (target time) / (timer resolution)
(# timer counts + 1) = (1 s) / (6.4e-5 s)
(# timer counts + 1) = 15625
(# timer counts) = 15625 - 1 = 15624

Why did we add the extra +1 to our number of timer counts? In CTC mode, when the timer matches our desired count it will reset itself to zero. This takes one clock cycle to perform, so we need to factor that into our calculations. In many cases, one timer tick isn’t a huge deal, but if you have a time-critical application it can make all the difference in the world.

Now we can rewrite our setup() function to configure the timer for these settings:

void setup()

{

pinMode(LEDPIN, OUTPUT);

// initialize Timer1

cli();          // disable global interrupts

TCCR1A = 0;     // set entire TCCR1A register to 0

TCCR1B = 0;     // same for TCCR1B

// set compare match register to desired timer count:

OCR1A = 15624;

// turn on CTC mode:

TCCR1B |= (1 << WGM12);

// Set CS10 and CS12 bits for 1024 prescaler:

TCCR1B |= (1 << CS10);

TCCR1B |= (1 << CS12);

// enable timer compare interrupt:

TIMSK1 |= (1 << OCIE1A);

sei();          // enable global interrupts

}

And we’ll need to replace our overflow ISR with a compare match version:

ISR(TIMER1_COMPA_vect)

{

digitalWrite(LEDPIN, !digitalRead(LEDPIN));

}

That’s all there is to it! Our LED will now blink on and off at precisely one second intervals. And as always, we’re free to do anything we want in loop(). As long as we don’t change the timer settings, it won’t interfere with our interrupts. With different mode and prescaler settings, there’s no limit to how you use timers.

Here’s the complete example in case you’d like to use it as a starting point for your own project. Double click to copy:

// Arduino timer CTC interrupt example

// www.engblaze.com

// avr-libc library includes

#include <avr/io.h>

#include <avr/interrupt.h>

#define LEDPIN 2

void setup()

{

pinMode(LEDPIN, OUTPUT);

// initialize Timer1

cli();          // disable global interrupts

TCCR1A = 0;     // set entire TCCR1A register to 0

TCCR1B = 0;     // same for TCCR1B

// set compare match register to desired timer count:

OCR1A = 15624;

// turn on CTC mode:

TCCR1B |= (1 << WGM12);

// Set CS10 and CS12 bits for 1024 prescaler:

TCCR1B |= (1 << CS10);

TCCR1B |= (1 << CS12);

// enable timer compare interrupt:

TIMSK1 |= (1 << OCIE1A);

// enable global interrupts:

sei();

}

void loop()

{

// do some crazy stuff while my LED keeps blinking

}

ISR(TIMER1_COMPA_vect)

{

digitalWrite(LEDPIN, !digitalRead(LEDPIN));

}



Going further

Keep in mind that you can use the built-in ISRs to extend timer functionality. For example, if you wanted to read a sensor every 10 seconds, there’s no timer setup that can go this long without overflowing. However, you can use the ISR to increment a counter variable in your program once per second, then read the sensor when the variable hits 10. Using the same CTC setup as in our previous example, our ISR would look something like this:

ISR(TIMER1_COMPA_vect)

{

seconds++;

if (seconds == 10)

{

seconds = 0;

readMySensor();

}

}

Note that in order for a variable to be modified within an ISR, it must be declared as volatile. In this case, we’d need to declare volatile byte seconds; or similar at the beginning of our program.

This tutorial covers the basics of timers. As you start to understand the underlying concepts, you’ll want to check the datasheet for more information on your particular chip. Datasheets are readily available on Atmel’s website. To find them, navigate to the page for your device (8-bit AVRs found here) or do a search for your chip model. There’s a lot of information to wade through, but the documentation is surprisingly readable if you have the patience.

Friday, February 24, 2012

ATtiny for your garden

Since I have a habit of being not home but still need my water gardened I was trying to find a way to do that. Ofcpourse there are the watering clocks like the Gardena or others that can be programmed to water the garden for a certain time ach day or each week and I have used thoes but the have two drawbacks:

They will water even if it is not necessary + I always feel a bit anxious to use those if I am away for a long time, just in case something goes wrong and the mains waterline will start spraying for days on end.

Therefore I decided to turn to a microcontroller. Ofcourse ‘Arduino’pops to mind but that seemed a bit like overkill for a simple task, but it seemed like a good idea to develop a system on the Arduino an then transfer it to an ATtiny45.

The idea was to let the Arduino measure moisture in my plantbed and then depending on a preset value for moisture or dryness, switch on a pump.

As the ATtiny has several pins that could be used as an output or input, I was thinking to find a use for the other pins a swell and I found the perfect solution. As the watering function will probably only be used in the summertime, after planting, I could use the device earlier alrady for heating up a propagator. yes I know there are heated propagators but I just could not find one in my local gardenshops.

First a bit of theory:

spanningsdelerIn this figure, the voltage Va is a function of the Voltage Vcc and the two resistors R1 and R2. The Current I flowing through R2 and R1 is Vcc/(R1+R2) and therefore the voltage over R1 and thus the voltage Va=I x R1.

If we subsitute I then the formula will be (Vcc/(R1+R2))xR1= Vcc*R1/(R1+R2)=Va.

With a Vcc of 5 V and both resistors being 10 k, the voltage Va therefore will be 2.5 V.

If R2 would be replaced by a variable resistor such as an NTC or a humidity sensor, the voltage over Va will vary dpending on the value of the temperature or the humidity and that voltage Va will subsequently be read by an analog port on the Attiny.

The temperature sensor

NTCThe temperature sensor is quite easy. a 10 k NTC will do the trick. It will come in the place of R2 so with a rising temperature the voltage on Va will rise as well.

The value of an NTC (e.g. 10k)  is usually given at an ambient temperature of 25 degrees Celsius. Therefore, at 25C the voltage on Va should be 1/2 Vcc.
Initially I had the intention to calculate the temperature with the Steinhart-Hart formula but as I only needed two temperatures, one to switch on the heating and one to switch off the heating in the propagator, it made more sense to just measure the Va at these specific temperature as this also takes into account a variation in the Vcc, in the resistor R1 and in the resistance of the leads to the NTC.
I wanted to keep the temperature between 21 and 26 degrees and you will find my values for that in the program. The values that you will need might be different. Mind that the Analogue ports will give you a reading between 0 and 1023. The Arduino can map this with the map command, but it is just as useful to divide by 4. The function that does this is the function ‘sample’. It will take the average of 5 readings and then divide that (‘map it’) by four.

The humidity sensor

Build-Your-Moisture-SensorThe humidity sensor is basically not more than 2 spikes driven into the ground. The citcuit is similar to that of the temperature reader, with the spikes taking the place of the NTC. In my experience, soil that is just sufficiently humid gives a resitence (with my humidity resistor) of  about 10 k. Also here if the soil dries out, the resistence of the sensor will increase and the voltage on the analogue port will rise. A specific dryness or wetness of the soil therefore corresponds with a certain value on the analogue port.
Construction
The construction of a humidity sensor is quite simple although there seem to be various ‘schools’ of thought. Some people willimbed their ‘spikes’ in plaster of paris to give a more even reading of moisture. Most people however will juet take two metal spikes that are inserted in the earth. There are however two things to remember: the spikes need to be attached to eachother, otherwise putting them in another position may cause the distance between the two spiks to cahnage and therefore also the resistance; the spikes need to be made of galvanized material will they stand any chance of surviving being in the soil. Galvanized nails are a popular material, but they ar ehard to solder. Th best way is to dile or sand the place where one wants to solder the wire. Wrap the wire around the spike and solder and then to top it off, pit some shrink wrap around it to ensure a good contact between the wire and the spike.

Metal spikes in the soil. especially if they have a DC current going through them, may corrode very quickly. In practice that has not bothered me that much, but there are some things that one could do to prevent corrosion: only put tension to the pins when the device is actually read. In that case the humidity sensor should not be connected to +Vcc but to digital pin 0 that can be switched on right before the reading and switched off  right after it. Another thing one can do –but in our example we are a pin short- is not to connect the pin and its pull down resistor to + V and earth, but to two digital output pinds that then will feed it with an AC current bij alternating the polarity of the two digital pins and only to read it when the proper pin is high.

The program

The program is fairly simple. First it starts with some descriptions and gives the lay-out for the Attiny. It then defines the pins and sets the variables.
It then reads the humidity sensor with the ‘sample’ function and stores the result in the variable ‘moist’. It compares the value for ‘moist’ with the value that is set for ‘irrigation’. If it is lower or equal to that value, it switches on the pump by setting the ‘pumpPin’ high.
If it is a level of 5 above that value, iyt switches the pump off.
This is done to avoid the pump switching on and off repeatedly and to make the soil wet enough. Depending on the flow of water in your particular set up, you may want to alter that value of ‘5’.
It then reads the temperature, again with the ‘sample’ function and stores the result in the variable ‘ntc’. It then sees if this value is less than ‘142’ (which is 21 degrees in my case)  and if so, it switches on the heating in my propagator. It then compares the value with ‘155’(which is 26 degrees in my case) and if so, switches off the heating.
as a heater I have used a 40 W lamp that is switched via a solidstate relais.

So just to summarize: I use this device in summer outside to water my plantbeds and in  early spring I use it inside to heat a propagator. The device therefore is never controlling both temperature and irrigation at the same time, but if you would use it in a greenhouse, ofcourse it could.
Although the irrigation could be done by switching an electric valve in the main waterline, I have opted to use an immersible pump in a large bucket with water. As immersible pumps should not run dry, I have used a reed switch with a magnet on a floating device to cut the power to the pump if the water level is too low.

Protection

protect

I am not going to point out that if you are switching a pump or a lamp that you have to be careful with the mains voltage that can kill you. If you have no knowledge of these things you should not attempt to fiddle with it. But there is something else I need to point out: If the lines of the of your sensors to your ATtiny are long, they may pick up spikes that could be too high for the ports on your microcontroller and you may want to consider a bit of protection. This can be obtained by adding a 1 k resistor and a 5v1 zener as is shown in the picture. Obviously if you use that you need to recalibrate the values that are read from the sensor. 

/* Garden  
February 2012 

Sensors:   
-NTC  aan analoog  pin1 
-Humidity spike aan analoog pin2   
*---------------------------------NTC-------------------------------
* Schematic:
*   [Ground] -- [10k-pad-resistor] -- | -- [NTC] --[Vcc] 

*                                     | 
*                                Analog Pin 3 
* 
*    Rntc=((Vcc * pad / Vpin1) - pad); 
*   =((1024 * pad / RawADC) - pad); 
*  
*-------------------------------Humid------------------------------ 

* Schematic: 
*   [Ground] -- [10k-pad-resistor] -- | -- [Spikes] --[Vcc] 
*                                     | 
*                                Analog Pin 2 
*  
Als de humidity sensor te droog wordt, stijgt de weerstand en
daarmee daalt de spanning op de analoge poort. Inschakelen relais
om te sproeien De sensor is ca 10 kOhm als de grond vochtig genoeg
is. De reading van de analoge poort moet dus op ca 512 komen (met 
een pull down weerstand van 10 k) 
* Als de temperatuur daalt, stijgt de weerstand van de NTC en 
daarmee daalt de spanning op de analoge poort ->Inschakelen relais 
om te verwarmen de weerstand moet zich tussen 12,5 en 10 kOhm 
bewegen (ca 21-23 graden) de reading van de analoge poort moet dus 
tussen de 512 en 455 gehouden worden. Dwz inschakelen relais bij 
455 en uitschakelen bij 512  (als niet wordt gemapt)

ATTiny pins   
Physical  Analoog  Digital     
1           0          5   Reset, PinChange Interrupt 
2           3          3   INT0,3 
3           2          4   INT0,4 
4           Ground 
5                      0   INT0,0 
6                      1   INT0,1 
7           1          2   INT0,2 
8           Vcc 
*/ 
//Pin definities 
int ntcPin= 3;   //analogue Pin3  ->Physical pin2  NTC 
int humidPin=2;   //analogue Pin2  ->Physical pin3  Humidity spike
int pumpPin =2;  //digital  Pin2  ->Physical pin7  Bevloeiingspomp 
int warmPin=1;   //digital  Pin1  ->Physical Pin6  Verwarming 
int signalPin=0;  //Digital  Pin0  ->Physical Pin5  Extra 

// Variabelen setting 
int moist=0;      // maybe float is not necessary  
int ntc=0;       // maybe float is not necessary 
int ldr=0;        // maybe float is not necessary   
int irrigate=512; // level for irrigation ca 126 after mapping 

//The following function, "setup", must always be present 
void setup()  
{
//Serial.begin(9600);// for debugging only, remove for Attiny 
pinMode(ntcPin,INPUT);// meet temperatuur 
pinMode(humidPin,INPUT);// meet vochtigheid 
pinMode(signalPin,OUTPUT); //Signaallamp oid 
pinMode(warmPin,OUTPUT);//  Output for heating element 
pinMode(pumpPin,OUTPUT);//  Output for Relais/waterpump  
digitalWrite(pumpPin, LOW); 
digitalWrite(warmPin, LOW);
}

void loop() //This function must always be present 
{ 
/* First we read the Humidity sensor. If the sensor is >10 k this
is considered dry 
As the sensor is in a voltage divider of 10k the value read
then will be 1/2 Vcc 
In analogue value this will be about 1/2 *1024=512 
and after mapping it will be 127 

So if the value read ('moist') is smaller than what is
considered dry ('irrigate'), the pump should be switched on 
for a specific time. 
This is done by indicating a higher treshhold for switching 
the pump off 

*/

moist=sample(humidPin);  //read humiditySensor De functie leest 5x en neemt het gemiddelde 
// Serial.print("Value Read: "); 
// Serial.println(moist);   
if (moist <= irrigate) digitalWrite(pumpPin, HIGH); 
if (moist >= irrigate+5) digitalWrite(pumpPin, LOW); 

/* Now read the temperature sensor. 
If  the sensor is 10 k this is considered 26 degrees, 
the analogue reading will be 512 and 128 after mapping 
at a higher value the heating should be switched off 

If the sensor is 12k5 this is considered 21 degrees, 
the analogue reading will be 455 and 113 after mapping this 
is the lower treshhold and the heating should be switched on 

In real life however the values 142 and 155 correponded 
better with those temperatures 
*/ 

ntc=sample(ntcPin); 
// Serial.println(ntc);   
  if (ntc <= 142) digitalWrite(warmPin, HIGH); 
  if (ntc >= 155) digitalWrite(warmPin, LOW);
} //end loop 

// Sample Function
int sample(int z)   
/* This function will read the Pin 'z' 5 times and take an average.
Afterwards it will be mapped by dividing by 4 
*/ 

{
int i; int sval = 0;
for (i = 0; i < 5; i++){   sval = sval + analogRead(z);    // sensor on analog pin 'z'  
}
sval = sval / 5;    // average 
sval = sval / 4;    // scale to 8 bits (0 - 255) 
//sval = 255 - sval;  // invert output  
return sval;
}


Getting the code in the ATtiny

Now this may all be very well, but how to get the program in the ATtiny, as it has no bootloader? Well, there are several ways. One can use the Arduino IDE to generate the hexfile and then program that with either a parallelport programmer or a USBtiny programmer, or if one has an Arduino, the Arduino can be used as a programmer. Several websites have described this and I will refer to the procedure as explained by John Boxal in his blog.

In principle the method is as follows.
  • Connect the 6 pins between the Arduino and the ATtiny that make up the official in line programming header.
  • Add a specific ATtiny board file to yr IDE.
  • Load the Arduino with the ‘ArduinoISP’ sketch.
  • Then choose ‘ATtiny 85 (w/ Arduino as ISP)’ as your board.
  • Load the ‘Garden’ sketch and upload as usual.

Do I really need a microcontroller for this

Some people may wonder if perhaps even using an ATtiny for this is overkill? Well, ofcourse it is because what you are basically doing here is comparing two values. The value for a measured temperature (or humidity) with a set value and there are devices that have been used for doing exactly that for years already: Opamps, like the trusty 741 

opamp

Obviously with this design it is harder to set a low and a high level (one can use two opamps for that) but that means it will keep the temperature (or humidity)  at a narrower level.

For other possible op amp solutions see here and here

Sunday, January 8, 2012

DS1307 RTC with LED

A DSR1307RTC IC is a pretty cheap and accurate Real Time Clock that only needs a few components to work.

The 3 volt battery is necessary for time keeping when the 5V power supply is off. A CR battery will be sufficient to keep time for many many years.

The PWM pin is not really necessary, but it can be used to output a pulse of e.g. 1Hz that can be used to drive a LED that will be pulsing 1x per second. For this one must write a 0x10 (10 in HEX format, 16 in digital format, 00010000 in binary format) to register 07 (HEX).
//-----------
/*
 reading and writing to the Maxim DS1307 real time clock IC
 based on code by Maurice Ribble
 17-4-2008 - http://www.glacialwanderer.com/hobbyrobotics
 ---------------------------------------
 CONTROL REGISTER
 The DS1307 control register is used to control
 the operation of the SQW/OUT pin.

 BIT 7  BIT 6  BIT 5  BIT 4  BIT 3  BIT 2  BIT 1  BIT 0
 OUT     0      0     SQWE     0     0     RS1    RS0

 Bit 4: Square-Wave Enable (SQWE): This bit, when set to logic 1,
       enables the oscillator output. 
 ---------------------------------------
 Pin 5 (SDA) naar analoog4
 Pin 6 (SCL) naar analoog5
 */

#include "Wire.h"   // available in the Arduino files
#define DS1307_I2C_ADDRESS 0x68

// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

// 1) Sets the date and time on the ds1307
// 2) Starts the clock
// 3) Sets hour mode to 24 hour clock

// Assumes you're passing in valid numbers

void setDateDs1307(byte second,        // 0-59
byte minute,        // 0-59
byte hour,          // 1-23
byte dayOfWeek,     // 1-7
byte dayOfMonth,    // 1-28/29/30/31
byte month,         // 1-12
byte year)          // 0-99
{
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0);
  Wire.send(decToBcd(second));    // 0 to bit 7 starts the clock
  Wire.send(decToBcd(minute));
  Wire.send(decToBcd(hour));     
  Wire.send(decToBcd(dayOfWeek));
  Wire.send(decToBcd(dayOfMonth));
  Wire.send(decToBcd(month));
  Wire.send(decToBcd(year)); 
  Wire.endTransmission();
}

// Gets the date and time from the ds1307
void getDateDs1307(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
  // Reset the register pointer
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0);
  Wire.endTransmission();

  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

  // A few of these need masks because certain bits are control bits
  *second     = bcdToDec(Wire.receive() & 0x7f);
  *minute     = bcdToDec(Wire.receive());
  *hour       = bcdToDec(Wire.receive() & 0x3f);  // Need to change this if 12 hour am/pm
  *dayOfWeek  = bcdToDec(Wire.receive());
  *dayOfMonth = bcdToDec(Wire.receive());
  *month      = bcdToDec(Wire.receive());
  *year       = bcdToDec(Wire.receive());
}


// sqw is set here
void sqw1() // set to 1Hz
{
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0x07); // move pointer to SQW address
  Wire.send(0x10); //  sends 0x10 (hex) 00010000 (binary)
  Wire.endTransmission();
}
//-------------------



void setup()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  Wire.begin();
  Serial.begin(9600);

  // Change these values to what you want to set your clock to.
  // You probably only want to set your clock once and then remove
  // the setDateDs1307 call.

  second = 0;
  minute = 52;
  hour = 9;
  dayOfWeek = 7;
  dayOfMonth = 7;
  month = 1;
  year = 12;
// uncomment instruction below to set the time
// setDateDs1307(second, minute, hour, dayOfWeek, dayOfMonth, month, year);

//----------------------------
sqw1();  //switch on 1Hz SQW 
//-----------------------------
}

void loop()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;

  getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  Serial.print(hour, DEC);// convert the byte variable to a decimal number when being displayed
  Serial.print(":");
  if (minute<10)
  {
      Serial.print("0");
  }
  Serial.print(minute, DEC);
  Serial.print(":");
  if (second<10)
  {
      Serial.print("0");
  }
  Serial.print(second, DEC);
  Serial.print("  ");
  Serial.print(dayOfMonth, DEC);
  Serial.print("/");
  Serial.print(month, DEC);
  Serial.print("/");
  Serial.print(year, DEC);
  //Serial.print("  Day of week:");
  switch(dayOfWeek){
  case 1: 
    Serial.println(" Sunday");
    break;
  case 2: 
    Serial.println(" Monday");
    break;
  case 3: 
    Serial.println(" Tuesday");
    break;
  case 4: 
    Serial.println(" Wednesday");
    break;
  case 5: 
    Serial.println(" Thursday");
    break;
  case 6: 
    Serial.println(" Friday");
    break;
  case 7: 
    Serial.println(" Saturday");
    break;
  }
  //  Serial.println(dayOfWeek, DEC);
  delay(1000);
}

/* To access the RAM (65 bytes)
   void WriteRam(int address,byte data){
   Wire.beginTransmission(0x68);    // Select DS1307
   Wire.send(address+8);   // address location starts at 8, 0-6 are date, 7 is control
   Wire.send(data);        // send data
   Wire.endTransmission();
}
  
*/

/*
//12 hours:

void setDateDs1307
….
Wire.send(decToBcd(hour) | _BV(6)); // set bit 6 for 12 hour mode

void getDateDs1307:
….
*hour = bcdToDec(Wire.receive() & 0x1f); // mask output for 12 hour mode
*/
//----------
Below a program that will make the SQW/PWM pin flash at several frequencies (but your eyes won't see it at the higher frequencies)
/*
DS1307 Square-wave machine
 Used to demonstrate the four different square-wave outputs from Maxim DS1307
 See page nine of data sheet for more information
 John Boxall - tronixstuff.wordpress.com
 Register:
 OUT 0 0 SQWE 0 0 RS1 RS2
 */
#include "Wire.h"
#define DS1307_I2C_ADDRESS 0x68 // each I2C object has a unique bus address, the DS1307 is 0x68
void setup()
{
  Wire.begin();
}
void sqw1() // set to 1Hz
{
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0x07); // move pointer to SQW address
  Wire.send(0x10); //  sends 0x10 (hex) 00010000 (binary)
  Wire.endTransmission();
}
void sqw2() // set to 4.096 kHz
{
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0x07); // move pointer to SQW address
  Wire.send(0x11); //  sends 0x11 (hex) 00010001 (binary)
  Wire.endTransmission();
}
void sqw3() // set to 8.192 kHz
{
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0x07); // move pointer to SQW address
  Wire.send(0x12); // sends 0x12 (hex) 00010010 (binary)
  Wire.endTransmission();
}
void sqw4() // set to 32.768 kHz (the crystal frequency)
{
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0x07); // move pointer to SQW address
  Wire.send(0x13); // sends 0x13 (hex) 00010011 (binary)
  Wire.endTransmission();
}
void sqwOff()
// turns the SQW off
{
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0x07); // move pointer to SQW address
  Wire.send(0x00); // turns the SQW pin off
  Wire.endTransmission();
}
void loop()
{
  sqw1();
  delay(5000);
  sqw2();
  delay(5000);
  sqw3();
  delay(5000);
  sqw4();
  delay(5000);
  sqwOff();
  delay(5000);
}

Monday, June 20, 2011

Adding a Facebook social bookmark or 'Share' button to your blog

Adding a Facebook social bookmark (or 'Share') button to your blogger/blogspot blog is quite easy. Only involves two steps:
1: localize the place in your templates HTML where it should be
2: add code

ad 1: search in your HTML for  the following code







Ad 2: Once you found that, insert the following code beneat the section above.









This code 'Likes' a specific posting and not your entire blog.
The icon in this example is taken directly from the facebook website. This enables for quick testing of the code. When you have the code working, it is a better idea to download the icon, store it on yr own webspace and change the reference accordingly

Sunday, June 12, 2011

Slave Unit for Digital Camera with Pre-flash

Not specifically a program, but a Slave Unit for a Flash. The essence of this circuit is that it will ignore the preflash that many digital camera's have.

Friday, July 3, 2009

searching and updating a database

search.html is the search form. passes the data to dbsearch.php
dbsearch.php  searches the database and presents the found records. passes the records to view.php for viewing or editinfo.php to edit the record.
dbsearch.php takes the record Id, adds it to a link and passes it thus to the required programs.
For a full explanation of the programs editinfo and update info, check it’s source


---search.html-------
<body>     
<link href="style.css" type="text/css" rel="stylesheet" />     
<link rel="stylesheet" type="text/css" href="style.css" /> 
<form action="dbsearch.php" method="get">    
Keywoord uit Acteur of Titel: <input type="text" name="q" />     
<br>    
<input type="submit" />     
</form>     
<body> 
------dbsearch.php----------------
<?php     
    include 'db.php';    // Include the database     
    // Set the page, use one if the get page is not a number or is negative     
    if(!isset($_GET['page']) || !ctype_digit($_GET['page']))     
        $page = 1;     
    else     
        $page = $_GET['page'];     
    // Set the maximum number of results     
    $max = 10;     
    // Set the start location (when viewing the next page)     
    $limit = ($page * $max) - $max;     
    $q = trim(mysql_real_escape_string($_GET['q'])); // Make a safe string     
    // Make a query, (change "code" and "name" to your column names)     
    $query = "SELECT SQL_CALC_FOUND_ROWS *,     
            MATCH(Titel) AGAINST ('$q' IN BOOLEAN mode) AS score1,     
            MATCH(Acteur) AGAINST ('$q' IN BOOLEAN mode) AS score2     
            FROM DVD     
            WHERE  
            MATCH(Titel,Acteur) AGAINST ('$q' IN BOOLEAN mode)     
            ORDER BY score1 DESC, score2 DESC LIMIT $limit, $max";     
    // Perform the query     
    $sql = mysql_query($query); 
    // Find how many results would have been returned if there wasn't a limit     $result_count = mysql_query("SELECT FOUND_ROWS()")or die(mysql_error());     // Get the number     $total = mysql_fetch_array($result_count);     // Search the array for the total     $totalrows = $total[0];     // Calculate the number of pages, if it is a decimal, then there are     // more reusults, but that number is less than our $max (total number of results     // to display on the page)     $pages = ceil($totalrows / $max);     // Display the results...     if(mysql_num_rows($sql) > 0){         echo '<p>Found <b>'.$totalrows.'</b> results for <b>"'.htmlentities($_GET['q']).'"</b></p>';         $i = $limit + 1;         while($row = mysql_fetch_array($sql)){             echo '<p>'.$i.'. <a href="view.php?id='.$row['x'].'">'.$row['Titel'].'</a>';             echo ' <b ><a href="editinfo.php?id='.$row['x'].'">Edit Data</a></b></p>';             $i++;         }     }else{         // No results were found         echo '<h2>No Results Found!</h2>';     }     // Display the page numbers (if there is more than one page)     if($pages > 1){         echo '<div style="padding:10px;">';             for($i = 1;$i<$pages+1;$i++){                 if($i == $page)                     echo '<span class="page" style="padding:10px;">'.$i.'</span>';                 else                     echo '<a style="padding:10px;" href="'.$_SERVER['PHP_SELF'].'?q='.$_GET['q'].'&amp;page='.$i.'">'.$i.'</a>';             }             echo '<span style="clear:both;display:block;font-size:1px;">&nbsp;</span>';         echo '</div>';     } // Sluit de database echo mysql_error(); mysql_close($db); ?>
-------view.php-------------------------
<?     
$id = trim(mysql_real_escape_string($_GET['id'])); // Make a safe string     
//echo $id;     
include 'db.php';    // Include the database     
$query=("SELECT * FROM DVD WHERE x=$id ORDER BY Titel ASC ")  or die (mysql_error());     
//echo $query;     
$result=mysql_query($query);     
//echo $result;     
$row = mysql_fetch_array($result);     
$x= $row[0];     
$titel= $row[1];     
$acteur = $row[2];     
$taal= $row[3];     
$jaar= $row[4];     
$imdb = $row[5];     
$summary=$row[6];     
$foto=$row[7];     
?> 
<div class="example1">    
<?php     
echo '<b>';     
echo $titel;     
echo '</b><br /><em>';     
echo $acteur;     
echo '</em><br />';     
echo $taal;     
echo '<br />';     
echo $jaar;     
echo '<br />';     
echo "<a href='{$row['IMDB']}'>$titel at imdb</a>";     
echo '<br />';     
echo $summary;     
echo '<br />';     
echo "<img src='{$row['Foto']}' >"; 
// Sluit de database    
echo mysql_error();     
mysql_close($db); 
?>    
</div> 
-------------- editinfo.php---------
This file is a slight alteration from the file found Here
<?php    
$id = trim(mysql_real_escape_string($_GET['id'])); // Make a safe string     
//replace usernaem,password, and yourdb with the information for your database     
mysql_connect("localhost","zz","zz") or die("Error: ".mysqlerror());     
mysql_select_db("DVD"); 
//replace TestTable with the name of your table    
//also in a real app you would get the id dynamically     
$sql = "select * from `DVD` where x = $id";     
$query = mysql_query($sql); 
while ($row = mysql_fetch_array($query)){ 
    $id = $row['x'];    
    $titel = $row['Titel'];     
    $acteur = $row['Acteur'];  
    $taal = $row['Taal'];     
    $jaar = $row['Jaar'];     
    $imdb = $row['IMDB'];  
    $summary =  $row['Summary'];     
    $foto = $row['Foto']; 
    //we will echo these into the proper fields 
}    
mysql_free_result($query);     
?> 
<html>    
<head>     
<title>Edit DVD Info</title>     
</head> 
<body> 
<form action="updateinfo.php" method="post"> 
id:<br/>    
<input type="text" value="<?php echo $id;?>" name="x" disabled />     
<input type="hidden" value="<?php echo $id;?>" name="x"  />     
<br/> 
-------------updateinfo.php-----------
see here for full description
Titel:<br/>     
<input type="text" value="<?php echo $titel;?>" name="Titel" size=60/> 
<br/> 
Acteur:<br/>    
<input type="text" value="<?php echo $acteur;?>" name="Acteur" size=60/> 
<br/> 
Taal:<br/>    
<input type="text" value="<?php echo $taal;?>" name="Taal"/> 
<br/> 
Jaar:<br/>    
<input type="text" value="<?php echo $jaar;?>" name="Jaar" size=10/> 
<br/> 
IMDB:<br/>    
<input type="text" value="<?php echo $imdb;?>" name="IMDB" size=38/> 
<br/>    
Summary:<br/>     

<TEXTAREA NAME="Summary"  ROWS=6 COLS=45>     
<?php echo $summary;?>     
</TEXTAREA>     
<br/> 
Foto locatie:<br/>    
<input type="text" value="<?php echo $foto;?>" name="Foto" size=59/> 
</br> 
<input type="submit" value="submit changes"/> 
</form>    
</body>     
</html>