Invalid conversion from 'const_FlashStringHelper*' to 'uint16_t'

if (!file.open(root, F("PACK.CFG"))) { }

The problem seems to originate from an overloaded function in the FAT lib:

uint8_t FatReader::open(FatReader &dir, uint16_t index) // Open by index
uint8_t FatReader::open(FatReader &dir, char *name) // Open by name

The compiler is choosing the first function for some reason.

I think the solution here is to explicitly cast F("PACK.CFG") to a char* but what's going on here and is that safe to do on this macro or whatever the heck F() is?

Don't use the F macro there, file.open is not designed to accept it.

Well damn. I thought I'd been using F() with my WAV playing functions all along and it was working fine, but it turns out I was just using regular strings to open those files. So I guess I'll do that here.

But it seems like if I want to do this, then the solution is to create another version of the file open function that takes that flashstringhelper type as a parameter:
http://forum.arduino.cc/index.php?topic=157709.0

Nick:
Thanks. Solved. :slight_smile:

I should mention that I thought what F() did was read the string from flash, store it in ram, and then passed it to the function. But I guess C++ doesn't have automatic garbage collection so that would probably not work very well.

Ugh, I get the same error on this line:

case 2: if (sscanf(line, F("maxSpeed %f"), &maxSpeed) == 1) n++; break;

I guess sscanf wasn't modified to accept this as printf was? Kind if annoying to tease me with all these memory savings that I can't get without jumping through hoops. :frowning:

This is the code I'm trying to optimize btw:

void readConfigFile() {
    
  char line[256]; // Next line of text in file.
  char f[32]; // Temporary variable for holding sscanf format strings read from flash.
  int n = 0; // Next parameter we want.
  int tmp; // Holds parameters that need to be modified before being put in another variable.
  
  if (!file.open(root, "PACK.CFG")) { Serial1.println(F("Failed to open PACK.CFG!")); halt(6); } // Error if file does not exist.

  while (file.fgets(line, sizeof(line))) { // Read lines from the file until we hit the end.
    
    if (*line == '#') continue; // Check first character on line.  If comment, skip to next line.

    switch (n) {
      
      case 0: if (sscanf(line, F("powerupDelay %i"), &powerupDelay) == 1) n++; break;
      case 1: if (sscanf(line, F("humFadeTime %i"), &humFadeTime) == 1) n++; break; 
      
      case 2: if (sscanf(line, F("maxSpeed %f"), &maxSpeed) == 1) n++; break;	
      case 3: if (sscanf(line, F("overheatWarning %f"), &overheatWarning) == 1) n++; break;	
      
      case 4: if (sscanf(line, F("minPowerDownSoundTime %f"), &minPowerDownSoundTime) == 1) n++; break;
      case 5: if (sscanf(line, F("midPowerDownSoundTime %f"), &midPowerDownSoundTime) == 1) n++; break;
      case 6: if (sscanf(line, F("maxPowerDownSoundTime %f"), &maxPowerDownSoundTime) == 1) n++; break;
      
      case 7: if (sscanf(line, F("shotgunShotsOverheat %i"), &tmp) == 1) { heatup_Shotgun = maxSpeed / (tmp * 0.625); n++; } break;
     
      case 8: if (sscanf(line, F("SWAP_LEDMODULES %i"), &SWAP_LEDMODULES) == 1) n++; break;
      
      case 9: if (sscanf(line, F("ENABLE_POWERDOWN %i"), &ENABLE_POWERDOWN) == 1) n++; break;
      case 10: if (sscanf(line, F("ENABLE_BLINDING_VENT %i"), &ENABLE_BLINDING_VENT) == 1) n++; break;
      
      case 11: if (sscanf(line, F("BARGRAPH_STANDBY %i"), &BARGRAPH_STANDBY) == 1) n++; break;
      case 12: if (sscanf(line, F("BARGRAPH_IDLE %i"), &BARGRAPH_IDLE) == 1) n++; break;
      case 13: if (sscanf(line, F("BARGRAPH_FIRE %i"), &BARGRAPH_FIE) == 1) n++; break;    
      case 14: if (sscanf(line, F("BARGRAPH_ALTFIRE %i"), &BARGRAPH_ALTFIRE) == 1) n++; break;  
      case 15: if (sscanf(line, F("POWERCELL_IDLE %i"), &POWERCELL_IDLE) == 1) n++; break;    
    
    }
	
  }
	
  if (n < 16) { Serial1.println(F("Failed to read all parameters from PACK.CFG!")); halt(6); }		

  // No need to bother closing file if we're not writing to it. 

}

That's a lot of strings to store in ram. But I don't want to have to modify the sscanf function to accept flash strings. So now I gotta figure out how to copy those flash strings to my temp char array so I can pass them to sscanf.

      case 0: if (sscanf(line, F("powerupDelay %i"), &powerupDelay) == 1) n++; break;

What's the variable name doing there in quotes?

This code reads a config file, formatted like so:

# This file contains parameters you can adjust to change the behavior of the proton pack kit.
# Do not change the order of the paramters or remove any of them.

