Invalid conversion when making a function that passes parameters to digitalWrite

I've got a function that takes a variable called nullTermedPayload and splits it into firstToken and secondToken.

nullTermedPayload can be something like "A0:33.33" or "relay1:OFF"

I then want to run a comparison on firstToken. If the first five characters are equal to "relay" then I call the controlRelay function, passing in firstToken and secondToken, which goes directly into the call to digitalWrite(relay1,OFF)

    char *firstToken, *secondToken, tempBuffer[25];

    // strtok mutates the char array given it!!!
    strcpy(tempBuffer, (const char *)nullTermedPayload);

    firstToken = strtok(tempBuffer, ":");
    Serial.print("First Token = ");
    Serial.println(firstToken);

    secondToken = strtok(NULL, ":");
    Serial.print("Second Token = ");
    Serial.println(secondToken);

    if (strncmp(firstToken, "relay", 5)) {
      controlRelay(firstToken, secondToken);
    };
void controlRelay(const char* relay, const char* state) {
  digitalWrite(relay, state);
}

The error that I'm getting is, yet again, a problem with invalid data types, which constitute 99% of my errors.

/home/admin/Workspace/Arduino/WemosD1MiniCloudMQTT/WemosD1MiniCloudMQTT.ino: In function 'void controlRelay(const char*, const char*)':
WemosD1MiniCloudMQTT:146: error: invalid conversion from 'const char*' to 'uint8_t {aka unsigned char}' [-fpermissive]
   digitalWrite(relay, state);
                            ^
In file included from sketch/WemosD1MiniCloudMQTT.ino.cpp:1:0:
/home/admin/.arduino15/packages/esp8266/hardware/esp8266/2.3.0/cores/esp8266/Arduino.h:195:6: error:   initializing argument 1 of 'void digitalWrite(uint8_t, uint8_t)' [-fpermissive]
 void digitalWrite(uint8_t pin, uint8_t val);
      ^
WemosD1MiniCloudMQTT:146: error: invalid conversion from 'const char*' to 'uint8_t {aka unsigned char}' [-fpermissive]
   digitalWrite(relay, state);
                            ^
In file included from sketch/WemosD1MiniCloudMQTT.ino.cpp:1:0:
/home/admin/.arduino15/packages/esp8266/hardware/esp8266/2.3.0/cores/esp8266/Arduino.h:195:6: error:   initializing argument 2 of 'void digitalWrite(uint8_t, uint8_t)' [-fpermissive]
 void digitalWrite(uint8_t pin, uint8_t val);
      ^
exit status 1
invalid conversion from 'const char*' to 'uint8_t {aka unsigned char}' [-fpermissive]

Do I need to convert from const char* to uint_8?

digitalWrite takes two arguments - what datatype is the first argument (the pin) and what data type is the second argument (HIGH or LOW)?

No, you can’t pass a string into digitalWrite. It expects HIGH or LOW which are actually the numbers 1 and 0. You’ll need an if statement to look at your string and decide based on it if it should write HIGH or LOW to the pin. You can’t just use the string directly like that.

