High Voltage programming/Unbricking for Attiny

Yikes! Invalid device signature

It sometimes happens: you are trying to program an Attiny and you get an error. There might be many causes for errors: you may not have selected the proper board, your programmer might be rotten, you did something wrong or the connections of your programmer are not correct, but sometimes it can be caused by the wrong bits being set in your Attiny: e.g. you set pin 1 (reset) to be an I/O pin. That makes it impossible to program it with ISP, or you set the wrong oscillator. For these cases a High Voltage programmer can be of help.
It happened to me when i was trying to ‘burn the bootloader’ on my attiny85 (there is no bootlaoder for the attiny 85 but this means setting the fuses). My  computer’s memory was kinda full when I  was busy and suddenly something crashed and I got the dreaded error message Yikes! Invalid device signature. As said, often this is because of a bad connection, but another chip didnt have that problem so I knew something was very wrong.
Time to build a High Voltage Programmer
Below you will see such a circuit. It is fairly simple: 6 resistors, a transistor  a DIL foot  and a 12 Volt source from somewhere. As I  didn’t expect to have to use the HVP often, I opted for a battery, but as I displaced it, I ended up using a 75 euroct 5 to 12 Volt converter that I plugged in.

Circuit
Circuit

 

 

 

 

 

 

 

 

 

And this is an easy way to build it on stripboard.

High Voltage programmer Stripboard
On Stripboard
The finished board ready to use
The finished board

 

 

 

 

 

 

 

I have built it such that it sticks into the D8-D13,ground header of an Arduino UNO. The only thing  you need to do is to attach a 12 Volt battery, or an other 12 Volt source.
There are various programs that can reset the bits back to factory setting. Below you will find 2 of them that all  go back to some initial work by Jos Keyzer.
The first one expects you to  set the  factory bits in the program depending on the chip you are using, the 2nd program actually reads what chip you are using, so I ended up using that one. Both programs  start after you send a random character to the serial port.

 

 

 

 

 

 

 

 

 

fuseburningWell, as it turned out, my fuses were set for E4 and DF. That means that the Attiny was expecting a 128 kHz oscillator signal. No idea how that happened as I have disabled that choice in my menu so I guess it happened coz of my computer crashing. We will never know, but the HVP set it back to factory settings: i.e. 8MHz internal oscillator with a prescaler of 8.
After that, I could just program my Attiny again.

In principle this programmer can be used for Attiny 15 and 12 as well, but as far as I recall they have some wires crossed, so you would need to make a hardware change (connect the D12 resistor to pin 3 instead of pin2), but Attiny 13/25/45/85 should work like a charm.
It can also be done on the 24/44/84 series, but they need a 14 pins DIL:
fusebit

 

 

 

5 to 12 Volt converterShould you need a cheap 12 Volt source, consider this 5 to 12 Volt converter

 

 

 

 

 

Program:

// AVR High-voltage Serial Programmer
// Originally created by Paul Willoughby 03/20/2010
// http://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/
// Inspired by Jeff Keyzer http://mightyohm.com
// Serial Programming routines from ATtiny25/45/85 datasheet

// Desired fuse configuration
#define  HFUSE  0xDF   // Defaults for ATtiny25/45/85
#define  LFUSE  0x62 
// For Attiny13 use
// #define HFUSE 0xFF
// #define LFUSE 0x6A  


#define  RST     13    // Output to level shifter for !RESET from transistor to Pin 1
#define  CLKOUT  12    // Connect to Serial Clock Input (SCI) Pin 2
#define  DATAIN  11    // Connect to Serial Data Output (SDO) Pin 7
#define  INSTOUT 10    // Connect to Serial Instruction Input (SII) Pin 6
#define  DATAOUT  9    // Connect to Serial Data Input (SDI) Pin 5 
#define  VCC      8    // Connect to VCC Pin 8

int inByte = 0;         // incoming serial byte Computer
int inData = 0;         // incoming serial byte AVR

void setup()
{
  // Set up control lines for HV parallel programming
  pinMode(VCC, OUTPUT);
  pinMode(RST, OUTPUT);
  pinMode(DATAOUT, OUTPUT);
  pinMode(INSTOUT, OUTPUT);
  pinMode(CLKOUT, OUTPUT);
  pinMode(DATAIN, OUTPUT);  // configured as input when in programming mode
  
  // Initialize output pins as needed
  digitalWrite(RST, HIGH);  // Level shifter is inverting, this shuts off 12V
  
  // start serial port at 9600 bps:
  Serial.begin(9600);
  
  establishContact();  // send a byte to establish contact until receiver responds 
  
}


