0

I've been working with the lovely I2C_Anything.h (thanks to the great work and support from Nick over at http://www.gammon.com.au/forum/?id=10896&reply=9#reply9).. All working great, I was using Serial communication between Teensy 3.1 & mini pro but looking to replace with I2C - I was sending a file name across that was fine, but not managing to figure it out with the I2C_Anything technique.. here's some snippets:

MASTER

const uint8_t MaxMP3Name = 13;
//====================================
// DATA STRUCTURE FOR MP3 SLAVE
//==================================== 
struct SEND_MP3_STRUCTURE{
  char Filename[MaxMP3Name];
  uint8_t VolLeft;
  uint8_t VolRight;
  uint8_t Command;
};
SEND_MP3_STRUCTURE txMP3;

#define MP3_SLAVE_ADDRESS 77 

const uint8_t PlayFile = 1;
const uint8_t PauseFile = 2;
const uint8_t StopFile = 3;

... etc

//#################################//
//##  SEND MP3 COMMAND           ##//
//#################################//
void sendMP3Command(String MP3File, uint8_t Command) {      
  char *TextArray[MaxMP3Name];
  MP3File.toCharArray(TextArray, MaxMP3Name);
  txMP3.Filename = TextArray;
  txMP3.VolLeft = VolLeft; 
  txMP3.VolRight = VolRight;  
  txMP3.Command = Command;
  Wire.beginTransmission(MP3_SLAVE_ADDRESS);
  I2C_writeAnything(txMP3);
  Wire.endTransmission(true);
}

...etc

... not entirely sure what i'm doing, I've tried most variations (using String in the struct vs char *Filename[] and various other attempts).

SLAVE

const uint8_t MaxMP3Name = 13;
//====================================
// RECEIVE DATA FORMAT
//==================================== 
struct RECEIVE_DATA_STRUCTURE{
  char Filename[MaxMP3Name];
  uint8_t VolLeft;
  uint8_t VolRight;
  uint8_t Command;   
};
volatile RECEIVE_DATA_STRUCTURE RXdata;
#define SLAVE_ADDRESS 77
volatile uint8_t newData; // flag to detect I2C activity

// MAKE GLOBAL VERSIONS
String RX_Filename;
uint8_t RX_VolLeft = 30;
uint8_t RX_VolRight = 30;
uint8_t RX_Command = 0;

... etc

//#################################//
//##  MASTER SENDING US DATA     ##//
//#################################//
void onReceiveEvent(int numbytes){
  if(numbytes == 16){ 
    I2C_readAnything(RXdata);      
    newData = 2;    
  }   
}

... etc



void loop(){ 
  if (newData==2){// I2C received Data 
    RX_Filename = RXdata.Filename;
    RX_VolLeft = RXdata.VolLeft;    
    RX_VolRight = RXdata.VolRight;            
    RX_Command = RXdata.Command;             
    newData = 0; // Clear data sent marker
  }
}

... etc

Hopefully you'll be able to see pretty quickly what i'm trying to do and the method I should be using to do it..

Thanks for any insights

Cheers

Andy


Sorry, still getting used to this comment vs editing questions business, didn't realise it would add my comment when I hit enter so it cut me off... here's full comment:


Hiya Nick, thanks for your suggestion, How you've recommended it is actually how I tried it originally (I don't like using Strings either) - which just ends up giving me the error:

"invalid conversion from 'volatile char*' to 'const char*' [-fpermissive]"

(on the Slave) - obviously the RXdata is updated during the interrupt so can't leave it out (despite the fact it compiles - but doesnt work)..

I got it working soon after I posted by breaking down the filename in the struct to separate chars, then assembling back into char array in a function I made.

That seemed to work fine, but only if I added null char at as the last char (0x00) - I can get the char Filename[MaxMP3Name]; working, but only if I lose the volatile declaration, and it skips the last char.

Thanks for any further help Nick, I really appreciate all you do for the community and I know you're the best guy to help me (not to take anything away from anyone else that wants to jump in ;)

Current solution i'm using that avoids the volatile erros

