How to Build a Custom Arduino Talking Reminder Machine, Part 2

403

Arduino reminder tutorialIn Part 1 we learned about solderless breadboards and the DS1307 real-time clock for keeping time on Arduino devices. Now we’re going to load and test the complete sketch for playing motion-activated audio reminders.

Putting it all together

Load your SD card with your recorded messages: pay bills, play with the dogs, favorite poems, inspirational quotations, favorite songs– anything you want. Your audio files must be mono 22050Hz WAV files. Then try out the Reminder Machine sketch. Change the audio file names to your audio filenames, set some near-future times, wave something in front of the MaxBotix, and see what happens.

/* Arduino Reminder Machine
*  Play audio reminders, inspirational
*  quotations, poetry, or anything
*  you like. Playback is triggered by
*  motion sensor and schedule
*/
#include <FatReader.h>
#include <SdReader.h>
#include <avr/pgmspace.h>
#include "WaveUtil.h"
#include "WaveHC.h"
#include <Wire.h>
#include "RTClib.h"
/* Variables for reading SD card
*  and printing filenames
*/
SdReader card;  
FatVolume vol;
FatReader root;  
FatReader f;  
WaveHC wave;  
uint8_t dirLevel;
dir_t dirBuf;    
#define error(msg) error_P(PSTR(msg))
RTC_DS1307 RTC;
void setup () {
    Serial.begin(9600);
    Wire.begin();
    RTC.begin();  
/* Begin initializing and checking SD card for errors
*/
  putstring_nl("nLet's start by testing the SD card and files.");
/* Test if card will play with the default 8 MHz SPI
*  (Serial Peripheral Interface) bus speed. If it doesn't, comment out
*   "if (!card.init()) " and uncomment "if (!card.init(true))"
*/
  //  if (!card.init(true)) {   
  if (!card.init()) {    
    error("Card init. failed!");
  } 
/* enable optimized reads - some cards may timeout. Disable if you're having problems
*/
   card.partialBlockRead(true);
/* Now we will look for a FAT partition!
*/
  uint8_t part;
  for (part = 0; part < 5; part++) {  
    if (vol.init(card, part))
      break;                        
  }
  if (part == 5) {                      
    error("Sorry, I could not find a valid FAT partition!");  
  }
/* Print partition information
*/
  putstring("I'm using partition ");
  Serial.print(part, DEC);
  putstring(", and the filesystem type is FAT");
  Serial.println(vol.fatType(), DEC);
/* Can the root directory be opened? Print an error
*  message if it can't.
*/  
  if (!root.openRoot(vol)) {
    error("Can't open root dir!");
  }
/* All right, everything looks good. Print
* the filenames found on the SD card.
*/
  putstring_nl("nI found these files on your SD card: (* = fragmented):");
  root.ls(LS_R | LS_FLAG_FRAGMENTED);
  putstring_nl("nEverything checks out, so let's get started!n");
}
/*** End Initialize and check SD card for errors ***/
/* Begin Reminder Machine sketch
* First display time, date, and 
* MaxBotix sensor value
*/
 