void loop()
{
  // if we get a valid byte, run:
  if (Serial.available() > 0) {
    // get incoming byte:
    inByte = Serial.read();
    Serial.println(byte(inByte));
    Serial.println("Entering programming Mode\n");

    // Initialize pins to enter programming mode
    pinMode(DATAIN, OUTPUT);  //Temporary
    digitalWrite(DATAOUT, LOW);
    digitalWrite(INSTOUT, LOW);
    digitalWrite(DATAIN, LOW);
    digitalWrite(RST, HIGH);  // Level shifter is inverting, this shuts off 12V
    
    // Enter High-voltage Serial programming mode
    digitalWrite(VCC, HIGH);  // Apply VCC to start programming process
    delayMicroseconds(20);
    digitalWrite(RST, LOW);   //Turn on 12v
    delayMicroseconds(10);
    pinMode(DATAIN, INPUT);   //Release DATAIN
    delayMicroseconds(300);
    
    //Programming mode
    
    readFuses();
    
    //Write hfuse
    Serial.println("Writing hfuse");
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x40, 0x4C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, HFUSE, 0x2C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x74);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x7C);
    
    //Write lfuse
    Serial.println("Writing lfuse\n");
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x40, 0x4C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, LFUSE, 0x2C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x64);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x6C);

    readFuses();    
    
    Serial.println("Exiting programming Mode\n");
    digitalWrite(CLKOUT, LOW);
    digitalWrite(VCC, LOW);
    digitalWrite(RST, HIGH);   //Turn off 12v
  }
}


void establishContact() {
  while (Serial.available() <= 0) {
    Serial.println("Enter a character to continue");   // send an initial string
    delay(1000);
  }
}

int shiftOut2(uint8_t dataPin, uint8_t dataPin1, uint8_t clockPin, uint8_t bitOrder, byte val, byte val1)
{
	int i;
        int inBits = 0;
        //Wait until DATAIN goes high
        while (!digitalRead(DATAIN));
        
        //Start bit
        digitalWrite(DATAOUT, LOW);
        digitalWrite(INSTOUT, LOW);
        digitalWrite(clockPin, HIGH);
  	digitalWrite(clockPin, LOW);
        
	for (i = 0; i < 8; i++)  {
                
		if (bitOrder == LSBFIRST) {
			digitalWrite(dataPin, !!(val & (1 << i)));
                        digitalWrite(dataPin1, !!(val1 & (1 << i)));
                }
		else {
			digitalWrite(dataPin, !!(val & (1 << (7 - i))));
                        digitalWrite(dataPin1, !!(val1 & (1 << (7 - i))));
                }
                inBits <<=1;
                inBits |= digitalRead(DATAIN);
                digitalWrite(clockPin, HIGH);
		digitalWrite(clockPin, LOW);
                
	}

        
        //End bits
        digitalWrite(DATAOUT, LOW);
        digitalWrite(INSTOUT, LOW);
        digitalWrite(clockPin, HIGH);
        digitalWrite(clockPin, LOW);
        digitalWrite(clockPin, HIGH);
        digitalWrite(clockPin, LOW);
        
        return inBits;
}

void readFuses(){
     //Read lfuse
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x04, 0x4C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x68);
    inData = shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x6C);
    Serial.print("lfuse reads as ");
    Serial.println(inData, HEX);
    
    //Read hfuse
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x04, 0x4C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x7A);
    inData = shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x7E);
    Serial.print("hfuse reads as ");
    Serial.println(inData, HEX);
    
    //Read efuse
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x04, 0x4C);
    shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x6A);
    inData = shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x6E);
    Serial.print("efuse reads as ");
    Serial.println(inData, HEX);
    Serial.println(); 
}

