Showing posts with label ATtiny. Show all posts
Showing posts with label ATtiny. Show all posts

Sunday, 17 May 2015

A literal "Swear Jar"

Recently at work, management instituted a ‘Swear Jar’ system, to try and limit the use of profanity in the office.

Personally, I really have no issue with swearing - words are just words, but many people don’t share that view, so I understand why they did it.


The problem for me is that now, whenever someone swears, there’s a chorus of people shouting “Swear Jar!” at the person who swore. This, to me, is far more annoying than the swearing ever was.

It give me an idea though. What if the phrase “Swear Jar”, was taken more literally - a jar which swears at you when you put money in?

So, yeah, that’s the flimsy premise of this project. Read on for the full write up, or just watch the video of it in action below. (It should really go without saying that the language in the video may be unsuitable for some people..)


The Jar
As a base, I picked up one of those basic coin-counting money boxes often found that those ‘gadget shops’ which tend to open up in shopping centers selling a bunch of battery-operated plastic junk for a few weeks before they inevitably fail.

The coin mechanism is nothing special, essentially a slider which completes connections at different points to detect different coin values. there's not really anything else of value in the circuitry, it's all hidden behind epoxy blobs, so I scrapped everything else.

Audio
To provide the audio, I’m using an old MP3 player that I had in my junk bin - it’s essentially a 512MB memory stick with three control buttons and a headphone jack.



I enlisted a friend's help in trawling through movie clips on YouTube to get audio clips of profane movie quotes (this is where the previous post came from). The end result being a bunch of clips, all 3 seconds in length.

I wired up a small speaker from the junk bin. Fortunately the internal amplifier in the MP3 player was powerful enough to drive the speaker directly - as it's originally designed just for headphones, I thought it might not have sufficient power.

Controller
As this is just a novelty project, and I wanted to get it done and in the office before the swear jar idea was forgotten about, so I didn't really have time to delve too deeply into interfacing with the MP3 players circuitry. I opted to simply use a microcontroller to imitate human interaction. Conveniently, all the functionality I need from the MP3 player is obtained from just one button.

I opted for an ATTiny2313, using the arduino cores for convenience, and a relay to toggle the MP3 players button. Source code is below.

Power
Another complexity with this project is although the MP3 player doubles as a USB disk, powering it via the USB port only allows it to be used as a memory stick, and disables the MP3 playback functionality. Therefore the only other way to power the device is via it's AAA battery slot, so I required 2 power rails - 5v to drive the microcontroller, and 1.5v to power the MP3 player. This was achieved by using a 7805 to arrive at 5v for the micro, then using that 5v fed into an LM317 to provide 1.5 volts for the MP3 player. The main power input is 9v, which was originally intended to come from a battery, but at the last minute swapped for a wall supply (because I couldn't find a battery)

Block Diagram
The below shows how the different components fit together. As this is a frivolous novelty project, I'm not going to put together a full KiCad schematic - the most complex bit was the TS7805 and LM317 circuit, but there's plenty of existing documentation for those online already.



Timing is everything
The problem that quickly became evident is that the microcontroller has no feedback from the MP3 player, so cannot tell if a clip is playing, or even if the MP3 player is switched on. This is why the audio clips needed to all be the same length. There is also the issue of switching the MP3 player on, which requires a long press of the button. This is addressed in the setup function of the following code.

Code
int startPin = 2;
int emptyPin = 3;
int optoPin = 4;
int blinkPin = 5;
int isActive = 0;

void setup() {
  pinMode(emptyPin, INPUT);
  pinMode(startPin, INPUT);
  pinMode(optoPin, OUTPUT);
  pinMode(blinkPin, OUTPUT);
  digitalWrite(optoPin, HIGH);
  delay(1500);
  digitalWrite(optoPin, LOW);
}

void loop() {
  if (digitalRead(startPin)) {
    isActive = 1;
  } else if (digitalRead(emptyPin) == HIGH) {
    if (isActive) {
      playSound();
      isActive = 0;
    }
  }
}