Obviously could be a for loop etc but I wrote it out this way just to debug and see where the issue was. Not perfect, but breaking it down this way seems to be overcoming the issue of trying to do anything with the volatile variable. (and i've got so much to figure out for so many other parts of the project that I was thinking i'd come back to it later once i'm done with other bits).. Cheers

//#################################//
//##  SEND MP3 COMMAND        ##//
//#################################//
void sendMP3Command(String MP3File, uint8_t Command) {      
  char FileName[8];
  MP3File.toCharArray(FileName, MaxMP3Name+1);
  txMP3.File1 = FileName[0];
  txMP3.File2 = FileName[1];
  txMP3.File3 = FileName[2];
  txMP3.File4 = FileName[3];
  txMP3.File5 = FileName[4];
  txMP3.File6 = FileName[5];
  txMP3.File7 = FileName[6];
  txMP3.File8 = FileName[7];
  txMP3.VolLeft = VolLeft; 
  txMP3.VolRight = VolRight;  
  txMP3.Command = Command;
  StartMicros = micros();
  Wire.beginTransmission(MP3_SLAVE_ADDRESS);
  I2C_writeAnything(txMP3);
  Wire.endTransmission(true);
  EndMicros = micros();
  //Serial.print("MP3 Packet Size: "); Serial.println(sizeof(txMP3));
  //Serial.print("Sending Command To MP3: "); Serial.print(MP3File);Serial.print("   Command; "); Serial.print(txMP3.Command);Serial.print("   Time; "); Serial.print(EndMicros-StartMicros);Serial.println("uS ");
}

... Oh, and on the slave end, I changed the numbytes to be sizeof, but as you say Nick, not even any need for that, i'm not using it on any of my other I2C slaves all running in the project (5 X MCU core system). the filename is then pulled from the I2C array in a siimilar (debugging) way as the master packs them in

//##############################//
// RECONSTRUCT FILENAME
//##############################//
String getFilename(){  
  char FileName[] = {
    RXdata.File1,
    RXdata.File2,
    RXdata.File3,
    RXdata.File4,
    RXdata.File5,
    RXdata.File6,
    RXdata.File7,
    RXdata.File8,
    0x00
  };
  return FileName;

}


//#################################//
//##  MASTER REQUESTING DATA     ##//
//#################################//
void onRequestEvent(void){  
    I2C_singleWriteAnything(TXdata);
    newData = 1;    
}

//#################################//
//##  MASTER SENDING US DATA ##//
//#################################//
void onReceiveEvent(int numbytes){
  if(numbytes == sizeof RXdata){ 
    I2C_readAnything(RXdata);      
    newData = 2;    
  }   
}
3
  • I'm not sure if you are saying you got it working or not, now. if not, can you please post the exact code (edit your question) that is not working?
    – Nick Gammon
    Jan 23, 2016 at 2:25
  • Hiya Nick, thanks for your help (and continued support to everyone, jeez i see your name just everywhere! lol) - so yeah, i've been off with the fairies in ESP8266 land and only just looked up ;) - in the end i just did a break down of the filename into literally seperate bytes and worked flawlessly ever since. the original issue I think must be teensy 3.1 specific as I've used char arrays all over the place with I2C with no issues, String was my final attempt. i'll post my current 'solution'.
    – Andology
    Jan 26, 2016 at 21:35
  • And accept my answer? Or if my answer just pointed you in the right direction, post your own answer and accept it. So long as the question has an accepted, or upvoted, answer. Then the system doesn't nag us. :)
    – Nick Gammon
    Jan 26, 2016 at 23:06

1 Answer 1

1

This part looks weird:

void sendMP3Command(String MP3File, uint8_t Command) {      
  char *TextArray[MaxMP3Name];
  MP3File.toCharArray(TextArray, MaxMP3Name);
  txMP3.Filename = TextArray;

You have made an array of 13 pointers to strings. This is not what you want. You want the file name to end up into txMP3.Filename.

This would be closer:

void sendMP3Command(String MP3File, uint8_t Command) {      
 MP3File.toCharArray(txMP3.Filename, MaxMP3Name);

Personally I wouldn't be using the String class at all, but that should be better.


Now:

void onReceiveEvent(int numbytes){
  if(numbytes == 16){ 
    I2C_readAnything(RXdata);      
    newData = 2;    
  }   
}

Are you sure it's 16 bytes? What if you change the struct? Let the compiler do the thinking for you:

void onReceiveEvent(int numbytes){
  if(numbytes == sizeof (RXdata)){ 
    I2C_readAnything(RXdata);      
    newData = 2;    
  }   
}

How you've recommended it is actually how I tried it originally (I don't like using Strings either) - which just ends up giving me the error "invalid conversion from 'volatile char*' to 'const char*' [-fpermissive]" (on the Slave)

This minimal example compiles OK:

#include <Wire.h>
#include <I2C_Anything.h>
const uint8_t MaxMP3Name = 13;
struct RECEIVE_DATA_STRUCTURE{
  char Filename[MaxMP3Name];
  uint8_t VolLeft;
  uint8_t VolRight;
  uint8_t Command;   
};

volatile RECEIVE_DATA_STRUCTURE RXdata;
volatile uint8_t newData; // flag to detect I2C activity

void onReceiveEvent(int numbytes){
  if(numbytes == sizeof RXdata){ 
    I2C_readAnything(RXdata);      
    newData = 2;    
  }   
}

void setup() 
{
}

void loop() 
{
}
5
  • Hiya Nick, thanks for your suggestion, How you've recommended it actually how I tried it originally - which just ends up giving me the error "invalid conversion from 'volatile char*' to 'const char*' [-fpermissive]" (on the Slave)
    – Andology
    Oct 24, 2015 at 19:53
  • Sorry, it cut me off, please see my original question as i've added it to it, seems it wont let me add it here as a comment, cheers
    – Andology
    Oct 24, 2015 at 20:10
  • You have 5 minutes after making a comment to edit it. Yes, hitting (enter) does commit the comment, but you can edit it.
    – Nick Gammon
    Oct 24, 2015 at 20:49
  • hmm. it's still not compiling, got the invalid conversion from volatile to const char error message as above. If I try type casting it I get "call of overloaded 'String(volatile char [13])' is ambiguous" . so something else is causing it to not compile since your minimal example compiles fine. i've got #include <SPI.h> #include <Adafruit_VS1053_NanoV3.h> #include <SD.h> #include <Wire.h> #include <I2C_Anything.h> #include <stdlib.h> think i'll just use my original fix - breaking struct into separate chars vs array then re-build since it works. thanks again for your efforts, much appreciated
    – Andology
    Oct 26, 2015 at 11:33
  • Using String is probably the reason, and I would not use String in an ISR. Since it does dynamic memory allocation you will probably find that the String library does not support re-entrant calls, and thus it will eventually fail.
    – Nick Gammon
    Oct 26, 2015 at 19:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.