An other program is:

    // AVR High-voltage Serial Fuse Reprogrammer
    // Adapted from code and design by Paul Willoughby 03/20/2010
    // http://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/
    // Fuse Calc:
    //   http://www.engbedded.com/fusecalc/

    #define  RST     13    // Output to level shifter for !RESET from transistor
    #define  SCI     12    // Target Clock Input
    #define  SDO     11    // Target Data Output
    #define  SII     10    // Target Instruction Input
    #define  SDI      9    // Target Data Input
    #define  VCC      8    // Target VCC

    #define  HFUSE  0x747C
    #define  LFUSE  0x646C
    #define  EFUSE  0x666E

    // Define ATTiny series signatures
    #define  ATTINY13   0x9007  // L: 0x6A, H: 0xFF             8 pin
    #define  ATTINY24   0x910B  // L: 0x62, H: 0xDF, E: 0xFF   14 pin
    #define  ATTINY25   0x9108  // L: 0x62, H: 0xDF, E: 0xFF    8 pin
    #define  ATTINY44   0x9207  // L: 0x62, H: 0xDF, E: 0xFFF  14 pin
    #define  ATTINY45   0x9206  // L: 0x62, H: 0xDF, E: 0xFF    8 pin
    #define  ATTINY84   0x930C  // L: 0x62, H: 0xDF, E: 0xFFF  14 pin
    #define  ATTINY85   0x930B  // L: 0x62, H: 0xDF, E: 0xFF    8 pin

    void setup() {
      pinMode(VCC, OUTPUT);
      pinMode(RST, OUTPUT);
      pinMode(SDI, OUTPUT);
      pinMode(SII, OUTPUT);
      pinMode(SCI, OUTPUT);
      pinMode(SDO, OUTPUT);     // Configured as input when in programming mode
      digitalWrite(RST, HIGH);  // Level shifter is inverting, this shuts off 12V
      Serial.begin(19200);
    }

    void loop() {
       if (Serial.available() > 0) {
        Serial.read();
        pinMode(SDO, OUTPUT);     // Set SDO to output
        digitalWrite(SDI, LOW);
        digitalWrite(SII, LOW);
        digitalWrite(SDO, LOW);
        digitalWrite(RST, HIGH);  // 12v Off
        digitalWrite(VCC, HIGH);  // Vcc On
        delayMicroseconds(20);
        digitalWrite(RST, LOW);   // 12v On
        delayMicroseconds(10);
        pinMode(SDO, INPUT);      // Set SDO to input
        delayMicroseconds(300);
        unsigned int sig = readSignature();
        Serial.print("Signature is: ");
        Serial.println(sig, HEX);
        readFuses();
        if (sig == ATTINY13) {
          writeFuse(LFUSE, 0x6A);
          writeFuse(HFUSE, 0xFF);
        } else if (sig == ATTINY24 || sig == ATTINY44 || sig == ATTINY84 ||
                   sig == ATTINY25 || sig == ATTINY45 || sig == ATTINY85) {
          writeFuse(LFUSE, 0x62);
          writeFuse(HFUSE, 0xDF);
          writeFuse(EFUSE, 0xFF);
        }
        readFuses();
        digitalWrite(SCI, LOW);
        digitalWrite(VCC, LOW);    // Vcc Off
        digitalWrite(RST, HIGH);   // 12v Off
      }
    }

    byte shiftOut (byte val1, byte val2) {
      int inBits = 0;
      //Wait until SDO goes high
      while (!digitalRead(SDO))
        ;
      unsigned int dout = (unsigned int) val1 << 2;
      unsigned int iout = (unsigned int) val2 << 2;
       for (int ii = 10; ii >= 0; ii--)  {
        digitalWrite(SDI, !!(dout & (1 << ii)));
        digitalWrite(SII, !!(iout & (1 << ii)));
        inBits <<= 1;         inBits |= digitalRead(SDO);
         digitalWrite(SCI, HIGH);
         digitalWrite(SCI, LOW);
       } 
       return inBits >> 2;
    }

    void writeFuse (unsigned int fuse, byte val) {
      shiftOut(0x40, 0x4C);
      shiftOut( val, 0x2C);
      shiftOut(0x00, (byte) (fuse >> 8));
      shiftOut(0x00, (byte) fuse);
    }

    void readFuses () {
      byte val;
            shiftOut(0x04, 0x4C);  // LFuse
            shiftOut(0x00, 0x68);
      val = shiftOut(0x00, 0x6C);
      Serial.print("LFuse "); // this line may show up corrupted in some browsers it is a Serial.print("LFuse: ");
      Serial.print(val, HEX);
            shiftOut(0x04, 0x4C);  // HFuse
            shiftOut(0x00, 0x7A);
      val = shiftOut(0x00, 0x7E);
      Serial.print(", HFuse: ");
      Serial.print(val, HEX);
            shiftOut(0x04, 0x4C);  // EFuse
            shiftOut(0x00, 0x6A);
      val = shiftOut(0x00, 0x6E);
      Serial.print(", EFuse: ");
      Serial.println(val, HEX);
    }

    unsigned int readSignature () {
      unsigned int sig = 0;
      byte val;
      for (int ii = 1; ii < 3; ii++) {
              shiftOut(0x08, 0x4C);
              shiftOut(  ii, 0x0C);
              shiftOut(0x00, 0x68);
        val = shiftOut(0x00, 0x6C);
        sig = (sig << 8) + val;
      }
      return sig;
    }

