Balido de oveja con Attiny85

Esta semana llega el día: mi cuñado tomará de su propia medicina.

Mi cuñado es de los que cuando se levantan todo el mundo tiene que estar levantado. Yo suelo madrugar, pero hay fines de semana que te apetece levantarte a las 9h

Bueno, uno de esos días en que quería levantarme tarde sufrí a mi cuñado… y le dije que un día lo pagaría. Ese día está cerca, muy cerca: este domingo

Para ello nada mejor que una pequeña broma electrónica: utilizando un attiny85 disparar uno de esos circuitos con soniditos a horas intempestivas (llámese sobre las 2h).

En principio el proyecto incluía poner mi voz en el chip, pero ciertamente llevo unos años sin tiempo ninguno y he tenido que recurrir a un circuito con los sonidos… Bueno, esta parte se queda en el TODO

Lo interensante del proyecto es que pongo el chip en modo sleep, y si a eso añadimos que deshabilitamos alguna que otra cosa innecesaria pasamos a consumir uA. Lo que nos da un tiempo más que suficiente para que dure la broma con una pila de botón.

Aquí utilizo el watchdog para ir despertando al Attiny85 a intervalos regulares (cosa que no había hecho hasta ahora en modo sleep).

Tengo dos prototipos que voy a utilizar:

  • uno con pilas AA (la broma puede durar años…)
  • otro con una pila de botón CR2032

¿Por qué utilizar el modo sleep? Principalmente por la pila de botón. Una CR2032 tiene una capacidad típica de unos 200mAh y si nuestro Attiny85 consume unos 2mA (a una frecuencia de 1Mhz, que la que estamos utlizando nosotros) solo por él mismo se comería la bateria en unas 100h (que vienen a ser unos 4 días)… nada, una miseria (no he considerado el carrillón porque solo consume durante unos 5sg, y estimo el consumo en 15mA -no lo medí, mea culpa). Por tanto está claro que tenemos que dormir mientras no hacemos nada.

El Attiny85 puede llegar a valores muy bajos de consumo en este modo (cerca de 1uA) pero yo no lo he desconectado absolutamente todo cuando duerme, por tanto podría estar hablando de unos 20uA. Esto ya es otra cosa: ¡¡estamos hablando de unas 10.000h de funcionamiento con una pila de botón. Más de un año funcionando!! En realidad el cálculo no es así puesto que nos despertamos cada 8sg para hacer unas pequeñás operaciones. Otra vez… no sé los datos exactos pero en el peor de los casos (y exagerando muchísimo) estaríamos hablando de consumir 10mA durante 1ms. Por tanto de media estamos consumiendo unos 30uA (voy a suponer 50uA…) por tanto tenemos una vida útil de la broma de unas 4.000h que son más de 5meses ¿No está mal verdad?

Pues eso no es nada. Para las AA, que son alkalinas tenemos nada más y nada menos que 2x2000mAh ¡¡¡es decir 20 veces más!!!

Aquí la imágenen de los dos prototipos:

Prototipo

Prototipo

Aunque hay cosas a mejorar te puede valer como punto de partida para experimentar con el modo sleep y el watchdog.

La verdad creo que no merece la pena poner el esquema porque la conexión depende del circuito carillón que utilices… pero bueno, ahí lo pongo:

Esquema Prototipo

Esquema Prototipo

Vista:

ASIC

ASIC

Para aumentar el volumen, cierro el buzzer con un trozo de celo:

Aumentar Volumen

Aumentar Volumen

P.D. Aunque está escrito con el Atmel Studio lo puedes traducir a Arduino de forma casi inmediata.

oveja.c

/*
 * oveja.c
 *
 * Created: 29/08/2016 7:41:44
 * Author: bull
 *
 * Disparar de madrugada con un intervalo aleatorio el balido de una oveja para despertar a mi cuñado
 */ 

#define F_CPU 1000000UL

#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <stdlib.h>         // random
#include <util/delay.h>
#include <avr/sleep.h>

#define TRIGGER_PIN  PB4
#define TRIGGER_DDR  DDRB
#define TRIGGER_PORT PORTB

// 1 semana son 604.800sg, que si nos despertamos cada 8sg son 75.600 -> necesitamos un uint32_t
// Desde las 00.00h hasta las 02:00h hay 7.200sg, que si nos despertamos cada 8sg son 900 veces. Y si queremos que
// el tiempo aleatorio esté en 3h = 10800sg = 1350 despertar jjj
const uint32_t semana = 75600;
const uint32_t las2 = 900;
const uint32_t x3h = 1350;

// TODO En realidad creo que no hace falta incrementar 'reloj' en la ISR, con lo que ya no necesitaría ser volatile.
//      Comprobar que funciona en el main.
volatile uint32_t reloj = 0;

ISR(WDT_vect)
{
  wdt_reset();
  //MCUSR = 0x00; NOTE Ahora ya no parece hacer falta...
  // No queremos el reset... pág 44 Bit 6 - WDIE
  WDTCR |= _BV(WDIE);
  
  reloj = (reloj + 1) % semana;
}

void bleating()
{
  TRIGGER_PORT |= _BV(TRIGGER_PIN);
  _delay_ms(50);
  TRIGGER_PORT &= ~_BV(TRIGGER_PIN);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

int main(void)
{
  // Puerto de salida
  TRIGGER_DDR |= _BV(TRIGGER_PIN);
  
  uint16_t alarma = 0xFFFF;
  
  srandom(1234);
  bleating();
  
  // Desactivar cualquier cosa no necesaria
  //
  cli();
  // Desactivar el ADC
  ADCSRA = 0x00;
  // Desactivar el AC (Analog Comparator)
  //ACSR &= ~_BV(ACIE);
  ACSR |= _BV(ACD);

  // Watchdog. Safety level 1. Levantar cada ~8sg  
  wdt_reset();
  MCUSR = 0x00;
  WDTCR |= (1 << WDCE) | (1 << WDE);
  WDTCR = _BV(WDIE) | _BV(WDE) | _BV(WDP3) | _BV(WDP0);
  sei(); 

  // Preparar para dormir
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  
  while(1)
  {    
    sleep_mode();
    
    if(reloj == las2)
      alarma = las2 + (random() % x3h);
    
    if(reloj == alarma)
    {
      bleating();
      alarma = 0xFFFF;
    }
  }
}