Really Weird Char Array Issue

In my program, to setup Adafruit IO, I have to pass this code four arguments, shown below.

Adafruit_MQTT_Client mqtt(&client, "io.adafruit.com", 8886, "BillyBobJoe", "3754a5c50ec647ab99ae315fcfab09c5");

With this code, the program runs flawlessly, but if I replace them with char arrays, I cannot connect to Adafruit IO. This indicates that I am passing the arguments the wrong values. Why is this? I included an example of this strange behavior below.

  server[40] = "io.adafruit.com";

Adafruit_MQTT_Client mqtt(&client, server, port, user, key);

:o :confused:

I'm guessing the problem is in the code you didn't post.

  server[40] = "io.adafruit.com";

That looks incomplete or incorrect. Please include more context (where is the declaration of "server"? You can only initialize strings as part of a declaration. The statement as included here is technically legal if server was declared elsewhere (i'd think it would generate a warning though), but doesn't do what you want...)

The function probably expects a constant pointer.

const char* server = “io.adafruit.com”;
char server[40] = "io.adafruit.com";
uint8_t port = 8886;
char user[40] = "BillyBob";
char key[40] = "3754a5c50ec647ab99ae315fcfab09c5";

Adafruit_MQTT_Client mqtt(&client, server, port, user, key);

I know it isn't just one argument/bad value because I have tried putting a variable in just one argument, and seeing if it will run for all of the arguments(example).

char user[40] = "BillyBob";

Adafruit_MQTT_Client mqtt(&client, "io.adafruit.com", 8886, user, "3754a5c50ec647ab99ae315fcfab09c5");

This is also the only code that initializes Adafruit IO, so I know the issue must be here.

The usual cause of this is neglecting the trailing nul.

A string like "Hello!" is 7 bytes long, not 6. The compiler adds a trailing '\0' to the end of a string, and most C libraries work with that standard.

If you size your arrays to exactly the length of your text:

char hi[6] = "Hello!";

that trailing '\0' doesn't get put there.

To fix, either make your arrays one longer, or don't specify the size at all - let the compiler do it.

char key[] = "3754a5c50ec647ab99ae315fcfab09c5";

Except that the length of the text is undetermined. The sketch reads the data from the ESP8266's filesystem. I tried using a pointer, and it works fine.

But if I try to convert the char array to a char, It won't connect what am I doing wrong?

char * server = aioServer;

Max_The_Ginus:
But if I try to convert the char array to a char, It won't connect what am I doing wrong?

char * server = aioServer;

What is aioServer?

The char array that the server data is stored in once the scetch gets it from the SPIFFS filesystem.

Max_The_Ginus:
The char array that the server data is stored in once the scetch gets it from the SPIFFS filesystem.

So perhaps it would be helpful if you showed how you were accomplishing that... your full code.

BulldogLowell:
So perhaps it would be helpful if you showed how you were accomplishing that... your full code.

Reply #1

We're up to Reply #11 now. OP, nice job wasting peoples' time by not posting your full code.

It's 850 lines long...
But here are the parts that relate to the issue

char aioServer[40];
char aioPort[40];
char aioUsername[40];
char aioKey[40];
char apiKey[40];
uint16_t uAioPort;
WiFiManager wifiManager; // Setup wifi manager
WiFiClientSecure client;
void setup() {

  EEPROM.begin(512); // Start EEPROM
  
  Serial.begin(115200); // Start serial communication
  delay(1000);

  Serial.println(F("Please wait..."));
  delay(1000);
  Serial.println("");
  
  if (!SPIFFS.begin()) { // Mount the SPIFFS filesystem
    Serial.println("Failed to mount the SPIFFS filesystem");  
    Serial.println("");
  }    

  skipLine = false;
  readFile("/aioserver.txt"); // Read MQTT data from filesystem
  strcpy(aioServer, charBuff);
  readFile("/aioport.txt");
  strcpy(aioPort, charBuff);
  readFile("/aiousername.txt");
  strcpy(aioUsername, charBuff);
  readFile("/aiokey.txt");
  strcpy(aioKey, charBuff);
  readFile("/apikey.txt");
  strcpy(apiKey, charBuff);
  if (skipLine == true) {
    Serial.println("");
  }

  Serial.println("Connecting to WiFi");  // Connect to WiFi access point. 
  if (eeGetInt(48) < 100) {
    deviceId = random(100,1000);
    eeWriteInt(48, deviceId);
  } else {
    deviceId = eeGetInt(48);
  }
  String deviceBuffer = "SALTY-";
  deviceBuffer += String(deviceId);  
  WiFiManagerParameter custom_aio_server("Adafruit IO Server", "Adafruit IO Server", "", 40);
  WiFiManagerParameter custom_aio_port("Adafruit IO Port", "Adafruit IO Port", "", 6);  
  WiFiManagerParameter custom_aio_username("Adafruit IO Username", "Adafruit IO Username", "", 15);  
  WiFiManagerParameter custom_aio_key("Adafruit IO Key", "Adafruit IO Key", "", 32);  
  WiFiManagerParameter custom_api_key("Google API Key", "Google API Key", "", 39);     
  wifiManager.addParameter(&custom_aio_server);
  wifiManager.addParameter(&custom_aio_port);
  wifiManager.addParameter(&custom_aio_username);
  wifiManager.addParameter(&custom_aio_key);
  wifiManager.addParameter(&custom_api_key);      
  wifiManager.autoConnect(deviceBuffer.c_str());
  wifiManager.setConfigPortalTimeout(180);
  DynamicJsonBuffer jsonBuffer;
  JsonObject& json = jsonBuffer.createObject(); 
  Serial.println(F("WiFi connected! "));
  Serial.print(F("IP address: ")); Serial.println(WiFi.localIP());
  Serial.println(" ");
  
  skipLine = false;  
  if (strlen(custom_aio_server.getValue()) > 4) { // Update MQTT settings
    Serial.println(custom_aio_server.getValue());
    strcpy(aioServer, custom_aio_server.getValue());
    writeFile("/aioserver.txt", aioServer);    
  } else if (strlen(aioServer) < 2) { 
    strcpy(aioServer, "io.adafruit.com");
    writeFile("/aioserver.txt", aioServer);         
  }
  if (strlen(custom_aio_port.getValue()) > 3) {   
    strcpy(aioPort, custom_aio_port.getValue());
    writeFile("/aioport.txt", aioPort);
  } else if (strlen(aioPort) < 2) {
    strcpy(aioPort, "8883");
    writeFile("/aioport.txt", aioPort);    
  }
  if (strlen(custom_aio_username.getValue()) > 4) {    
    strcpy(aioUsername, custom_aio_username.getValue());
    writeFile("/aiousername.txt", aioUsername);
  }  
  if (strlen(custom_aio_key.getValue()) > 4) {       
    strcpy(aioKey, custom_aio_key.getValue()); 
    writeFile("/aiokey.txt", aioKey);   
  }    
  if (strlen(custom_api_key.getValue()) > 4) {    
    strcpy(apiKey, custom_api_key.getValue()); 
    writeFile("/apikey.txt", apiKey);
  } else if (strlen(apiKey) < 2) {
    strcpy(apiKey, "AIzaSyDrmCNZtBlV5F1O-J5Kwla9P2cXcBUJGQc"); 
    writeFile("/apikey.txt", apiKey);      
  }
  SPIFFS.end();
  if (skipLine == true) {
    Serial.println(" ");
  }

  String strBuff = String(aioPort); // Convert char*s from JSON to uint16_t
  uAioPort = strtol(strBuff.c_str(), NULL, 0); 

  Serial.println("Generating MQTT credentials/addresses from SPIFFS data ...done! "); // Generate Adafruit IO addresses with username data from user
  Serial.print("  AIO server: "); Serial.println(aioServer);
  Serial.print("  AIO port: "); Serial.println(aioPort);
  Serial.print("  AIO username: "); Serial.println(aioUsername);
  Serial.print("  AIO key: "); Serial.println(aioKey);
  strcpy(urlGpg, aioUsername); 
  strcat(urlGpg, "/feeds/salty.grains-per-gallon");
  Serial.print("  Grains per gallon feed address: "); Serial.println(urlGpg);
  strcpy(urlPpm, aioUsername);
  strcat(urlPpm, "/feeds/salty.parts-per-million");
  Serial.print("  Parts per million feed address: "); Serial.println(urlPpm);
  strcpy(urlAlerts, aioUsername);
  strcat(urlAlerts, "/feeds/salty.alerts");
  Serial.print("  Alerts/WaterIQ feed address: "); Serial.println(urlAlerts);
  strcpy(urlWateriq, aioUsername);
  strcat(urlWateriq, "/feeds/salty.wateriq"); 
  Serial.print("  WaterIq on/off feed address: "); Serial.println(urlWateriq);
  char * server = aioServer;
}

Adafruit_MQTT_Client mqtt(&client, aioServer, uAioPort, aioUsername, aioKey); // Setup Adafruit IO
Adafruit_MQTT_Publish gpg = Adafruit_MQTT_Publish(&mqtt, urlGpg);
Adafruit_MQTT_Publish ppm = Adafruit_MQTT_Publish(&mqtt, urlPpm);
Adafruit_MQTT_Publish alerts = Adafruit_MQTT_Publish(&mqtt, urlAlerts);
Adafruit_MQTT_Subscribe iqOnOffButton = Adafruit_MQTT_Subscribe(&mqtt, urlWateriq);
void readFile (String file) {
  File f = SPIFFS.open(file, "r");
  if (f.available()) {
    skipLine = true; 
    String dataBuff = f.readStringUntil('\n'); 
    Serial.println(String("Read ") + dataBuff + String(" from ") + file);             
    dataBuff.toCharArray(charBuff, 40);
    f.close();
  } else {
    Serial.println(String("Failed to read from file ") + file);    
  }
}

void writeFile(String file, char * data) {
  File f = SPIFFS.open(file, "w+");
    skipLine = true; 
    Serial.println(String("Wrote ") + data + String(" to ") + file);    
    f.println(String(data));
    f.close();
}

The full code is attached

Salty_Pro_AI2.ino (27.5 KB)

You statically define your mqtt object with this call to the Adafruit_MQTT_Client constructor:

Adafruit_MQTT_Client mqtt(&client, "io.adafruit.com", 8883, "MaxMaeder", "3754a5c50ec647ab99ae315fcfab09c5");

This executes before the setup() function runs. So, you can't just replace those constants with char string variables that you read from SPIFFS and constructed during setup().

The only way I can think of reading the strings you want from SPIFFS and using them in the constructor is to dynamically allocate the mqtt object using the 'new' operator.

I define the MQTT object after setup, where I get my values, and before the loop.

Max_The_Ginus:
I define the MQTT object after setup, where I get my values, and before the loop.

Like I said, that's not when the constructor executes.