---

One may find this article interesting as well
If you want to see how this board (in a skightly  more luxurious build) is used in practice you may want to check a youtube video by Ralph Bacon.

Advertisement

Using Attiny with RCSwitch and RemoteSwitch

It is possible to use the RCSwitch library in combination with an Attiny85 in order to send data.

/* 
Example for different sending methods
http://code.google.com/p/rc-switch/ 
*/ 
#include <rcswitch.h>
RCSwitch mySwitch = RCSwitch();
 
void setup() {
   // Transmitter is connected to Attiny Pin PB3  <--
   // That is physical pin2
  mySwitch.enableTransmit(3);
 
  // Optional set pulse length.
  // mySwitch.setPulseLength(320);
  // Optional set protocol (default is 1, will work for most outlets)
  // mySwitch.setProtocol(2);
  // Optional set number of transmission repetitions.
  // mySwitch.setRepeatTransmit(15);
}
 
void loop() {
  /* See Example: TypeA_WithDIPSwitches */
  mySwitch.switchOn("10101", "10000");
  delay(1000);
  mySwitch.switchOff("10101", "10000");
  delay(2000);
 }

The sketch uses 4754 bytes of program memory (has 8k) and 203 bytes (has 512 bytes) of SRAM. Receiving data  with an Attiny85, using the RCSwitch library is NOT possible as the receiver functions have been explicitly #define’d out for the Attiny

The other main 433MHz library, the RemoteSwitch library, does not compile for the Attiny because even when transmitting, a call is made to the receiver library, that has SerialPrint routines. These will not compile for the Attiny85. However, the modification of the RemoteSwitch library made by Jeroen Meijer, does compile on, and works with the Attiny 85 and 45. (No longer on GitHub but here)

/*
Switches the 3 devices from the Elro AB440
and 3 devices of the SelectRemote1728029
*/
#include <RemoteSwitch.h>
// declaration of the Elro AB440 creator
ElroAb440Switch ab440Switch(3);
//declaration of the SelectRemote creator
BlokkerSwitch3 blokkerTransmitter(7);
void setup() {
}
void loop() {
//Switch on SelectRemote 1728029 device 1
blokkerTransmitter.sendSignal(1,true);
blokkerTransmitter.sendSignal(2,true);
blokkerTransmitter.sendSignal(3,true);
//Switch on AB440-device B on system code 29.
//dip switch 1-5 is on-off-on-on-on = 10111... but is read as 11101='29'
ab440Switch.sendSignal(29,'A',true);
ab440Switch.sendSignal(29,'B',true);
ab440Switch.sendSignal(29,'C',true);
delay(2000);
blokkerTransmitter.sendSignal(1,false);
blokkerTransmitter.sendSignal(2,false);
blokkerTransmitter.sendSignal(3,false);
ab440Switch.sendSignal(29,'A',false);
ab440Switch.sendSignal(29,'B',false);
ab440Switch.sendSignal(29,'C',false);
delay(2000);
}

This program compiles in 2622k of program memory and 61 bytes of dynamic memory

Simple Attiny programmer on Arduino

programmer1After I was really fed up always making the MOSI, MISO, Reset, SCK, Vcc and Ground connection with wires in order to program an Attiny85/45/13, I bought an USBASP programmer (real cheap).
Didn’t get that to work, always got the dreaded ‘Yikes, invalid device signature’ message, that is usually a sign of a bad connection. Couldn’t quickly locate it, maybe a rotten cable, but I didn’t want to lose more time so I put together a quick and simple Attiny programmer.

I know there are dozens of circuits and designs floating around on the internet, but I just never got around to actually making one, so if you are like me, this is just a little push to tell you to ‘just do it’ it takes maybe 10 minutes from scrap to programmer.