void playSound() {
  digitalWrite(optoPin, HIGH);
  delay(100);
  digitalWrite(optoPin, LOW);
  delay(3200);
  digitalWrite(optoPin, HIGH);
  delay(100);
  digitalWrite(optoPin, LOW);
  delay(200);
}

Limitations
  • The MP3 player also has an auto switch-off function that causes it to turn itself off after a short period of time. This means that if no money is put in the swear jar for a while, the next time someone does, nothing happens, and requires the power to be cycled to reboot the whole thing.
  • The timing requirements are far too precise - even though all the audio clips used were 3 seconds, over time the slightest variances would add up, causing the microcontroller to lose sync with the audio.

Sunday, 13 July 2014

Dog-controlled treat dispenser part 2


This is the second part of my project to create a dog treat dispenser that my dog, Jack, can operate on his own. For the first part, click here.

Building an enclosure
I thought about 3d-printing an enclosure for the control box, but before I commit the time and effort to designing and printing one, I want to make sure that the project works - and that Jack actually uses it.
So for now, I've managed to fit the electronics into an old business card box, with basic cut outs for the buttons and wiring. It's not pretty, but it works.

The enclosure (the croc clips are for power - final version recycled a charger from a Playstation portable)

The floor switch is connected to the control box by a length of old VGA cable - it's nice and sturdy, and has more than enough wires for this project.

The components for the floor switch


In use
I finally managed to get a short video clip of Jack using it


Future improvements 

The floor switch is too light, as you can see in the video, when Jack uses it, he knocks it across the floor, so I've since mounted it to a bit of wood which helps hold it in place.

The small metal tin that I used to make the contact area larger & easier for Jack to use, ended up making the capacitive switch too sensitive - it basically became like a motion sensor - so I removed that, and used the original metal disc (about 15mm diameter) instead. Although the disc itself is smaller, it still manages to detect capacitance from contact anywhere on the switch, so it works just fine.
The floor switch - unfortunately the hot glue made a bit of a mess, but it's all contained on the underside of the switch, and actually helps diffuse the light a bit.


Source Code (Arduino cores for ATTiny2313
int latchPin = 8;
int clockPin = 12;
int dataPin = 11;
int capBtn = 7;

int plusBtn = 3;
int minBtn = 4;

int enabledLight = 5;

int motorPin = 6;

int treatCount = 0;
int valToDisplay = 0;
int timerBypassPin = 9;
long myTime;
boolean treatEnabled = false;

void setup() {
  //set pins to output so you can control the shift register
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(capBtn, INPUT);
  pinMode(enabledLight, OUTPUT);
  pinMode(motorPin, OUTPUT);
  pinMode(plusBtn, INPUT);
  pinMode(minBtn, INPUT);
  pinMode(timerBypassPin, INPUT);
  digitalWrite(motorPin, LOW);
  digitalWrite(enabledLight, LOW);
  treatCount = 3;
}

void loop() {
  if (digitalRead(plusBtn) == HIGH) {
    treatCount++;
    if (treatCount > 8) { treatCount = 8; }
    delay(500);
  }
  
  if (digitalRead(minBtn) == HIGH) {
    treatCount--;
    if (treatCount < 0) { treatCount = 0; }
      delay(500);
  }
  
  if (digitalRead(timerBypassPin) == HIGH) {
    treatEnabled = true;
  }
  
  if (
      ((millis() - myTime) < 0) ||
      ((millis()-myTime) >= 3600000)
     ) {
    myTime = millis();
    treatEnabled = true;
  }
  digitalWrite(enabledLight, treatEnabled);

  if (digitalRead(capBtn) == HIGH) {
    if (treatEnabled) {
        digitalWrite(enabledLight, LOW);
        treatEnabled = false;
        treatCount--;
        digitalWrite(motorPin, HIGH);
        delay(3000);
        digitalWrite(motorPin, LOW);
    }
  }
  

  switch (treatCount) {
    case 0: valToDisplay = 0; break;
    case 1: valToDisplay = 1; break;
    case 2: valToDisplay = 3; break;
    case 3: valToDisplay = 7; break;
    case 4: valToDisplay = 15; break;
    case 5: valToDisplay = 31; break;
    case 6: valToDisplay = 63; break;
    case 7: valToDisplay = 127; break;
    case 8: valToDisplay = 255; break;
  }


  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, valToDisplay);
  digitalWrite(latchPin, HIGH);
  
}