void loop() 
{    
    DateTime now = RTC.now();
    int a0value = analogRead(0);
    Serial.print("The reading from the sensor, taken from analog pin 0, is ");
    Serial.print(analogRead(0));
    Serial.println();
    Serial.print("The day of the week is ");
    Serial.print(now.dayOfWeek(), DEC);
    Serial.println();
    Serial.println("The date and time are:");    
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(' ');
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.println(now.second(), DEC);   
/*** End date, time, and sensor value display ***/  
/* Now we schedule audio playback. Audio will play
* when two conditions are met: the sensor detects
* movement, and it is the scheduled time
*/
    if  ((a0value <= 45) && (now.hour() == 14)) {            
       Serial.println("nNow playing: Walk the dogs!n");
       playcomplete("WALKDOGS.WAV");
       delay(600000); //do not replay for 10 minutes
       }    
    else if ((a0value <=45) && (now.day() == 5)) {
       Serial.println("nNow playing: Mow the lawn!n");
       playcomplete("MOWLAWN.WAV");
       delay(600000);
       }
       
    else if ((a0value <=45) && (now.dayOfWeek() == 7) && (now.hour() == 18)) {
      Serial.println("nNow playing: Relax and read a book!n");
      playcomplete("READBOOK.WAV");
       delay(600000);
       }
    else {
      Serial.print("nSorry, your numbers don't match. No music for you.nn");
       delay(30000);
      }       
}
/*** End playback schedule ***/
/* Audio playback and error functions
*/
void error_P(const char *str) {
  PgmPrint("Error: ");
  SerialPrint_P(str);
  sdErrorCheck();
  while(1);
}
void sdErrorCheck(void)
{
  if (!card.errorCode()) return;
  Serial.print("nrSD I/O error: ");
  Serial.println(card.errorCode(), HEX);
  Serial.print(", ");
  Serial.println(card.errorData(), HEX);
  while(1);
}
/* Play a WAV file from beginning to end with no pause.
* Play only one file at a time
*/
void playcomplete(char *name) {
  playfile(name);
  while (wave.isplaying) {
     }
}
void playfile(char *name) {
  if (wave.isplaying) {
    wave.stop();
  }
  if (!f.open(root, name)) {
    Serial.print("nCouldn't open file ");
    Serial.println(name);
    return;
  }
  if (!wave.create(f)) {
    Serial.print("nNot a valid WAV"); return;
  }  
  wave.play();
}
/*** end Audio playback and error functions ***/
/*** end sketch ***/

A successful start

When it starts successfully it will say this:

Let's start by testing the SD card and files.
I'm using partition 1, and the filesystem type is FAT32
I found these files on your SD card: (* = fragmented):
MOWLAWN.WAV  
WALKDOGS.WAV  
READBOOK.WAV  
Everything checks out, so let's get started!
The reading from the sensor, taken from analog pin 0, is 11
The day of the week is 3
The date and time are:
2014/5/21 15:59:35
Sorry, your numbers don't match. No music for you.

When the conditions are met to play an audio file, this is what you’ll see:

The reading from the sensor, taken from analog pin 0, is 39
The day of the week is 3
The date and time are:
2014/5/21 16:0:11
Now playing: Walk the dogs!

The code that reads your SD card and controls audio playback came from the WaveHC example sketches. The date and time code came from the RTClib example sketches. The payload in the middle, the loop code that controls playing back your audio files, is a simple series of if–else–if statements. You can customize this sketch by editing the loop function.

You may not want your reminder files to play every time you walk by after they are triggered the first time, and there is an easy way to control this with Arduino’s delay() function. delay() takes a single parameter of milliseconds, so 1000 is one second. If you set the delay() value to 600000, that is 10 minutes that your reminder will not play. The delay period starts after your WAVE file stops playing.

Your a0value setting should specify the maximum distance so that your sensor is not set off by an opposite wall or other object that does not need reminders.

The examples in the sketch play your reminders during a period of a single hour. You can narrow the time range with minutes, like this:

((a0value >= 30) && (now.hour() == 16)  && ((now.minute() >= 0) && (now.minute() < = 30)))

This sets the reminder for 4:00 to 4:30 p.m. Mind your parentheses! They can drive you nuts.

This sketch can easily be modified to play reminders only on a schedule, or only when the sensor detects movement, so have fun mixing it up.

This concludes our wonderful and fun project. Please consult the References section to learn more.

References

How to Build a Custom Arduino Talking Reminder Machine, Part 1
Ladyada.net Arduino Tutorials
Arduino language reference
An excellent introductory book for novices is Programming Arduino: Getting Started with Sketchesby Simon Monk, andArduino Cookbook, 2nd Editionby Michael Margolis is great if you already know a little about electronics and a smidgen of C programming.