I got my inspiration from a Spanish site from a guy that inspired me to build my first Arduino, but when I checked there, the stripboard-illustrations of his programmer were gone.
No sweat, just simple to make one myself.
programmer2The idea is a little piece of stripboard that plugs in the necessary headers of the Arduino. A 20hole x 9 strips piece is enough, but I made that 22 holes, just to give a couple of small ‘wings’ to help me pull it out.
BOM
20×9 stripboard
male header 7 pins
male header 6 pins
2 LEDs
2 resistors 560-1k
1 electrolytic capacitor 10 uF (in fact I used a 33uF, that worked as well)
8 pins dil IC holder

The Arduino ISP offers 3 LEDs to keep track of status. I implemented 2: The heartbeat and the Error (D9 and D8). Adding the ‘Programming’ LED is a bit more of a hassle because that needs D7 which is just at the other side of the non standardized gap in the Arduino headers, But if you want you could add a slightly bend pin to slot into D7.
The board plugs in easily, but is not completely straight because of how the arduino headers line up, but it works great.

Just a word on the capacitor. In order to avoid that the Arduino will reset when the attiny program is uploaded, the reset pin must be kept high. there are two ways how that is usually done: a resistor (120 Ohm) to the Vcc or a capacitor (10uF) between earth and Reset. Both work well.
However, the capacitor is also the reason that you cannot upload the ArduinoISP program with the board inserted, so you have to pull it out. You could of course add a jumper to the board, or a small switch, to temporarily disconnect the capacitor

Important!!!
Make double sure and double check that you have not forgotten to make all of the necessary cuts in the stripboard. Check it after each cut, use a multimeter if necessary.
After you soldered it all up, check again that there are no stray connections or shorts. The reason for this is that you do not want to short any pins on your arduino or connect any pins to where they shouldn’t be connected. You do not want any of your Digital-Out pins to be connected to Ground when you pull them high.

Anyway, build it yesterday and works great. I have programmed more Attiny’s in half a day than I did in my life before.

For people that are new to programming an Attiny: The steps are as follows.
Make sure you have an Attiny core installed (There are many, try here or here , you also may want to check here)
Remove the programming board and upload the ‘ArduinoISP’ program to your Arduino.
Insert the program board and load the desired Attiny sketch.
Choose the proper Attiny setting under ‘Tools-Board’
Go to ‘File-Upload Using Programmer’ in older IDE-versions this might be just ‘Upload’
You may want to check here or here for more information on the programming.
important is to know that even with a succesful programming, you will get two error messages:
“avrdude: please define PAGEL and BS2 signals in the configuration file for part ATtiny85”.
just ignore those messages.
If you are perfectionist, you can solve those error messages by edit file avrdude.conf, found in

programmer4programmer3(windows) C:\Program Files (x86)\arduino-1.0.6\hardware\tools\avr\etc
(Ubuntu) Home\Arduino-1.06\hardware\tools
Find the word Attiny85 and go down a few lines up to:

chip_erase_delay = 4500;
and add below these lines:
# Added to eliminate the bug to build.
pagel = 0xB3;
bs2 = 0xB4;
Find this section lower down and add the highlighted code:
memory “lock”
size = 1;
write = “1 0 1 0 1 1 0 0 1 1 1 x x x x x”,”x x x x x x x x 1 1 i i i i i i”;
read = “0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0″,”0 0 0 0 0 0 0 0 o o o o o o o o”;
min_write_delay = 9000;
max_write_delay = 9000;
;
Thanks baelza.bubba for putting me on track for that. If you have trouble understanding, check the two pictures. Understand that you dont HAVE to alter the file, it will all work. The alteration is just to get rid of the error messages.

Just a tip: when I was programming Attiny’s I used the setting for the 8MHz internal clock and found my timings to be far off. coincidence or not, it seemed they were a factor 8 off (slower). When I tried at 1 MHz, It all seemed OK again. This is caused by the following: The standard operating speed for the Attiny is 1Mhz, and it gets that by dividing the internal oscillator by 8. Where I presumed the 8MHz setting would set the proper fuses to tell the Attiny it shouldnt divide the internal oscillator anymore, it seems that it may not do that. The solution would be to first set the proper fuses by selecting ‘burn bootloader’ That doesnt burn the bootloader, but it does set the right fuses.