Tuesday, 24 June 2014

Dog-controlled treat dispenser

I picked up one of these treat dispensers from Maplins on a whim - one of those "I'm-sure-I'll-find-a-use-for-this-someday" things. Subsequently I started wondering if I could train my dog, Jack, to use it (with some modifications, obviously)

An initial attempt involved simply wiring a switch to a footpedal for Jack to step on. That lasted all of 10 seconds as the plastic lid I'd repurposed as a pedal cracked under his weight.

One of the things that attracted me to the gadget in the first place was the capacitive button that it used. A quick test with my phone screen showed that dog's paws can operate capacitive switches.

Simply extending the leads alone wouldn't be enough. Although I want to give Jack the freedom to get his own treats, I still want to keep control over how many!

The touch sensors board has simple connections - just power, and leads out to the motor. I didn't really want to waste time modifying the board, as it's all small surface-mount components. Since I know the motor was only driven one direction by the board, I simply connected an optocoupler to the motor outputs. A couple of pull-up resistors later and the switch was done.

The finished capacitive button PCB with optocoupler


I connected a metal lid in place of the small capacitive disc to make it more dog-friendly.

Now onto the human's control system..

My original plan was to use something web connected such as an old android device, so that I could give Jack treats whilst out at work, but in practice, it was totally overkill, so I opted for a microcontroller based system instead.

The only controls I really need are to limit the number of available treats, set a time limit so he doesn't eat them all at once, and a way to see how many were available.

I found an old PCB with two buttons on it in my junk box. One button to add a treat, one to remove.
 
The salvaged button PCB (I believe it once belonged to a toaster...)


For a display I went with a 10-segment LED, controlled by a shift register. Due to the limitations of the shift register, the maximum treats available at any one time is 8 - which is more than enough anyway. The remaining 2 LEDs are left unused.
The 10 segment display (3 treats available) - the bottom 2 LEDs are not used.



The time limit I chose to fix at 1 hour in the microcontroller. As it's impossible to precisely measure the amount of treats that will be dispensed, the motor was set to a 3 second interval.

Finally I added a small LED module to the pedal, that lights when the timer ticks down and a treat is available, so that Jack could see when he can get a treat. According to the Pavlovian theory, he should soon learn that light=treat.

