Showing posts with label Trinket Pro. Show all posts
Showing posts with label Trinket Pro. Show all posts

Wednesday, November 9, 2016

Halloween Candy Cauldron V3.1

V3.1 in the enclosure. Ignore the external speaker--it's a vestige. Ready to tidy up for V4.
As I said in my last post on this project, all that was left to do was tidy up the connections and enclose the electronics. I decided that the enclosure I was using was too big. The reason it was too big was that I had the project on a breadboard instead of soldering the components to a PCB. I have always wondered why places like Radio Shack that sell enclosures and PCBs don't sell PCBs that fit in the enclosures--with mounting holes and screws.  So, I went looking and found this enclosure with matching PCB and a battery compartment. A little pricey, but good quality and they have a variety of sizes and features. (I'm using the term PCB to include proto boards here.) Follow-up note: I found that the soldering pads on the pcb were insufficient.  I'll accept blame for my soldering technique, but I had more trouble with this than any other I've worked with.

Since the new enclosure was a little smaller, I decided to use an  +Arduino Pro Mini (actually an Arducam clone) instead of the +Adafruit Industries Pro Trinket.  It's a little smaller, and I had a few in inventory. It needs an FTDI to serial module to program, but I was using one anyway for the Trinket to be able to use the serial monitor.

The code compiled with no changes.  (See link to last post in opening sentence to see code.) In order to upload to the board, the IDE required some changes (to point to the right board--Nano, not mini), and the USB Port needed a tweak in Windows Device Manager (I'm on Windows 10). Arduino.cc has a clear and concise getting started article describing this better than I can.

Interrupt

Note that the interrupt code also works as-is.  I need to work on the program to make the sleep more useful. As you can see in the video (the blue LED is on when it's sleeping), it doesn't sleep much, I'm using the Echo pin on the HC-SR04 sensor to wake it up. I put it to sleep at the top of the loop, then wait for Echo to go LOW or HIGH. It works the same either way, at least to the naked eye.

I used LOW because IDLE is the only mode that can be woken from anything other than LOW, and I wanted to be able to play with other modes. I'm thinking about using an inverter so I can set the interrupt on LOW, but have the pin go LOW when the Echo pin goes HIGH. +Home DIY Electronics has a good article on interrupts and the HC-SR04.

Since the sensor is ranging constantly, it wakes up pretty quickly. If I try to put it in power down mode, it does not wake up (maybe because the sensor shuts down?):  The LED indicating sleep stays on and no gesture will wake it up. There are 5 sleep modes and these are only the 2 extremes. I will play with some of the intermediate modes--next year, or the next time I'm playing with interrupts on an AVR board.

Construction
  1. Test the mini
    Since I changed boards, I needed to upload the code, wire the circuit on a breadboard, and test. As described above, all was well.
  2. Prep the enclosure
    In this design, the speaker and the battery are now inside the enclosure, I only need holes to bring in wires from the switch and from the HC-SR04. I also drilled 7 holes to let the sound out, places where I planned to put the speaker.
  3. Design the board layout
    I needed to mount the Pro Mini, then find room for the amplifier and Micro SD Reader.  This required some planning, so I simulated with header pins on the PCB and marked where I would place the components.  All the components had male headers attached, because I originally set set this up for a breadboard, so I soldered the headers to the printed side of the board.
  4. Wire the busses
    I created power and ground busses on the inside edge of the board, next to the battery, using tinned copper bus wire. The board has pairs of adjacent rows of connected holes, so I used two of these pairs (one row for each bus, each adjacent row for connections to the circuit).  The power rail is supplied from the voltage regulator circuit 5V output, and the ground from the 9V battery's negative lead. The positive lead from the battery goes to a 2-wire JST connector, which mates to another connector with leads going to a toggle switch. The other lead from the case (with the + from the battery) is the 9V input to the voltage regulator.
  5. Test the Voltage Regulator Circuit
    Since I have habit of messing up connections, and power is a key part of this, I wanted to be sure that I was getting 5V out of this part of the circuit before completing the soldering. When I got it right (after some stupid soldering tricks), I was ready to move on.
  6. Mount the components
    All the components had male headers attached, because I originally set set this up for a breadboard, so I soldered the headers to the printed side of the board. I also added an LED and resistor for visual representation of the sleep mode (the code for this was already included, so what the heck).
  7. Test the circuit
    Before fastening the circuit board to the enclosure, make sure everything works.  Here's where I ran into trouble.  I worked the continuity function of my multi-meter extensively. I thought I had everything in order, but it failed. I went back to a version of the sketch that had serial commands for testing in it, and saw that the SD board was not initializing.  I rechecked the connections, checked the SD card to see if it could be read, and then got drastic:  clipped the jumpers from the mini to the SD reader, and connected them via alligator leads to another reader.  Still not initializing, so I was pretty sure it was my connections.  One more continuity test revealed a problem with one of the jumpers. I re-soldered, put it back together, and it worked. Stupid soldering tricks, indeed!
    Working circuit, mounted. Clockwise from upper left: Adafruit Class D Mono Amp (wires to speaker), voltage regulator circuit, indicator LED and resistor, Pro Mini, Micro SD Card Reader. Yellow and green wires go to HC-SR04, red and black wires on right are power to HC-SR04 (red and black) and from 9V battery and switch (black and gray). 
  8. Put it in the box
    See photo at the top of this post.  Enclosing was a matter of adding screws in the prepared holes to secure the PCB, then passing the 4 wires to the HC-SR04 through one hole and the battery connection through another, position the speaker under the holes, and closing the box.
  9. Video
    Available on Youtube (my channel)



Wednesday, October 19, 2016

Halloween Candy Cauldron V3

Cauldron V3 in-process.
The large speaker will be removed--replaced by the small green on in the enclosure.
The red FTDI Friend and USB cable are there for programming.
We'll shorten the wires, neaten it up, and put the cover on the enclosure.
The red LED is for testing--turned on before sleeping, turned off on waking.

When last we looked a this (last Halloween), I was having trouble making the electronics small. To recap, I started out with an +Arduino Uno, an SD card shield, +Adafruit Industries class D mono amp, and an HC-SR04 Ultrasonic Sensor.  Then someone reaches in to grab a piece of candy, the sensor reacts, code on the arduino then chooses one of nine sounds to play, via the amp and an 8 Ohm speaker.

Objective

The big issue is that the electronics are loose in the bottom of the cauldron, so I wanted to use a smaller board and smaller SD breakout (like a MicroSD), and put the whole thing in an enclosure. I tried the Adafruit Pro Trinket 5V last year and ran out of time before I could get it to work.

Turns out this was a stupid wiring trick. The circuit is powered by a 9V battery, run through a 7805 5V Voltage Regulator circuit.  The dumb thing I did last year was connect the BAT pin on the Trinket to the battery.  I'm not sure what, if anything, else I did wrong (I may have also connected that same pin to the 5V rail), but this year I connected the BAT pin to the 5V rail. Last year I got only static, this year it works. In both cases I powered the peripherals from the 5V rail (output of the VR circuit), to provide enough current, since the Trinket's 5V pin provides 150mA max.

Further, I wanted to put the Trinket to sleep while waiting for trick-or-treaters to save battery.

Video

Interrupts

Sleep mode turned out to be simple, but required some learning.  As described in this post on interrupts on the M0, I just completed my first successful Arduino project using interrupts.  So, I started with a __WFI(); instruction to wait for an interrupt. It turns out that different processors use different instructions, since interrupts are so hardware dependent, so this did not compile.

My main source of information was here. The avr\sleep.h library (which does not work with the M0), was the key, since the main differences between the 2 processor families is how you put the processor to sleep, and the sleep modes. On the M0, the top of loop tested to see if one of our interrupts had been fired, and if not issued a wfi--back to sleep.  If so, go on to process In the case of the Trinket, also at the top of loop, we attached the interrupt, enabled sleep, went to sleep, and on waking, disabled sleep and detached the interrupt. The code:
//*****sleep**************************
    digitalWrite(ledPin, HIGH);  //turn on the LED (testing)
    attachInterrupt(digitalPinToInterrupt(echoPin), echoISR, HIGH); //attach interrupt
    sleep_enable();          // enables the sleep bit in the mcucr register
    sleep_mode();            // here the device is actually put to sleep!!
//***********************************/

//wait here for echoPin interrupt--ignore all others
//*****wake up************************
    sleep_disable();         // first thing after waking from sleep: disable sleep...
    detachInterrupt(digitalPinToInterrupt(echoPin));      // disables interrupt because pin stays high
//***********************************/

echoPin is Trinket pin 3, the only one that sees interrupts
echoISR just sets a volatile boolean, handIn
The next code tests handIn to see if it was set, meaning that we woke up because of the interrupt we care about, then goes on to reset the boolean and process.

In set up, we enabled interrupts ("interrupts();") and set the sleep mode ("set_sleep_mode(SLEEP_MODE_IDLE);"). According to the article cited above, IDLE is the only mode that can be awakened by a mode other than LOW, and I thought since we want to be interrupted when echoPin goes HIGH, we're stuck with it, even though it saves the least power of all modes.

However, some experimentation yielded that LOW works similarly to HIGH on the attachInterrupt.  I tried SLEEP_MODE_PWR_DOWN, AND it seemed to sleep a little more, but the board hung with regularity, so I went back to IDLE.

I'm sure there are more and better ways to do this, but this is working for me.

Other Enhancements

I was never happy with how the code looked, There's a lot of function-specific processing that made the loop long. So, I broke out the code to calculate the distance based on the sensor pin values and the code to choose which sound to play and put them in functions.  Not rocket science, but good programming practice.

I replaced the 3.5" speaker with a 1.5". That allowed me to enclosed the speaker in the box, reducing wires. I also made it a little quieter--young trick-or-treaters don't like the loud version. Yes I know I can control the volume on the amp, but this works well.

Fritzing Diagram


Code

/*2014-10-05 Virgil Machine Halloween Candy Dish: Play random sound when kid (or greedy adult) reaches into bowl

2016-10-18 V3 VM  never got Trinket to work last year, this week I did (stupid wiring trick)
 also replaced external speaker with a 1.5" that dit in the enclosure
 Added interrupt  handling to put it to sleep to save battery (thanks to http://playground.arduino.cc/Learning/ArduinoSleepCode)
 had  to change echoPin to #3--only pin on Trinket that is interruptable; changed trigPin to 4 to be
side-by-side for wiring, added ledPin for testing
Also, put the song selection/playing and distance calculation code in functions to make loop more readable.

2015-10-14 V2 VM change to Pro Trinket from Uno to put the circuit in an enclosure
 Echo to 6 (Pro Trinket does not have pin 7)

2014-10-05 VM HC_SR04 Distance sensor code added
Neeed to modify pins for Halloween (13&12 used by SPI)

2014-04-26 VM Downloaded from Instructables
 HC-SR04 Ping distance sensor]
 VCC to arduino 5v GND to arduino GND
 Echo to Arduino pin 13 Trig to Arduino pin 12 (used 8&7 instead--need SPI pins for SD reader)
*/

/******* see virgilmachine.blogspot.com*****/

/*includes*/
#include    //SPI library
#include     //SD card library
#include //library for playing sound
#include  //sleep functions
#include  //interrupt functions

/*constants*/
#define SD_ChipSelectPin 10
#define echoPin 3 //so we can have an interrupt (D3 is the only one)
#define trigPin 4 //to be next to echo for wiring
#define ledPin  5

/*variables*/
int song = 0;   // song number for random function
volatile boolean handIn = false; //variables in ISR need to be volatile
/*objects*/
TMRpcm speaker;   // create an object for use in this sketch


void echoISR() //ISR for distrance sensor
{
    handIn = true;  //someone put his or her hand in
}

void setup(){
  randomSeed(analogRead(0));  //initialize random (A0 unconected)
  pinMode(trigPin, OUTPUT);   //pins for distance sensor
  pinMode(echoPin, INPUT);
  pinMode(ledPin, OUTPUT);   //LED fot testing
  digitalWrite(ledPin, LOW);  //default to off
  speaker.speakerPin = 9; //output to amp
  speaker.loop(0); //2014-10-05 do not play repeatedly
//  Serial.begin(9600); //Serial is for testing--comment to reduce time/power consumption
  if (!SD.begin(SD_ChipSelectPin))
     {  // see if the card is present and can be initialized:
//Serial.println("SD not initialized");
      return;   // don't do anything more if not
      }
//   else
//      {
//       Serial.println("SD initialized");
     //
//     }  
  speaker.volume(1);
//  speaker.setVolume(7);   //attempt to increase volume

interrupts(); //enable interrupts (should not need to do this, but just for drill...)
set_sleep_mode(SLEEP_MODE_IDLE);   // sleep mode is set here
//set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // PWR_DOWN hangs periodically
}

void loop() {

//*****sleep**************************
    digitalWrite(ledPin, HIGH);  //turn on the LED (testing)
    attachInterrupt(digitalPinToInterrupt(echoPin), echoISR, LOW); //attach interrupt
    sleep_enable();          // enables the sleep bit in the mcucr register
    sleep_mode();            // here the device is actually put to sleep!!
//***********************************/

//wait here for echoPin interrupt--ignore all others
//*****wake up************************
    sleep_disable();         // first thing after waking from sleep: disable sleep...
    detachInterrupt(digitalPinToInterrupt(echoPin));      // disables interrupt because pin stays high
//***********************************/
  while (handIn) {   //someone wants candy, hcsr04 interrupted
    digitalWrite(ledPin, LOW);  //turn off LED (testing)
    handIn=(!handIn); //reset the boolean
//2014-10-05 If distance is <8in cm="" hand="" his="" in="" nbsp="" p="" put="" someone="">//2016-10-18 (the getDistance function calculates that)
    if (getDistance() < 20)
    {
      playSong(); //function to select and play a random song
    } //distance
      delay(2500);  //give the song a chance to play
  } //while

} //loop

void playSong() {
        song = random(1,10); //get random number from 1 to 9
//      Serial.print("song: "); //for testing
//      Serial.println(song); //for testing
      switch (song) {
        case 1:
          speaker.play("1.wav");
          break;
        case 2:
          speaker.play("2.wav");
          break;
        case 3:
          speaker.play("3.wav");
          break;
        case 4:
          speaker.play("4.wav");
          break;
        case 5:
          speaker.play("5.wav");
          break;
        case 6:
          speaker.play("6.wav");
          break;
        case 7:
          speaker.play("7.wav");
          break;
        case 8:
          speaker.play("8.wav");
          break;
        case 9:
          speaker.play("9.wav");
          break;
      } //switch/case
} //playSong

long getDistance() {
  long duration, distance;
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
//trigPin must me high for 10 microsecs to do ranging
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
//duration is the time between pings of the sensor  in microseconds as returned on echoPin
  duration = pulseIn(echoPin, HIGH);
//duration is a round-trip, so divide by 2; speed of sound is 29.1 cm/microsec, so distance is in cm
  distance = (duration/2) / 29.1;
  return distance;
} //getDistance  


Thursday, October 29, 2015

Halloween Candy Cauldron V2.1 upGRADE

Forget all that stuff in my last post about what I cooked.  I went back to the Uno version, really at a loss about why everything stopped working.  I decided to check power, first on the 5V rails--no power!  Then, I check what was coming from the 5V pin on the Uno. Nothing.  The only answer (other than a cooked Uno, which I did not believe) was a bad jumper wire.  Sure enough, I did continuity tests on the wires and one was bad.  I replaced them and it worked. So, the sensor and amp are both fine.



I went back to the Trinket.  It's putting out static only.  Same sensor, same amp. So, maybe I cooked the Trinket.  I'll have to decide whether I want to solder up another Trinket and perhaps sacrifice it this year or wait until next. As for now, I've spent a WHOLE LOT OF TIME working on something that already worked,  I have the bigger cauldron, and I have a switch, and it works again.

My other upgrade for next year is to put the Trinket--or Uno if I can't get the Trinket to work--into low-power mode while it's dormant to save battery and avoid having to turn it on and off so much. I've just learned how to do that in concept and this appears to be a good application.

So. I can take it to work tomorrow to entertain my co-workers. Since Halloween is a Saturday, I'll decide then if I want to spend more time (and maybe money) getting the Trinket version to work again.

I feel much better! On to 3.0!


Halloween Candy Cauldron V2.1 (update,not upgrade--more of a downgrade)

I was having a problem with my candy cauldron: the Pro Trinket was resetting at random intervals, and the circuit would not resume unless I powered off and back on.

I surmised that the problem was power.  I was powering the 5V rails from the 5V pin on the Trinket. (This worked on the Uno, but maybe the Trinket doesn't put out as much current).  So, I took the 9V Vcc to the BAT and GND pins of the Trinket, then to the IN and GND pins of an LM7805 voltage regulator, and powered the 5V rails from the OUT pin on the LM7805.

All was good. No more reset.  I took the cauldron to a meeting in the morning and had fun.

Then I got greedy. The cauldron is very small, which limits the candy.  It's limited anyway, and in addition if candy is piled too high the sensor sees it and sets off sounds. Also, there was a sound problem: since the HC-SR04 sensor is ultrasonic, if the sound being played is long the sensor detects it and plays the next sound even if no one reaches in, and will continue until it gets to a shorter sound. For example: the howl is long but the cackle is short--if the randomizer selects the howl, it will play at least one additional sound until it gets one like the cackle; if it selects the cackle, it plays only that one sound. I had put in a 5 second delay to wait for the sounds dissipate, but that wasn't enough for all sounds and I didn't want to make it longer because that makes the candy grabbers wait too long.

So, I got a bigger cauldron and that worked fine--briefly. It played only one sound at a time since there is more room for the sound to dissipate. Then the problems: I do not know what happened, but it started producing static only and resetting--at first randomly and then continuously, then stopped producing sound altogether.  I thought I might have cooked the Trinket, but it doesn't work on the Uno either. The sensor is not registering, so I probably cooked my last one. According to my meter, there is a signal on the speaker pin when there is supposed to be, so I may have cooked the amp too. I swapped that out, with no success...but the amp I swapped to was the one I thought was cooked earlier but discovered I wired it wrong--maybe I really did cook it. I have more, but I need to solder on headers and a terminal block before testing.

So, I don't know if I'll be ready for Halloween.  If I get the amp to work I may invest in a retail HC-SR04 at Radio Shack while I wait for my $0.88 ones to arrive from China.

I'm not happy.

Monday, October 26, 2015

Halloween Candy Cauldron V2.0

Last year I was enticed into a Halloween Project.  I was working on my donkey, which involved sound, at the same time many of  the +Adafruit Industries  weekly #ShowandTell and #WearableWednesday projects had a Halloween theme, and of course there's the #electroninchalloween hashtag.

So, I came up with the idea of a candy dish that made Halloween-ey sounds when someone reaches in for candy. It was a big hit with adults, but kids less impressed.  I realized that I had some design flaws.

Here's my appearance on the Adafruit Show and Tell, while I was still working on the SD card reader. It should start just before my section (after you skip the ad), but if not, I start at about minute 18.

First, I did not enclose the circuitry, so aggressive candy grabbers could introduce failure (adults were bigger offenders here).  Second, I wanted to use the +SeeedStudio SD Card Shield, which meant I needed to include an Arduino Uno. That was fine, because the shield sits on top of the Uno and the Adafruit Class D Amp uses 5V. Most SD card readers, particularly MicroSDs, want 3.3V.  I could do this off the Uno, but I needed another approach if I wanted to use a different board with one voltage level or the other.

I had a Adafruit Pro Trinket (5V) around, so I decided to use it.   I need the Pro Trinket as opposed to Trinket, because the smaller (non-Pro) version doesn't have enough pins to support SPI, which I need for the SD card.  I does not have a digital pin #7, which I was using for the Echo Pin on the ultrasonic sensor, so I has to change that to #6. Fortunately, I also had an LC Studios SD Card Reader that accepted 5V or 3.3V. That simplified matters, avoiding a voltage regulator and  logic level converter (which I have, but simpler is better),  The Pro Trinket can be powered from up to 16V, so I'll bring 9V  into it and power a 5V rail from the Trinket.

See issues, below,  I had 2 of the LC Studios boards. One would not work at all.  The other I got to work with the Uno but not with the Trinket. So, I ordered an Adafruit Micro SD Breakout Board. More expensive, but it works--and I am supporting a my favorite supplier.

Now I needed an enclosure.  This is a case where a 3D Printer would be really handy so I could design my own (I'm working on that).  Stock enclosures almost always have standoffs in the wrong places, so at best the standoffs are useless and at worst are in the way and have to be cut out, AND they're never exactly the right size.

To recap last year's project, I had:
Ultrasonic Distance Sensor and 3" speaker from a defunct clock radio as peripherals, with an Arduino Uno, SD card shield, and an amp--all loose in the bottom of the cauldron (except the speaker and sensor). The battery was external.

The changes are:
  1. add an enclosure; mine is 5" X 1.75" X 2.5"--bigger than I really need but everything fits, including a half-sized breadboard
  2. change from the Uno to a 5V Pro Trinket
  3. use a MicroSD reader instead of the SD shield
  4. I considered a circuit board or perma-proto), but decided to keep the half-sized breadboard for modularity and simplicity
  5. move the battery into the enclosure and use a switch to power on/off (last year I had the 9V battery outside the cauldron, inside a switched battery holder
  6. as noted below, I used long header pins to connect components to the breadboard
  7. change the pin assignment for the Echo Pin from 7 to 6
I will need to connect 8 wires from the enclosure: 4 to the distance sensor,  2 to the speaker, and 2 from the battery to the switch so I'll need to have holes in the enclosure and devise a way to connect them (short of hardwiring). I have some Radio Shack RCA phono plugs and jacks for the speaker, and holes for the sensor wires and power wires. I will use JST connectors for the power and sensor wires.
Since I'm keeping the breadboard, I can use long header pins in the breadboard, and just plug the JST female ends into them.

Everything else is as in V1.

Issues along the way:

After I got an SD card reader that worked (see above), I had it all working.  Then I must have messed up a connection, because I was getting no sound. I suspected that the HC-SR04 was cooked, so I put on my FTDI board so I could use the serial monitor to see the testing messages I put in the code. Sure enough, it was not registering.  I had 2 more, so I swapped one in. same result.  Swapped the other in--all is well.  I knew I would need more, and my options were 2 for $9 at amazon or $0.88 each off ebay. I ordered 5, If one works I break even with Amazon.  

Next problem:  still no sound. I check every connection and reran everything, so I concluded that the amp was bad too (this happened in another project with the same amp).  I ordered 4 more. As soon as they arrived I soldered the header and terminal block and swapped it in.  No sound. I turned the volume to see if that was the problem,  Just in case, I checked the wiring again. Sure enough, the wire that was supposed to connect A- to GND was misplaced. I moved it and tiedd again. Still no sound. Remembering that I had adjusted the volume, I turned it back. Sure enough, I had turned it down earlier, so now its all good.

New circuit:


Parts

  1. enclosure (mine is 5"L X 1.75"H X 2.5"W)--I don't remember where I got it, but Radio Shack has several that are close, so does Micro Center
  2. half-sized breadboard
  3. Adafruit Pro Trinket (5V)
  4. Adafruit Micro SD Breakout Board
  5. HC-SR04 Ultrasonic sensor (link for example--it can be found for
  6. Adafruit Class D Amp 
  7. 3in 8 Ohm speaker (I took mine from a clock radio)
  8. SPST toggle switch
  9. Sparkfun FTDI Basic Breakpout (for serial debugging)
  10. 9V Battery  (or any power supply > 5V)
  11. snap-on battery cap with leads (for 9V--other options for other power--e.g., a 4XAA enclosure)
  12. female-female jumpers
  13. JST connectors
  14. RCA phono plug and jack
  15. long header pins 

Construction:

  1. Upload the code to the Trinket.  I really didn't have to do this first, but since is was written for the UNO on an earlier version of the IDE, I wanted to make sure.  All good on IDE version 1.6.5...and I had to do it again once I realized that I needed to re-assign the Echo Pin
  2. I used long header pins (long on both sides) in the breadboard, and used JST female ends to connect to them, and soldered the JSTs to the leads to battery/switch, sensor,  and amp. I used female-female jumpers to connect to the SD breakout (also Speaker Pin to the amp). The amp itself is plugged into the breadboard via its header pins
  3. Drill holes in the enclosure (1/8" for the wires to the JST's, 3/8" for the RCA plug--yes I put the plug on the leads to the amp and the jack on the leads to the speaker)
  4. Solder header pins on the Trinket
  5. Solder leads to the RCA jack and plug (only need one of each since there's just one speaker), and the JST connectors
  6. Connect, test, and go

Code

/*2014-10-05
Halloween Candy Dish: Play random sound when kid reaches into bowl
*/
/*2014-10-05 HC_SR04 Distance sensor code added
 Need to modify pins for Halloween (13&12 used by SPI)
 2014-04-26 Downloaded from Instructables
 HC-SR04 Ping distance sensor]
 VCC to arduino 5v GND to arduino GND
 Echo to Arduino pin 13 Trig to Arduino pin 12 (used 8&7 instead)
*/
/* 2015-10-14
 Change to Pro Trinket from Uno to put the circuit in an enclosure
 Echo to 6 (Pro Trinket does not have pin 7)
 see virgilmachine.blogspot.com
*/

/*includes*/
#include    //SPI library
#include     //SD card library
#include //library for playing sound

/*constants*/
#define SD_ChipSelectPin 10
#define trigPin 8
#define echoPin 6 //used to be 7 on Uno--Pro Trinket does not have a pin 7
/*variables*/
int song = 0;   // song number for random function
/*objects*/
TMRpcm speaker;   // create an object for use in this sketch

void setup(){
  randomSeed(analogRead(0));  //initialize random (A0 unconected)
  pinMode(trigPin, OUTPUT);   //pins for distance sensor
  pinMode(echoPin, INPUT);
  speaker.speakerPin = 9; //output to amp
  speaker.loop(0); //2014-10-05 do not play repeatedly
  Serial.begin(9600);
  if (!SD.begin(SD_ChipSelectPin))
     {  // see if the card is present and can be initialized:
     Serial.println("SD not initialized");
      return;   // don't do anything more if not
      }
   else
      {
       Serial.println("SD initialized");
     }  
  speaker.volume(1);
//  speaker.setVolume(7);   //attempt to increase volume
}

void loop() {
  long duration, distance;
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
//trigPin must me high for 10 microsecs to do ranging
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
//duration is the time between pings of the sensor  in microseconds as returned on echoPin
  duration = pulseIn(echoPin, HIGH);
//duration is a round-trip, so divide by 2; speed of sound is 29.1 cm/microsec, so distance is in cm
  distance = (duration/2) / 29.1;
    Serial.print(distance);  //debug
    Serial.println(" cm");  //debug
//2014-10-05 If distance is <~5in someone has reached in 12/2.54cm=just under 5in
if (distance < 12)
    {
      song = random(1,10); //get random number from 1 to 9   sounds on SD card are 1.wav, etc.
      Serial.print("song: "); //for debugging
      Serial.println(song); //for debugging
      switch (song) {
        case 1:
          speaker.play("1.wav");
          break;
        case 2:
          speaker.play("2.wav");
          break;
        case 3:
          speaker.play("3.wav");
          break;
        case 4:
          speaker.play("4.wav");
          break;
        case 5:
          speaker.play("5.wav");
          break;
        case 6:
          speaker.play("6.wav");
          break;
        case 7:
          speaker.play("7.wav");
          break;
        case 8:
          speaker.play("8.wav");
          break;
        case 9:
          speaker.play("9.wav");
          break;
      }
        delay(5000);
  }
delay(50);
}



Friday, November 28, 2014

The Head Move Donkey



In 1978, I bought a wind-up donkey for my daughter, then an infant. The donkey moved its head while playing “Donkey Serenade.”  I figured that I could replicate that with an Arduino, a motor, sound on an SD card, and a speaker.  It took me 10 weeks, but I did it. Here’s the story.

I got sidetracked for a while doing my Halloween Candy Cauldron, which came out of this one. While I was working on the donkey, learning about playing sound with an Arduino, I began seeing lots of Halloween projects on Adafruit shows, so I decided to try my hand on Halloween sounds.

This was an incredibly rewarding project that was challenging, fun, interesting, and educational—all the reasons I wanted to start with Arduino in the first place!

Here’s a video, and another pre-completion, and there are links to two appearances on Adafruit’s Show-And-Tell under “Resources” below.

Parts List (about $80 in parts done this way, assuming you have all the tools and supplies)

2.1mm power plug and jack (doesn’t matter what you get as long as they match)
Micro SD Card
Speaker (I used a 2” 8ohm speaker from an MFC printer I took apart)
Sound editing software, like Audacity
Hook-up wire (I used 22 gauge solid wire to connect the speaker, and to run from the terminal block on the PWM board to the power connector because that was easier
Adafruit silicone-coated stranded wire in various colors (very flexible--helpful for connections inside the enclosure)
Header pins (male, female, and right-angle)
Soldering iron and accessories
Heat Shrink tubing
Dremel or similar tool for cutting out the stand-offs on the enclosure
Drill and bits for making holes in the enclosure (1 for the power jack and 1 for the wires to the speaker and servo)
Electrician’s tape to keep stuffing out of the wire hole
Sewable Velcro strips for closing up the donkey
Needle and thread

Step 1:  play music
First, I located and downloaded the song from http://www.mp3olimp.net/donkey-serenade/ .
Using a Seeed Studio SD shield, the TMRpcm library and this tutorial (http://www.instructables.com/id/Playing-Wave-file-using-arduino/) I was able to play the song. Note the need for specific sound file format, that’s where Audacity came in. I also shortened the song to delete a couple of the more jazzy parts.  Nothing against the music, but this is a child’s toy. The sound needed amplification so I tried the diy amp from http://vcctoground.com/tutorials/simple-pocket-audio-amplifier/   but did not get much improvement (NPN and PMP transistors, etc.).

Step 2:  operate servo
I had a micro servo and an example from Jeremy Blum’s tutorial (http://www.jeremyblum.com/2011/01/31/arduino-tutorial-5-motors-and-transistors/) and got that to work fine.

Step 3: put it together
I could get the servo to move or the music to play, but not both. With help from Adafruit support (https://forums.adafruit.com/viewtopic.php?f=25&t=59793), buying a PWM breakout board (https://www.adafruit.com/product/815), and some soldering, I got it to work. The problem was a timer conflict.

Step 4: getting small
I asked Becky Stern, Director of Wearable Technology at Adafruit, for some advice on getting all this in a donkey. She answered my question on Wearable Wednesday (~21:30)!  I also needed to change some components. First I swapped out the Arduino Uno for a Pro Mini 5V (I thought).  Next I bought a microSD breakout to replace the shield. The Micro SD runs on 3.3V, so I thought I needed a voltage regulator and maybe something else (http://forums.adafruit.com/viewtopic.php?f=19&t=60813 ).  In the process I found out that the 5V pro mini was really a 3.3V, and figured out that that was really what I wanted (power the Pro Mini, the amplifier, the PWM board, and the servo from the battery, and power the SD from the Mini).

Step 5: refine
I figured out how to play the song in a loop without resetting the Arduino (TMRpcm.loop(1)). In the process of doing all this I cooked my Mini—it won’t put out 3.3V anymore (more like .9V) so I’m back on the Uno until my 3V Pro Trinket comes next week.  I also decided to go with a 6V coin cell holder with switch (https://www.adafruit.com/product/783 ) for power.  I gave up on the amplifier and put in an LM386 circuit.  It works OK, but I also ordered an amplifier breakout (https://www.adafruit.com/product/2130) just in case I can’t get the sound I want out of a smaller speaker.

Step 6: construction
Donkey arrived 10/4/2014.  It will accommodate a small project box, servo, speaker, and battery holder. Now I need to be sure I can get the whole circuit on a board small enough to fit into the enclosure. The Altoids tin will fit in the donkey, but I also have a project box if I can’t get the circuit in the Altoids tin.
Programming the Pro Trinket was somewhat flaky at first. I needed to install the hardware updates for the Arduino IDE so it would recognize the board. Also, had to set the programmer to USBTinyUSB. When that didn’t work (“can’t find USBTiny”) I discovered I needed the Windows drivers.  After I got that all done I was able to upload the example blink program, so I’m in business, I think. Now I need to solder the headers so I can test the circuit. I have another Pro Trinket coming tomorrow, so I will use that for the real thing, soldering directly, but I’ll need this one with the headers anyway. UPDATE:  there may be an issue with the ProTrinket and device recognition on Windows (or at least my Windows systems).  I got it to work with Ubuntu, and in the process discovered that the upload took longer than the bootloader was active, so I have to wait to press the reset button to activate the bootloader, but leave enough time in the compile process for it to initialize.  Kind of a pain, but it’s working. I never did get it working on Windows 7 (that may be my fault), but it’s working OK on Windows 8 and Ubuntu.

Step 7: more construction
I tested the 6V coin cell battery, and the power lights on the boards come on but no music and no motion.  I need find another power source. I’m considering a 9V with a voltage regulator to bring it to 5V. A 4bXAA works (6V) but it’s big. Also I got the Adafruit mono amp, and it puts out more volume than the LM386. I also takes up a lot less space. Next, I got some SMT breakout boards (https://www.adafruit.com/product/1208) so I’ll see if I can solder the TSSOP-28 PCA9685 to it and save more space. That didn’t work—not skilled enough. I will try to make a DIY breakout. I shorted something and cooked my amp, just got new amps today and will try again. The servo moves but no music…hopefully the new amp will get me back where I was. The new amp fixed the problem. I decided to stick with the Adafruit PCA9685 PWM board because my surface mount skills are not there yet.

Step 9: The enclosure
I got a 4x2x1 enclosure from Radio Shack.  I fixed an Adafruit 1/8 size Perma-Proto Board to the enclosure with a screw, and drilled a ½” hole for a power receptacle and a 3/8” hole for the servo and speaker wires. In another 3V Pro Trinket, I soldered female headers to A4&A5 and Adafruit silicone coated flexible wires to the pins I need for the circuit. I put right angle headers on the I2C pins of the PWM board, so I can slide those into the female headers and have the bigger board flat with the Trinket on its side perpendicular to the PWM board. All the power and ground connections will go to the Perma-Proto Board + and - rails, with the power connector supplying those rails.  The power supply will be an Adafruit 4xAA battery pack with switch so there is an external switch  access to batteries for changing without opening the donkey, and less space to take up inside.

Step 10: new Trinket
I tried wiring up the circuit with the new, newly soldered Trinket. It powers up, and puts out 3.3V on that
Rail, but it does not play sound or move the motor. Either I did not wire it correctly or there is something wrong with my soldering.  More testing tomorrow.
The next week:  I wired it up again.  After some fussing with connections I got the whole thing to work. I spent some design time on the enclosure, so on to soldering/enclosing.

Step 11: The enclosure, part 2
Day 1:  I discovered that I could not fit the Perma-Proto board and the other components in the enclosure.  The next size larger is too big for the donkey.  Also, the internal stand-offs in the enclosure were in the way, so it was redesign time.  I moved the power connector/receptacle from the end to the side to give more room. I used my Dremel knock-off tool to cut out the internal standoffs. Now everything fits.  Without the Perma-Proto Board, I will need to connect the wires for power and ground directly to the connector. Next I got to soldering:
1.       I connected a 2.1mm jack (to plug into the power receptacle) to the battery pack and covered the splices with heat shrink tubing.
2.       I soldered new, solid core wire leads to the speaker.  Solid core works better in the terminal blocks.
3.       I connected solid core wire to the external power terminal block on the PWM board, and ran them under the board to the power connections.
4.       I removed the jumper wires from the amplifier, and soldered wires directly to the header pins.  The A- and GND wires to go to GND, the Vin to go to power, and the A+ pin is soldered to the wire going to digital pin 9 on the Trinket (SpeakerPin).
So far, so good. Tomorrow I’ll replace the six jumper wires on the SD card with direct soldered connections, and test the audio. Assuming that works, I’ll do the 4 remaining wires on the PWM board.

Step 11: The enclosure, part 3
It works!  Without the circuit board there was a lot of soldering. I soldered all the power leads together, two at a time, adding the next one in turn until I had one lead to connect to the power connector + lead, then did the same with the grounds, to the – lead.  I left the jumper wires on the SD board because they’re pretty secure and didn’t want to risk cross-connections due to sloppy soldering.  I did solder leads to the headers for Vcc and GND on the PWM board.  For the SD board, I cut and stripped the jumpers and connected them to the correct leads:
·         GND to the GND complex
·         3.3V to the 3.3V pin on the Trinket
·         SPI pins to the leads I already had soldered to Trinket pins 10-13
I used heat shrink tubing on the splices to protect the connections from touching.
Everything fits—I can close up the enclosure and secure it with screws. The music plays and the servo moves.
Tomorrow: open up the donkey.

Step 12: the donkey 1
I used an x-acto knife to cut the donkey’s belly along a seam, and took out some stuffing.  I got the servo in the neck, and it will turn the head, but I need to fix it to something solid so the horn of the servo turns the donkey instead of the donkey holding the horn while the servo turns. Meanwhile, I stripped the plastic gears on the servo.  This is the second servo I’ve ruined.  So, I ordered two new servos, one micro and one standard, both with metal gears from Adafruit. Hopefully one of them will do the trick.

Step 13: the donkey 2
I used the standard servo. It’s substantial, and it does the trick. I was able to close up the enclosure and screw on the cover with only one mishap: the ground lead on to the power connector broke and I had to de-solder/re-solder it.  I pulled more stuffing out of the head to get the servo in closer. It moves a little, but with more stuffing, it should be even better than it is now.  There’s a little interference from the servo (actually I think it’s from the PWM board) but it’s OK.
Next: sew on the Velcro and re-stuff.

Step 14: the donkey 3
First, I sewed on strips of Velcro that I had cut to the length of the slit in the donkey’s belly.  I’m not great at sewing, so it was a little frustrating, but I got through it.  Next, I tried different positions for the servo to get it to move the head. I re-stuffed the neck to try to keep the servo in position, placed the speaker, re-stuffed the rest of the donkey put the enclosure in and closed the Velcro.  After one more iteration to get the servo better positioned, it’s done.  I’d like more range of motion on the head, but it does move.

Step 15: futures
1.       I’d like to get more movement from the Servo. I have to experiment with different horns, different positions, and different programming.  I was time-limited on this one—I needed to have it ready for Thanksgiving Dinner, so I haven’t been able to pursue more options yet.
2.       I would shorten the wires inside the enclosure, and probably move the power connector to the end of the box (rather than coming up from the bottom). 
3.       I’d also like to find a box that fits the components a little better, and to fasten the boards to the enclosure (right now, they’re loose, and any jostling by grandchildren may disconnect something). I think I could put standoffs on the end of the enclosure for the Trinket and the bottom for the PWM Board.
4.       Rather than connect the Pro Trinket to the PWM board via right-angle headers, I might want to solder wires directly from the holes on the Trinket to the holes on the PWM board.
5.       To accomplish #3 and other improvements, I would need to hold off on soldering header pins to any components until I know how I really want to wire up and solder the whole project. I soldered the headers first thinking generically, then when I got inside I found that some of the pins were in the way.
6.       I’d like to see if I can make a PCA9685 breakout on my own to save space. New Year’s Resloution #1:  get some SMT soldering skills.
7.       Investigate 60MHz vs 50MHz on the Servo (see comment in sketch)



Circuit as laid out on a breadboard



Resources

Presentation on Adafruit Show-and-Tell
In-Process https://www.youtube.com/watch?v=Y8tFcF2-klw about minute 13 to 16:30
Finished  http://www.adafruit.com/blog/2014/11/26/show-and-tell-google-live-hangout-wednesday-night-at-730pm-et-112614-video/ about 4:50 to 6:45
 
Discussion on Wearable Wednesday
http://www.adafruit.com/blog/2014/09/17/wearable-electronics-with-becky-stern-9172014-live-2pm-et/

Donkey:

Sound:

Tutorial:

Sound from SD:

Micro SD:

TMRpcm library

Power servo

PWM/Servo Breakout

Amplifier

Increase volume (LM386)

Amplifier I used

Servo I finally used

Arduino Sketch

/*September-November 2014
THE HEAD MOVE DONKEY
Uses TMRpcm library to play music from and SD card, and Adafruit_PWMServoDriver library to drive the servo.
Some comments are from example code that came with those libraries
Objective is to move the donkey's head with the servo while playing "Donkey Serenade.
When I was on the Uno and had the serial monitor, I used an led to prove that I was moving through the loop. That is now commented.
Circuit uses the Adafruit Class D mono amp, Adafruit PWM servo board driving a micro servo, a micro SD card board, an 8 ohm speaker, and a 4xAA batter holder
It now runs on an Adafruit 3V Pro Trinket, feeding 3V to the SD card and using battery power for the Trinket, Servo, PWM board, and amp
*/
/*includes*/
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);

#include <SD.h> // need to include the SD library
#include <TMRpcm.h>           //  also need to include this library...

/*constants*/

// Depending on your servo make, the pulse width min and max may vary, you
// want these to be as small/large as possible without hitting the hard stop
// for max range. You'll have to tweak them as necessary to match the servos you
// have!
#define SERVOMIN  200 // this is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX  350 // this is the 'maximum' pulse length count (out of 4096)
/*SERVOMAX reduced from 600 in example to 500 to save the servo (I stripped the gears on one already).  That gave me ~160 degrees. 
I reduced it again to 350, so it goes ~90 degrees: 45 to left and 45 to right if I have the motor and horn positioned properly. That's what I want
*/

// our servo # counter

#define SD_ChipSelectPin 10  //using digital pin 4 on arduino nano 328 2014-09-03 changed to 10 to match datalogger
//#define ledPin 5   //pin to control led   2014-10-25 testing
/*variables*/
int angle = 0;   // servo position in degrees
uint8_t servonum = 0;
/*objects*/
TMRpcm speaker;   // create an object for use in this sketch


void setup(){

 
  speaker.speakerPin = 9; //11 on Mega, 9 on Uno, Nano, etc
  speaker.loop(1); //2014-10-03 play repeatedly
  Serial.begin(9600);
  if (!SD.begin(SD_ChipSelectPin))
     {  // see if the card is present and can be initialized:
     Serial.println("SD not initialized");
      return;   // don't do anything more if not
      }
   else
      {
       Serial.println("SD initialized");
     }    
  speaker.volume(1);
  speaker.play("hmd.wav"); //play Donkey Serenade each time the arduino powers up, or is reset (as of 2014-10-03, plays repeatedly)
  pwm.begin();
  pwm.setPWMFreq(50);  // Analog servos run at ~60 Hz updates (says Adafruit--I found that 50 worked better with my servo from Radio Shack—never set it back for the Adafruit servo)
}

void loop()

{
//blink led to show arduino is looping through this whether servo moves or not
//  digitalWrite(ledPin, LOW); // 2014-10-25 testing only
  delay(1000);
//  digitalWrite(ledPin, HIGH); //testing
    for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
    pwm.setPWM(servonum, 0, pulselen);
    delay (100); //was 50, doubling the delay to move the head more slowly
  }
  delay(1000); //pause when limit is reached
  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
    pwm.setPWM(servonum, 0, pulselen);
    delay (100);
  }
  delay(1000); //pause when limit is reached

}