Problems with converting Arduino Nano code over to Maple Mini. Help needed.

Post Reply
Buleste
Posts: 43
Joined: Sun Nov 06, 2016 12:19 pm

Problems with converting Arduino Nano code over to Maple Mini. Help needed.

Post by Buleste » Tue Jan 24, 2017 4:34 pm

I am trying to convert some Arduino Nano projects of mine over to the Maple Leaf Mini with the hope of combining them into one project but adapting the code is proving problematic to say the least.

The simplest project, ArduiTape, is a simple WAV file player that uses the TMRpcm library and whilst that has been converted to STM32 it stubbornly refuses to verify and comes back with a the following error.

error: 'TMRpcm' does not name a type

TMRpcm tmrpcm; //Initialise tmrpcm

You can download the Arduino code here.
https://mega.nz/#!qNty1JCY!jnHxeC-qtmhP ... PWMjXD4O0Y

The other two have the same problem which is converting TimerOne library calls to using HardwareTimer instead.
There are several TimerOne functions that are not so easily translated and I was wondering if you can help?

You can download the Arduino codes for TZXDuino one of the projects from here. https://mega.nz/#!yRVkhITZ!geW4k0jrY4BG ... HeMSz6N7Gs

I'm a total noob to the STM32 so any and all help would be appreciated.

victor_pv
Posts: 1607
Joined: Mon Apr 27, 2015 12:12 pm

Re: Problems with converting Arduino Nano code over to Maple Mini. Help needed.

Post by victor_pv » Tue Jan 24, 2017 4:53 pm

Buleste wrote:I am trying to convert some Arduino Nano projects of mine over to the Maple Leaf Mini with the hope of combining them into one project but adapting the code is proving problematic to say the least.

The simplest project, ArduiTape, is a simple WAV file player that uses the TMRpcm library and whilst that has been converted to STM32 it stubbornly refuses to verify and comes back with a the following error.

error: 'TMRpcm' does not name a type

TMRpcm tmrpcm; //Initialise tmrpcm

You can download the Arduino code here.
https://mega.nz/#!qNty1JCY!jnHxeC-qtmhP ... PWMjXD4O0Y

The other two have the same problem which is converting TimerOne library calls to using HardwareTimer instead.
There are several TimerOne functions that are not so easily translated and I was wondering if you can help?

You can download the Arduino codes for TZXDuino one of the projects from here. https://mega.nz/#!yRVkhITZ!geW4k0jrY4BG ... HeMSz6N7Gs

I'm a total noob to the STM32 so any and all help would be appreciated.
I did a basic conversion of the TMRpcm library for the stm32. It required heavy modifications since the timer peripherals are 100% different.
Which version of the TMRpcm library are you using, is it that one?
In which folder is it?

You are including it with this line, make sure it's in the path. Otherwise you may want to add the path to the library, or copy the library files to the same folder as the sketch.
#include <TMRpcm.h>

Buleste
Posts: 43
Joined: Sun Nov 06, 2016 12:19 pm

Re: Problems with converting Arduino Nano code over to Maple Mini. Help needed.

Post by Buleste » Tue Jan 24, 2017 5:42 pm

Thanks Victor.

I am using your version. I've only just found your wav player example though and figured out where I was going wrong and what needed changing.

I've made change so that it now at least verifies and can be uploaded to my Maple Leaf Mini however it looks like I need to do a lot more code to alter including my LCD and SD card code (LCD is on I2C1 and SD is on SPI1 btw).

https://mega.nz/#!GYdHDI7a!YOxbKmn3D7Cm ... IG4LnGFYlI

Code: Select all

// ---------------------------------------------------------------------------------
// DO NOT USE CLASS-10 CARDS on this project - they're too fast to operate using SPI
// ---------------------------------------------------------------------------------
// ArduiTape Arduino based 8-bit computer tape player. 
// Play WAV files from an SD card.  Based on the TMRpcm code.

//#include <SdFat.h>
#include <TMRpcm.h> 
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

//TMRpcm tmrpcm;   //Initialise tmrpcm

SdFat sd;    //Initialise Sd card 

SdFile entry;  //SD card file

#define filenameLength    100

char fileName[filenameLength+1];  //Current filename
char sfileName[13];

const int chipSelect = 7;    //Sd card chip select pin

// ---------------------------------------------------------------------------------
// set cardType to 'oldCard' if using an old SD card (more than a few years old) or
// to 'newCard' if using a newly-purchase Class-4 card.
int cardType = SPI_FULL_SPEED;
// ---------------------------------------------------------------------------------

#define btnPlay       20    //Play Button
#define btnStop       19    //Stop Button
#define btnUp         18    //Menu Up button
#define btnDown       17    //Menu Down button
#define btnMotor      10    //Motor Sense (connect pin to gnd to play, NC for pause)
#define btnMselect    11    //Motor Control on/off button 

#define scrollSpeed   250   //text scroll delay
#define scrollWait    3000  //Delay before scrolling starts
byte scrollPos=0;
unsigned long scrollTime=millis()+scrollWait;

int mselectState = 1;//Motor control state 1=on 0=off
int wasPlaying = 0;  //Was there a file playing?
int motorState = 1;  //Current motor control state
int finished = 0;    //Has the file finished?
int start = 0;       //Currently playing flag
int pauseOn = 0;     //Pause state
int currentFile = 1; //Current position in directory
int maxFile = 0;     //Total number of files in directory
int isDir = 0;       //Is the current file a directory
unsigned long timeDiff = 0;  //button debounce