if(strcmp(someString, "Off") == 0){
    digitalWrite(somePin, LOW);
else if (strcmp(someString, "On") == 0){
    digitalWrite(somePin, HIGH);
}

Note, this code assumes that the only thing in the string is “On” or “Off”. You have more to your string so you’ll have to cut out the On or Off part to use here.

Delta_G:
No, you can't pass a string into digitalWrite. It expects HIGH or LOW which are actually the numbers 1 and 0. You'll need an if statement to look at your string and decide based on it if it should write HIGH or LOW to the pin. You can't just use the string directly like that.

if(strcmp(someString, "Off") == 0){

digitalWrite(somePin, LOW);
else if (strcmp(someString, "On") == 0){
   digitalWrite(somePin, HIGH);
}





Note, this code assumes that the only thing in the string is "On" or "Off". You have more to your string so you'll have to cut out the On or Off part to use here.

I do this and I get the same error:

void controlRelay(const char* relay, const char* state) {
  if(strcmp(state, "LOW") == 0){
      digitalWrite(relay, LOW);
  } else if (strcmp(state, "HIGH") == 0){
      digitalWrite(relay, HIGH);
  }
}

Ok, so when I do digitalWrite(myPin1, LOW), the LOW or HIGH is obviously not written as an integer. It's LOW or HIGH. What is LOW or HIGH? Is this some weird aliasing going on in the background or something? You type LOW but Arduino compiles it to the integer 0?

And is myPin1 supposed to be a const char* datatype?

Same deal with the pin number. You can’t put a string in there. Remember that after the code is compiled the variable names all become memory locations and the names all disappear. So you can’t expect the board to recognize the string “relay” as being anything to do with the variable relay.

HIGH and LOW are #define’d in the core. It’s not something weird. We use symbolic names for numbers all the time in programming.

From Arduino/Arduino.h at 2.3.0 · esp8266/Arduino · GitHub

#define HIGH 0x1
#define LOW 0x0

The line:

#include <Arduino.h>

is automatically added to your .ino file by the Arduino IDE before compilation.

I have the MQTT server send a message of "relay1:HIGH" or whatever to the Arduino. My relay pins are called "relay1" and "HIGH" is the exact command.

But since they're still different types, my code is required to look like this?

void controlRelay(const char* relay, const char* state) {

  if(strcmp(relay, "relay1") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay1, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay1, HIGH);
    }
  } else if(strcmp(relay, "relay2") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay2, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay2, HIGH);
    }
  } else if(strcmp(relay, "relay3") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay3, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay3, HIGH);
    }
  } else if(strcmp(relay, "relay4") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay4, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay4, HIGH);
    }
  } else if(strcmp(relay, "relay5") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay5, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay5, HIGH);
    }
  } else if(strcmp(relay, "relay6") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay6, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay6, HIGH);
    }
  } else if(strcmp(relay, "relay7") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay7, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay7, HIGH);
    }
  } else if(strcmp(relay, "relay8") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay8, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay8, HIGH);
    }
  }
}

PS: HIGH and LOW being symbolic of the integers 1 and 0 are not stated in the docs:

https://www.arduino.cc/en/Reference/Constants

https://www.arduino.cc/en/Reference/DigitalWrite

There's no reason to document the values of HIGH and LOW. That would just make the Arduino API less flexible. That could be harmful in case there was ever a situation where it was desirable to define HIGH and LOW as other values. The values are irrelevant as long as you know that using those names will always give you the expected results.

fuzzybabybunny:
I have the MQTT server send a message of "relay1:HIGH" or whatever to the Arduino. My relay pins are called "relay1" and "HIGH" is the exact command.

But since they're still different types, my code is required to look like this?

void controlRelay(const char* relay, const char* state) {

if(strcmp(relay, "relay1") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay1, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay1, HIGH);
    }
  } else if(strcmp(relay, "relay2") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay2, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay2, HIGH);
    }
  } else if(strcmp(relay, "relay3") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay3, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay3, HIGH);
    }
  } else if(strcmp(relay, "relay4") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay4, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay4, HIGH);
    }
  } else if(strcmp(relay, "relay5") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay5, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay5, HIGH);
    }
  } else if(strcmp(relay, "relay6") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay6, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay6, HIGH);
    }
  } else if(strcmp(relay, "relay7") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay7, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay7, HIGH);
    }
  } else if(strcmp(relay, "relay8") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay8, LOW);
    } else if (strcmp(state, "HIGH") == 0){
        digitalWrite(relay8, HIGH);
    }
  }
}

Yes, that's what the code would look like. It could be made much more compact with the use of a few arrays. Note that any time you catch yourself putting numbers on the end of your variable names what you really want is an array. Make those numbers count for something and make them something you can use in your program.

If the relay pins were in an array then you could just parse the number off the end and convert it from ascii to a real number and use it to grab the right pin number out of the array without any string comparison at all. You could also reduce all that repeated code so that you only have to type the comparisons for HIGH and LOW once instead of over and over.

Even without the arrays you could do the comparison with state ONCE at the beginning of the function and save the state in a variable to use later. That way you don't have to keep repeating that part over and over.

What data type are D1, D2, D3, etc? If I want to create an array I need to first explicitly define the size and also the data types to be stored in the array, right?

fuzzybabybunny:
What data type are D1, D2, D3, etc? If I want to create an array I need to first explicitly define the size and also the data types to be stored in the array, right?

You mean the pin numbers? They're just numbers. I usually use byte so they'll take up only one byte each. Some folks use int, but to me that's wasting a byte of memory. I also usually make them const to keep me from accidentally changing one somewhere.

