Using the Attiny’s Reset Pin without setting Fuse Bits

attiny13_reset The Attiny 13, 25, 45, 85 are charming little chips that as the name says it, are tiny. They are supposed to have 6 I/O pins but  pin number one  (PB5/ADC0) doubles as RESET pin and in order to use it as an I/O pin, one needs to set the proper fuses in the chip. That is not so difficult but the problem is that once that fuse is set, the chip cannot be reprogrammed  by SPI, but needs a High Voltage Programmer that first needs to reset  the specific fusebit again.
Though it was a bit unclear to me what the required Low Voltage is that the Reset pin needs for a Reset, it seems that it is lower than what is  generally interpreted as a ‘LOW’.
That potentially opens possibilities to use the range in between +Vcc and Vreset for input, without resetting the chip
To test this I used an Attiny13, hooked up an LED  and resistor to PB0 and connected the middle contact of a 25k variable resistor to Pin 1 and the outer contacts to Vcc and 0V respectively.
I then loaded the Attiny with the following program:

// Using the Reset pin as ADC0

const int Led = 0;
int x=0;
void setup() {
pinMode(Led, OUTPUT);
}

void loop() {
digitalWrite(Led,HIGH);
x=analogRead(0);
delay(x);
digitalWrite(Led,LOW);
delay(x);
}

When the variable resistor is turned all the way up to the +Vcc rail, the LED flashes in a steady rhythm. When I turned down the variable resistor, the flashing frequency went up, i.e. a faster flashing LED.. as expected. This went on till the LED suddenly stopt flashing (as the RESET function kicked in).
It turns out that the point of reset was at  9K Ohm. Which is equal to 5 *(9/25)= 45/25=9/5=2.2 Volt.
That is generally not much different from what is considered a LOW and it is a bit higher than what I understood the Vreset to be.

As ofcourse you cant have a circuit  that is always on the brink of resetting, we need to build in some form of protection: something that keeps the voltage on pin 1 from hitting 2.2 Volt.
attiny13_reset2Let’s consider the following circuit. Suppose that the lowest resistance we  measure under the light circumstances we are  using is 1k. Then we know that the current through that 1 k must be minimally 2.2 mA to stay above the Reset voltage. Hence the total resistance of the LDR + the  Resistor must be  5/2.2=2.27k, hence the resistor must be not more than 1.27k. The closest E12 values are 1.2 k and 1.5 k and we should choose 1.2k to be safe.

Of course one can use the circuit with the resistor and LDR interchanged, but then  it is much harder to calculate a safe resistor as in darkness the value of the LDR may go up to several Mega Ohm, calling for a resistor that is in that same range.

Given the fact that the Reset level  is on 2.2 Volt, I have not bothered to try if it would work with digitalRead

Advertisements

Flashing an LED with Attiny13: Timer, Watchdog timer and Sleep

Counting

led_flasherI have written two articles on timers in one article I discussed timers in general and in the 2nd article I went more into a couple of practical examples.
As I recently started to use the Attiny13 a lot, I thought it would be good to write something specifically on  how to use timers in the Attiny13.
As usual a Led flasher is a good example and we will start using the Timer OverFlow register to achive our task but also give an example using the Watchdog Timer.

Before I go on, if you are using the well known Smeezekitty core for the Attiny13 you are in trouble. In order to correct the timing, John Smeezekitty uses the Timer0 Overflow vector in his core for the Attiny13. You may want to use this core. If you continue to use that core you may want to  make this modification as well.
Also, if you would want to adopt this for another Attiny chip, apart from changing some of the register names (like TIMSK rather than TIMSK0), you may find that the Timer0 OverFlow Vector is  used in a variety of other cores. For the Attiny85 for instance, you may encounter a ‘Vector_5’  error, which is the Attiny85’s version of the Attiny13’s ‘Vector_3’error. Vector5 and Vector3 are the Timer0 Overflow vectors for resp the Attiny85 and the Attiny13
The ATtiny13 datasheet lists the timer interrupts (and other interrupts) in §9.1.
In Table 9.1 we find the Timer/Counter Overflow interrupt vector.
It is listed as TIM0_OVF. We then know the Interrupt Service Routine (ISR) we need to call in our program is “TIM0_OVF_vect”

The next chapter with information on the behaviour of the timer is in §11.7.1, it says that in “normal mode” (WGM02:0 = 0), the counter counts up, and once it gets to the “top” (0xFF as it is an 8 bit timer) it resets back to zero and the TOV0 (Timer/Counter Overflow Flag) bit is set.
How we enable the Timer/Counter0 Overflow Interrupt is found in §11.9.6: it states that if you enable the Timer/Counter0 Overflow Interrupt by setting the TOIE0 bit to 1 in the TIMSK0 register and interrupts are enabled, whenever the TOV0 bit is set, the interrupt will occur.

The timer that we are using to generate an interrupt in the Attiny13 is an 8-bit register. Unless ‘prescaled’ (see later) it increments once per clock cycle.  As it is an 8-bit register its maximum value is 255, therefore once every 256 clock cycles the timer reaches its maximum value and resets back to zero.  When this occurs the Timer/Counter0 Overflow Interrupt will be generated, and we can can catch this in and Interrupt Service Routine (ISR) in our program, to respond to the generated interrupt, and flash a LED.

The Attiny13, knows 3 major frequencies: 9.6 MHz, 4.8 Mhz and 1.2 MHz.
The frequency of 1.2 MHz is obtained by dividing  the internal 9.6 MHz clockspeed by 8 by setting some fuses. Often, when sold, those fuses are set and your brand new Attiny13  is set to the 1.2 Mhz clockspeed.
So, how often does that Timer Interrupt Overflow occur then?
Well that is easy: at 1.2 MHz, the length of a clock cycle is 0.00083333333333333 ms. As the interrupt is set when the timer register reaches 255, that means there are 256 Clock cycles (remember, we are counting from 0). Thus the interrupt occurs every 0.213333333 milisecs which is 1000/0.213333333=4687.5 per second. (I used a bit of an extensive calculation here to  make it clear. Normally you would just divide the frequency by the register (1.2Mhz/256=4687.5)).  That is a bit fast to  see the led flashing.