void setup() {
  
  lcd.init();  //Initialise LCD (16x2 type)
   lcd.backlight();
  pinMode(chipSelect, OUTPUT); //Setup SD card chipselect pin
  if (!sd.begin(chipSelect,cardType)) {  //Start SD card and check it's working
    lcd_clearline(0);
    lcd.print("No SD Card");
    return;
  }
  sd.chdir();  //set SD to root directory

  speakerPin = 9;  //Set speaker pin
  
  pinMode(btnPlay,INPUT_PULLUP);
  digitalWrite(btnPlay,HIGH);
  pinMode(btnStop,INPUT_PULLUP);
  digitalWrite(btnStop,HIGH);
  pinMode(btnUp,INPUT_PULLUP);
  digitalWrite(btnUp,HIGH);
  pinMode(btnDown,INPUT_PULLUP);
  digitalWrite(btnDown,HIGH);
  pinMode(btnMotor, INPUT_PULLUP);
  digitalWrite(btnMotor,HIGH);
  pinMode(btnMselect, INPUT_PULLUP);
  digitalWrite(btnMselect, HIGH);//Setup buttons with internal pullup 
  lcd.clear();
  lcd.print("ArduiTape v1.4");
  delay(1000);
  lcd.clear();
  getMaxFile();  //get the total number of files in the directory
  seekFile(currentFile);  //move to the first file in the directory
  lcd_clearline(0);
  lcd.print("Ready");
}

void loop(void) {
  if(!TMRpcm_playing && wasPlaying == 1) { 
    stopFile();
    //if the file has finished stop trying to play the file
  }

  if((millis()>=scrollTime) && start==0 && (strlen(fileName)>16)) {
    scrollTime = millis()+scrollSpeed;
    scrollText(fileName);
    scrollPos +=1;
    if(scrollPos>strlen(fileName)) {
      scrollPos=0;
      scrollTime=millis()+scrollWait;
      scrollText(fileName);
    }
  }
  
  motorState=digitalRead(btnMotor);
  if (millis() - timeDiff > 50) { // check switch every 100ms 
     timeDiff = millis(); // get current millisecond count
      
      if(digitalRead(btnPlay) == LOW) {
        if(start==0) {
          playFile();
          delay(200);
        } else {
        
          
          while(digitalRead(btnPlay)==LOW) {
            delay(50);
          }
            TMRpcm_pause();
            if (pauseOn == 0) {
              lcd_clearline(0);
              lcd.print("Paused");
              pauseOn = 1;
            } else {
              lcd_clearline(0);
              lcd.print("Playing");
              pauseOn = 0;
            }
       }
     }
     if(digitalRead(btnMselect)==LOW){
       if(mselectState==0) {
         lcd_clearline(0);
         lcd.print("Motor CTRL On");
         mselectState=1; 
       } else { 
         lcd_clearline(0);
         lcd.print("Motor CTRL Off");
         mselectState=0; 
       }
       while(digitalRead(btnMselect)==LOW) {
         delay(50);
       }
     }
     if(digitalRead(btnStop)==LOW && start==1) {
       stopFile();
       delay(200);
     } else {
      if (digitalRead(btnStop)==LOW && start==0){
      //Return to root of the SD card.
       sd.chdir(true);
       getMaxFile();
       currentFile=1;
       seekFile(currentFile);  
       while(digitalRead(btnStop)==LOW) {
         //prevent button repeats by waiting until the button is released.
         delay(50);
       }
     }
     }
     if(digitalRead(btnUp)==LOW && start==0) {
       upFile();
       while(digitalRead(btnUp)==LOW) {
         delay(50);  //wait until button is released
       }
     }
     if(digitalRead(btnDown)==LOW && start==0) {
       downFile();
       while(digitalRead(btnDown)==LOW) {
         delay(50);
       }
     }
     if(mselectState==1 && start==1) {  //if file is playing and motor control is on then handle current motor state
       if(motorState==1 && pauseOn==0) {
         TMRpcm_pause();
         lcd_clearline(0);
         lcd.print("Paused");
         pauseOn = 1;
       } 
       if(motorState==0 && pauseOn==1) {
         TMRpcm_pause();
         lcd_clearline(0);
         lcd.print("Playing");
         pauseOn = 0;
       }
     }
  }
}

void upFile() {    
  //move up a file in the directory
  
  currentFile--;
  if(currentFile<1) {
    getMaxFile();
    currentFile = maxFile;
  } 
  seekFile(currentFile);
}

void downFile() {    
  //move down a file in the directory
  
  currentFile++;
  if(currentFile>maxFile) { currentFile=1; }
  seekFile(currentFile);
}


void seekFile(int pos) {    
  //move to a set position in the directory, store the filename, and display the name on screen.
  
  entry.cwd()->rewind();
  for(int i=1;i<=currentFile;i++) {
    entry.openNext(entry.cwd(),O_READ);
    entry.getName(fileName,filenameLength);
    entry.getSFN(sfileName);
    if(entry.isDir() || !strcmp(sfileName, "ROOT")) { isDir=1; } else { isDir=0; }
    entry.close();
  }
  lcd_clearline(1);
  scrollPos=0;
  scrollText(fileName);
}

void stopFile() {
  TMRpcm_stopPlayback();
  if(start==1){
    lcd_clearline(0);
    lcd.print("Stopped");
    start=0;
  }
}

void playFile() {
  if(isDir==1) {
    changeDir();
  } else {
    if(entry.cwd()->exists(sfileName)) {
      lcd_clearline(0);
      lcd.print("Playing");
      lcd_clearline(1);
      scrollPos=0;
      scrollText(fileName);
      TMRpcm_play(sfileName);
      wasPlaying = 1;
      start=1; 
      TMRpcm_pause();
      lcd_clearline(0);
      lcd.print("Paused");
      pauseOn = 1;
    } else {
      lcd_clearline(1);
      lcd.print("No File Selected");
    }
  }
}

void getMaxFile() {    
  //gets the total files in the current directory and stores the number in maxFile
  
  entry.cwd()->rewind();
  maxFile=0;
  while(entry.openNext(entry.cwd(),O_READ)) {
    entry.getName(fileName,filenameLength);
    entry.close();
    maxFile++;
  }
  entry.cwd()->rewind();
}

