Program an ATtiny with an Arduino Nano

Many sites already described how to use the Arduino as a programmer for the ATtiny series, but as some people still have some problems with it, it won’t hurt to share my experience in doing this with an Arduino Nano.

The connection between the Arduino and the ATtiny is in essence the same, regardless of what Arduino one has. One needs to connect the ISP hearder between both chips. But for the UNO and the Nano there is a little addition in the form of a capacitor between  ground and the reset pin:
arduino_prog

Most circuits will show a 10uF electrolytic capacitor, but I used a 33uF elco and that worked perfectly.

In order to use the ATtiny 45 or 85 you need to download some software. For the Attiny13 there are other cores. If you are using the Attiny13 core you also need to create a boot.txt file
You will also find a core for the Attiny here. and a number of them here.

Now create a folder called ‘Hardware’ in the folder where your Arduino IDE saves it’s sketches. Unzip this file to the newly created hardware folder. If you had your Arduino IDE running, you need to close it and restart it so it can read the proper files.

Now do the following:

  • Connect Arduino Nano and Attiny85 as described above, but do not connect the capacitor yet.
  • Load the ‘Arduino as ISP sketch’
  • Disconnect USB
  • Insert capacitor
  • Reconnect USB
  • Load Attiny sketch of your choosing in the Arduino IDE (e.g. the ‘blink’ sketch)
  • Choose ‘ATtiny85 with Arduino as ISP’ in yr ‘Tools->boards’
  • Upload
  • Ignore the errors about PAGEL and BS2
  • Remove USB
  • Remove capacitor and at least the line to the reset of the Attiny (the one from pin 1)

Your sketch should now be correctly in your ATtiny.

Configuring the ATtiny to run at 8 MHz (for SoftwareSerial support)

By default, the ATtiny’s run at 1 MHz (the setting used by the unmodified “ATtiny45″, etc. board menu items). You need to do an extra step before the programming to configure the microcontroller to run at 8 MHz – necessary for use of the SoftwareSerial library. Once you have the microcontroller connected, select the appropriate item from the Boards menu (e.g. “ATtiny45 (8 MHz)”). Then, run the “Burn Bootloader” command from the Tools menu. This configures the fuse bits of the microcontroller so it runs at 8 MHz. Note that the fuse bits keep their value until you explicitly change them, so you’ll only need to do this step once for each microcontroller. (Note this doesn’t actually burn a bootloader onto the board; you’ll still need to upload new programs using an external programmer.)

With regard to writing a sketch for the ATtiny85, there is only a limited instruction set:

See also:

Programming an Attiny with Arduino 1.01.

TinyWireM & TinyWireS: Wire (I2C / TWI) library for the ATtiny85 (using USI)

Advertisements

Timer interrupts

This article will discuss AVR and Arduino timers and how to use them in Arduino projects or custom AVR circuits.

What is a timer?

Like in real life, in microcontrollers a timer is something you set to trigger an alert at a certain point in the future. When that point arrives, that alert interrupts the microprocessor, reminding it to do something, like run a specific piece of code.

Timers, like external interrupts, run 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.

So suppose you have a device that needs to do something –like blink an LED every 5 seconds. If you are not using timers but just conventional 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. The LED will blink perfectly on time, regardless of what your main program was just doing

How do timers work?

Timers work by incrementing a counter variable known as a counter register. The counter register can count to a certain value, depending on its size (usually 8 bits or 16 bits). 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. This flag can be checked manually, or you can have the timer trigger an interrupt as soon as the flag is set. And as with any other interrupt, you can specify an Interrupt Service Routine (ISR) to run your own code when the timer overflows. The ISR will automatically reset the overflow flag, so using interrupts is usually your best option for simplicity and speed.

To increment the counter value at regular intervals, the timer must have a clock source.  The clockprovides a consistent signal.  Every time the timer detects this signal, it increases its counter by one.

Since timers are dependent on the clock source, the smallest measurable unit of time will be the period of the clock.  If you provide a 16 MHz clock signal to a timer, the timer resolution (or timer period) is:

T = 1 / f  (f is the clock frequency)
T = 1 /(16* 10^6)
T = (0.0625 * 10^-6) s

The timer resolution thus is 0.0625 millionth of a second.

For 8 MHz this would be 0.125 millionth of a second

and for 1 MHz exactly one millionth of a second

You can supply an external clock source for use with timers, but usually the chip’s internal clock is used as the clock source. The 16 MHz crystal that is usually part of a setup for an Atmega328 can be considered as part of the internal clock.

Different timers

In the standard Arduino variants or the 8-bit AVR chips, there are several timers at your disposal.

The ATmega8, 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. The Mega series has 3 additional timers.

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 unless you know what you are doing, timer 0 is best left alone.

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 keep that in mind if you use this timer in your projects.

Timer2

Timer2 is an 8-bit timer that is very similar to Timer0. It is used 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 the timer register

In order to use these timers the built-in timer registers on the AVR chip that store timer settings need to be configured.  There are a number of registers per timer.  Two of these registers –the Timer/Counter Control Registers- hold setup values, and are called TCCRxA and TCCRxB, where x is the timer number (TCCR1A and TCCR1B, etc.).   Each register holds 8 bits, and each bit stores a configuration value.  The ATmega328 datasheet specifies those as follows:

TCCR1A
Bit 7 6 5 4 3 2 1 0 TCCR1A
0x80 COM1A1 COM1A0 COM1B1 COM1B0 WGM11 WGM10
ReadWrite RW RW RW RW R R RW RW
Initial Value 0 0 0 0 0 0 0 0
TCCR1B
Bit 7 6 5 4 3 2 1 0 TCCR1B
0x81 ICNC1 ICES1 WGM13 WGM12 CS12 CS11 CS10
ReadWrite R/W R/W R R/W R/W R/W R/W R/w
Initial Value 0 0 0 0 0 0 0 0

The most important settings are the last three bits in TCCR1B, CS12, CS11, and CS10.  These determine the timer clock setting.  By setting these bits in various combinations, you can make the timer run at different speeds.  This table shows the required settings:

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.  Suppose you want to have Timer1 run at clock speed, with one count per clock cycle.  When it overflows, you want to run an Interrupt Service Routine (ISR) that toggles a LED tied to pin 13 on or off. Below you will find the Arduino code for this example, for completeness I use avr-libc routines wherever they don’t make things overly complicated.

First, initialize the timer:

// avr-libc library includes
#include <avr/io.h>
#include <avr/interrupt.h>
#define LEDPIN 13

void setup()
{
pinMode(LEDPIN, OUTPUT);
// initialize Timer1
cli();         // disable global interrupts
TCCR1A = 0;    // set entire TCCR1A register to 0
TCCR1B = 0;    // set entire TCCR1B register to 0 
               // (as we do not know the initial  values) 

// enable Timer1 overflow interrupt:
TIMSK1 | = (1 << TOIE1); //Atmega8 has no TIMSK1 but a TIMSK register

// Set CS10 bit so timer runs at clock speed: (no prescaling)
TCCR1B |= (1 << CS10); // Sets bit CS10 in TCCR1B
// This is achieved by shifting binary 1 (0b00000001)
// to the left by CS10 bits. This is then bitwise
// OR-ed into the current value of TCCR1B, which effectively set
// this one bit high. Similar: TCCR1B |= _BV(CS10);

// enable global interrupts:
sei();
}

The register TIMSK1 is the Timer/Counter1 Interrupt Mask Register. It controls which interrupts the timer can trigger. Setting the TOIE1 bit (=Timer1 Overflow Interrupt Enable) tells the timer to trigger an interrupt when the timer overflows. It can also be set to other bits to trigger other interrupts. More on that later.

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

Next define the ISR:

ISR(TIMER1_OVF_vect)
{

digitalWrite(LEDPIN, !digitalRead(LEDPIN));
// or use: PORTB ^= _BV(PB5);// PB5 =pin 19 is digitalpin 13
}

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

How fast will the LED blink with this code?

