Thank you. I noticed that you had 'const' and I forgot that.
Now I have it working, but I'm not happy with the result.
// Must be unsigned char (or byte) to use the PROGMEM function.
prog_uchar test2[] PROGMEM = { "Test 2" } ;
void setup()
{
Serial.begin( 9600);
Serial.println( "Start");
}
void loop()
{
Serial.print( "1: ");
WriteSlave( "Test 1", 6);
Serial.print( "2: ");
WriteSlave( test2, 6);
// PSTR can't be used.
// PSTR is (const PROGMEM char *)
// The PROGMEM is an attribute.
// The compiler can not see the difference between (const char *) and (const PROGMEM char *).
// The PSTR would be directed to the normal (const char *) function.
// Serial.print( "3: ");
// WriteSlave( PSTR("Test 3"), 6);
Serial.print( "4: ");
WriteSlave( F("Test 4"), 6);
Serial.println("");
delay(5000);
}
void WriteSlave( const char *pData, int size)
{
// write to serial monitor instead of I2C slave
Serial.write( (const uint8_t *)pData, size);
Serial.println(""); // new line
}
// The compiler can not see the difference between
// a (const char *) and a (const PROGMEM char *),
// that is why this function uses a byte pointer.
// The normal function uses a 'char' and the PROGMEM uses a 'byte'.
void WriteSlave( const PROGMEM byte *pData, int size)
{
byte buffer[20];
Serial.print( "-> PROGMEM -> ");
memcpy_P( buffer, pData, size);
WriteSlave( (const char *)buffer, size);
}
// The compiler does see a __FlashStringHelper,
// created with the "F()" macro.
// This function casts the pointer to a PROGMEM pointer.
void WriteSlave( const __FlashStringHelper* pData, int size)
{
Serial.print( "-> __FlashStringHelper -> ");
// Convert the __FlashStringHelper pointer to a PROGMEM pointer.
// After that, call the function that handles te PROGMEM pointer.
WriteSlave( (const PROGMEM byte *) pData, size);
}
It turns out, that the compiler can see the __FlashStringHelper, and directs it to that function.
However, the compiler can not distinguish between (const char *) and (const PROGMEM char *).
I had to use a trick to make it work. My PROGMEM string is a byte and the normal string in ram is a char. But if I would forget that, the wrong function is called. So I will not use that.
The next example uses the (void *) again, but uses a different function WriteSlave_P() for PROGMEM data.
prog_char test2[] PROGMEM = { "Test 2" } ;
void setup()
{
Serial.begin( 9600);
Serial.println( "Start");
}
void loop()
{
Serial.print( "1 : ");
WriteSlave( "Test 1", 6);
Serial.print( "2 : ");
WriteSlave_P( test2, 6);
Serial.print( "3 : ");
WriteSlave_P( PSTR("Test 3"), 6);
// The "F()" macro is handled by a seperate function,
// but it can also call WriteSlave_P direct.
Serial.print( "4a: ");
WriteSlave( F("Test4a"), 6);
Serial.print( "4b: ");
WriteSlave_P( F("Test4b"), 6);
Serial.println("");
delay(5000);
}
void WriteSlave( const void *pData, int size)
{
// write to serial monitor instead of I2C slave
Serial.write( (const uint8_t *)pData, size);
Serial.println(""); // new line
}
// Use a seperate function "..._P" for a PROGMEM pointer.
// This way it is always clear how the pointer is handled.
void WriteSlave_P( const PROGMEM void *pData, int size)
{
byte buffer[20];
Serial.print( "-> PROGMEM -> ");
memcpy_P( buffer, pData, size);
WriteSlave( (const void *)buffer, size);
}
// The compiler can handle a __FlashStringHelper,
// created with the "F()" macro.
// This function casts the pointer to a PROGMEM pointer.
void WriteSlave( const __FlashStringHelper* pData, int size)
{
Serial.print( "-> __FlashStringHelper -> ");
WriteSlave_P( (const PROGMEM void *) pData, size);
}
That is still not ideal, since the compiler does not warn if the wrong function is used.