void lcd_clearline(int l) {    
  //clear a single line on the LCD
  
  lcd.setCursor(0,l);
  lcd.print("                    ");
  lcd.setCursor(0,l);
}

void changeDir() {    
  //change directory, if fileName="ROOT" then return to the root directory
  //SDFat has no easy way to move up a directory, so returning to root is the easiest way. 
  //each directory (except the root) must have a file called ROOT (no extension)
                      
  if(!strcmp(fileName, "ROOT")) {
    sd.chdir(true);
  } else {
    sd.chdir(fileName, true);
  }
  getMaxFile();
  currentFile=1;
  seekFile(currentFile);
}

void scrollText(char* text)
{
  if(scrollPos<0) scrollPos=0;
  char outtext[16];
  for(int i=0;i<16;i++)
  {
    int p=i+scrollPos;
    if(p<strlen(text)) 
    {
      outtext[i]=text[p];
    } else {
      outtext[i]='\0';
    }
  }
  lcd_clearline(1);
  lcd.print(outtext);
}

victor_pv
Posts: 1607
Joined: Mon Apr 27, 2015 12:12 pm

Re: Problems with converting Arduino Nano code over to Maple Mini. Help needed.

Post by victor_pv » Tue Jan 24, 2017 6:31 pm

Buleste wrote:Thanks Victor.

I am using your version. I've only just found your wav player example though and figured out where I was going wrong and what needed changing.

I've made change so that it now at least verifies and can be uploaded to my Maple Leaf Mini however it looks like I need to do a lot more code to alter including my LCD and SD card code (LCD is on I2C1 and SD is on SPI1 btw).

https://mega.nz/#!GYdHDI7a!YOxbKmn3D7Cm ... IG4LnGFYlI

Code: Select all

// ---------------------------------------------------------------------------------
// DO NOT USE CLASS-10 CARDS on this project - they're too fast to operate using SPI
// ---------------------------------------------------------------------------------
// ArduiTape Arduino based 8-bit computer tape player. 
// Play WAV files from an SD card.  Based on the TMRpcm code.

//#include <SdFat.h>
#include <TMRpcm.h> 
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

//TMRpcm tmrpcm;   //Initialise tmrpcm

SdFat sd;    //Initialise Sd card 

SdFile entry;  //SD card file

#define filenameLength    100

char fileName[filenameLength+1];  //Current filename
char sfileName[13];

const int chipSelect = 7;    //Sd card chip select pin

// ---------------------------------------------------------------------------------
// set cardType to 'oldCard' if using an old SD card (more than a few years old) or
// to 'newCard' if using a newly-purchase Class-4 card.
int cardType = SPI_FULL_SPEED;
// ---------------------------------------------------------------------------------

#define btnPlay       20    //Play Button
#define btnStop       19    //Stop Button
#define btnUp         18    //Menu Up button
#define btnDown       17    //Menu Down button
#define btnMotor      10    //Motor Sense (connect pin to gnd to play, NC for pause)
#define btnMselect    11    //Motor Control on/off button 

#define scrollSpeed   250   //text scroll delay
#define scrollWait    3000  //Delay before scrolling starts
byte scrollPos=0;
unsigned long scrollTime=millis()+scrollWait;

int mselectState = 1;//Motor control state 1=on 0=off
int wasPlaying = 0;  //Was there a file playing?
int motorState = 1;  //Current motor control state
int finished = 0;    //Has the file finished?
int start = 0;       //Currently playing flag
int pauseOn = 0;     //Pause state
int currentFile = 1; //Current position in directory
int maxFile = 0;     //Total number of files in directory
int isDir = 0;       //Is the current file a directory
unsigned long timeDiff = 0;  //button debounce

void setup() {
  
  lcd.init();  //Initialise LCD (16x2 type)
   lcd.backlight();
  pinMode(chipSelect, OUTPUT); //Setup SD card chipselect pin
  if (!sd.begin(chipSelect,cardType)) {  //Start SD card and check it's working
    lcd_clearline(0);
    lcd.print("No SD Card");
    return;
  }
  sd.chdir();  //set SD to root directory

  speakerPin = 9;  //Set speaker pin
  
  pinMode(btnPlay,INPUT_PULLUP);
  digitalWrite(btnPlay,HIGH);
  pinMode(btnStop,INPUT_PULLUP);
  digitalWrite(btnStop,HIGH);
  pinMode(btnUp,INPUT_PULLUP);
  digitalWrite(btnUp,HIGH);
  pinMode(btnDown,INPUT_PULLUP);
  digitalWrite(btnDown,HIGH);
  pinMode(btnMotor, INPUT_PULLUP);
  digitalWrite(btnMotor,HIGH);
  pinMode(btnMselect, INPUT_PULLUP);
  digitalWrite(btnMselect, HIGH);//Setup buttons with internal pullup 
  lcd.clear();
  lcd.print("ArduiTape v1.4");
  delay(1000);
  lcd.clear();
  getMaxFile();  //get the total number of files in the directory
  seekFile(currentFile);  //move to the first file in the directory
  lcd_clearline(0);
  lcd.print("Ready");
}