Just another tip: There are various Attiny cores and not all are full implementations. If you intend to use the ‘OneWire’ library, you may run into errors like :

/libraries/OneWire/OneWire.cpp:105: error: ‘digitalPinToBitMask’ was not declared in this scope
/libraries/OneWire/OneWire.cpp:106: error: ‘digitalPinToPort’ was not declared in this scope
/libraries/OneWire/OneWire.cpp:106: error: ‘portInputRegister’ was not declared in this scope

There is an easy fix for that:

• Locate and open OneWire.h
• Locate this section of code towards the top…

#if ARDUINO >= 100
#include “Arduino.h” // for delayMicroseconds, digitalPinToBitMask, etc
#else #include “WProgram.h” // for delayMicroseconds
#include “pins_arduino.h” // for digitalPinToBitMask, etc
#endif

• Change into:

#if ARDUINO >= 100
#include “Arduino.h” // for delayMicroseconds, digitalPinToBitMask, etc
#include “pins_arduino.h” // for digitalPinToBitMask, etc
#else #include “WProgram.h” // for delayMicroseconds
#include “pins_arduino.h” // for digitalPinToBitMask, etc
#endif

• Save and close OneWire.h

• No need to restart the Arduino IDE
The problem is not with the library but with the core. It is just easier to fix in the library

And yet another tip: If you are trying to use the Attiny with the LCD library from F. Malpertida, you may run in some error messages that have to do with the print.h and print.cpp files of the core.
These can be fixed. There are two publications on how to do that, but most likely, using the proper core will solve that without having to change anything.   Useful info also here.

Sending wireless data from one Attiny85 to another

schema-manchesterI have struggled a lot with sending RF data between two Attiny85 chips, so I thought it might be helpful if I just explain how I did it. There are a number of such projects being described on the internet, but yet, it didn’t prove to be such a straightforward project, which i found to be mostly due to not using the right libraries and cores.

BOM
Transmitter:
Attiny85 – 0.82 euro/10
10k resistor
433 MHz transmittermodule – 1 euro per set
mini breadboard – 58 cts/piece

Receiver:
Attiny85
10k resistor
433 MHz Receiver module
mini breadboard
Optional: 2 wire LCD

There are two main libraries used to send and receive data on an Arduino platform: VirtualWire and Manchester code.
As Virtualwire is a bit older, no longer supported, and supposedly can only send characters (though there is a way around that) I decided to use Manchester code.

To make a long story short, it didn’t work. I had the MANCHESTER.h and MANCHESTER.cpp file and ran into a lot of trouble, until I discovered that was the wrong/old library, you need the Manchester.h and Manchester.cpp file from here. When I used that I got my transmitter to work, I could send data to an Arduino, that was already a big relief.

However……. whatever I did, I did not get my receiver to work. In testing something on an Attiny it is very frustrating to just stare at an LED that is supposed to light, but doesnt, without knowing what and why and how.
So i decided to add an LCD to the Attiny, so at least I could see what was happening..
However, the LCD on my attiny gave me other problems… and when I solved those, that proved to be the solution for my receive problem as well: I was using the wrong core. I was using the ‘tiny core’ rather than the ‘attiny core’
The latter is the one from Highlowtech.
NOTE: it is generally accepted that the ‘tiny core‘ works with the Manchester code and the attiny core does not, so it is possible that I mixed up the two. However, I had a line added to the Attiny core that I forgot about that will make it work with the Manchester code. Open up  the “variants/tiny8/pins_arduino.h” file and add the line”#define __AVR_Attinyx5__”

attiny_manchester

The build of the transmitter is easy:
send-manchester-breadboard
Plug the attiny chip into your breadboard,
Connect a 433 MHz Transmitter module with its data in to pin PB0 (that is pin 5 on the chip).
Connect Vcc and Ground of the transmitter module to Vcc (pin 8) and ground (pin 4) of the Attiny
Insert a 10 k resistor between pin 1 (Reset) and pin 8 (Vcc)
Connect Vcc and ground to 5 Volt
Take a 17 cm stiff wire and attach that to the antenna hole of the transmitter.
use the following program:

#include <Manchester.h>
/*
  Manchester Transmitter example
  In this example transmitter will send one 16 bit number
  per transmission.
  Try different speeds using these constants, your maximum
  possible speed will depend on various factors like transmitter 
  type, distance, microcontroller speed, ...
  MAN_300 0
  MAN_600 1
  MAN_1200 2
  MAN_2400 3
  MAN_4800 4
  MAN_9600 5
  MAN_19200 6
  MAN_38400 7
*/
  #define TX_PIN 0  //pin where your transmitter is connected
  uint16_t transmit_data = 2761;
  void setup() {
  man.setupTransmit(TX_PIN, MAN_1200);
} 
void loop() {
  man.transmit(transmit_data);
  delay(200);
}

Just a word on the 2716 that I am sending. The program is not mine, I found it as such and since it worked and I was very happy to see the ‘2716’ appear in my Arduino after days of fruitless trying, I decided to leave it there as a tribute. (it might have found it here)

Building the receiver is easy:
receive-manchester-breadboardPut the programmed attiny chip in your breadboard.
Connect a 10 k resistor between pin 1 and pin 8
Put your 433 MHz Receiver module in the breadboard
Connect the datapin (usually either one of the two middle pins) to pin PB1 (physical pin6) on the attiny.
Connect the Vcc and Ground of the transmitter module to Vcc (pin 8) and Ground (pin4) of the Attiny
Connect the LCD interface to pin PB0 (pin 5) (Clock) and pin PB2 (pin 7) (Data/Enable)
Connect Vcc and ground of the LCD to Vcc and Ground.
Attach a 17 cm (1/4 lambda for 433 MHz) to the Receiver module.
Use the following program in your chip:

#include <Manchester.h>
#include <LiquidCrystal_SR.h>
LiquidCrystal_SR lcd(0,2,TWO_WIRE);
/*
  Manchester Receiver for Attiny
  In this example receiver will receive one 16 bit number per
  transmittion to switch a relay on or off. 
  try different speeds using these constants, your maximum possible
  speed will depend on various factors like transmitter type, 
  distance,  microcontroller speed, ...

  MAN_300 0
  MAN_600 1
  MAN_1200 2
  MAN_2400 3
  MAN_4800 4
  MAN_9600 5
  MAN_19200 6
  MAN_38400 7
*/
#define RX_PIN 1 //= pin 6
uint8_t moo = 1;
void setup()
{
lcd.begin(16,2);
lcd.home();
lcd.print("Receive");
lcd.setCursor(0,1);
  man.setupReceive(RX_PIN, MAN_1200);
  man.beginReceive();
}
void loop() {
  if (man.receiveComplete()) {
    uint16_t m = man.getMessage();
    man.beginReceive(); //start listening for next message right
                        //after you retrieve the message
   moo = ++moo % 2;
    lcd.print(m);
  }
}

The LCD is ofcourse optional. You can use an LED that gets the variable ‘moo’ send to its pin and thus will flash on complete receival of the signal, but I just wanted to make sure that what I got was what I sent

lcd-manchesterThe results are good, no garbage is being received, but the reach is about 4-5 meters, with the antennas. The antenna on the receiver only makes a small difference. The one on the Transmitter makes a big difference.
Still, this is surprising as the transmitter module is capable of switching remote switches at greater distances even on different floors.

With regard to the length of the Antennas:

as f * λ= c (frequency * wavelength = lightspeed)
λ=c/f
λ= 299,792,458 /433,920,000
The wavelength is 0.690893386 meters.
Antenna would be λ/4= 0.172723346 meters (17.3 cm)
That is about 6.80013 inches.
If you would be using 315 MHz modules, the antenna would be: 0.238 m or 23.8 cm

You can also use the wavelength calculator.
Supposedly the transmitter module can take 12 Volts and still be steered by a 5 Volt microcontroller pin and will have a further reach then. Increasing the voltage on the receiver of course makes no difference

Once you have established The link between two attiny’s, linking one of them with an arduino should not be a problem. I use mine to send data to an Arduino ( e,g. temperature, or the status of a tripwire), or to receive data from an Arduino to steer a servo or an RGB LED.

If you want to trigger a relay, you would do it like this:

void loop() {
  if (man.receiveComplete()) {
    uint16_t m = man.getMessage();
    man.beginReceive(); //start listening for next message 
                        //right after you retrieve the message
    moo = ++moo % 2;
    lcd.print(m);
    if (m==2761){digitalWrite(Relay,HIGH);}
    if (m==0000){digitalWrite(Relay,LOW);}
  }

Ofcourse you need to define the Relay pin in the ‘setup’

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)

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)