The standard practice then is to add a counter in the ISR and toggle the LED once the counter reaches a certain number of interrupt overflows.  Earlier we calculated that the interrupt will be triggered approx 4688 (1.2 MHz *1/256) times per second. With an 8 bit counter variable we cannot even count to 4688: we can count a max of 256 interrupts. as the interrupts occur every 0.2133333 msecs that would be 54.613333248 msec, which is about 18 times a second. Still a bit fast for a flashing LED.
If we use a 16-bit variable instead: 65536/4688 gives us a maximum flash time of ~13 seconds. So that is one way of doing it, but there is another (better?) way…

Earlier I touched upon ‘prescaling’ the timer.  This slows down the increment of the timer counter by using 1 out of every so many clock cycles to increment the timer counter (see §12.1).   These prescale factors are found in the datasheet in §11.9.2, table 11-9.  The largest slow down or ‘prescale’ factor is 1024 (by setting CS02 and CS00 bits to 1 in the TCCR0B register).

If prescaling is set to1024, the timer register gets incremented 1172 times a second (1.2MHz/1024), and thus overflows 4.6 times per second. With an 8-bit variable to count the overflows we can count to about 56 seconds.

This gives the final code:

volatile byte counter = 0;// interrupt needs volatile variable

ISR(TIMER0_OVF_vect) {
   if (++counter > 5) {// interrupt occurs 4.6 times per second
      // Toggle Port B pin 3 output state
      PORTB ^= 1<<PB3;// Toggle Port PB3
      counter = 0; //reset the counter
   }
}

void setup() {
   // Clear interrupts, just to make sure
   cli();
   // Set up Port B pin 3 mode to output by setting the proper bit
   // in the DataDirectionRegisterB
    DDRB = 1<<DDB3;

   // set prescale timer to 1/1024th 
   TCCR0B |= (1<<CS02) | (1<<CS00);// set CS02 and CS00 bit in TCCR0B

   // enable timer overflow interrupt
   TIMSK0 |=1<<TOIE0;// left shift 1 to TOIE0 and OR with TIMSK
                     //  = set TOIE0 bit
   sei(); //start timer
}
  void loop() {
      // ISR handles the flashing so no code here
   }

Sleeping.. with an alarm clock

With the LED flashing as in the program above, the Attiny13 doesnt do much more than waiting for the right amount of interrupts to pass by and then toggle the LED.
All that time it is using full energy. Now if only we could put it to sleep, only to wake up when the LED needs to be toggled,  theat would save energy consumption and thus extend  a possible battery life..
There are 3 sleep modes of which ‘Power down’ gives the deepest sleep and thus the least energy consumption. In order to wake from sleep the sleeping chip either needs an external interrupt, or it needs to be woken by the watchdog timer. The watchdog timer is described in chaper 8 of the Attiny13 datasheet. Its operation is quite simple: it needs bits set in the Watchdog Timer Control Register (WDTCR) to determine the time it will run and then  it needs to be enabled to start it. When the watchdog timer sends an interrupt, the WDT_vect ISR is called and executed and the program goes back to where it was put asleep.
As we only need to toggle an LED, that can be done in the WDT_vect ISR


ISR(WDT_vect) {
// Toggle PB3 output
PORTB ^= 1<<PB3;
}

void setup(){
// Set PB3 to output
DDRB = 1<<DDB3;
//set timer to 1 sec
WDTCR |= (0<<WDP3) | (1<<WDP2) | (1<<WDP1) | (0<<WDP0);
// set timer to 0.5s
//WDTCR |= (0<<WDP3) | (1<<WDP2) | (0<<WDP1) | (1<<WDP0);

// Set watchdog timer in interrupt mode
WDTCR |= (1<<WDTIE);
WDTCR |= (0<<WDE);

sei(); // Enable global interrupts
}
void loop() {
// Everything is already handled in the ISR
}

This program will  let a led flash every second, or should you desire, flash within a second.
Once we have done that we can put the the  Attiny to sleep, only to be woken up by the Watchdog timer.
To handle the sleep mode we make use of the sleep.h header file that is part of the avr library. In the setup we set the sleepmode to Power Down. with that we in fact set two bits in the MCUCR register, SM[1:0] to 1 resp 0.
Then in the loop we do a call to sleep_mode()
The command sleep_mode() in fact makes a call to three routines:

sleep_enable(); \
sleep_cpu(); \
sleep_disable(); \

sleep_enable() sets the Sleep Enable bit in the MCUCR register
sleep_cpu() issues the SLEEP command
sleep_disable() clears the SE bit.
So what happens is that at the end of the setup routine the watchdog is set and the sleepmode is set. Upon entry in the loop the is put to sleep and kept in sleepmode. When a Watchdog interrupt occurs, the Attiny13 wakes up, the interrupt routine is carried out and the  program jumps back to where it was  put asleep. there it finds the sleepdisable macro that resets the Sleep Enable bit. This is in case  you want to perform more tasks. But as there aren’t any more tasks, the Attiny is brought right back to sleep again, till the next Watchdog interrupt

#include <avr/interrupt.h>
#include <avr/sleep.h>
ISR(WDT_vect) {
// Toggle Port B pin 3 output state
PORTB ^= 1<<PB3;
}

void setup(){
// Set up Port B as Input
DDRB = 0; // usually not necessary but it will save current
// Set Port B pin 3 mode back to output
DDRB = 1<<DDB3;

//set timer to 1 sec
WDTCR |= (0<<WDP3) | (1<<WDP2) | (1<<WDP1) | (0<<WDP0);
// set timer to 0.5s
// WDTCR |= (1<<WDP2) | (1<<WDP0);
// set timer to 4 sec
// WDTCR |= (1<<WDP3);

// Set watchdog timer in interrupt mode
WDTCR |= (1<<WDTIE);
WDTCR |= (0<<WDE);
sei(); // Enable global interrupts

set_sleep_mode(SLEEP_MODE_PWR_DOWN);
}
void loop() {
sleep_mode();   // go to sleep and wait for interrupt...
}

