Heartbeat

It may not have actuators (yet) but I -guess- it could still be classed as robotic (What is a robot again exactly?). Heartbeat was a project I made over the course of a couple of evenings as a Valentines Day present for my fiancée. It was also a great chance for me to use my AVR programmer for something more than blinking a LED. This time it was going to be used to charlieplex 11 of the suckers!

The hardest part of all of this was the physical construction. The heart itself is made from Das airdry modelling clay (awesome stuff!) and I just modelled it roughly by hand while sitting on my balcony. The clay dried after a couple of days after which I painted it with some darkish gauche paint. Next time I will use acrylic as I think it will stick to the clay better. I only went with gauche because it was cheaper and at the time I was in the middle of a panic about the Global Financial Crisis. :)

Anyway, after the paint dried I applied a gloss to give the piece more of a ‘freshly removed’ glistening appearance. It came out ok but in hindsight I could have probably applied more coats. Then I left it to dry and started on the case.

The case is constructed of expanded foam sheeting from an art store. I just cut it with a Stanley knife and superglued it together. If you dont like the feeling of highly volatile chemicals melting with your brain I would definitely recommend a mask of some sort and glasses to protect you at this point. The superglue basically melts the foam and all sorts of crazy stuff comes off it. My eyes felt a bit grimy for a while after!

Now, onto the electronics… Unfortunately I don’t have the circuit diagram but I can explain it all because it is so straightforward. The powersupply board seen in the pictures connected to the battery is actually a kit I bought ages ago from Sparkfun capable of 3.3 and 5v output. That feeds 5v into the veroboard circuit which just consists of an AVR and some resistors. The AVR is using its onboard clock so nothing else is required. A resistor is connected to most pins (it’s only an 8 pin in total chip) which, through the power of charlieplexing, allows me to control a lot of leds. In this case 11.

The program that controls the pins basically cycles through them and flashes each led on and off for a fraction of a second. There is also fading going on by varying the ratio of off to on. Leave me a comment if you would like to know more.

Unfortunately I killed the battery in it on the second day by leaving it on overnight. The next version will have an auto-off function as well as an awesome laser cut wooden frame from Ponoko to replace the current foam one.

The code is really simple (I have uploaded it to snipplr.com):

Heartbeat
Posted by ausrobotics on March 29th, 2009
The code for my heartbeat electronic sculpture. Built using AVR Studio 4 for the ATiny8.

  1. #include <avr/io.h>
  2. #include <util/delay.h>
  3.  
  4. // Some macros that make the code more readable
  5. #define output_low(port,pin) port &= ~(1<<pin)
  6. #define output_high(port,pin) port |= (1<<pin)
  7. #define set_input(portdir,pin) portdir &= ~(1<<pin)
  8. #define set_output(portdir,pin) portdir |= (1<<pin)
  9.  
  10. #define LON     1
  11. #define LOF     0
  12.  
  13. #define LED_COUNT       12
  14.  
  15. uint8_t led_table[] =
  16. {
  17.         LOF, PB0, PB1,
  18.         LOF, PB1, PB0,
  19.         LOF, PB0, PB2,
  20.         LOF, PB2, PB0,
  21.         LOF, PB0, PB4,
  22.         LOF, PB4, PB0,
  23.         LOF, PB1, PB2,
  24.         LOF, PB2, PB1,
  25.         LOF, PB1, PB4,
  26.         LOF, PB4, PB1,
  27.         LOF, PB2, PB4,
  28.         LOF, PB4, PB2
  29. };
  30.  
  31. void clear_leds()
  32. {
  33.         set_input(DDRB, PB0);
  34.         set_input(DDRB, PB1);
  35.         set_input(DDRB, PB2);
  36.         set_input(DDRB, PB4);
  37. }
  38.  
  39. void set_led(uint8_t index, uint8_t state)
  40. {
  41.         uint8_t i = (index * 3);
  42.  
  43.         uint8_t s  = led_table[i];
  44.         uint8_t p1 = led_table[i + 1];
  45.         uint8_t p2 = led_table[i + 2];
  46.  
  47.         set_output(DDRB, p1)
  48.         set_output(DDRB, p2);
  49.  
  50.         if(state == LON)
  51.         {
  52.                 output_low(PORTB, p1);
  53.                 output_high(PORTB, p2);
  54.         }
  55.         else
  56.         {
  57.                 output_low(PORTB, p1);
  58.                 output_low(PORTB, p2);
  59.         }
  60. }
  61.  
  62. void delay_10us(uint8_t us)
  63. {
  64.         uint8_t delay_count = F_CPU / 2000000;
  65.         volatile uint8_t i;
  66.  
  67.         while (us != 0) {
  68.         for (i=0; i != delay_count; i++);
  69.            us--;
  70.         }
  71.  }
  72.  
  73. void do_pwm_fade(uint8_t index, uint8_t start_duty, uint8_t end_duty, uint8_t rate)
  74. {
  75.         uint8_t duty;
  76.         uint8_t j;
  77.  
  78.         duty = start_duty;
  79.         while (duty != end_duty)
  80.         {
  81.         for (j = 0; j < rate; j++)
  82.                 {
  83.          set_led(index, LON);
  84.          delay_10us(duty);
  85.          set_led(index, LOF);
  86.          delay_10us(255-duty);
  87.         }
  88.  
  89.         if (start_duty < end_duty)
  90.          duty++;
  91.                 else
  92.          duty--;
  93.         }
  94. }
  95.  
  96. int main(void)
  97. {
  98.         // initialize the direction of the B port to be outputs
  99.         // on the 3 pins that have LEDs connected
  100.                        
  101.         while(1)
  102.         {
  103.                 for(int i=0; i < LED_COUNT; i++)
  104.                 {
  105.                         clear_leds();
  106.                         do_pwm_fade(i, 0, 255, 1);
  107.                         do_pwm_fade(i, 255, 0, 1);
  108.                 }
  109.         }
  110. }