Timer1 is set to interrupt on an overflow, so if you are 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.25*10-8 s. That means 65535 timer counts will pass in (65535 * 6.25*10-8s) and the ISR will trigger in  about 0.0041 seconds. Then again and again, every four thousandths of a second after that. That is too fast to see it blink. 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 preloading

To control this 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, if you want the LED blink at one second intervals. In the TCCR1B register, there are three CS bits to set a better timer resolution.  If you set CS10 and CS12 using:

TCCR1B |= (1 << CS10); and TCCR1B |= (1 << CS12);, the clock source is divided by 1024. This gives a timer resolution of 1/(16*10⁶ / 1024), or 0.000064 seconds (15625 Hz). Now the timer will overflow every (65535 * 6.4*10-5s), or 4.194s.

If you would set only CS12 using TCCR1B |=(1<<CS12); (or just TCCR1B=4), the clock source is divided by 256. This gives a timer resolution of 1/(16*10⁶/256), or 0.000016 sec (62500 Hz) and the timer will overflow every (65535 *0.000016=) 1.04856 sec.

Suppose you do not want an 1.04856 sec interval but a 1 sec interval. It is clear to see that if the counter wasn’t 65535 but 62500 (being equal to the frequency), the timer would be set at 1sec. The counter thus is 65535-62500=3035 too high. To have more precise 1 second timer we need to change only one thing – timer’s start value saved by  TCNT1 register (Timer Counter ). We do this with TCNT1=0x0BDC; BDC being the hex value of 3035. A Value of 34286 for instance would give 0.5 sec ((65535-34286)/62500)

The code looks as follows:

// avr-libc library includes
#include <avr/io.h> //  can be omitted
#include <avr/interrupt.h> // can be omitted
#define LEDPIN 13
/* or use
DDRB = DDRB | B00100000;  // this sets pin 5  as output                       // without changing the value of the other
                         // pins 
*/
void setup()
{
pinMode(LEDPIN, OUTPUT);

// initialize Timer1
cli();         // disable global interrupts
TCCR1A = 0;    // set entire TCCR1A register to 0
TCCR1B = 0;    // set entire TCCR1A register to 0

// enable Timer1 overflow interrupt:
TIMSK1 |= (1 << TOIE1);
// Preload with value 3036
//use 64886 for 100Hz
//use 64286 for 50 Hz
//use 34286 for 2 Hz
TCNT1=0x0BDC;
// Set CS10 bit so timer runs at clock speed: (no prescaling)
TCCR1B |= (1 << CS12); // Sets bit CS12 in TCCR1B
// This is achieved by shifting binary 1 (0b00000001)
// to the left by CS12 bits. This is then bitwise
// OR-ed into the current value of TCCR1B, which effectively set
// this one bit high. Similar: TCCR1B |= _BV(CS12);
//  or: TCCR1B= 0x04;

// enable global interrupts:
sei();
}

ISR(TIMER1_OVF_vect)
{
digitalWrite(LEDPIN, !digitalRead(LEDPIN));
TCNT1=0x0BDC; // reload the timer preload
}

void loop() {}

CTC

But 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, you need to figure out how many counts you need to get to a 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

You have to add the extra +1 to the number of timer counts because in CTC mode, when the timer matches the desired count it will reset itself to zero. This takes one clock cycle to perform, so that needs to be factored into the 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 the setup() function to configure the timer for these settings is as follows:

void setup()
{

pinMode(LEDPIN, OUTPUT); // you have to define the LEDPIN as say 13
                         // or so earllier in yr program
// 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 you need to replace the overflow ISR with a compare match version:

 

ISR(TIMER1_COMPA_vect)
{
digitalWrite(LEDPIN, !digitalRead(LEDPIN));
}

The LED will now blink on and off at precisely one second intervals. And you are free to do anything you want in loop(). As long as you don’t change the timer settings, it won’t interfere with the 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.

// Arduino timer CTC interrupt example
//
// avr-libc library includes
#include <avr/io.h>
#include <avr/interrupt.h>
#define LEDPIN 13
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()
{
// main program
}

ISR(TIMER1_COMPA_vect)
{
digitalWrite(LEDPIN, !digitalRead(LEDPIN));
}

Remember 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 set-up 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, the ISR would look something like this:

 

ISR(TIMER1_COMPA_vect)
{
seconds++;
if(seconds == 10)
{
seconds = 0;
readSensor();
}
}

For a variable to be modified within an ISR, it is good custom to declare it as volatile. In this case, you need to declare volatile byte seconds; or similar at the start of the program.

A word on the Atmega8

The Atmega8 seems to give people problems with use of the timers, one reason is that it doesn’t have a TIMSK1 register (in fact it doesnt have a TIMSKn register), it does have a TIMSK register though that is shared amongst the 3 timers. As I do not have an Atmega8 (like the early Arduino NG) I can not test it, but if you encounter problems, the following programs will help:

// this code sets up counter0 with interrupts enabled on an Atmega8
// beware, it may generate  errors in Arduino IDE 
// as 'milis' uses timer0 
#include <avr/io.h>
#include <avr/io.h>

void setup()
{
DDRD &= ~(1 << DDD4); // Clear the PD4 pin
// PD0 is now an input

PORTD |= (1 << PORTD4); // turn On the Pull-up
// PD4 is now an input with pull-up enabled

TIMSK |= (1 << TOIE0); // enable timer interrupt

TCCR0 |= (1 << CS02) | (1 << CS01) | (1 << CS00);
// Turn on the counter, Clock on Rise

sei();
}
void loop()
{
// Stuff
}


ISR (TIMER0_OVF_vect)
{
// interrupt just fired, do stuff
}

A 1 sec flasher using the timer 1 CTC mode for the Atmega 8 would look like this:

 

void setup()
{     
pinMode(13,OUTPUT);
/* or use:
DDRB = DDRB | B00100000;  // this sets pin 5  as output
                       // without changing the value of the other pins 
*/
// Disable interrupts while loading registers
cli();
// Set the registers
TCCR1A = 0; //Timer Counter Control register
// Set mode
TCCR1B = (1 << WGM12); // turn on CTC mode
// Set prescale values (1024). (Could be done in same statement
// as setting the WGM12 bit.)
TCCR1B |= (1 << CS12) | (1 << CS10);
//Enable timer compare interrupt===> TIMSK1 for ATmega328, 
//TIMSK for ATmega8
TIMSK |= (1 << OCIE1A);
// Set OCR1A
OCR1A = 15624;
// Enable global interrupts
sei();
}
void loop(){}
ISR (TIMER1_COMPA_vect) {
   digitalWrite(13, !digitalRead(13));
   //PORTB ^= _BV(PB5); // as digitalWrite(13,x) is an Arduino 
   //function, direct writing to the port may be preferable
}

It is obvious that this is very akin to the CTC program presented earlier for the Atmega328 and in fact will work on the Atmega238 as well by renaming ‘TIMSK’ to ‘TIMSK1’

Other Atmega chips:

TCCR0 should be TCCR0A in ATmega164P/324P/644

Attiny

The Attiny series has timer interrupts too. This code sets up a 50uS timer in CTC mode on the Attiny85 (pag 79 datasheet)

TCCR0A = (1 << WGM01);   //CTC mode. set WGM01
TCCR0B = (2 << CS00);    //divide by 8  sets 
OCR0A = F_CPU/8 * 0.000050 - 1;    // 50us compare value
TIMSK |= (1<<OCIE0A);              //set interrupt

ISR(TIMER0_COMPA_vect)
{
                                   // code of choice!
}

More on timers here

here

here

here

here

here

here

 

pwm generation by timers

here (Atmega8)

Atmega8 Datasheet

Atmega328 Datasheet

Arduino hardware interrupt

What is an interrupt?

On a very basic level, an interrupt is an signal that interrupts the current processor activity. It may be triggered by an external event (change in pin state) or an internal event (a timer or a software signal).  Once triggered, an interrupt pauses the current activity and causes the program to execute a different function.  This function is called an interrupt handler or an interrupt service routine (ISR).  Once the function is completed, the program returns to what it was doing before the interrupt was triggered.

ISR

If you’re new to the world of software development, you might wonder why all this complication is necessary just to respond to external events.  After all, you can check the state of external pins at any time, or create your own timers.

You certainly can do all of these things in your main code, but interrupts give you a key advantage – they are asynchronous.  An asynchronous event is something that occurs outside of the regular flow of your program – it can happen at any time, no matter what your code is crunching on at the moment.  This means that rather than manually checking whether your desired event has happened, you can let your AVR do the checking for you.

Let’s use a real-world example.  Imagine you’re sitting on your couch, enjoying a frosty brew and watching a movie after a long day.  Life is good.  There’s only one problem: you’re waiting for an incredibly important package to arrive, and you need it as soon as possible.  If you were a normal AVR program or Arduino sketch, you’d have to repeatedly stop your movie, get up, and go check the mailbox every 5 minutes to make sure you knew when the package was there.

Instead, imagine if the package was sent Fedex or UPS with delivery confirmation.  Now, the delivery man will go to your front door and ring the doorbell as soon as he arrives. That’s your interrupt trigger.  Once you get the trigger, you can pause your movie and go deal with the package.  That’s your interrupt service routine.  As soon as you’re done, you can pick up the film where you left off, with no extra time wasted. That’s the power of interrupts.

The AVR chips used in most Arduinos are not capable of parallel processing, i.e. they can’t do multiple things at once.  Using asynchronous processing via interrupts enables us to maximize the efficiency of our code, so we don’t waste any precious clock cycles on polling loops or waiting for things to occur.  Interrupts are also good for applications that require precise timing, because we know we’ll catch our event the moment it occurs, and won’t accidentally miss anything.

Types of Interrupts

There are two main types of interrupts:

  • Hardware interrupts, which occur in response to an external event, such as an input pin going high or low
  • Software interrupts, which occur in response to an instruction sent in software

8-bit AVR processors, like the ones that power most Arduino boards, aren’t capable of software interrupts. So we’ll focus on hardware for now.  Most tutorials out there talk about handling external interrupts like pin state changes.  If you’re using an Arduino, that’s the only type of interrupt the Arduino “language” supports, using the attachInterrupt() function.  That’s fine, but there’s a whole different category of hardware interrupts that rely on the AVR’s built in timers, which can be incredibly useful.  We’ll cover external interrupts in this tutorial, then go over timer interrupts in a follow-up tutorial, since there’s enough information to warrant splitting things up.

How to get an interrupt to do what we want – Interrupt Service Routines

Every AVR processor has a list of interrupt sources, or vectors, which include the type of events that can trigger an interrupt.  When interrupts are enabled and one of these events occur, the code will jump to a specific location in program memory – the interrupt vector.  By writing an ISR and then placing a link to it at the interrupt vector’s memory location, we can tell our code to do something specific when an interrupt is triggered.

Let’s implement this using a simple example: detecting when a pushbutton has been pressed, and performing an action based on that press.

Implementing an interrupt in a program

In order to successfully use an interrupt, we’ll need to do three things:

  1. Set the AVR’s Global Enable Interrupts bit in Status Register
  2. Set the interrupt enable bit for our specific interrupt vector (each vector has it’s own on/off switch)
  3. Write an ISR and attach it to our target interrupt vector

Starting with the first step, we’ll include the interrupt library from avr-libc, then use an avr-libc function to set our global interrupt enable bit.  Next, we need to enable the interrupt we want.  Most 8-bit AVR’s like the ATMega328 have 2 hardware interrupts, INT0 and INT1.  If you’re using a standard Arduino board, these are tied to digital pins 2 and 3, respectively.  Let’s enable INT0 so we can detect an input change on pin 2 from a button or switch.

#include <avr/interrupt.h> //Not really needed for Arduino
void setup(void){sei();  // Enable global interrupts
EIMSK |= (1 << INT0);    // Enable external interrupt INT0

Next, we need to set the sensing method of our interrupt.  For a pin change interrupt, we have four options: rising edge, falling edge, any logical state change, or a low level on the pin.  The default for INT0 and INT1 is to trigger on the pin being low level; you can use the datasheet to read more about how to set each method.  Let’s use falling edge for this example, just to show how it’s done.

EICRA |= (1 << ISC01);    // Trigger INT0 on falling edge

Finally, we’ll define an ISR that performs our desired task.  Every ISR is defined as:

ISR({vector}_vect)
{
// Perform task here
}

where {vector} is the name of our chosen interrupt vector.  Again, the names of these vectors are defined in the processor datasheet; here, the one we want is INT0.  For this example, we’ll use the ISR to toggle the built-in LED on pin 13. With our LED code added, here’s how it looks all together:

#include <avr/interrupt.h> //not really needed for arduino 
//
void setup(void)
{
  pinMode(2, INPUT);
  digitalWrite(2, HIGH); // Enable pull-up resistor
  sei();                 // Enable global interrupts
  EIMSK |= (1 << INT0);  // Enable external interrupt INT0
  EICRA |= (1 << ISC01); // Trigger INT0 on falling edge
}
//
void loop(void)
{
  //
}
//
// Interrupt Service Routine attached to INT0 vector
ISR(INT0_vect)
{
 digitalWrite(13,!digitalRead(13));// Toggle LED on pin 13
}

Note that we can use the program’s main loop() to do anything we want in the meantime, and it won’t affect the functionality of our LED toggle.

Arduino interrupt functionality

There’s an alternative way to implement INT0 and INT1 using the Arduino programming “language”. I put “language” in quotes because although that’s how the Arduino website refers to it, it’s more like a wrapper around avr-libc to make certain things easier. But that’s an article for another day. For our example, we could use the attachInterrupt() function to enable the same interrupt in setup():

setup(void)
{
attachInterrupt(0, pin2ISR, FALLING);
}

In this case, we define our own custom ISR (pin2ISR) and pass it as an argument. pin2ISR() would simply take the place of ISR(EXT_INT0_vect). This is often a bit simpler than using the raw avr-libc functions, and it does abstract away any difference in chip models. However, the AVR methods described earlier can be used with any interrupt vector, while attachInterrupt() only works with external interrupts.

Putting it all together

There are a few final things to keep in mind when implementing interrupts.
First, keep in mind that whatever you use your ISR for, it’s good practice to keep it short, because while the ISR is executing, it’s holding up the rest of your program. If you have lots of interrupts, this can slow things to a crawl. Also, by “the rest of your program”, we do mean everything: Arduino functions like millis() won’t increment, and delay() won’t work within an ISR.
Next, if you want to modify any variables within your ISR, you’ll need to make them global variables and mark them as volatile to ensure that your ISR has proper access to them. For example, instead of globally declaring

int myInterruptVar;

you would declare

volatile int myInterruptVar;

Finally, interrupts are normally globally disabled inside of any ISR (this is why delay() and millis() don’t work). This means that if more interrupt events occur before your ISR has completed, your program won’t catch them. This is another reason to keep ISRs short.

Wrapping up

That’s about it for the basics. If you’re ready to keep going, check out my followup article on timer interrupts. It builds on the concepts discussed here and helps you use the AVR’s built-in timers to do useful things. In the meantime, implement this example and dive into the Atmel datasheets to discover other interrupt vectors. They sky’s the limit!

PCInt and PCChangeInt libraries mentioned on the Arduino site here:   http://www.arduino.cc/playground/Main/PinChangeInt with samples here:  http://arduino.cc/playground/Main/PinChangeIntExample which gives you flexibility to use any pin for an interrupt.

A common example of where you use interrupts is using quadrature encoders on motors.   As the motor spins two signals come in from the encoders which can be captured with interrupts to determine direction, speed, and amount of rotation of your motors.

Sample code can be found here:  http://www.arduino.cc/playground/Main/RotaryEncoders
See also: Newbies guide on timers


	

LED Fade

/*
* Code for cross-fading 3 LEDs, red, green and blue, or one tri-color LED, using PWM
* The program cross-fades slowly from red to green, green to blue, and blue to red
*
* Clay Shirky <clay.shirky@nyu.edu>
*/

// Output
int redPin   = 9;   // Red LED,   connected to digital pin 9
int greenPin = 10;  // Green LED, connected to digital pin 10
int bluePin  = 11;  // Blue LED,  connected to digital pin 11

// Program variables
int redVal   = 255; // Variables to store the values to send to the pins
int greenVal = 1;   // Initial values are Red full, Green and Blue off
int blueVal  = 1;

int i = 0;     // Loop counter
int wait = 50; // 50ms (.05 second) delay; shorten for faster fades
int DEBUG = 0; // DEBUG counter; if set to 1, will write values back via serial

void setup()
{
pinMode(redPin,   OUTPUT);   // sets the pins as output
pinMode(greenPin, OUTPUT);
pinMode(bluePin,  OUTPUT);
if (DEBUG) {           // If we want to see the pin values for debugging…
Serial.begin(9600);  // …set up the serial ouput on 0004 style
}
}

// Main program
void loop()
{
i += 1;      // Increment counter
if (i < 255) // First phase of fades
{
redVal   -= 1; // Red down
greenVal += 1; // Green up
blueVal   = 1; // Blue low
}
else if (i < 509) // Second phase of fades
{
redVal    = 1; // Red low
greenVal -= 1; // Green down
blueVal  += 1; // Blue up
}
else if (i < 763) // Third phase of fades
{
redVal  += 1; // Red up
greenVal = 1; // Green low
blueVal -= 1; // Blue down
}
else // Re-set the counter, and start the fades again
{
i = 1;
}

analogWrite(redPin,   redVal);   // Write current values to LED pins
analogWrite(greenPin, greenVal);
analogWrite(bluePin,  blueVal);

if (DEBUG) { // If we want to read the output
DEBUG += 1;     // Increment the DEBUG counter
if (DEBUG > 10) // Print every 10 loops
{
DEBUG = 1;     // Reset the counter

Serial.print(i);       // Serial commands in 0004 style
Serial.print(“\t”);    // Print a tab
Serial.print(“R:”);    // Indicate that output is red value
Serial.print(redVal);  // Print red value
Serial.print(“\t”);    // Print a tab
Serial.print(“G:”);    // Repeat for green and blue…
Serial.print(greenVal);
Serial.print(“\t”);
Serial.print(“B:”);
Serial.println(blueVal); // println, to end with a carriage return
}
}
delay(wait); // Pause for ‘wait’ milliseconds before resuming the loop
}

ATtiny for your garden

This post is largely theoretical: Finished project here

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. Of pourse 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 will embed their ‘spikes’ in plaster of paris to give a more even reading of moisture. Most people however will just 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 chanage 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 file 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.

Also, avoid using copper. Copper seems to be pretty good in actually picking up voltages from the earth whcih may cause some weird effects when you are trying to read the resistance. You may get complte different readings if you reverse the connection to those copper spikes.

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.

 /*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
*
If the humidity sensor gets too dry, the resistance goes up and the voltage on the analog pin goes down. As a result the pump relais is activated. The sensor measures ca 10 kOhm if the soil is moist enough. The reading therefore should be at 512 (with a pull down resistor of 10k).
* If the temperature goes down the resistance of the NTC goes up and therefore the voltage on the analogue port goes down and a relay can be switched (for heating or for ventilation). In my case the resistance should be between 12.5 and 10k Ohm (21-23 degrees Centigrade) the reading on the analogue port thus must be kept between 512 and 455.

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 definitions
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
//This function samples 5 times and averages
// 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 corresponded better with those temperatures (apparently theory and practice do not always match)
*/

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

Using a microcontroller however has advantages over an op amp because it is easier to add functions (e.g. time) or look at things like ‘is the reservoir still full, won’t my pump run dry?’. With an op amp that means ‘extra parts’ (actually, that is not always necessary: Check here)