In order to save  even more energy, there is more we can do:  disable the ADC channels and set  all I/O lines to input (but when out of sleep you have to set back the one that toggles the LED).
In the setup routine we can switch the ADC off by the command:

cbi(ADCSRA,ADEN);    // switch Analog to Digitalconverter OFF

or

ADCSRA &= ~(1<<ADEN); //

Which is the same

With the commands:

ADCSRA &= ~(1<<ADEN);     //turn off ADC
ACSR |= _BV(ACD);         // disable analog comparator

I got the following results:
sleep: 6.6uA (0.0066mA)
non sleep: 3600 uA (3.6mA)
That is a factor 600. Of course this was with the LED unplugged as I was only interested in the consumption of the chip. The LED would add another 4-5 mA.
Disabling the analog input buffers with the command:

DIDR0 |= (1<< AIN1D)|(1 << AIN0D);//disable

didn’t change the power-consumption. It should be possible to go even lower with lower chip frequency (mine was 9.6Mhz) and lower voltage (mine was 5V). On 3.3 Volts the current went to 5uA. Mind you that also the use of the WDT ‘contributes significantly to the power consumption’ according to the datasheet.
If you are using an attiny85, it may pay to also issue the   power_all_disable(); command that sets the PRR, but the attiny13 doesnt have that. The Attiny13A does, but as the core used is thinking it is an Attiny13, the <avr/iotn13a.h> file is not loaded.
One can counter that by adding the following definitions:

#define PRR _SFR_IO8(0x25)
#define PRADC 0
#define PRTIM0 1

and for the BOD CONTROL Register

#define BODCR _SFR_IO8(0x30)
#define BPDSE 0
#define BPDS 1

However, activating the PRR register or closing the BOD did not change the power consumption of my attiny13A.

Note: if you are using the MicroCore for the attiny13 you may get a vector 8 error. That is because the WDT is already used to set millis(). You then need to disable that in the core_settings.h file. If you installed the core via the boards manager you are most likely to find it here: user\xxxxxxx\AppData\Local\Arduino15\packages\MicroCore\hardware\avr\1.0.2\cores\microcore

Attiny Motorboard

tinystepperFor a project I needed to drive a steppermotor, a servo and possibly a small regular DC motor. Piece of cake of course with an Arduino, but I am anal retentive where it comes to  underusing an Arduino and as I had come by a couple of Attiny13A’s why not use those.
attiny-stepperA circuit is easily drawn up and doesn’t hold too many surprises. The chip of choice is the ULN2003 that can take 500mA per port. Driving a steppermotor for a while can make the chip unpleasantly hot and adding a heatsink is a wise precaution. My Steppermotor, the 55SI-25DAWC (1350 g/cm torque) has a 6 prong connector with the two  midconnectors that receive the  Motor Voltage on one side and the 4 coil connectors next to that, so that kinda dictated my design on K1. It is 36 Ohm at 12 Volt, so  each coil  uses 333mA.
The servo connector K2 is rather standard: Signal-Vcc-Ground. The DC motor connector K3 is fed by the open collector output of the ULN2003. The signal for that is shared with the Servo, as for now I probably wont be using both, but If I need both, I might be feeding it of  PB5, though that would mean resetting the fuses to disable the reset that is attached to pin1. It also means I need a high voltage programmer if I need to  reprogram the chip.
tinystepper-stripboard

A prototype is easily set up on a piece of stripboard. The interruption of the second strip between F and G is just there in case I want to add a jumper to switch between servo use and ULN2003 use on Output Q3. Rather than making K3 just a two prong connector, I decided to add  an entire row that is connected to V++, so if I so wish, I can also use  Q1-Q7 just for  DC motors. It gives a bit more flexibility. As the Attiny13 has a max of 6 I/O pins and the ULN2003 has 7 drivers, one can always decide to  use the drivers in parallel to drive bigger loads.

Programming:
The Attiny13  has only 1 kByte available, but that is enough  for a simple application with a servo and a steppermotor.
As my steppermotor is a rather old one already, I doubt if too many people have it. so my program is most likely no use  for anybody else, but driving steppermotors is rather easy.
With the unipolar steppermotor that I use, there are 3 ways of driving it: Halfstepping, Full stepping or wave driving
Wave driving is the easiest: you just make each pin high in the right sequence.
coils.jpgFor a unipolor motor that would be: 1a-2a-1b-2b. That would spin the motor in low torque because there is always a moment in which there is no coil magnetized.
Half stepping requires alternating between energising a single coil and two coils at the same time. This increases the angular resolution, but the torque fluctuates between utilising a single coil and both coils.. In principle though it means that you keep one coil magnetized while you already switch on the second one. So that would be:
1a-1a2a-2a-2a1b-1b-1b2b-2b-2b1a,
Full stepping is when two coils are always energised at the same time. This allows the maximum torque of the motor to be used, at the expense of more current. This looks like:
1a2a-2a1b-1b2b-2b1a.
For completeness sake: the 55SI-25DAWC can also be used as bipolar stepper motor just by not using the white wires. This gives more torque and less power consumption
The below program is just a quick illustration of how to drive a stepper in Full stepping mode:

/*
                                _____
                               |     |
 Hardware:             +5V     |     |
             ________   |      |     |     
      ___   |        |  |      |     |  ___  
 +5V-|___|--|RES  VCC|--+   B3-|I4 O4|-|___|-+Q3 brown
            |        |         |     |  ___  
        B3--|PB3  PB2|---------|I5 O5|-|___|-+Q2 yellow
                     |         |     |  ___  
          --|PB4  PB1|---------|I6 O6|-|___|-+Q4 blue
            |        |         |     |  ___  
         |--|GND  PB0|---------|I7 O7|-|___|-+Q1 red
            |________|         |     |       
             ATtiny13        |-|GNDCC|------- +12Volt white
                               |_____|    |__ +12Volt white
                                ULN2003 
Funktioning: sequence: red-yellow-brown-blue
    Stepmotor drive sequence and encoding:
      Portbit      PB0   PB1   PB2   PB3   |            |
      Color         rd    bl    yl    bn   | Portbit    | Byte
      Step          O7    O6    O5    O4   | 3  2  1  0 |
      -------------------------------------+------------+------
         1          1      0    1     0    | 0  1  0  1 |  05
         2          0      0    1     1    | 1  1  0  0 |  12 / 0C
         3          0      1    0     1    | 1  0  1  0 |  10 / 0A
         4          1      1    0     0    | 0  0  1  1 |  03
If you would use D8-D11 on an Atmega328, you could use the same values 
for the Portbits, as D8-D11 is equivalent to PB0-PB3

Coils
 red    ------------+------------ brown
 yellow-------------+------------ blue
 */
 #define delayTime  200// time between steps
 void setup()
 {
 DDRB=31; // set Port 0-5 as output
 }
 void loop()
{
 stepper(3); //=90 degrees. The angle is 7.5 degr,=> 4 steps=30 degr.
 }
 void stepper(byte turn)
 {
 for (byte pos=0;pos<turn;pos++)
 {
 PORTB=5;
 delay(delayTime);
 PORTB=12;
 delay(delayTime);
 PORTB=10;
 delay(delayTime);
 PORTB=3; delay(delayTime);
 }
delay(1000);
 }

The above program is just rough, to show how to drive a stepper and it misses some  sophistication. Writing directly to PORTB is OK, but in this case it will also write a “0” to PB4 and PB5. PB5 is not much of a problem, but you may want to use PB4. In my case that is where I put my servo and that doesnt really cause a problem as I do not use them at the same time.
If you want to avoid writing to PB4 and PB5, use a mask to only write to bit 0-3. The mask to do that is B00001111.
If you then want to set bits bits 0 and 2, go like this:
PORTB=(PORTB &~mask) | (B00000101);
For those who find this too cryptic:
it  first ANDs the value of PORTB with NOT mask and OR’s the result with the value we want to write and assigns that back to PORTB.
So, suppose PORTB= 00010000 and we want to write 00000101 to it, we cannot assign that immediately because that would clear PB4.
However, if we do as described, it becomes:
PORTB=(PORTB & 11110000) | 00000101
PORTB=(00010000 & 11110000)  | 00000101
PORTB=00010000 | 00000101
PORTB= 00010101
We have written our value and kept PB4
So, why cant we immediately OR PORTB with the value we want in stead of AND-ing it first?
Well because  that might keep PB4 and PB5… but it also keeps PB3-PB0 unchanged if  one of them already contained a ‘1’
Of course inverting the  mask wouldn’t be necessary if we would define it already inverted, but it is common practice to do it as such

Writing to the Servo is quite easy:
add this  function:

void pulseOut( byte pin, byte p){
digitalWrite(pin,HIGH);
delayMicroseconds(300+p*(2500/180));
digitalWrite(pin,LOW);
}

and call that like this:

for (byte pos=0;pos<180;pos++)
{
pulseOut(servoPin,pos);
delay(20);
}

High Torque mode uses more power than low Torque mode. In Low Torque mode each step in my case (12V, 36 Ohm coil resistance) takes 333mA. In high torque mode, each step takes 667mA (2×333.33).

In case you are wondering what project I needed it for: I need  a doll to turn and  nod its head.

Self build alert
Smart-Electronics-28BYJ-48-5V-4-Phase-DC-Gear-Stepper-Motor-ULN2003-Driver-Board-for-ArduinoI enjoy building stuff, but it isn’t always the wisest thing to do. There is a ULN2003 based driver board, including a good steppermotor available for about 1.80 euro. That is a good deal. In my case, I  just wanted the Attiny13 and ULN2003 driver to be on one PCB, I already had a spare ULN2003 and I had the steppermotor so that was a no brainer, But for anybody else wanting to  drive a steppermotor, consider that cheap motor and driver.
My cost for just the driver section (chip,  pins, socket), I estimate 45 ct (euro). Not sure what my stepper costed me 30 years ago, but a decent hobby stepper now will be between 1.60 euro and 5 euro, so the savings in  self built (not even counting the solder and electricity use) are minimal to non existent. So only do it for fun, educational purposes or if you need something tailored like i did.

That trusty Attiny13
Finally I have to praise the Attiny13 for being extremely resilient. Due to some stupidity from my side, I connected PB3 with O4 instead of I4, So it received 12 Volt through a 36  ohm resistor and also tried to drive a 330mA motor coil. This situation lasted for at least a week in which it would sometimes be  steering the stepper for hours, with me  trying to find out why it wasn’t working optimally.
I only found out when I accidentally felt the chip was getting pretty hot.
I fixed the connection and  the chip was still working (and the motor turned better). A week of 12 Volt on one of its pins and getting hot and still working great

Reading the BMP180 pressure sensor with an Attiny85

IMG_20160210_232932The BMP180 pressure sensor is a relatively cheap and popular sensor to read atmospheric pressure. Additionally it can read temperature.
If you want to use this sensor on an arduino, Adafruit has a library (for the BMP085 and the BMP180) that wil read it for you. However, the new library also needs their general ‘Sensor library’ and those are memory guzzlers. Perhaps OK on an Arduino, but not on an attiny. They do have one for the Tiny85 as well. Sparkfun also has a library  for the Arduino.
Furthermore, the BMP180 is an I2C device and I2C is not a standard on the Attiny series.

So, if you want to read the BMP180 sensor on an attiny, you would need to do some work yourself.
Fortunately, the datasheet is very very clear. Page 15 tells us exactly what to do.
The sequence is as follows:
1-Read the chip specific calibration data
2-Read the uncorrected temparature value
3-Read the uncorrected pressure value
4-Calculate true temperature
5-calculate true pressure

It also shows you what should be in a loop and what not:
reading the calibration data only needs to be done once and therefore goes in the ‘Setup’ routine. The rest is in a loop and therefore goes in the ‘loop’ routine.