The LED module (it's just from one of those battery operated 'cupboard lights' you see in nearly every pound shop) - for this project I just connected a jumper over the button.


As with other projects I opted for using an Attiny (in this case the 2313) with the Attiny arduino cores, as they make it nice and easy to prototype with Arduino and switch to a regular AVR for the end product.

Stay tuned for part 2 - building an enclosure for it, some pictures of the completed project, source code, and hopefully some pictures/video of Jack using it.

UPDATE - Part 2 is now available here.

Monday, 24 March 2014

Motion activated Pac-Man lamp


Version 2

Migrated the control over to an ATtiny25, using the ATtiny cores for Arduino.
I found a ported version of the IRRemote library on Github. It looks as though this port was for a specific purpose, and lacked the IR codes I needed, however it was easy enough to port the necessary parts from the orignal IRRemote library.

This is the revised code:

#include "IRremoteTiny.h"

IRsend irsend;

#define PIR PB2 // pin 7 on ATtiny

void setup(void) 
{
  DDRB &= ~(_BV(PIR)); // Set as input
  DDRB |= _BV(PB3);

}

void loop(void) {
  
  if (digitalRead(PIR) == HIGH) {
    digitalWrite(PB3, HIGH);
    for (int i = 0; i < 3; i++) {
      irsend.sendNEC(0xF7C03F, 32);
      delay(40);
    }
    delay(10000);
    for (int i = 0; i < 3; i++) {
      irsend.sendNEC(0xF740BF, 32);
      delay(40);
    }
  } else {
    digitalWrite(PB3, LOW);
  }
}


Version 1
I was given a Pac-Man lamp for Christmas.
It's a simple RGB colour changing lamp that is remote controlled.


In my flat the hallway can be quite dark, and when leaving, there can be a few seconds between turning the flat's hall light off, and the external hall light being triggered.

I was considering getting a simple plug-in 'night light' type thing, but I didn't want something that stayed on all the time. And besides, I wanted to put Pac-Man to good use.

As it was a Christmas gift, it didn't seem right pulling it apart straight away, so the plan is to create a motion-triggered device that mimics the remote.

The first thing is to find the remote protocol being used.
To do this use a IR receiver module (I scavenged one from a VCR that was on the junk pile), and hook it up to an Arduino, using Ken Shirriff's arduino IR library. Using the 'IRrecvDump' sample sketch you can find the details of the protocol in the serial monitor - it'll look something like

F7C03F
Decoded NEC: F7C03F (32 bits) Raw (68): 15538 8950 -4200 700 -400 700 -400 650 -450 700 -400 700 -400 650 -450 650 -450 650 -450 650 -1500 700 -1500 700 -1500 650 -1550 650 -450 650 -1500 700 -1500 700 -1500 700 -1500 700 -1500 650 -450 650 -450 650 -400 700 -400 700 -400 700 -400 700 -400 700 -400 650 -1550 650 -1500 700 -1500 700 -1500 700 -1500 700 -1500 650 F740BF

Decoded NEC: F740BF (32 bits) Raw (68): 26860 8950 -4200 700 -400 700 -400 650 -400 700 -400 700 -400 700 -400 700 -400 650 -450 650 -1550 650 -1500 700 -1500 650 -1550 650 -450 650 -1500 650 -1550 700 -1500 650 -400 700 -1500 700 -400 700 -400 650 -450 650 -400 700 -400 700 -400 700 -1500 650 -450 650 -1550 650 -1500 700 -1500 650 -1550 650 -1500 700 -1500 650

My first thought that the pacman remote looks very similar to the remotes that tend to get bundled with rolls of RGB leds, and I had a couple of those going spare, so perhaps one of them could've been sacrificed, however performing the same test with those showed that they use a different protocol, and so wouldn't work.

The second thought was to make use of an old Sky TV remote - they have a feature that allows the protocol to be programmed to control several makes of TV, but after a while trying various codes, it appeared that the one I was after wasn't one of them.

So the final option was to use the arduino itself for sending the signal. It seemed a bit overkill, but I can always migrate it to a smaller microcontroller when I have one spare. The IR led was taken from the old Sky remote.

The next stage is to wire in the PIR sensor (motion detector)

I used one of these:


They're a simple 3 pin setup - Vcc, Ground, and Data. Data simply goes high for a few seconds when motion is detected.
It can be easily wired in, just effectively like a simple button.


Here's the code I used - when motion is detected, turn the lamp on for 10 seconds, then off again.

#include <IRremote.h>

IRsend irsend;

void setup()
{
  pinMode(4,INPUT);
  pinMode(13, OUTPUT);
}

void loop() {
  if (digitalRead(4) == HIGH) {
    digitalWrite(13, HIGH);
    for (int i = 0; i < 3; i++) {
      irsend.sendNEC(0xF7C03F, 32);
      delay(40);
    }
    delay(10000);
    for (int i = 0; i < 3; i++) {
      irsend.sendNEC(0xF740BF, 32);
      delay(40);
    }
  } else {
    digitalWrite(13, LOW);
  }
}