Watch Audio

From Hamsterworks Wiki!

Jump to: navigation, search

This project that uses the RF interface of my Texas Instruments hackable Chronos eZ430 watch to control MP3 playback on my Raspberry Pi - the watch is on the left, and the RF USB dongle is on the right in the photo.

Audio watch.png

I was working on my Raspberry Rower project which uses the watches accelerometer to sense motion and provide feedback, but wanted something immediately useful, so here is the result of an hour of hacking.

Contents

Getting audio working

If you are using the Debian distro on your Raspberry Pi getting audio working is easy:

 sudo apt-get install alsa-utils
 sudo modprobe snd_bcm2835

Note: You will need to "modprobe" every time you reboot your Pi.

If you want to set the audio signal to be sent through the 3.5mm socket (rather than over the HDMI) do this:

 sudo amixer cset numid=3 1

Playing back mp3s on the command line

Playing back the MP3s on the Raspberry Pi pretty simple too! Install MPG3321:

 sudo apt-get install mpg321

You can the use a shell script to play your music:

 for i in /home/pi/u2/The\ Joshua\ Tree/* ; 
 do 
   sudo mpg321 "$i"
 done

My Project

The Chronos eZ430 has a RF interface that allows you to send button presses to a USB host. The default watch firmware uses this to send PowerPoint or iTunes keypresses.

It isn't two hard to use this feature under Linux

  • Copy the code below into watch_audio.c
  • Compile the code below with "gcc -o watch_audio watch_audio.c -Wall -O2"
  • Connect the RF USB dongle
  • Start the program with "./watch_audio /dev/ttyACM0"
  • Set the watch into "ppt" mode and start transmitting.
  • Press the left buttons to change the volume
  • Top right button to stop playing the current MP3 - it 'kills' the mpg321, allowing your script to start the next track.

The 'C' program does the following:

  • Opens the serial port
  • Sets it to 115,200 baud, no flow control
  • Sends the hex string: 0xFF 0×07 0×03
  • Check that the watch replies 0xFF 0×06 0×03 to confirm a connection has been made
  • The data is requested by sending 0xFF, 0×08, 0×07, 0×00, 0×00, 0×00, 0×00
  • The watch/RF dongle will then return seven bytes back in the format of "ff 06 07 tt xx yy zz" where the last three bytes are the accelerometer values (if the watch is in 'Acc' mode).
  • The 'tt' values are the Watch's switches, allowing you to control stuff from your wrist
  • The different 'tt' values cause the program to run different external commands

Known issues

  • The ALSA driver for the Raspberry Pi is a little bit dodgy. Sometimes the "amixer" command hangs during on one of the USB IOCTLs, which in turn hangs playback. To get around this I kill the "amixer" command after one second.
  • The starting and stopping of the audio causes 'pops'

Project source

/**************************************************************
* watch_audio.c - Control the audio mixer from a TI eZ430 watch
*
* Author: Mike Field [hamster@snap.net.nz]
*
* Supplied as is, where is - use it however you like, but don't
* blame me!
*
* See http://hamsterworks.co.nz/mediawiki/index.php/Watch_Audio
* for more info (and bugs!).
*
* Tested on the Raspberry Pi using Debian.
*
**************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>

static unsigned char hello_str[3] = { 0xFF, 0x07, 0x03 };
static unsigned char req_data[7] = {0xFF, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00};

/********************************************************************/
static int read_switches(int f)
{
  int nRead;
  char buffer[12];

  for(;;)
  {
    if(write(f,req_data,sizeof(req_data))!= sizeof(req_data))
      return -1;

    nRead =  read(f,buffer,sizeof(buffer));
    if(nRead == -1) return -1;

    if(buffer[0] == (char)0xFF && buffer[1] == 0x6 && buffer[2] == 0x7 )
    {
      return buffer[3] & 0xff;
    }
  }
}

/********************************************************************/
static void setVolume(int volume)
{
   int pid;

   if(volume < 0 || volume > 100) {
      printf("Invalid volume\n");
      return;
   }
   pid = fork();
   if(pid == -1)
     return;

   if(pid == 0)
   {
     char buffer[5];
     sprintf(buffer,"%i%%",volume);
     execlp("amixer", "amixer", "cset", "name=PCM Playback Volume", buffer, NULL);
     exit(0);
   }
   sleep(1);
   kill(pid,SIGHUP);
   wait(&pid);
}
/********************************************************************/
int main(int argc, char *argv[])
{
  int f;
  int volume = 80;
  struct termios cf;

  if(argc != 2) {
    printf("usage: %s [serial port - usually /dev/ttyACM0]\n",argv[0]);
    return 0;
  }

  f = open(argv[1],O_RDWR);
  if(f == -1) {
    printf("Unable to open '%s'\n",argv[1]);
    return 0;
  }

  if(tcgetattr(f, &cf) == -1) {
    printf("Unable to get termios details\n");
    return 0;
  }

  if(cfsetispeed(&cf, B115200) == -1 || cfsetospeed(&cf, B115200) == -1) {
    printf("Unable to set speed\n");
    return 0;
  }

  /* Make it a raw stream and turn off software flow control */
  cfmakeraw(&cf);
  cf.c_iflag &= ~(IXON | IXOFF | IXANY);

  if(tcsetattr(f, TCSANOW, &cf) == -1) {
    printf("Unable to set termios details\n");
    return 0;
  }

  /* See if you can get a response to a hello */
  for(;;)
  {
    unsigned char hello_response[3];
    int i;

    usleep(250000);

    if(write(f,hello_str,sizeof(hello_str)) != sizeof(hello_str)) {
      printf("Unable to say hello\n");
      return 0;
    }

    i = read(f,hello_response,sizeof(hello_response));
    if(i < 0) {
      printf("Unable to read response\n");
      return 0;
    }

    if(i != sizeof(hello_response))
      continue;

    if(hello_response[0] == 0xFF && hello_response[1] == 0x6 && hello_response[2] == 0x3)
      break;
  }

  printf("Waiting for switch press\n");
  for(;;)
  {
     int switches = read_switches(f);
     if(switches == -1)
       break;

     switch(switches) {
        case 0x12:
           volume+=5;
           if(volume > 100)
                volume = 100;
           setVolume(volume);
           read_switches(f);
           break;
        case 0x22:
           volume-=5;
           if(volume < 0)
                volume = 0;
           setVolume(volume);
           read_switches(f);
           break;
        case 0x32:
           setVolume(0);
           system("killall -1 mpg321");
           setVolume(volume);
           read_switches(f);
           break;

        case 0xFF:
          usleep(100000);
          break;
        default:
          printf("Unknown Switches %4x\n",switches);
          break;

     }
     fflush(stdout);
     usleep(100000);
  }

  close(f);
  return 0;
}

Personal tools