const byte pinNumbers[] = {4, 5, 6, 7, 8};

What would be the benefit in this case to using an array? Any numbers as arguments coming from MQTT that go into the function can’t be used as an array index anyway since they’re char

static const uint8_t digitalPins[] = {D0, D1, D2, D3, D4, D5, D6, D7};

void setup() {

  for (int i = 0; i < 8; i++) {
    pinMode(digitalPins[i], OUTPUT);
  }

}

// pinNumber could be "1" and state could be "HIGH"
void controlRelay(const char* pinNumber, const char* state) {

  if(strcmp(state, "LOW") == 0){
    digitalWrite(digitalPins[pinNumber], LOW)
  } else if (strcmp(state, "HIGH") == 0){
    digitalWrite(digitalPins[pinNumber], HIGH)
  } else {
    // error handling
  }

}

Delta_G:
You mean the pin numbers? They're just numbers. I usually use byte so they'll take up only one byte each. Some folks use int, but to me that's wasting a byte of memory. I also usually make them const to keep me from accidentally changing one somewhere.

const byte pinNumbers[] = {4, 5, 6, 7, 8};

Eh, sorry, on the Wemos the digital pins are D1, D2, D3, etc.

In Arduino terms, what are A0, A1, A2? Are they also numbers, in that they are symbolically linked to numbers?

  if(strcmp(relay, "relay1") == 0){
    if(strcmp(state, "LOW") == 0){
        digitalWrite(relay1, LOW);

That will work. You'd normally use some sort of table:

cmdType NameToRelayNo[] = {
  {"relay1", relay1},
  {"relay2", relay2},
  {"pump", relay2},  // another name for relay2
  {"relay3", relay3},
  {"lights", relay4} ...
}

If you really only want to accept names like "relayNN", you can convert the NN part to a number directly.

relayNo = atoi(&relay[5]);  // parse number starting at 6th character.

westfw:

  if(strcmp(relay, "relay1") == 0){

if(strcmp(state, "LOW") == 0){
        digitalWrite(relay1, LOW);




That will work. You'd normally use some sort of table:



cmdType NameToRelayNo = {
  {"relay1", relay1},
  {"relay2", relay2},
  {"pump", relay2},  // another name for relay2
  {"relay3", relay3},
  {"lights", relay4} ...
}





If you really only want to accept names like "relayNN", you can convert the NN part to a number directly.


relayNo = atoi(&relay[5]);  // parse number starting at 6th character.

  • Are there any downsides or edge cases to using atoi?
  • The "table" looks like a 2D array. So NameToRelayNo[0][0] would be the string name or char name, and NameToRelayNo[0][1] would be the variable. So like digitalWrite(NameToRelayNo[0][1], LOW)

There's still my question of how to use char or string "numbers" like "1" or "0" if I'm forced to use them (since they're coming from the MQTT server).

Would it have to use atoi then?

void relayControl(const char relayNo){
  int relayNumber = atoi(relayNo);
  digitalWrite(NameToRelayNo[relayNumber][1], LOW);
}

fuzzybabybunny:
Eh, sorry, on the Wemos the digital pins are D1, D2, D3, etc.

In Arduino terms, what are A0, A1, A2? Are they also numbers, in that they are symbolically linked to numbers?

Yes A0, A1, D1, D2, those are all just numbers. They are #define'd somewhere in the core. pins_arduino.h I think is where they are. Or it might be in the specific IO header, you know the one where there's a different one for each chip. You could just search the core for "#define D1" and wherever you find it they're all going to be together.

Either way, somewhere out there is a number you could use instead of D1 and you would get the exact same code. Anything with a # in front of it is a pre-processor command. These tell the pre-processor to add the contents of another file (#include) or do a find-and-replace of a given text symbol (#define) or throw an error message (#error) or any number of other things that can be done there. This all gets done to the text of your code before it gets sent to the compiler.

From Arduino/pins_arduino.h at 2.3.0 · esp8266/Arduino · GitHub

static const uint8_t D0   = 16;
static const uint8_t D1   = 5;
static const uint8_t D2   = 4;
static const uint8_t D3   = 0;
static const uint8_t D4   = 2;
static const uint8_t D5   = 14;
static const uint8_t D6   = 12;
static const uint8_t D7   = 13;
static const uint8_t D8 = 15;

From Arduino/common.h at 2.3.0 · esp8266/Arduino · GitHub

static const uint8_t A0 = 17;

As for A1 and A2, the ESP8266 doesn't have those pins but you will find them defined in the variant files for boards that do have them. For example, the variant used by the Arduino Uno Arduino/pins_arduino.h at 1.8.4 · arduino/Arduino · GitHub

#define PIN_A0   (14)
#define PIN_A1   (15)
#define PIN_A2   (16)
#define PIN_A3   (17)
#define PIN_A4   (18)
#define PIN_A5   (19)
#define PIN_A6   (20)
#define PIN_A7   (21)

static const uint8_t A0 = PIN_A0;
static const uint8_t A1 = PIN_A1;
static const uint8_t A2 = PIN_A2;
static const uint8_t A3 = PIN_A3;
static const uint8_t A4 = PIN_A4;
static const uint8_t A5 = PIN_A5;
static const uint8_t A6 = PIN_A6;
static const uint8_t A7 = PIN_A7;

fuzzybabybunny:
Any numbers as arguments coming from MQTT that go into the function can’t be used as an array index anyway since they’re char

But I know how those char variables are encoded. They’re ascii. They’re just numbers too. The only difference is that they’re numbers that stand for letters. All those letters, you can treat them as numbers. You just gotta understand how the ascii code works. Google “ASCII table” and check it out. And pay attention to the difference between " " and ’ ’ and remember that between ’ and ’ you can only hit one key on the keyboard. If you can’t type it in one key, it don’t go between ’ and '.

I just happened to have a little learning that I was doing and I had a little fun with a piece of demo code and it’s comments and some python regex and turned a little lesson in char vs. int into something you can just read off the serial monitor. This is the kind of fun you can have when you understand the connection between the ascii codes and real numbers and how the print function treats char variables vs. int variables.

void setup(){

  Serial.begin(9600);
  // clear off the monitor
  for( int x = 0; x < 20; x++){
    Serial.println();
  }

  
  Serial.println(F("ASCII is just number code"));
  Serial.println(F("Like those secret codes from when you were a kid"));
  Serial.println();
  
  Serial.println(F(" \"A\" is a char array of size 2 (A + the null terminator) because of the double quotes "));
  Serial.println(F("Serial.print(\"A\")"));
  Serial.println(F("A")); 
  Serial.println();
  Serial.println();

  Serial.println(F(" 'A' is a char, a signed 8 bit value equal to 65 note the single quotes"));
  Serial.println(F("Serial.print('A')"));
  Serial.println('A');  
  Serial.println();
  Serial.println(F("Lie and tell the compiler 65 is a char and it's the same as above, see it really is just 65"));
  Serial.println(F("Serial.print(char(65));"));
  Serial.println(char(65)); 
  Serial.println();
  Serial.println(F("Let's tell the compiler 'A' is really an int"));
  Serial.println(F("Serial.print(int('A'));  See what I mean?"));
  Serial.println(int('A')); 
  Serial.println();
  Serial.println(F("When we print an int, it converts the digits to ascii"));
  Serial.println(F("Serial.print(65);"));
  Serial.println(65);     
  Serial.println();   
  char buf[45];
  sprintf(buf, "65 as an int %d, but as char %c", 65, 65);
  Serial.println(buf);
  Serial.println();
  sprintf(buf, "'A' as an int %d, but as char %c", 'A', 'A');
  Serial.println(buf);  
  Serial.println();
  Serial.println(F("I can even do math with them"));
  Serial.println(F("Serial.print('0' + '1');"));
  Serial.println('0' + '1');
  Serial.println();
  Serial.println(F("Or convert ascii to numbers"));
  Serial.println(F("char c = '8';"));
  char c = '8';
  Serial.println(F("Serial.print(c);"));
  Serial.println(c);
  Serial.println();
  Serial.println(F("int i = c"));
  
  int i = c;
  Serial.println(F("Serial.print(i);"));
  Serial.println(i);
  Serial.println();
  Serial.println(F("Serial.print(i - '0');"));
  Serial.println(i - '0');
  Serial.println();
  Serial.println(F("A simple conversion from ascii digits to real digits)"));
  Serial.println(F("i = i - '0';"));
  i = i - '0';
  Serial.println(F("And now Serial.print(i);  gives:"));
  Serial.print(i);
  
}

void loop(){}