wave shield PROGMEM code question

Adafruit Ethernet, Motor, Proto, Wave, Datalogger, GPS Shields - etc!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
cap
 
Posts: 26
Joined: Mon Apr 23, 2012 10:15 am

wave shield PROGMEM code question

Post by cap »

Howdy! I finally got my wave shield, ping sensors and servos working, thanks to your support using ServoTimer2;-) Now I want to optimize and I have stumbled badly with trying to put my code into PROGMEM space. First a description of what I am trying to do.

I have 2 servos, 2 ping sensors and an arduino uno driving a wave shield embedded in my sculptures pedastal. I sweep a 360 deg. with the 2 servos and ping distances from the pedastal. When a sensor reads a value it is transformed into an index which selects one of 90 wavs on my SD card. I used the example in the library, "play by index". I divided a 4 1/2 min. song into 90- 3 sec. snippets which play for different times depending on the distance to the pedastal. If you walk around it the correct way, it will play the whole song, thanks to your dapc code.

Well, I decided to add more songs to the SD card and add code to trigger a different song depending on the direction you approach the pedastal.
Bang! I hit the wall on RAM. So I put the wav file names in PROGMEM space. I want to put the index array into PROGMEM space too but am having difficulty with a command

Code: Select all

strcpy_P(name, PSTR("tdxx.wav"));   // copy flash string to RAM
" . I need to change the first two characters to change the song. But I find that the command actually indexes into the card itself...and adds 3 to the index!! Well, I figured out how to change the index by changing a "-1" to a "-3" on this line

Code: Select all

wav_index[i] = root.readPosition()/32 - 1;  
I do not understand why the file name is in PROGMEM space in the first place.

I have been looking for examples of PROGMEM code usage and the http://www.avrfreaks.net gave me hope that I may be able to optimize my code and fill my SD card with songs cut up into sound bites. I confess I am not C++ proficient and have looked at the WaveHC library for hours. Please, would you explain what the strcpy_P command is doing? How does it access the SD card when it is a command looking into program memory?

I am pasting the whole working sketch below. I would paste two images of my sculpture, too, if I could figure out how to use the img tags:-(. There are 2 songs but no code to switch between the two. Thank you for any help you can offer.

Code: Select all

// Ancient Energy Antenna
// Cappy Jack 2012

/* The Ancient Energy Antenna sits on a pedastal which has 3 rings of sensitivity.
 A 4 1/2 min. song plays complete, if you know the trick, otherwise just snippets
 with 3 different play times. A 360 deg. sweep occurs every 15 seconds unless sensors detect an 
 object within the rings.
 */

#include <ServoTimer2.h>  // the servo library
#include <WaveUtil.h>
#include <WaveHC.h>
#include <avr/pgmspace.h>

SdReader card;    // This object holds the information for the card
FatVolume vol;    // This holds the information for the partition on the card
FatReader root;   // This holds the information for the filesystem on the card
FatReader file;   // This object represent the WAV file 
WaveHC wave;      // This is the only wave object
uint8_t dirLevel; // indent level for file/dir names

#define play_time1 600    // ring 1  time to play each wav in milliseconds
#define play_time2 400    // ring 2
#define play_time3 200    // ring 3
#define play_time4 500   // no signal
#define FILE_COUNT 180     // number of wav files
#define error(msg) error_P(PSTR(msg))

// file names are of the form sdxx.wav where x is one of
// the numbers from fileLetter[]
const char fileLetter[] PROGMEM =  {
  '0','0','0','0','0','0','0','0','0','1','1','1','1','1','1','1','1','1','1',
  '2','2','2','2','2','2','2','2','2','2','3','3','3','3','3','3','3','3','3','3','4','4','4','4',
  '4','4','4','4','4','4','5','5','5','5','5','5','5','5','5','5','6','6','6','6','6','6','6','6',
  '6','6','7','7','7','7','7','7','7','7','7','7','8','8','8','8','8','8','8','8','8','8','9',
  '0','0','0','0','0','0','0','0','0','1','1','1','1','1','1','1','1','1','1',
  '2','2','2','2','2','2','2','2','2','2','3','3','3','3','3','3','3','3','3','3','4','4','4','4',
  '4','4','4','4','4','4','5','5','5','5','5','5','5','5','5','5','6','6','6','6','6','6','6','6',
  '6','6','7','7','7','7','7','7','7','7','7','7','8','8','8','8','8','8','8','8','8','8','9'}; 

const char fileLetter1[] PROGMEM =  {
  '1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4',
  '5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0',
  '1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4',
  '5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
  '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0'};

uint16_t wav_index[FILE_COUNT];  // index of wav files in the root directory
dir_t dirBuf;                    // buffer for directory reads in play all wavs

const int pin6 = 8;   //attaches servos to pins 6 & 8
const int pin8 = 6;
const int ping1 = 9;  //attaches ping sensors to pins 7 & 9
const int ping2 = 7;

long duration1 = 0;   // used in ping function
long duration2 = 0;
long cm1;             // distance value- 2 pings
long cm2;
int num1;              // direction value out of servo1
int num2;              // direction value out of servo2
int dir;               // var for switch to sweep servos back & forth in move
int position_1;        // full circle var with 30 positions
int count;             // if count reaches 30 play the song complete
int backnforth;        // sweep 360 deg with 2 servos
int w_index;           // index into SD card for a wav file
int play_time;         // how long to play a wav - max time is 3 sec.

ServoTimer2 servo1;    // declare objects for servos
ServoTimer2 servo2;

void setup() {
  Serial.begin(9600);      // initialize serial communication:
  servo1.attach(pin6);     // attach pins to the servos 
  servo2.attach(pin8);
  int dir = 0;             // set direction of servos to zero
  position_1 = 15;         // servos start half way of 30 steps
  int num1 = 0;            // initialize direction of servo movement
  int num2 = 0;
  int backnforth = 1;    // switching between servos for 360 sweep

  pinMode(2, OUTPUT);   // Set the output pins for the DAC control 
  pinMode(4, OUTPUT);   // These pins are defined in the waveHC library
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);

  putstring("Free RAM: ");       // This can help with debugging, running out of RAM is bad
  Serial.println(FreeRam());
  if (!card.init()) error("card.init");

  card.partialBlockRead(true);  // enable optimized read 

  if (!vol.init(card)) error("vol.init");

  if (!root.openRoot(vol)) error("openRoot");

  index();  //index wav files

}  // end of setup