powerupDelay 750 		# Time to wait before playing the powerup sound and continuing with pack animation. Allows amp time to warm up. (milliseconds)
humFadeTime 5			# Time over which hum fades out to conserve battery power. Hum will return to full volume again as soon as any other sound is played. (minutes) (NOT YET IMPLEMENTED)

maxSpeed 10			# How long blast stream needs to be fired continuously to get lights to move at maximum speed and for the pack to overheat. (seconds)
overheatWarning 2 		# Time before reaching max speed that you get warning beeps for impending overheat. (seconds)

minPowerDownSoundTime 3 	# How long fire button needs to be held to get the short powerdown sound. (seconds)
midPowerDownSoundTime 5	# How long fire button needs to be held to get the medium powerdown sound. (seconds)
maxPowerDownSoundTime 7 	# How long fire button needs to be held to get the long powerdown sound. (seconds)

shotgunShotsOverheat 8	# Number of times shotgun must be fired to cause pack to overheat.

SWAP_LEDMODULES 0 		# 0 = Default LED module order, 1 = Reverse order of powercell and bargraph LED modules to allow Mighty to be installed in pack.

ENABLE_POWERDOWN 0		# 0 = No powerdown sound on deactivate. 1 = Powerdown sound when deactivate. (pwrdown.wav)
ENABLE_BLINDING_VENT 0	# 0 = Disable, 1 = Enable.  With the Blinding Vent enabled, the main strobe must be connected to port 14. The Blinding Vent's control module then attaches to port 18. 

BARGRAPH_STANDBY 1 		# Safety ON animaton
BARGRAPH_IDLE 2 		# Ready to fire
BARGRAPH_FIRE 3 		# Black button - Capture stream
BARGRAPH_ALTFIRE 4 		# Red button - Blast stream

POWERCELL_IDLE 1		# Idling animation

# Bargraph animations:
#
# 0 - Off
# 1 - Fill and empty (Default safety)
# 2 - Random fill and empty (Default ready to fire)
# 3 - Ends to center to ends (Default capture stream)
# 4 - Ends to center (Default blast stream)
# 5 - Music mode (Displays current song)
# 6 - Reset (Resets shotgun animation)
# 7 - Ends to center (Default shotgun animation - One shot until reset)
# 8 - Do nothing (For future use)
# 9 - Solid
# 10 - Vent drain (Used when venting - drains quickly)
# 11 - Overheat drain (Used after overheat - drains slowly)
#
# For full movie accuracy, set STANDBY to 9, and IDLE, FIRE, and ALTFIRE to 1.  
# To swap the random fill and normal fill animations, set STANDBY to 2, and IDLE to 1.

# Powercell animations:
# 
# 1 - Fill normally, speeding up as pack heats up.
# 2 - Fill halfway, then as pack heats up speed up and increase leds that light.

I didn't want to just stick the numbers on each line. Originally the plan was to allow the parameters to be ordered in any way but that turned out to be too much of a pain in the ass to code. Or at least, in an optimal manner. I didn't want the thing scanning every line for every possible keyword. So, there are keywords, but in a specific order in the file.

I found this documentation:
http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html#ga652cbaf54885c73c7ebbefe73524fa22

Looks like strcpy_PF() is what I need to use. But I need a far pointer to the string in flash. Is that what F() returns?

   switch (n) {
      
      case 0: if (sscanf(line, F("powerupDelay %i"), &powerupDelay) == 1) n++; break;

Why use the switch? Since each line is identified by a name, surely the order doesn't matter?

You are going to have to rethink a bit. sscanf on the Arduino does not support floats, so the %f is going to fail anyway.

This is about the best I can come up with.

void mysscanf (char * line, const PROGMEM char * s, float & f)
  {
  char paramName [50];
  char result [10];
  strcpy_P (paramName, s);
  strcat (paramName, " %s");
  if (sscanf (line, paramName, result) > 0)
    f = atof (result);
  }  // end of mysscanf

void mysscanf (char * line, const PROGMEM char * s, int & i)
  {
  char paramName [50];
  char result [10];
  strcpy_P (paramName, s);
  strcat (paramName, " %s");
  if (sscanf (line, paramName, result) > 0)
    i = atoi (result);
  }  // end of mysscanf

float minPowerDownSoundTime;
int powerupDelay;

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  
  // some test lines
  char line1 [100] = "minPowerDownSoundTime 42.34\n";
  char line2 [100] = "powerupDelay 750\n";
  
  // scan for stuff
  mysscanf (line1, PSTR ("minPowerDownSoundTime"), minPowerDownSoundTime); 
  mysscanf (line2, PSTR ("powerupDelay"), powerupDelay); 
    
  Serial.println (minPowerDownSoundTime);
  Serial.println (powerupDelay);
  }  // end of setup

void loop () { }

A "custom" sscanf function (mysscanf) copies the PROGMEM constant into a temporary buffer, appends %s and does the scan. If it matches it does the appropriate conversion to convert the number back (to float or int). If no match it leaves the variable alone.