void loop(void) {
  if(!TMRpcm_playing && wasPlaying == 1) { 
    stopFile();
    //if the file has finished stop trying to play the file
  }

  if((millis()>=scrollTime) && start==0 && (strlen(fileName)>16)) {
    scrollTime = millis()+scrollSpeed;
    scrollText(fileName);
    scrollPos +=1;
    if(scrollPos>strlen(fileName)) {
      scrollPos=0;
      scrollTime=millis()+scrollWait;
      scrollText(fileName);
    }
  }
  
  motorState=digitalRead(btnMotor);
  if (millis() - timeDiff > 50) { // check switch every 100ms 
     timeDiff = millis(); // get current millisecond count
      
      if(digitalRead(btnPlay) == LOW) {
        if(start==0) {
          playFile();
          delay(200);
        } else {
        
          
          while(digitalRead(btnPlay)==LOW) {
            delay(50);
          }
            TMRpcm_pause();
            if (pauseOn == 0) {
              lcd_clearline(0);
              lcd.print("Paused");
              pauseOn = 1;
            } else {
              lcd_clearline(0);
              lcd.print("Playing");
              pauseOn = 0;
            }
       }
     }
     if(digitalRead(btnMselect)==LOW){
       if(mselectState==0) {
         lcd_clearline(0);
         lcd.print("Motor CTRL On");
         mselectState=1; 
       } else { 
         lcd_clearline(0);
         lcd.print("Motor CTRL Off");
         mselectState=0; 
       }
       while(digitalRead(btnMselect)==LOW) {
         delay(50);
       }
     }
     if(digitalRead(btnStop)==LOW && start==1) {
       stopFile();
       delay(200);
     } else {
      if (digitalRead(btnStop)==LOW && start==0){
      //Return to root of the SD card.
       sd.chdir(true);
       getMaxFile();
       currentFile=1;
       seekFile(currentFile);  
       while(digitalRead(btnStop)==LOW) {
         //prevent button repeats by waiting until the button is released.
         delay(50);
       }
     }
     }
     if(digitalRead(btnUp)==LOW && start==0) {
       upFile();
       while(digitalRead(btnUp)==LOW) {
         delay(50);  //wait until button is released
       }
     }
     if(digitalRead(btnDown)==LOW && start==0) {
       downFile();
       while(digitalRead(btnDown)==LOW) {
         delay(50);
       }
     }
     if(mselectState==1 && start==1) {  //if file is playing and motor control is on then handle current motor state
       if(motorState==1 && pauseOn==0) {
         TMRpcm_pause();
         lcd_clearline(0);
         lcd.print("Paused");
         pauseOn = 1;
       } 
       if(motorState==0 && pauseOn==1) {
         TMRpcm_pause();
         lcd_clearline(0);
         lcd.print("Playing");
         pauseOn = 0;
       }
     }
  }
}

void upFile() {    
  //move up a file in the directory
  
  currentFile--;
  if(currentFile<1) {
    getMaxFile();
    currentFile = maxFile;
  } 
  seekFile(currentFile);
}

void downFile() {    
  //move down a file in the directory
  
  currentFile++;
  if(currentFile>maxFile) { currentFile=1; }
  seekFile(currentFile);
}


void seekFile(int pos) {    
  //move to a set position in the directory, store the filename, and display the name on screen.
  
  entry.cwd()->rewind();
  for(int i=1;i<=currentFile;i++) {
    entry.openNext(entry.cwd(),O_READ);
    entry.getName(fileName,filenameLength);
    entry.getSFN(sfileName);
    if(entry.isDir() || !strcmp(sfileName, "ROOT")) { isDir=1; } else { isDir=0; }
    entry.close();
  }
  lcd_clearline(1);
  scrollPos=0;
  scrollText(fileName);
}

void stopFile() {
  TMRpcm_stopPlayback();
  if(start==1){
    lcd_clearline(0);
    lcd.print("Stopped");
    start=0;
  }
}

void playFile() {
  if(isDir==1) {
    changeDir();
  } else {
    if(entry.cwd()->exists(sfileName)) {
      lcd_clearline(0);
      lcd.print("Playing");
      lcd_clearline(1);
      scrollPos=0;
      scrollText(fileName);
      TMRpcm_play(sfileName);
      wasPlaying = 1;
      start=1; 
      TMRpcm_pause();
      lcd_clearline(0);
      lcd.print("Paused");
      pauseOn = 1;
    } else {
      lcd_clearline(1);
      lcd.print("No File Selected");
    }
  }
}

void getMaxFile() {    
  //gets the total files in the current directory and stores the number in maxFile
  
  entry.cwd()->rewind();
  maxFile=0;
  while(entry.openNext(entry.cwd(),O_READ)) {
    entry.getName(fileName,filenameLength);
    entry.close();
    maxFile++;
  }
  entry.cwd()->rewind();
}

void lcd_clearline(int l) {    
  //clear a single line on the LCD
  
  lcd.setCursor(0,l);
  lcd.print("                    ");
  lcd.setCursor(0,l);
}

void changeDir() {    
  //change directory, if fileName="ROOT" then return to the root directory
  //SDFat has no easy way to move up a directory, so returning to root is the easiest way. 
  //each directory (except the root) must have a file called ROOT (no extension)
                      
  if(!strcmp(fileName, "ROOT")) {
    sd.chdir(true);
  } else {
    sd.chdir(fileName, true);
  }
  getMaxFile();
  currentFile=1;
  seekFile(currentFile);
}

void scrollText(char* text)
{
  if(scrollPos<0) scrollPos=0;
  char outtext[16];
  for(int i=0;i<16;i++)
  {
    int p=i+scrollPos;
    if(p<strlen(text)) 
    {
      outtext[i]=text[p];
    } else {
      outtext[i]='\0';
    }
  }
  lcd_clearline(1);
  lcd.print(outtext);
}
The SDFat library should work fine, it was used on my example also. I believe there have been some fixes included in the original repo from Greyman. The on in my github should work too, and was used by that example.

I didn't finish my TMCpcm library. Among other things, it doesn't use classes at the moment. I also do not remember if I allowed to use more timers or only the ones I used in the example, so to start you may want to use the same pins/Timers as I used in the example. I remember it could play mono, but don't remember if I set it up to play stereo too.

In general needs a cleanup, it's very rough now, but I haven't had time to work on it.
To be honest, I think the functions in that library should be broken up in 2 parts:
1.- Audio output device class that can set up the timer and play PCM data passed to it on a buffer at the set frequency.
2.- Wav player that can read a wav file from sd card or anywhere else for that matter, and load it to the buffer, and keep reloading the buffer.

Breaking them up in 2 parts would allow one to easily change the audio output device, so it uses a timer now, but can be switched to i2s tomorrow, or internal DAC for MCUs with internal DAC.