long ping()   //function to drive the ping sensors
{
  pinMode(ping1, OUTPUT);    
  digitalWrite(ping1, LOW);
  delayMicroseconds(4);  //was 2
  digitalWrite(ping1, HIGH);
  delayMicroseconds(10);   //was 5 
  digitalWrite(ping1, LOW);
  pinMode(ping1, INPUT);
  duration1 = pulseIn(ping1, HIGH);

  pinMode(ping2, OUTPUT);
  digitalWrite(ping2, LOW);
  delayMicroseconds(4);  //was 2
  digitalWrite(ping2, HIGH);
  delayMicroseconds(10);   //was 5  
  digitalWrite(ping2, LOW);
  pinMode(ping2, INPUT);
  duration2 = pulseIn(ping2, HIGH);

  cm1 = microsecondsToCentimeters(duration1);
  cm2 = microsecondsToCentimeters(duration2);
  return cm1, cm2;
}  

long microsecondsToCentimeters(long microseconds)
{
  // The speed of sound is 340 m/s or 29 microseconds per centimeter.
  // The ping travels out and back, so to find the distance of the
  // object we take half of the distance travelled.
  return microseconds / 29 / 2;
}  // end of ping

long out()           //function to print distances
{
  Serial.print(cm1);
  Serial.print("  cm1  ");
  Serial.print(cm2);
  Serial.print("  cm2");
  Serial.println();
}

int move(){        // move the servos back and forth
  switch (dir) {
  case 0:
    num1 = (servo1.read() + 50);
    num2 = (servo2.read() - 50);
    delay(5);      //  not sure about timers....ping still returns 0 sometimes 5/23/12
    servo1.write(num1);
    servo2.write(num2);
    delay(250);     // still getting an occasional zero??
    position_1 = position_1 + 1;
//    Serial.print(position_1);
//    Serial.println("  plus");
    if (num1 > 2200){ // only get 30 when nums are 2250 750...test- OK!
      dir = 1;
    }
    break;
  case 1:
    num1 = (servo1.read() - 50);
    num2 = (servo2.read() + 50);
    delay(5);
    servo1.write(num1);
    servo2.write(num2);
    delay(250); 
    position_1 = position_1 - 1;
//    Serial.print(position_1);
//    Serial.println("  minus");
    if (num1 <= 800){
      dir = 0;
    }
    break;  
  }
}

int select_rings(){        // 3 rings of distance
  w_index = 0;
  if (backnforth == 1){   // alternate w_index from 2 servos
    if ((cm1 > 0) && (cm1 < 100)){ 
      w_index =  position_1;
      play_time = play_time1;
      count = count + 1;
    }
    if ((cm1 > 100) && (cm1 < 200)){ 
      w_index = position_1 + 30;
      play_time = play_time2;
      count = 1;
    }
    if ((cm1 > 200) && (cm1 < 300)){ 
      w_index = position_1 + 59; //  cannot access 90 - array starts with 0!
      play_time = play_time3;
      count = 1;      // counter for playing whole song in playByIndex function
    }
    if (w_index <= 0) {         // when we are outside the ping envelope
      w_index = 98; 
      play_time = play_time4;
    }
    backnforth = 0;
  }
  else {
    if ((cm2 > 0) && (cm2 <= 100)){ // plays 1st in inner ring? reverse it...makes more sense
      w_index =  position_1;
      play_time = play_time1;
      count = count + 1;    
    }
    if ((cm2 > 100) && (cm2 <=200)){ 
      w_index = position_1 + 30;
      play_time = play_time2;
      count = 1;
    }
    if ((cm2 > 200) && (cm2 <= 300)) {
      w_index = position_1 + 59;
      play_time = play_time3;
      count = 1;
    }
    if (w_index <= 0) { // when we are outside the ping envelope
      w_index =98; 
      play_time = play_time4;
    }
    backnforth = 1;
  } 
  return w_index;
}