So, programming is a breeze if you follow the flow chart on page 15…. we only need to ‘translate’ that into language the I2C protocol understands.
We therefore start the program with defining some general parameters:
For the Attiny there is the TinyWireM library that implements an I2C protocol on the attiny, so we need to load that library.
We need the I2C address of the BMP180 (which is 0x77), and we need to declare a whole bunch of variables. Most of the variables used will contain the chip specific calibration data that we will be reading from the chip’s EEPROM, we will need some variables for the various calculations and we will need some variables to contain the output (temperature and pressure)
To keep it easy, I have chosen names for the variables as mentioned in the datasheet.

So, the first lines of a program will look like this:

//The connection for  Attiny & BMP180 are  SDA pin 5 ,SCL pin 7 for I2C 
#include  <TinyWireM.h>
#define BMP180_ADDRESS 0x77  // I2C address of BMP180   
// define calibration data for temperature:
int ac1;
int ac2; 
int ac3; 
unsigned int ac4;
unsigned int ac5;
unsigned int ac6;
int b1; 
int b2;
int mb;
int mc;
int md;
long b5; 
//define variables for pressure and temperature calculation
long x1,x2;
//define variables for pressure calculation
long x3,b3,b6,p;
unsigned long b4,b7;
//define variables for temperature and pressure reading

short temperature;
long pressure;
const unsigned char OSS = 0;  // Oversampling Setting
/* blz 12 Datasheet
OSS=0 ultra Low Power Setting, 1 sample, 4.5 ms 3uA
OSS=1 Standard Power Setting, 2 samples, 7.5 ms 5uA
OSS=2 High Resolution,              4 samples, 13.5 ms 7uA
OSS=3 Ultra High Resolution,    2 samples, 25.5 ms 12uA
*/

Then we have to define the ‘Setup’ routine. Frankly, the only thing we have to do there is read the calibration data. To keep it simple, i will just call a procedure ‘bmp180ReadInt(address)’, which we then can implement later.
Our Setup therefore will look like this:

void setup() {
  TinyWireM.begin();
  // First read calibration data from EEPROM
  ac1 = bmp180ReadInt(0xAA);
  ac2 = bmp180ReadInt(0xAC);
  ac3 = bmp180ReadInt(0xAE);
  ac4 = bmp180ReadInt(0xB0);
  ac5 = bmp180ReadInt(0xB2);
  ac6 = bmp180ReadInt(0xB4);
  b1 = bmp180ReadInt(0xB6);
  b2 = bmp180ReadInt(0xB8);
  mb = bmp180ReadInt(0xBA);
  mc = bmp180ReadInt(0xBC);
  md = bmp180ReadInt(0xBE);

}

Ofcourse I could have just called 1 procedure and call that ‘bmp180ReadCalibration’ but that procedure then would do the same as I now defined already in the setup

The ‘loop’ procedure is equally simple. It is basically
Read uncorrected temperature
Correct that uncorrected temperature
Read uncorrected pressure
Correct that uncorrected pressure
But as no one is interested in the uncorrected data, we make that procedure:
Correct(Read Uncorrected temperature)
Correct(Read Uncorrected pressure)
like this:

void loop() {
 // first, read uncompensated temperature
 //temperature = bmp180ReadUT();
 //and then calculate calibrated temperature
 temperature = bmp180CorrectTemperature(bmp180ReadUT());
 // then , read uncompensated pressure
 //pressure = bmp180ReadUP();
 //and then calculate calibrated pressure
 pressure = bmp180CorrectPressure(bmp180ReadUP());
 
}

So that is it. We now only have to define the procedures that we call.
We will start with ‘bmp180ReadInt(address)’
This procedure will use the TinyWireM library to read an integer from a given address. In getting data from an I2C device, the general rule is to first write to that device to tell it what to do and then to read at a specific address for the outcome. As we will be reading from the EEPROM there is no specific command we have to send, other than to notify the I2C port where we want to be (at the I2C address of the chip) and send the address we want to read and how many bytes we want to read. We then combine those two butes in an integer and return that.
Our precedure will thus look like this:

int bmp180ReadInt(unsigned char address)
{
  unsigned char msb, lsb;
  TinyWireM.beginTransmission(BMP180_ADDRESS);
  TinyWireM.send(address);
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(BMP180_ADDRESS, 2);
  while(TinyWireM.available()<2);
  msb = TinyWireM.receive();
  lsb = TinyWireM.receive();
  return (int) msb<<8 | lsb;
}

The next procedure we need is to read the uncompensated temperature. To get that we have to first send the value of 0x2E to register 0xF4 and wait at least 4.5 msec. That is the time the chip needs to take 1 reading. After we waited we will read the uncompensated temperature from registers 0xF6 and 0xf7. That last read we do with the earlier defined ‘bmp180ReadInt’ procedure that reads 2 bytes and combines them into an integer.
The procedure thus will look like this:

unsigned int bmp180ReadUT()
{
  unsigned int ut;
  
  // Write 0x2E into Register 0xF4 and wait at least 4.5mS
  // This requests a temperature reading 
  // with results in 0xF6 and 0xF7
  TinyWireM.beginTransmission(BMP180_ADDRESS);
  TinyWireM.send(0xF4);
  TinyWireM.send(0x2E);
  TinyWireM.endTransmission();
  
  // Wait at least 4.5ms
  delay(5);
  
  // Then read two bytes from registers 0xF6 (MSB) and 0xF7 (LSB)
  // and combine as unsigned integer
  ut = bmp180ReadInt(0xF6);
  return ut;
}