In fact I think I'm going to modify it that way, to work exactly as the i2s class, functions with the same name and parameters, which is now standard in Arduino. Only it will use timers rather than an i2s device.
Last edited by victor_pv on Wed Jan 25, 2017 7:36 pm, edited 2 times in total.

Buleste
Posts: 43
Joined: Sun Nov 06, 2016 12:19 pm

Re: Problems with converting Arduino Nano code over to Maple Mini. Help needed.

Post by Buleste » Wed Jan 25, 2017 7:05 pm

That sounds great. It would be nice to get TMRpcm fully functional.

EDIT: Managed to get everything verifying but can't get it to recognise the SD Card at the moment. I know it's connected correctly as a SD.h example can find and read it. I probably need to use a different library.

Buleste
Posts: 43
Joined: Sun Nov 06, 2016 12:19 pm

Re: Problems with converting Arduino Nano code over to Maple Mini. Help needed.

Post by Buleste » Thu Jan 26, 2017 3:15 pm

Nope. Not a problem with SDFat so it must be an issue with the buttons or rather the code for the buttons.

EDIT
Altered some of the code and the pins and some of the buttons work properly now. I can at least scroll up and down the files.
Can't get anything to play though yet.

Code: Select all

// ---------------------------------------------------------------------------------
// DO NOT USE CLASS-10 CARDS on this project - they're too fast to operate using SPI
// ---------------------------------------------------------------------------------
// ArduiTape Arduino based 8-bit computer tape player. 
// Play WAV files from an SD card.  Based on the TMRpcm code.

#include <SPI.h>
#include <SdFat.h>
#include <TMRpcm.h> 
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

//TMRpcm tmrpcm;   //Initialise tmrpcm

SdFat SD;    //Initialise Sd card 

SdFile entry;  //SD card file

#define filenameLength    100

char fileName[filenameLength+1];  //Current filename
char sfileName[13];

const int SD_ChipSelectPin = 8;    //Sd card chip select pin

// ---------------------------------------------------------------------------------
//int cardType = SPI_CLOCK_DIV2;
// ---------------------------------------------------------------------------------

#define btnPlay       3    //Play Button
#define btnStop       2    //Stop Button
#define btnUp         1    //Menu Up button
#define btnDown       0    //Menu Down button
#define btnMotor      10    //Motor Sense (connect pin to gnd to play, NC for pause)
#define btnMselect    11    //Motor Control on/off button 

#define scrollSpeed   250   //text scroll delay
#define scrollWait    3000  //Delay before scrolling starts
byte scrollPos=0;
unsigned long scrollTime=millis()+scrollWait;

int mselectState = 1;//Motor control state 1=on 0=off
int wasPlaying = 0;  //Was there a file playing?
int motorState = 1;  //Current motor control state
int finished = 0;    //Has the file finished?
int start = 0;       //Currently playing flag
int pauseOn = 0;     //Pause state
int currentFile = 1; //Current position in directory
int maxFile = 0;     //Total number of files in directory
int isDir = 0;       //Is the current file a directory
unsigned long timeDiff = 0;  //button debounce

void setup() {
  
  lcd.init();  //Initialise LCD (16x2 type)
   lcd.backlight();
  pinMode(SD_ChipSelectPin, OUTPUT); //Setup SD card chipselect pin
  if (!SD.begin(SD_ChipSelectPin,SPI_CLOCK_DIV4)) {  //Start SD card and check it's working
    lcd_clearline(0);
    lcd.print("No SD Card");
    return;
  }
  SD.chdir();  //set SD to root directory

  speakerPin = 9;  //Set speaker pin
  
  pinMode(btnPlay,INPUT_PULLUP);
  digitalWrite(btnPlay,HIGH);
  pinMode(btnStop,INPUT_PULLUP);
  digitalWrite(btnStop,HIGH);
  pinMode(btnUp,INPUT_PULLUP);
  digitalWrite(btnUp,HIGH);
  pinMode(btnDown,INPUT_PULLUP);
  digitalWrite(btnDown,HIGH);
  //pinMode(btnMotor, INPUT_PULLUP);
  //digitalWrite(btnMotor,LOW);
  //pinMode(btnMselect, INPUT_PULLUP);
  //digitalWrite(btnMselect, LOW);//Setup buttons with internal pullup 
  lcd.clear();
  lcd.print("ArduiTape v1.4");
  delay(1000);
  lcd.clear();
  getMaxFile();  //get the total number of files in the directory
  seekFile(currentFile);  //move to the first file in the directory
  lcd_clearline(0);
  lcd.print("Ready");
}