void index() {      //Find files and save file index.  A file's index is the

  char name[8];     //index of it's directory entry in it's directory file

  strcpy_P(name, PSTR("tdxx.wav"));   // copy flash string to RAM  Need code to change song.

  for (uint8_t i = 0; i < FILE_COUNT; i++) {  // Make file name
    name[2] = pgm_read_word (&fileLetter[i]);
    name[3] = pgm_read_word (&fileLetter1[i]);

    if (!file.open(root, name)) error("open by name");  // Open file by name
    // Save file's index (byte offset of directory entry divided by entry size)
    // Current position is just after entry so subtract one.
    wav_index[i] = root.readPosition()/32 - 1;   
  }
}

void playByIndex(int i) {
  uint32_t t = millis();
  if (!file.open(root, wav_index[i])) {    // open wav by index
    error("open by index");
  }

  if (!wave.create(file)) error("wave.create");    // create and play wav
  wave.play();

  if (count == 30){   //  trick to play the whole song!
    play_all();
    count = 1;
  }  
  else {  
    while((millis() - t) < play_time);     // stop after play_time ms
    wave.stop();

    sdErrorCheck();  // check for play errors
  }
}
/*   change volume by rings? which way?? 5/31/12 make this a function
 while (wave.isplaying) {
 putstring("Vol: ");
 
 // DVOLUME must be nonzero in WaveHC.h to use volume.
 Serial.println(wave.volume, DEC);
 
 delay(2000);
 wave.volume++;
 if ( wave.volume == 12) {
 wave.volume = 0;
 }
 }
 */
void sdErrorCheck(void){
  if (!card.errorCode()) return;
  putstring("\n\rSD I/O error: ");
  Serial.print(card.errorCode(), HEX);
  putstring(", ");
  Serial.println(card.errorData(), HEX);
  while(1);
}
void error_P(const char *str) {  // print error message and halt
  PgmPrint("Error: ");
  SerialPrint_P(str);
  sdErrorCheck();
  while(1);
}
void play_all(){ //  plays all 90 wav files 
  root.rewind();
  play(root);
}
void play(FatReader &dir) {
  FatReader file;
  while (dir.readDir(dirBuf) > 0) {    // Read every file in the directory one at a time

    // Skip it if not a subdirectory and not a .WAV file
    if (!DIR_IS_SUBDIR(dirBuf)
      && strncmp_P((char *)&dirBuf.name[8], PSTR("WAV"), 3)) {
      continue;
    }

    Serial.println();            // clear out a new line

    for (uint8_t i = 0; i < dirLevel; i++) {
      Serial.write(' ');       // this is for prettyprinting, put spaces in front
    }
    if (!file.open(vol, dirBuf)) {        // open the file in the directory
      error("file.open failed");          // something went wrong
    }

    if (file.isDir()) {                   // check if we opened a new directory
      putstring("Subdir: ");
      printEntryName(dirBuf);
      Serial.println();
      dirLevel += 2;                      // add more spaces
      // play files in subdirectory
      play(file);                         // recursive!
      dirLevel -= 2;    
    }
    else {
      // Aha! we found a file that isnt a directory
      putstring("Playing ");
      printEntryName(dirBuf);              // print it out
      if (!wave.create(file)) {            // Figure out, is it a WAV proper?
        putstring(" Not a valid WAV");     // ok skip it
      } 
      else {
        Serial.println();                  // Hooray it IS a WAV proper!
        wave.play();                       // make some noise!

        uint8_t n = 0;
        while (wave.isplaying) {// playing occurs in interrupts, so we print dots in realtime
          putstring(".");
          if (!(++n % 32))Serial.println();
          delay(100);
        }       
        sdErrorCheck();                    // everything OK?
        if (wave.errors)Serial.println(wave.errors);     // wave decoding errors
      }
    }
  }
}

void loop()
{
  select_rings();  
  ping();
  playByIndex(w_index);
  //  out();
  move();
  // Serial.println(count);
    Serial.print(w_index);
  //  Serial.println("  w_index"); // }
  //  Serial.print(position_1);
  //  Serial.println(   "  pos_1");
}

User avatar
adafruit_support_bill
 
Posts: 88088
Joined: Sat Feb 07, 2009 10:11 am

Re: wave shield PROGMEM code question

Post by adafruit_support_bill »

This link gives a good overview of Arduino memory usage: http://itp.nyu.edu/~gpv206/2008/04/maki ... o_mem.html

There is also a nice "F()" feature in 1.0 and later versions of the IDE that simplifies the task of optimizing memory usage for strings: http://www.arduino.cc/playground/Learning/Memory

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino Shields from Adafruit”