Subsequently we have to calculate the corrected temperature from the uncorrected temperature.
The datasheet defines that as follows:
UT=uncompensated temperature
X1=(UT-AC6)*AC5/2^15
X2=(MC * 2^11 /(X1+MD)
B5=X1+X2
T=(B5+8)/2^4
in software that looks like this

double bmp180CorrectTemperature(unsigned int ut)
{
  x1 = (((long)ut - (long)ac6)*(long)ac5) >> 15;
  x2 = ((long)mc << 11)/(x1 + md);  
  b5 = x1 + x2; 
  return (((b5 + 8)>>4));  
}

Well the temperature is done, now we need to read the uncompensated pressure. For that we need to write the value 0x34 in the register 0xF4, but we also have to set the value vor the oversampling rate.
The oversampling rate determines the amount of samples the chip needs to make before giving a result.
Page 4 of the datasheet tells we have 4 choices:
OSS=0 ultra Low Power Setting, 1 sample, 4.5 ms 3uA
OSS=1 Standard Power Setting, 2 samples, 7.5 ms 5uA
OSS=2 High Resolution, 4 samples, 13.5 ms 7uA
OSS=3 Ultra High Resolution, 12 samples, 25.5 ms 12uA
For this program I have chosen the OSS to be 0
The OSS contains bits 6 and 7 in register 0xF4. Bit 0-4 determine the control of the measurement.
if we write the value 0x34 that is in binary: 00110100. Bits 0 to 4 are not so important for now, but bit 5 will also be set and thus start the conversion. It will stay high during the conversion and reset to LOW after the conversion. In order to set the bits 6 and or 7 we have to left shift 6 the value of OSS. Suppose we had wanted to set OSS as 3. in binary that is 0b11 if we left shift 6 that, it will be 11000000 (=192d or 0xC0), which will set bits 6 and 7. 0x34+0xC0=0xF4=0b11110100 which as we can see is the same as 0x34 plus bit 6 and 7 set.
As we are using ‘0’ for the OSS value, both bit 6 and 7 will not be set.
after we start the conversion we have to wait between 4.5 and 25.5 msecs (depending on OSS). As we have OSS=0 we will wait 5msec.
Subsequently we will read 3 bytes as the temperature is a ‘long’ (4 bytes) not an integer, we will however only need 3 bytes.
With regard to the delay, it would be nice if we will define it as a dependency of the OSS so you do not need to manually change it when you change the OSS. The Adafruit library solevs this with some IF statements:
if (oversampling == BMP085_ULTRALOWPOWER)
delay(5);
else if (oversampling == BMP085_STANDARD)
delay(8);
else if (oversampling == BMP085_HIGHRES)
delay(14);
else
delay(26);
However, I hoped to find a formula that will determine it. As it isn’t a strict linear function, the closest one gets is the formula: 5+(OSS*5).
OSS=0->5
OSS=1->10
OSS=2->15
OSS=3->25
Well, I guess that would be close enough
The procedure is as follows

/-------------------------------------------
// Read the uncompensated pressure value
unsigned long bmp180ReadUP()
{
  unsigned char msb, lsb, xlsb;
  unsigned long up = 0;
  
  // Write 0x34+(OSS<<6) into register 0xF4
  // Request a pressure reading w/ oversampling setting
  TinyWireM.beginTransmission(BMP180_ADDRESS);
  TinyWireM.send(0xF4);
  TinyWireM.send(0x34 + (OSS<<6));
  TinyWireM.endTransmission();
  
  // Wait for conversion, delay time dependent on OSS
  delay(5 + (5*OSS));
  
  // Read register 0xF6 (MSB), 0xF7 (LSB), and 0xF8 (XLSB)
  TinyWireM.beginTransmission(BMP180_ADDRESS);
  TinyWireM.send(0xF6);
  TinyWireM.endTransmission();
  TinyWireM.requestFrom(BMP180_ADDRESS, 3);
  
  // Wait for data to become available
  while(TinyWireM.available() < 3)
    ;
  msb = TinyWireM.receive();
  lsb = TinyWireM.receive();
  xlsb = TinyWireM.receive();
  
  up = (((unsigned long) msb << 16) | ((unsigned long) lsb << 8) | (unsigned long) xlsb) >> (8-OSS);
  
  return up;
}

Now that is done, we need to correct the uncompensated pressure. The result will be in Pascal

double bmp180CorrectPressure(unsigned long up)
{ 
  b6 = b5 - 4000;
  // Calculate B3
  x1 = (b2 * (b6 * b6)>>12)>>11;
  x2 = (ac2 * b6)>>11;
  x3 = x1 + x2;
  b3 = (((((long)ac1)*4 + x3)<<OSS) + 2)>>2;
  
  // Calculate B4
  x1 = (ac3 * b6)>>13;
  x2 = (b1 * ((b6 * b6)>>12))>>16;
  x3 = ((x1 + x2) + 2)>>2;
  b4 = (ac4 * (unsigned long)(x3 + 32768))>>15;
  
  b7 = ((unsigned long)(up - b3) * (50000>>OSS));
  if (b7 < 0x80000000)
    p = (b7<<1)/b4;
  else
  p = (b7/b4)<<1;   
  x1 = (p>>8) * (p>>8);
  x1 = (x1 * 3038)>>16;
  x2 = (-7357 * p)>>16;
  p += (x1 + x2 + 3791)>>4;
  
  return p;
}


With the above program one can decide for oneself what to do with the found data: either  send it to a display, or perhaps send it via an RF link to a base station.
As said, the output of the pressure reading is in Pascal (Pa). hectoPascals are a more convenient unit. Some other units it can be  calculated in are:
1 hPa = 100 Pa = 1 mbar = 0.001 bar
1 hPa = 0.75006168 Torr
1 hPa = 0.01450377 psi (pounds per square inch)
1 hPa = 0.02953337 inHg (inches of mercury)
1 hpa = 0.00098692 atm (standard atmospheres)

One last advice still:  When you use the BMP180, remember it needs 3.3 Volt. 5 Volt will kill it. Using it on the I2C from a 5 Volt microcontroller shouldnot cause a problem though. Various break outboards actually do have a  3.3 Voltage regulator on it.

Warning
When I wanted to display the values found by the BMP180, I  grabbed a two wire LCD interface that I ahd build with a 164 Shift Register. I subsequently tried to figure out for several hours why I wasnt getting any decent read out. In fact, the read out didnt change wether I connected the BMP180 or not. After many many trials I started to suspect my display interface and decided to  hook up an I2C LCD. That worked like a charm.
The LiquidCrystal_I2C from Francisco Malpertida doesn’t work on the Attiny85. I used the classic LiquidCrystal_I2C that is adapted by Bro Hogan to work on the Attiny85 as well.
He did that by changing the line:

#include <Wire.h>
into
#if defined(__AVR_ATtiny85__) || (__AVR_ATtiny2313__)
#include "TinyWireM.h"      // include this if ATtiny85 or ATtiny2313
#else
#include <Wire.h>           // original lib include
#endif

Servo on Attiny13

Wanted to use an Attiny13 to sweep a Servo. There is an 8 bits Servo library for the Attiny85 series, that will compile after  some changes (TIMSK->TIMSK0 and TIFR->TIFR0) but that will be over the memory limit of the Attiny13, some other programs I found that were playing directly with the timing registers didn’t really work and one even broke the gears on my servo, so I decided to just make  my own program, which is in fact much simpler than I thought once I started:

byte s=0;
void setup() {
pinMode(s, OUTPUT);

}

void loop() {
for (byte pos=0;pos<180;pos++)
{
pulseOut(s,pos);
delay(20);
}
}

void pulseOut( byte pin, byte p){
digitalWrite(pin,HIGH);
delayMicroseconds(300+p*(2500/180));
digitalWrite(pin,LOW);
//Serial.println(p*(1000/180));
}

The line “delayMicroseconds(300+p*(2500/180));” needs a bit of explanation:
“p” is the position in degrees, so if “p” is ‘0’, the pulsewidht for 0 degrees is sent to the server. In this case that is 300 uS. if p=180, the value for a full sweep is sent to the servo. In this case that is 2800uS.The values of 300 and 2500 are experimental: I started out with 1000 and 1000 but then the servo turned only some 90 degrees. Some programs for Attiny85 mention 60 and 4000uS as the min and max values for the pulse width
As  the values are  experimental, it doesn’t really matter if the  timing of the delayMicroseconds is off a bit on the Attiny13 core, but they seem OK for a UNO as well.
Though only a few lines, it still takes 540 bytes on the Attiny13.
True, due to the use of ‘delay’ the processor will mainly be ‘waiting’. Ofcourse one can solve this by using millis, or by setting up a timer, but it is an Attiny13…. there really isnt so much memory space to cramp a lot of other programming in it anyway, so sweeping the servo is the core  function.

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.

Installing Attiny13 core files in Arduino IDE 1.6.x and 1.7.x

Note! Currently I suggest you follow the instructions here, to install via the boardmanager

The installation for the Attiny13 in the Arduino IDE 1.0x was  fairly straightforward and is described in my ibble.
The Arduino IDE from Version 1.6  and newer, have a different structure for choosing the processor-board: Under ‘Tools’ you have to seperately choose the board, the processor and the speed. If you have installed the MIT cores for attiny 44-84 and 45-85, under ‘Tools-Board’ you will see a seperate class for  ‘Attiny’ processors, while under ‘Tools-Processors’ you will be able to choose for an  attiny85/45 or attiny44/84. You will however no longer see your attiny13 core there.

In order to be able to select the attiny13 again you need to put it in the new file structure.
I will describe that for the Smeezekitty core.

First, close your IDE, as any changes will need a restart anyway.
Download the Smeezekitty core. You will get a file called Core13-20.zip that contains 15 files but no boards.txt file.

Open your explorer and go to your hardware folder, that is located in your sketches folder. In the hardware folder you should see  the folder ‘attiny'(if you have the MIT attiny core installed). If it doesn’t exist, create it. If you open the attiny folder you will see a folder called “avr” open that one. If it doesn’t exist, create the folder “avr”. When you open it, you will see a  folder ‘variants’ and the “boards.txt” file. If they do not exist, create them.
open the folder variants. You probably will see 2 folders called “tiny8” and “tiny14”. Create a folder called ‘Core13’
Open  the downloaded Smeezekitty core and put the  15 files directly in the ‘Core13’ folder. Not in a subfolder

If you already have the smeezekitty core installed for the  Arduino IDE 1.0x  then copy that folder – that is likely called ‘core13’- in the ‘variants’ folder.

Go back to the  ../hardware/attiny/avr/ folder and make a copy of the boards.txt, then open the boards.txt and add the following lines at the bottom:
#################################################
attiny.menu.cpu.attiny13=ATtiny13
attiny.menu.cpu.attiny13.upload.maximum_size=1024
attiny.menu.cpu.attiny13.build.mcu=attiny13
attiny.menu.cpu.attiny13.build.variant=core13

attiny.menu.clock.internal96=9.6MHz (internal)
attiny.menu.clock.internal96.bootloader.low_fuses=0x7A
attiny.menu.clock.internal96.bootloader.high_fuses=0xff
attiny.menu.clock.internal96.build.f_cpu=9600000L
################################################

attiny.menu.clock.internal48=4.8MHz (internal)
attiny.menu.clock.internal48.bootloader.low_fuses=0x79
attiny.menu.clock.internal48.bootloader.high_fuses=0xff
attiny.menu.clock.internal48.build.f_cpu=4800000L
################################################

If you had to create your boards.txt file yourself Then make sure you add at the top the following lines:
##############################################
menu.cpu=Processor
menu.clock=Clock

attiny.name=ATtiny
attiny.bootloader.tool=arduino:avrdude
attiny.bootloader.unlock_bits=0xff
attiny.bootloader.lock_bits=0xff
attiny.build.core=arduino:arduino
attiny.build.board=attiny
attiny.upload.tool=arduino:avrdude
##############################################

save the boards.txt file and start your Arduino IDE 1.6.x or 1.7.x. Go to Tools-Board and select ‘Attiny’. Then go to ‘Tools-Processor’ and check if the Attiny13 is present. Go to ‘Tools-Clock’ and check for 4.8 MHz and 9.6MHz (internal).

Anyway, just to make sure you have the right folder stucture: this is what you should have:
…/hardware/attiny/avr/variants/core13/(some 15 files)
…/hardware/attiny/avr/boards.txt

NOTE: If on burning the bootloader to set the proper fuses you get an ‘efuse’ error, that is because teh boards.txt defines the root core as “Arduino” that might be fine for the 25/45/85 series as they have an extended fuse. But the Attiny13 has not. Changing it in the  ‘variants’ section apparently is not enough. You then have to give the attiny13 its own menu section. You do that like this:
###############################
menu.cpu=Processor
menu.clock=Clock

attiny13.name=Attiny13
attiny13.bootloader.tool=arduino:avrdude
attiny13.bootloader.unlock_bits=0xff
attiny13.bootloader.lock_bits=0xff
attiny13.build.core=arduino:core13
attiny13.build.board=attiny
attiny13.upload.tool=arduino:arduinoisp

#########################

attiny.name=ATtiny45/85
attiny.bootloader.tool=arduino:avrdude
attiny.bootloader.unlock_bits=0xff
attiny.bootloader.lock_bits=0xff
attiny.build.core=arduino:arduino
attiny.build.board=attiny
attiny.upload.tool=arduino:avrdude

attiny.menu.cpu.attiny45=ATtiny45
attiny.menu.cpu.attiny45.upload.maximum_size=4096
attiny.menu.cpu.attiny45.build.mcu=attiny45
attiny.menu.cpu.attiny45.build.variant=tiny8

attiny.menu.cpu.attiny85=ATtiny85
attiny.menu.cpu.attiny85.upload.maximum_size=8192
attiny.menu.cpu.attiny85.build.mcu=attiny85
attiny.menu.cpu.attiny85.build.variant=tiny8

attiny.menu.cpu.attiny44=ATtiny44
attiny.menu.cpu.attiny44.upload.maximum_size=4096
attiny.menu.cpu.attiny44.build.mcu=attiny44
attiny.menu.cpu.attiny44.build.variant=tiny14

attiny.menu.cpu.attiny84=ATtiny84
attiny.menu.cpu.attiny84.upload.maximum_size=8192
attiny.menu.cpu.attiny84.build.mcu=attiny84
attiny.menu.cpu.attiny84.build.variant=tiny14

attiny.menu.clock.internal1=1 MHz (internal)
attiny.menu.clock.internal1.bootloader.low_fuses=0x62
attiny.menu.clock.internal1.bootloader.high_fuses=0xdf
attiny.menu.clock.internal1.bootloader.extended_fuses=0xff
attiny.menu.clock.internal1.build.f_cpu=1000000L

attiny.menu.clock.internal8=8 MHz (internal)
attiny.menu.clock.internal8.bootloader.low_fuses=0xe2
attiny.menu.clock.internal8.bootloader.high_fuses=0xdf
attiny.menu.clock.internal8.bootloader.extended_fuses=0xff
attiny.menu.clock.internal8.build.f_cpu=8000000L

attiny.menu.clock.external8=8 MHz (external)
attiny.menu.clock.external8.bootloader.low_fuses=0xfe
attiny.menu.clock.external8.bootloader.high_fuses=0xdf
attiny.menu.clock.external8.bootloader.extended_fuses=0xff
attiny.menu.clock.external8.build.f_cpu=8000000L

attiny.menu.clock.external16=16 MHz (external)
attiny.menu.clock.external16.bootloader.low_fuses=0xfe
attiny.menu.clock.external16.bootloader.high_fuses=0xdf
attiny.menu.clock.external16.bootloader.extended_fuses=0xff
attiny.menu.clock.external16.build.f_cpu=16000000L

attiny.menu.clock.external20=20 MHz (external)
attiny.menu.clock.external20.bootloader.low_fuses=0xfe
attiny.menu.clock.external20.bootloader.high_fuses=0xdf
attiny.menu.clock.external20.bootloader.extended_fuses=0xff
attiny.menu.clock.external20.build.f_cpu=20000000L

#################################################
attiny13.menu.cpu.attiny13=ATtiny13
attiny13.menu.cpu.attiny13.upload.maximum_size=1024
attiny13.menu.cpu.attiny13.build.mcu=attiny13
attiny13.menu.cpu.attiny13.build.variant=core13

attiny13.menu.clock.internal48=4.8MHz (internal)
attiny13.menu.clock.internal48.bootloader.low_fuses=0x7A
attiny13.menu.clock.internal48.bootloader.high_fuses=0xff
attiny13.menu.clock.internal48.build.f_cpu=4800000L

attiny13.menu.clock.internal96=9.6MHz (internal)
attiny13.menu.clock.internal96.bootloader.low_fuses=0x79
attiny13.menu.clock.internal96.bootloader.high_fuses=0xff
attiny13.menu.clock.internal96.build.f_cpu=9600000L

################################################
attiny.menu.cpu.attiny2313=Attiny2313
attiny.menu.cpu.attiny2313.upload.maximum_size=2048
attiny.menu.cpu.attiny2313.build.mcu=attiny2313
attiny.menu.cpu.attiny2313.build.variant=attiny2313

attiny.menu.clock.internal8=8 MHz (internal)
attiny.menu.clock.internal8.bootloader.low_fuses=0xE4
attiny.menu.clock.internal8.bootloader.high_fuses=0xDF
attiny.menu.clock.internal8.bootloader.extended_fuses=0xFF
attiny.menu.clock.internal8.build.f_cpu=8000000L

Everything should be ok now but if you experience problems with delay and mills functions, you may still have an old Arduinocore. Check the code in hardware/arduino/cores/arduino/wiring.c, and find this section:

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
SIGNAL(TIM0_OVF_vect)
#else
SIGNAL(TIMER0_OVF_vect)
#endif

The header file for the attiny13 (iotn13.h) shows the interrupt macro for timer overflow for attiny13 to be TIM0_OVF_vect and not TIMER0_OVF_vect. The fix is adding an additional OR condition:

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || (__AVR_ATtiny13__)
SIGNAL(TIM0_OVF_vect)
#else
SIGNAL(TIMER0_OVF_vect)
#endif