void loop(void) {
  if(!TMRpcm_playing && wasPlaying == 1) { 
    stopFile();
    //if the file has finished stop trying to play the file
  }

  if((millis()>=scrollTime) && start==0 && (strlen(fileName)>16)) {
    scrollTime = millis()+scrollSpeed;
    scrollText(fileName);
    scrollPos +=1;
    if(scrollPos>strlen(fileName)) {
      scrollPos=0;
      scrollTime=millis()+scrollWait;
      scrollText(fileName);
    }
  }
  
  motorState=digitalRead(btnMotor);
  if (millis() - timeDiff > 50) { // check switch every 100ms 
     timeDiff = millis(); // get current millisecond count
      
      if(digitalRead(btnPlay) == LOW) {
        if(start==0) {
          playFile();
          delay(200);
        } else {
        
          
          while(digitalRead(btnPlay)==LOW) {
            delay(50);
          }
            TMRpcm_pause();
            if (pauseOn == 0) {
              lcd_clearline(0);
              lcd.print("Paused");
              pauseOn = 1;
            } else {
              lcd_clearline(0);
              lcd.print("Playing");
              pauseOn = 0;
            }
       }
     }
     /*if(digitalRead(btnMselect)==LOW){
       if(mselectState==0) {
         lcd_clearline(0);
         lcd.print("Motor CTRL On");
         mselectState=1; 
       } else { 
         lcd_clearline(0);
         lcd.print("Motor CTRL Off");
         mselectState=0; 
       }
       while(digitalRead(btnMselect)==LOW) {
         delay(50);
       }
     }*/
     if(digitalRead(btnStop)==LOW && start==1) {
       stopFile();
       delay(200);
     } else {
      if (digitalRead(btnStop)==LOW && start==0){
      //Return to root of the SD card.
       SD.chdir(true);
       getMaxFile();
       currentFile=1;
       seekFile(currentFile);  
       while(digitalRead(btnStop)==LOW) {
         //prevent button repeats by waiting until the button is released.
         delay(50);
       }
     }
     }
     if(digitalRead(btnUp)==LOW && start==0) {
       upFile();
       while(digitalRead(btnUp)==LOW) {
         delay(50);  //wait until button is released
       }
     }
     if(digitalRead(btnDown)==LOW && start==0) {
       downFile();
       while(digitalRead(btnDown)==LOW) {
         delay(50);
       }
     }
     /*if(mselectState==1 && start==1) {  //if file is playing and motor control is on then handle current motor state
       if(motorState==1 && pauseOn==0) {
         TMRpcm_pause();
         lcd_clearline(0);
         lcd.print("Paused");
         pauseOn = 1;
       } 
       if(motorState==0 && pauseOn==1) {
         TMRpcm_pause();
         lcd_clearline(0);
         lcd.print("Playing");
         pauseOn = 0;
       }
     }*/
  }
}

void upFile() {    
  //move up a file in the directory
  
  currentFile--;
  if(currentFile<1) {
    getMaxFile();
    currentFile = maxFile;
  } 
  seekFile(currentFile);
}

void downFile() {    
  //move down a file in the directory
  
  currentFile++;
  if(currentFile>maxFile) { currentFile=1; }
  seekFile(currentFile);
}


void seekFile(int pos) {    
  //move to a set position in the directory, store the filename, and display the name on screen.
  
  entry.cwd()->rewind();
  for(int i=1;i<=currentFile;i++) {
    entry.openNext(entry.cwd(),O_READ);
    entry.getName(fileName,filenameLength);
    entry.getSFN(sfileName);
    if(entry.isDir() || !strcmp(sfileName, "ROOT")) { isDir=1; } else { isDir=0; }
    entry.close();
  }
  lcd_clearline(1);
  scrollPos=0;
  scrollText(fileName);
}

void stopFile() {
  TMRpcm_stopPlayback();
  if(start==1){
    lcd_clearline(0);
    lcd.print("Stopped");
    start=0;
  }
}

void playFile() {
  if(isDir==1) {
    changeDir();
  } else {
    if(entry.cwd()->exists(sfileName)) {
      lcd_clearline(0);
      lcd.print("Playing");
      lcd_clearline(1);
      scrollPos=0;
      scrollText(fileName);
      TMRpcm_play(sfileName);
      wasPlaying = 1;
      start=1; 
      TMRpcm_pause();
      lcd_clearline(0);
      lcd.print("Paused");
      pauseOn = 1;
    } else {
      lcd_clearline(1);
      lcd.print("No File Selected");
    }
  }
}

void getMaxFile() {    
  //gets the total files in the current directory and stores the number in maxFile
  
  entry.cwd()->rewind();
  maxFile=0;
  while(entry.openNext(entry.cwd(),O_READ)) {
    entry.getName(fileName,filenameLength);
    entry.close();
    maxFile++;
  }
  entry.cwd()->rewind();
}

void lcd_clearline(int l) {    
  //clear a single line on the LCD
  
  lcd.setCursor(0,l);
  lcd.print("                    ");
  lcd.setCursor(0,l);
}

void changeDir() {    
  //change directory, if fileName="ROOT" then return to the root directory
  //SDFat has no easy way to move up a directory, so returning to root is the easiest way. 
  //each directory (except the root) must have a file called ROOT (no extension)
                      
  if(!strcmp(fileName, "ROOT")) {
    SD.chdir(true);
  } else {
    SD.chdir(fileName, true);
  }
  getMaxFile();
  currentFile=1;
  seekFile(currentFile);
}

void scrollText(char* text)
{
  if(scrollPos<0) scrollPos=0;
  char outtext[16];
  for(int i=0;i<16;i++)
  {
    int p=i+scrollPos;
    if(p<strlen(text)) 
    {
      outtext[i]=text[p];
    } else {
      outtext[i]='\0';
    }
  }
  lcd_clearline(1);
  lcd.print(outtext);
}

victor_pv
Posts: 1607
Joined: Mon Apr 27, 2015 12:12 pm

Re: Problems with converting Arduino Nano code over to Maple Mini. Help needed.

Post by victor_pv » Thu Jan 26, 2017 5:22 pm

Buleste wrote:Nope. Not a problem with SDFat so it must be an issue with the buttons or rather the code for the buttons.

EDIT
Altered some of the code and the pins and some of the buttons work properly now. I can at least scroll up and down the files.
Can't get anything to play though yet.

Code: Select all

// ---------------------------------------------------------------------------------
// DO NOT USE CLASS-10 CARDS on this project - they're too fast to operate using SPI
// ---------------------------------------------------------------------------------
// ArduiTape Arduino based 8-bit computer tape player. 
// Play WAV files from an SD card.  Based on the TMRpcm code.

#include <SPI.h>
#include <SdFat.h>
#include <TMRpcm.h> 
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

//TMRpcm tmrpcm;   //Initialise tmrpcm

SdFat SD;    //Initialise Sd card 

SdFile entry;  //SD card file

#define filenameLength    100

char fileName[filenameLength+1];  //Current filename
char sfileName[13];

const int SD_ChipSelectPin = 8;    //Sd card chip select pin