scswift:
I should mention that I thought what F() did was read the string from flash, store it in ram, and then passed it to the function. But I guess C++ doesn't have automatic garbage collection so that would probably not work very well.

It does not store the value in ram. It does the other stuff you mention. Not storing the data in ram is the reason for wrapping text in the F() macro.

F() just does a cast really. That lets functions which are designed to notice it do extra work, for example Print::print

size_t Print::print(const __FlashStringHelper *ifsh)
{
  const char PROGMEM *p = (const char PROGMEM *)ifsh;
  size_t n = 0;
  while (1) {
    unsigned char c = pgm_read_byte(p++);
    if (c == 0) break;
    n += write(c);
  }
  return n;
}

Notice there that it pulls the string, one byte at a time, out of program memory and prints it. However sscanf has no such overridden function so it is not going to be too impressed with your F() macro, and if you cast it away, well the program will just access the wrong sort of memory.

I've improved the above code a bit. I changed the fixed string sizes in the helper functions to dynamic sized arrays, and allowed for maximum int/float lengths:

void mysscanf (char * line, const PROGMEM char * s, float & f)
  {
  char paramName [strlen_P (s) + 1];
  char result [10];
  strcpy_P (paramName, s);
  strcat (paramName, " %9s");
  if (sscanf (line, paramName, result) > 0)
    f = atof (result);
  }  // end of mysscanf

void mysscanf (char * line, const PROGMEM char * s, int & i)
  {
  char paramName [strlen_P (s) + 1];
  char result [8];
  strcpy_P (paramName, s);
  strcat (paramName, " %7s");
  if (sscanf (line, paramName, result) > 0)
    i = atoi (result);
  }  // end of mysscanf

float minPowerDownSoundTime;
int powerupDelay;

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  
  // some test lines
  char line1 [] = "minPowerDownSoundTime 42.34";
  char line2 [] = "powerupDelay 750";
  
  // scan for stuff 
  mysscanf (line1, PSTR ("minPowerDownSoundTime"), minPowerDownSoundTime); 
  mysscanf (line2, PSTR ("powerupDelay"), powerupDelay); 
    
  Serial.println (minPowerDownSoundTime);
  Serial.println (powerupDelay);
  }  // end of setup

void loop () { }

In this example the parameters can come in any order in the config file, as the mysscanf function calls modify the parameter if it is found. Of course, you do the mysscanf in a loop (like you had) where you pass each line to every one of them, in case one matches.

Warning: Bugs in function above. See post on next page for correction. (Not enough room allocated for paramName).

I should point out also to anyone reading this, my code above does not implement a full replacement for sscanf. It only looks for a keyword and a single argument which is an int or a float. It would not handle something more generic like "%i %f %i %f". If you need that you'll have to use the above as a guide only.

True... but if I have 25 lines in the file and 25 parameters that's something like 300 string comparisons I may need to do on average to parse the file.

I thought sscanf was a standard C function? Why would they implement it for ints only? It seems like a pretty important function and implementing strings floats and ints seems like the bare minimum you'd want to implement. Ugh.

Thanks a bunch for the code!

I should point out also to anyone reading this, my code above does not implement a full replacement for sscanf. It only looks for a keyword and a single argument which is an int or a float. It would not handle something more generic like "%i %f %i %f". If you need that you'll have to use the above as a guide only.

I'll have to modify it then, because my LED configuration file is formatted like so:

# Format: 
# index scale speed

# "index" is the LED number you want to set the parameters of. (First LED is index 1.  You may skip some indexes, but they must all be in numerical order.)
# "scale" is the max brightness of the LED. [0..1] (Default = 1.0)
# "speed" is the rate at which the LED changes. [-1 or 0..99] (Default = -1)

# All LEDs that follow the last specified LED take on its parameters. 
# So, specifying the 1st LED and then the 16th makes LEDs 2-15 use the same parameters as LED 1.  This is useful for things like bargraphs.

# Valid scales range between 0 and 1, where 0.5 means that the LED will be half as bright as it would normally be when it's brightness is set to 1.

# A speed of 10 means an LED can go from 0 to 100% in 1/10th of a second. 
# Lower values mean the LED takes longer to move from one brightness to the next.  A speed of 0.5 for example would cause the led to take 2 seconds to go from fully off to fully on.
# A speed of -1 will force an LED to change instantly.
 
1  1.0  15 # Bargraph (1..15) 

16 1.0  10 # Slo-blo (3mm on rear instrument bar)
17 0.5  10 # Overheat (White hat above bargraph)
18 0.25 10 # Capture (3mm above clippard)
19 0.5  10 # Standby (White hat mext to clippard)
20 1.0  10 # Vent
21 0.66 10 # Orange hat

22 1.0  10 # Budget strobe - White, blue, orange (22..24) 

25 0.33 10 # Powercell (25..39)
40 1.0  10 # Cyclotron (40..43)

44 0.5  5  # White rib leds (44..48)

But you've given me a great starting point so that shouldn't be an issue.