// ---------------------------------------------------------------------------------
//int cardType = SPI_CLOCK_DIV2;
// ---------------------------------------------------------------------------------

#define btnPlay       3    //Play Button
#define btnStop       2    //Stop Button
#define btnUp         1    //Menu Up button
#define btnDown       0    //Menu Down button
#define btnMotor      10    //Motor Sense (connect pin to gnd to play, NC for pause)
#define btnMselect    11    //Motor Control on/off button 

#define scrollSpeed   250   //text scroll delay
#define scrollWait    3000  //Delay before scrolling starts
byte scrollPos=0;
unsigned long scrollTime=millis()+scrollWait;

int mselectState = 1;//Motor control state 1=on 0=off
int wasPlaying = 0;  //Was there a file playing?
int motorState = 1;  //Current motor control state
int finished = 0;    //Has the file finished?
int start = 0;       //Currently playing flag
int pauseOn = 0;     //Pause state
int currentFile = 1; //Current position in directory
int maxFile = 0;     //Total number of files in directory
int isDir = 0;       //Is the current file a directory
unsigned long timeDiff = 0;  //button debounce

void setup() {
  
  lcd.init();  //Initialise LCD (16x2 type)
   lcd.backlight();
  pinMode(SD_ChipSelectPin, OUTPUT); //Setup SD card chipselect pin
  if (!SD.begin(SD_ChipSelectPin,SPI_CLOCK_DIV4)) {  //Start SD card and check it's working
    lcd_clearline(0);
    lcd.print("No SD Card");
    return;
  }
  SD.chdir();  //set SD to root directory

  speakerPin = 9;  //Set speaker pin
  
  pinMode(btnPlay,INPUT_PULLUP);
  digitalWrite(btnPlay,HIGH);
  pinMode(btnStop,INPUT_PULLUP);
  digitalWrite(btnStop,HIGH);
  pinMode(btnUp,INPUT_PULLUP);
  digitalWrite(btnUp,HIGH);
  pinMode(btnDown,INPUT_PULLUP);
  digitalWrite(btnDown,HIGH);
  //pinMode(btnMotor, INPUT_PULLUP);
  //digitalWrite(btnMotor,LOW);
  //pinMode(btnMselect, INPUT_PULLUP);
  //digitalWrite(btnMselect, LOW);//Setup buttons with internal pullup 
  lcd.clear();
  lcd.print("ArduiTape v1.4");
  delay(1000);
  lcd.clear();
  getMaxFile();  //get the total number of files in the directory
  seekFile(currentFile);  //move to the first file in the directory
  lcd_clearline(0);
  lcd.print("Ready");
}

void loop(void) {
  if(!TMRpcm_playing && wasPlaying == 1) { 
    stopFile();
    //if the file has finished stop trying to play the file
  }

  if((millis()>=scrollTime) && start==0 && (strlen(fileName)>16)) {
    scrollTime = millis()+scrollSpeed;
    scrollText(fileName);
    scrollPos +=1;
    if(scrollPos>strlen(fileName)) {
      scrollPos=0;
      scrollTime=millis()+scrollWait;
      scrollText(fileName);
    }
  }
  
  motorState=digitalRead(btnMotor);
  if (millis() - timeDiff > 50) { // check switch every 100ms 
     timeDiff = millis(); // get current millisecond count
      
      if(digitalRead(btnPlay) == LOW) {
        if(start==0) {
          playFile();
          delay(200);
        } else {
        
          
          while(digitalRead(btnPlay)==LOW) {
            delay(50);
          }
            TMRpcm_pause();
            if (pauseOn == 0) {
              lcd_clearline(0);
              lcd.print("Paused");
              pauseOn = 1;
            } else {
              lcd_clearline(0);
              lcd.print("Playing");
              pauseOn = 0;
            }
       }
     }
     /*if(digitalRead(btnMselect)==LOW){
       if(mselectState==0) {
         lcd_clearline(0);
         lcd.print("Motor CTRL On");
         mselectState=1; 
       } else { 
         lcd_clearline(0);
         lcd.print("Motor CTRL Off");
         mselectState=0; 
       }
       while(digitalRead(btnMselect)==LOW) {
         delay(50);
       }
     }*/
     if(digitalRead(btnStop)==LOW && start==1) {
       stopFile();
       delay(200);
     } else {
      if (digitalRead(btnStop)==LOW && start==0){
      //Return to root of the SD card.
       SD.chdir(true);
       getMaxFile();
       currentFile=1;
       seekFile(currentFile);  
       while(digitalRead(btnStop)==LOW) {
         //prevent button repeats by waiting until the button is released.
         delay(50);
       }
     }
     }
     if(digitalRead(btnUp)==LOW && start==0) {
       upFile();
       while(digitalRead(btnUp)==LOW) {
         delay(50);  //wait until button is released
       }
     }
     if(digitalRead(btnDown)==LOW && start==0) {
       downFile();
       while(digitalRead(btnDown)==LOW) {
         delay(50);
       }
     }
     /*if(mselectState==1 && start==1) {  //if file is playing and motor control is on then handle current motor state
       if(motorState==1 && pauseOn==0) {
         TMRpcm_pause();
         lcd_clearline(0);
         lcd.print("Paused");
         pauseOn = 1;
       } 
       if(motorState==0 && pauseOn==1) {
         TMRpcm_pause();
         lcd_clearline(0);
         lcd.print("Playing");
         pauseOn = 0;
       }
     }*/
  }
}

void upFile() {    
  //move up a file in the directory
  
  currentFile--;
  if(currentFile<1) {
    getMaxFile();
    currentFile = maxFile;
  } 
  seekFile(currentFile);
}

void downFile() {    
  //move down a file in the directory
  
  currentFile++;
  if(currentFile>maxFile) { currentFile=1; }
  seekFile(currentFile);
}


void seekFile(int pos) {    
  //move to a set position in the directory, store the filename, and display the name on screen.
  
  entry.cwd()->rewind();
  for(int i=1;i<=currentFile;i++) {
    entry.openNext(entry.cwd(),O_READ);
    entry.getName(fileName,filenameLength);
    entry.getSFN(sfileName);
    if(entry.isDir() || !strcmp(sfileName, "ROOT")) { isDir=1; } else { isDir=0; }
    entry.close();
  }
  lcd_clearline(1);
  scrollPos=0;
  scrollText(fileName);
}

void stopFile() {
  TMRpcm_stopPlayback();
  if(start==1){
    lcd_clearline(0);
    lcd.print("Stopped");
    start=0;
  }
}

void playFile() {
  if(isDir==1) {
    changeDir();
  } else {
    if(entry.cwd()->exists(sfileName)) {
      lcd_clearline(0);
      lcd.print("Playing");
      lcd_clearline(1);
      scrollPos=0;
      scrollText(fileName);
      TMRpcm_play(sfileName);
      wasPlaying = 1;
      start=1; 
      TMRpcm_pause();
      lcd_clearline(0);
      lcd.print("Paused");
      pauseOn = 1;
    } else {
      lcd_clearline(1);
      lcd.print("No File Selected");
    }
  }
}

void getMaxFile() {    
  //gets the total files in the current directory and stores the number in maxFile
  
  entry.cwd()->rewind();
  maxFile=0;
  while(entry.openNext(entry.cwd(),O_READ)) {
    entry.getName(fileName,filenameLength);
    entry.close();
    maxFile++;
  }
  entry.cwd()->rewind();
}

void lcd_clearline(int l) {    
  //clear a single line on the LCD
  
  lcd.setCursor(0,l);
  lcd.print("                    ");
  lcd.setCursor(0,l);
}

void changeDir() {    
  //change directory, if fileName="ROOT" then return to the root directory
  //SDFat has no easy way to move up a directory, so returning to root is the easiest way. 
  //each directory (except the root) must have a file called ROOT (no extension)
                      
  if(!strcmp(fileName, "ROOT")) {
    SD.chdir(true);
  } else {
    SD.chdir(fileName, true);
  }
  getMaxFile();
  currentFile=1;
  seekFile(currentFile);
}

void scrollText(char* text)
{
  if(scrollPos<0) scrollPos=0;
  char outtext[16];
  for(int i=0;i<16;i++)
  {
    int p=i+scrollPos;
    if(p<strlen(text)) 
    {
      outtext[i]=text[p];
    } else {
      outtext[i]='\0';
    }
  }
  lcd_clearline(1);
  lcd.print(outtext);
}
I don't remember for sure of the top of my head, but I think my version of the library was possibly hard coded to use a certain time and pin, even though it may let you set a different pin. It was my plan to make it compatible with all the timers, but I don't think I got to that point.
As a test, did you test with my sketch to make sure all works? if so, then try using the exact same pin I used for testing in my demo sketch.

Buleste
Posts: 43
Joined: Sun Nov 06, 2016 12:19 pm

Re: Problems with converting Arduino Nano code over to Maple Mini. Help needed.

Post by Buleste » Thu Jan 26, 2017 5:37 pm

I tried to just verify as I don't have the screen and just seem to get the following error.

exit status 1
'swap' was not declared in this scope

I do have a ILI9341 though so I could try that. Just need to figure out what to wire to where.

victor_pv
Posts: 1607
Joined: Mon Apr 27, 2015 12:12 pm

Re: Problems with converting Arduino Nano code over to Maple Mini. Help needed.

Post by victor_pv » Thu Jan 26, 2017 8:52 pm

Buleste wrote:I tried to just verify as I don't have the screen and just seem to get the following error.

exit status 1
'swap' was not declared in this scope

I do have a ILI9341 though so I could try that. Just need to figure out what to wire to where.
Well, try to use your own sketch, but set the tmrpcm to use the same pin as I used, and test with different wav files (mono 8bit, mono 16 bit, stereo 8bit, stereo16 bit).

I believe I had it working on both mono and stereo files of 8 and 16bits (16 bits is downconverter to 8bit if I remember right).
But I do not remember if the playing was mono, even for stereo files, or stereo (I think the later).

You can try to just code a simple sketch that only opens one particular file and plays it, so it doesn't have to browse folders, list entries etc. Just open the file and play. No diplay, no buttons, nothing. That's how I did my initial test to check the pwm output, that way you eliminate other possible sources of errors.

EDIT: I checked my last version of the library, and it should work with any timer as long as the pins are free BUT, you need to select 2 pins that belong to the same timer device. In my example I used:

Code: Select all

  speakerPin = 27; //TMR1 CH1
  speakerPin2 = 26; //TMR1 CH2
EDIT2:
I had a look at your code, it is never calling this function TMRpcm_buffer_load()
That is the function that loads data from the open file to the buffer. Since my example uses an RTOS, there is one tasks set to call that periodically.
If you dont plan on using an RTOS (I definitely would use it, makes multitasking much easier and only takes like 1KB of flash), you can hook that function to the systick timer interrupt in your code, or just uncomment this line in the tmrpcm.cpp file:

Code: Select all

systick_attach_callback(&TMRpcm_buffer_load);
That will make the systick interrupt, which happens every 1ms, call the buffer loader function to fill any empty buffer. That is often enough.
At 48Khz stereo the buffer is enough for 10ms. (1024/ 2ch*48khz ~=10ms)

When using an RTOS you could adjust to call it less frequently and so letting the MCU run other things, besides not keeping it servicing an interrupt, which may affect the millis() accuracy.

Buleste
Posts: 43
Joined: Sun Nov 06, 2016 12:19 pm

Re: Problems with converting Arduino Nano code over to Maple Mini. Help needed.

Post by Buleste » Fri Jan 27, 2017 3:13 pm

Thanks for that. Added the buffer and it's trying to play files now.

The sound is terrible for all the various wavs I've tried but it's a start. Making progress and that's what counts.

Post Reply