Nextion Display Button to Control a Relay - Still new to all this

I have a Nextion Display, Photon and a 4 relay board. I want to be able to push a button on the Display and turn on one of the relays and then push again to turn off the relay.I have Particle.Fuction working and I can control the relay from console.particle.io but i have 8 separate functions.

here is the code

#include <Particle_SI7021.h>
#include <ITEADLIB_Nextion.h>
#include <MQTT.h>
#include <math.h>

SI7021 sensor;

//MainPage Page Setup
NexProgressBar j100  = NexProgressBar(0, 8, "j100");
NexText t0 = NexText(0, 1, "t0");
NexText t2 = NexText(0, 9, "t2");
NexNumber n0 = NexNumber(0, 6, "n0");
NexNumber n1 = NexNumber(0, 7, "n1");
NexButton b0 = NexButton(0, 4, "b1");
NexButton b1 = NexButton(0, 5, "b0");

//AC Page Setup
//NexProgressBar j100  = NexProgressBar(0, 3, "j100");
//NexText t0 = NexText(1, 3, "t0");
NexNumber n100 = NexNumber(1, 7, "n100");
NexNumber n101 = NexNumber(1, 8, "n101");
NexButton b100 = NexButton(1, 6, "b100");
NexButton b101 = NexButton(1, 1, "b101");
NexButton b102 = NexButton(1, 2, "b102");

//Setting Page Setup
//NexProgressBar j0  = NexProgressBar(0, 3, "j0");
//NexText t0 = NexText(2, 4, "t0");
NexButton b200 = NexButton(2, 3, "b200");
NexButton b201 = NexButton(2, 1, "b201");
NexButton b202 = NexButton(2, 2, "b202");
NexButton b203 = NexButton(2, 5, "b203");

//Display Page Setup
//NexProgressBar j0  = NexProgressBar(0, 3, "j0");
//NexText t0 = NexText(3, 7, "t0");
NexNumber n300 = NexNumber(3, 3, "n300");
NexText t300 = NexText(3, 4, "t300");
NexButton b300 = NexButton(3, 2, "b300");
NexButton b301 = NexButton(3, 5, "b301");
NexButton b302 = NexButton(3, 2, "b302");

//DateTime Page Setup
//NexProgressBar j0  = NexProgressBar(0, 3, "j0");
//NexText t0 = NexText(4, 3, "t0");
NexNumber n400 = NexNumber(4, 2, "n400");
NexButton b400 = NexButton(4, 1, "b400");

//Relay Page Setup
NexButton b1100 = NexButton(11, 1, "b1100");
NexButton b1101 = NexButton(11, 2, "b1101");
NexButton b1102 = NexButton(11, 3, "b1102");
NexButton b1103 = NexButton(11, 4, "b1103");
NexButton b1104 = NexButton(11, 5, "b1104");
NexButton b1105 = NexButton(11, 6, "b1105");
NexButton b1106 = NexButton(11, 7, "b1106");
NexButton b1107 = NexButton(11, 8, "b1107");

USARTSerial & nexSerial = Serial1;

/*******************************************************************************
 IO mapping
*******************************************************************************/
// A0 : photoresistor
// A5 : power
int photoresistor = A0; // This is where your photoresistor is plugged in. The other side goes to the "power" pin (below).
int power = A5;
int lightlevel;
int acrelay = D3;
int heatrelay = D4;
int fanrelay = D5;
int auxrelay = D6;

// The following values are the read values of the photoresistor
int Closedvalue=5; // This is the average value that the photoresistor reads while in the fridge.
int Openvalue=260; // This is the average value that the photoresistor reads when exposed to average lighting.
int Cutoff=((Closedvalue+Openvalue)/2);

String dspHostname = "hostname";
String dspSSID = "None";
String dspIP = "None";
int temperature = 0;
int humidity = 0;

int ACRelayOn (String command);
int ACRelayOff (String command);
int HeatRelayOn (String command);
int HeatRelayOff (String command);
int FanRelayOn (String command);
int FanRelayOff (String command);
int AuxRelayOn (String command);
int AuxRelayOff (String command);



void setup()   {
    
  
  Serial.begin(115200);
  Serial1.print("baud=115200");
  Serial1.write(0xff);
  Serial1.write(0xff);
  Serial1.write(0xff);
  Serial1.begin(115200);
  Serial.println("Si7021 test");
  Time.zone(-5);

  nexInit();
  sensor.begin();

NexTouch *nex_listen_list[] = {
  &b203,
  &b1100,
  &b1101,
  &b1102,
  &b1103,
  &b1104,
  &b1105,
  &b1106,
  &b1107,
  NULL
};

//Initiize the relay control pings as output
  pinMode(acrelay, OUTPUT);
  pinMode(heatrelay, OUTPUT);
  pinMode(fanrelay, OUTPUT);
  pinMode(auxrelay, OUTPUT);
// Initialize all relays to an OFF state
  digitalWrite(acrelay, LOW);
  digitalWrite(heatrelay, LOW);
  digitalWrite(fanrelay, LOW);
  digitalWrite(auxrelay, LOW);

// photoresistor setup
  pinMode(photoresistor,INPUT);  // Our photoresistor pin is input (reading the photoresistor)
  pinMode(power,OUTPUT); // The pin powering the photoresistor is output (sending out consistent power)
  digitalWrite(power,HIGH);


  WiFi.setHostname(dspHostname);
  
  dspSSID = WiFi.SSID();
  dspIP = WiFi.localIP();
  
  Particle.variable("Office Temperature", &temperature, INT);
  Particle.variable("Office Humidity", &humidity, INT);
  Particle.variable("WiFi SSID", &dspSSID, STRING);
  
  
  Particle.function("TurnOnAC", TurnOnAC);
  Particle.function("TurnOffAC", TurnOffAC);
  Particle.function("TurnOnHeat", TurnOnHeat);
  Particle.function("TurnOffHeat", TurnOffHeat);
  Particle.function("TurnOnFan", TurnOnFan);
  Particle.function("TurnOffFan", TurnOffFan);  
  Particle.function("TurnOnAux", TurnOnAux);
  Particle.function("TurnOffAux", TurnOffAux);   
  
  
  WiFiAccessPoint aps[20];
int found = WiFi.scan(aps, 20);
for (int i=0; i<found; i++) {
    WiFiAccessPoint& ap = aps[i];
    Serial.print("SSID: ");
    Serial.println(ap.ssid);
    Serial.print("Security: ");
    Serial.println(ap.security);
    Serial.print("Channel: ");
    Serial.println(ap.channel);
    Serial.print("RSSI: ");
    Serial.println(ap.rssi);
}


}


void loop() {

//Relay Control




// temperature is an integer in hundredths
  float temperature = sensor.getFahrenheitHundredths();
  temperature = temperature / 100;


// humidity is an integer representing percent
  float humidity = sensor.getHumidityPercent();


  
  WiFiSignal sig = WiFi.RSSI();
  float strength = sig.getStrength();
  
 
  
  //Particle.publish("WiFi SSID", String(dspSSID), 1800, PRIVATE);
  Particle.publish(dspHostname, String(temperature), 120000, PRIVATE);
  Particle.publish(dspHostname, String(humidity), 120000, PRIVATE);
  //Particle.publish("WiFi Strength", String(strength), 1800, PRIVATE);
  Serial.print("Humi: "); Serial.println(humidity);
  Serial.print("Temp: "); Serial.println(temperature);

//Displaying on Nextion
  n0.setValue(temperature);
  n1.setValue(humidity);
  t0.setText(Time.format(Time.now(), TIME_FORMAT_DEFAULT));
  j100.setValue(strength);
  t2.setText(dspHostname + " : " +dspSSID + " : " + dspIP);
  
  //nexLoop(nex_listen_list);

}

/*******************************************************************************

 * Function Name  : getTime
 * Description    : returns the time in the following format: 14:42:31
                    TIME_FORMAT_ISO8601_FULL example: 2016-03-23T14:42:31-04:00
 * Return         : the time
 *******************************************************************************/

String getTime() {
  String timeNow = Time.format(Time.now(), TIME_FORMAT_DEFAULT);
  timeNow = timeNow.substring(11, timeNow.length()-6);
  return " " + timeNow;
}

//Turn on the AC Relay
int TurnOnAC(String command){
    if(command == "TurnOnAC"){
        digitalWrite(acrelay, HIGH);
    return 1;
    }
    else
    {
        digitalWrite(acrelay, LOW);
        return 0;
    }
    return -1;
}

//Turn off the AC Relay
int TurnOffAC(String command){
    if(command == "TurnOffAC"){
        digitalWrite(acrelay, LOW);
    return 1;
    }
    else
    {
        digitalWrite(acrelay, HIGH);
        return 0;
    }
    return -1;
}

//Turn on the Heat Relay
int TurnOnHeat(String command){
    if(command == "TurnOnHeat"){
        digitalWrite(heatrelay, HIGH);
    return 1;
    }
    else
    {
        digitalWrite(heatrelay, LOW);
        return 0;
    }
    return -1;
}

//Turn off the Heat Relay
int TurnOffHeat(String command){
    if(command == "TurnOffHeat"){
        digitalWrite(heatrelay, LOW);
    return 1;
    }
    else
    {
        digitalWrite(heatrelay, HIGH);
        return 0;
    }
    return -1;
}

//Turn on the Fan Realy
int TurnOnFan(String command){
    if(command == "TurnOnFan"){
        digitalWrite(fanrelay, HIGH);
    return 1;
    }
    else
    {
        digitalWrite(fanrelay, LOW);
        return 0;
    }
    return -1;
}

//Turn off the Fan Realy
int TurnOffFan(String command){
    if(command == "TurnOffFan"){
        digitalWrite(fanrelay, LOW);
    return 1;
    }
    else
    {
        digitalWrite(fanrelay, HIGH);
        return 0;
    }
    return -1;
}

//Turn on the Aux Relay
int TurnOnAux(String command){
    if(command == "TurnOnAux"){
        digitalWrite(auxrelay, HIGH);
    return 1;
    }
    else
    {
        digitalWrite(auxrelay, LOW);
        return 0;
    }
    return -1;
}

//Turn off the Aux Relay
int TurnOffAux(String command){
    if(command == "TurnOffAux"){
        digitalWrite(auxrelay, LOW);
    return 1;
    }
    else
    {
        digitalWrite(auxrelay, HIGH);
        return 0;
    }
    return -1;
}
1 Like

You are stating what you have, what you intend and show some code, but what are you “asking” for?

Sorry about that I want to be able to push one of the buttons that I have built on the Nextion they are NexButton b1100 - NexButton b1107. I would like to be able to push NexButton b1100 and turn on acrelay and push b1101 and turn off acrelay. etc so on and so forth

What is working and what not.

You should rewrite your object creations from

NexProgressBar j100  = NexProgressBar(0, 8, "j100");

to this

NexProgressBar j100(0, 8, "j100");

In order to understand how Nextion handles notifications you should try some of the library examples and study the code.
In a nutshell, you also need to set the release event in Nextion Editor to trigger the Send component ID message

And in your code you need to hook-up a handler for that message like shown in this example
https://build.particle.io/libs/ITEADLIB_Nextion/0.0.16/tab/example/CompButton.ino

Also your nex_listen_list[] mustnot be local to setup() but global and you must call nexLoop(nex_listen_list) for it to work - although you only needs to list the components that actually do produce events you want to subscribe to.

That’s all in the example.

For your plathora of Particle.function() blocks since all your functions expect a parameter that’s nothing else than the function name, why would you not go with a single particle.function() that handles them all?

Like this

  Particle.function("control", controlRelay);
...

int controlRelay(String cmd) {
  int retVal = -1;

  if (cmd == "TurnOnAC") {
    digitalWrite(acrelay, HIGH);
    retVal = 11; // relay 1 on
  }
  else if (cmd == "TurnOffAC") {
     digitalWrite(acrelay, LOW);
     retVal = 10; // relay 1 off
  else if(cmd == "TurnOnHeat") {
    digitalWrite(heatrelay, HIGH);
    return 21; // relay 2 on
  }
  else if(cmd == "TurnOffHeat"){
    digitalWrite(heatrelay, LOW);
    retVal = 20; // relay 2 off
  }
  else if(cmd == "TurnOnFan") {
    digitalWrite(fanrelay, HIGH);
    retVal = 31; // relay 3 on
  }
  else if(cmd == "TurnOffFan") {
    digitalWrite(fanrelay, LOW);
    retVal = 30; // relay 3 off
  }
  else if(cmd == "TurnOnAux"){
    digitalWrite(auxrelay, HIGH);
    retVal = 41; // relay 4 on
  }
  else if(cmd == "TurnOffAux") {
    digitalWrite(auxrelay, LOW);
    retVal = 40; // relay 4 off
  }

  return retVal;
}

But I’d go even further and reduce that funtion even more by replacing that if() ... else if() ... “tree” into a loop which traverses an array of commands and additional parameters.

1 Like

cause i wasnt sure how to do that just yet. i followed an example of a single relay and adapted for 4 i know it ugly and overkill. but i was happy to actually be able to talk to the relay in any way “remotely” i figured out how to turn it on per in the void loop

This would be a more streamlined version

struct cmd_t {
  char cmd[16];
  int  pin;
  int  state;
};

cmd_t cmdList[] = 
{ { "ACRelayOn"   , acrelay  , HIGH }
, { "ACRelayOff"  , acrelay  , LOW  }
, { "HeatRelayOn" , heatrelay, HIGH }
, { "HeatRelayOff", heatrelay, LOW  }
, { "FanRelayOn"  , fanrelay , HIGH }
, { "FanRelayOff" , fanrelay , LOW  }
, { "AuxRelayOn"  , auxrelay , HIGH }
, { "AuxRelayOff" , auxrelay , LOW  }
};
const int cmdCount = sizeof(cmdList) / sizeof(cmdList[0]); // calculate number of commands

int controlRelay(const char* cmd) {
  retVal = -1;
  for (int i = 0; i < cmdCount; i++) {
    if (strcmp(cmd, cmdList[i].cmd) == 0) { // when cmd matches a command in the list
      digitalWrite(cmdList[i].pin, cmdList[i].state);
      retVal = cmdList[i].pin * 10 + cmdList[i].state);
      break;
    }
  }
  return retVal;
}

This is also flexible when you want to add more commands, you just expand the cmdList[] and the rest of the code will act just the same.

4 Likes

Thanks this is working perfectly.

I have tested the both CompButton.ino and HMI they both work perfectly. I am having trouble implementing into my project. I am still trying to figure it out but if I cannot I will ask for more help. If that is okay?

1 Like

Sure, any time.

I’ve been messing with this for a week now lol and I’m no closer to why its not working. I am trying to get button b1101 working. It’s on the 8th page of my hmi.

#include <Particle_SI7021.h>
#include <Nextion.h>
#include <MQTT.h>
#include <math.h>
USARTSerial& nexSerial = Serial1;

STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));

SI7021 sensor;



//MainPage Page Setup
NexProgressBar j100(0, 8, "j100");
NexText t0(0, 1, "t0");
NexText t2(0, 9, "t2");
NexText t3(0, 11, "t3");
NexNumber n0(0, 6, "n0");
NexNumber n1(0, 7, "n1");
//NexButton b0 = NexButton(0, 4, "b1");
//NexButton b1 = NexButton(0, 5, "b0");

//AC Page Setup
//NexProgressBar j100  = NexProgressBar(0, 3, "j100");
//NexText t0 = NexText(1, 3, "t0");
NexNumber n100(1, 7, "n100");
NexNumber n101(1, 8, "n101");
//NexButton b100 = NexButton(1, 6, "b100");
//NexButton b101 = NexButton(1, 1, "b101");
//NexButton b102 = NexButton(1, 2, "b102");
NexButton m100(1, 9, "m100");
NexButton m101(1, 10, "m101");

//Setting Page Setup
//NexProgressBar j0  = NexProgressBar(0, 3, "j0");
//NexText t0 = NexText(2, 4, "t0");
//NexButton b200 = NexButton(2, 3, "b200");
//NexButton b201 = NexButton(2, 1, "b201");
//NexButton b202 = NexButton(2, 2, "b202");
//NexButton b203 = NexButton(2, 5, "b203");

//Display Page Setup
//NexProgressBar j0  = NexProgressBar(0, 3, "j0");
//NexText t0 = NexText(3, 7, "t0");
NexNumber n300(3, 3, "n300");
NexText t300(3, 4, "t300");
//NexButton b300 = NexButton(3, 2, "b300");
//NexButton b301 = NexButton(3, 5, "b301");
//NexButton b302 = NexButton(3, 2, "b302");

//DateTime Page Setup
//NexProgressBar j0  = NexProgressBar(0, 3, "j0");
//NexText t0 = NexText(4, 3, "t0");
NexNumber n400(4, 2, "n400");
//NexButton b400 = NexButton(4, 1, "b400");

//Relay Page Setup
NexButton b1101(8, 2, "b1101");




NexTouch *nex_listen_list[] =
{
  //&b203,
  &m100,
  &m101,
  &b1101,
  NULL
};

/*******************************************************************************
 IO mapping
*******************************************************************************/
// A0 : photoresistor
// A5 : power
int photoresistor = A0; // This is where your photoresistor is plugged in. The other side goes to the "power" pin (below).
int power = A5;
int lightlevel;
int acrelay = D3;
int heatrelay = D4;
int fanrelay = D5;
int auxrelay = D6;

// The following values are the read values of the photoresistor
int Closedvalue=5; // This is the average value that the photoresistor reads while in the fridge.
int Openvalue=260; // This is the average value that the photoresistor reads when exposed to average lighting.
int Cutoff=((Closedvalue+Openvalue)/2);

String dspHostname = "pcns-base-ac";
String dspSSID = "None";
String dspIP = "None";
int temperature = 0;
int humidity = 0;
//int setTemp;
int ACRelayOn (String command);
int ACRelayOff (String command);
int HeatRelayOn (String command);
int HeatRelayOff (String command);
int FanRelayOn (String command);
int FanRelayOff (String command);
int AuxRelayOn (String command);
int AuxRelayOff (String command);

retained float setTemp;



void b1101PopCallback(void *ptr) {
    Serial.println("I've been pushed!!!");
    digitalWrite(acrelay, HIGH);
}


void remoteTemp(const char *event, const char *data)
{
  Serial.print("Remote Temp: ");
  Serial.println(data);
  int myValue = atoi(data);
  n101.setValue(myValue);
}

void setup(void)   {
    
  Particle.subscribe("Office-Temperature", remoteTemp, MY_DEVICES);
  
  
  //Serial.begin(115200);
  //Serial1.print("baud=115200");
  Serial1.write(0xff);
  Serial1.write(0xff);
  Serial1.write(0xff);
  //Serial1.begin(9600);
  Serial.println("Si7021 test");
  Time.zone(-5);

  sensor.begin();

  nexInit();


  b1101.attachPop(b1101PopCallback, &b1101);


//Initiize the relay control pings as output
  pinMode(acrelay, OUTPUT);
  pinMode(heatrelay, OUTPUT);
  pinMode(fanrelay, OUTPUT);
  pinMode(auxrelay, OUTPUT);
// Initialize all relays to an OFF state
  digitalWrite(acrelay, LOW);
  digitalWrite(heatrelay, LOW);
  digitalWrite(fanrelay, LOW);
  digitalWrite(auxrelay, LOW);

// photoresistor setup
  pinMode(photoresistor,INPUT);  // Our photoresistor pin is input (reading the photoresistor)
  pinMode(power,OUTPUT); // The pin powering the photoresistor is output (sending out consistent power)
  digitalWrite(power,HIGH);


  WiFi.setHostname(dspHostname);
  
  dspSSID = WiFi.SSID();
  dspIP = WiFi.localIP();
  
  Particle.variable("Base Temperature", &temperature, INT);
  Particle.variable("Base Humidity", &humidity, INT);
  Particle.variable("WiFi SSID", &dspSSID, STRING);
  
  
  Particle.function("Control", controlRelay);
 
  
  
  WiFiAccessPoint aps[40];
int found = WiFi.scan(aps, 40);
for (int i=0; i<found; i++) {
    WiFiAccessPoint& ap = aps[i];
    Serial.print("SSID: "); Serial.println(ap.ssid); //Serial.print(" -- "); 
    //Serial.print("Security: "); Serial.print(ap.security); Serial.print(" -- ");
    //Serial.print("Channel: "); Serial.print(ap.channel); Serial.print(" -- ");
    //Serial.print("RSSI: "); Serial.println(ap.rssi);
}



}


void loop(void) {

  nexLoop(nex_listen_list);



// temperature is an integer in hundredths
  float temperature = sensor.getFahrenheitHundredths();
  temperature = temperature / 100;


// humidity is an integer representing percent
  float humidity = sensor.getHumidityPercent();


  
  WiFiSignal sig = WiFi.RSSI();
  float strength = sig.getStrength();
  
 
  
  //Particle.publish("WiFi SSID", String(dspSSID), 1800, PRIVATE);
  Particle.publish("Base-Temperature", String::format("%.2f",temperature), 120000, PRIVATE);
  Particle.publish("Base-Humidity", String::format("%.2f",humidity), 120000, PRIVATE);
  //Particle.publish("WiFi Strength", String(strength), 1800, PRIVATE);
  //Serial.print("Humi: "); Serial.println(humidity);
  Serial.print("Temp: "); Serial.println(temperature);
  //Serial.print("Remote Temp: "); Serial.println(data);

  
//Displaying on Nextion
  n0.setValue(temperature);
  n1.setValue(humidity);
  n100.setValue(temperature);
  t0.setText(Time.format(Time.now(), TIME_FORMAT_DEFAULT));
  j100.setValue(strength);
  t2.setText(dspHostname);
  t3.setText(dspSSID + " : " + dspIP);


}

/*******************************************************************************

 * Function Name  : getTime
 * Description    : returns the time in the following format: 14:42:31
                    TIME_FORMAT_ISO8601_FULL example: 2016-03-23T14:42:31-04:00
 * Return         : the time
 *******************************************************************************/

String getTime() {
  String timeNow = Time.format(Time.now(), TIME_FORMAT_DEFAULT);
  timeNow = timeNow.substring(11, timeNow.length()-6);
  return " " + timeNow;
}


//Controlling Relays from Particle Web Console
struct cmd_t {
  char cmd[16];
  int  pin;
  int  state;
};

cmd_t cmdList[] = 
{ { "AC-On"   , acrelay  , HIGH }
, { "AC-Off"  , acrelay  , LOW  }
, { "Heat-On" , heatrelay, HIGH }
, { "Heat-Off", heatrelay, LOW  }
, { "Fan-On"  , fanrelay , HIGH }
, { "Fan-Off" , fanrelay , LOW  }
, { "Aux-On"  , auxrelay , HIGH }
, { "Aux-Off" , auxrelay , LOW  }
};
const int cmdCount = sizeof(cmdList) / sizeof(cmdList[0]); // calculate number of commands

int controlRelay(const char* cmd) {
  int retVal = -1;
  for (int i = 0; i < cmdCount; i++) {
    if (strcmp(cmd, cmdList[i].cmd) == 0) { // when cmd matches a command in the list
      digitalWrite(cmdList[i].pin, cmdList[i].state);
      retVal = (cmdList[i].pin * 10 + cmdList[i].state);
      break;
    }
  }
  return retVal;
}

What exactly is not working?
One issue I see is that you have two Particle.publish() calls but no way to prevent that from running into the rate limit (avg. 1/s with a burst of 4 with 4sec cool-down).
I’d also pull both publishes together into one.

void loop(void) {
  static uint32_t ms = 0;
  char   data[64];
  nexLoop(nex_listen_list);
  if (millis() - ms < 1000) return; // adhere rate limit
  ms = millis();
  ...
  snprintf(data, sizeof(data), "T=%.2f; H=%.2f", temperature, humidity);
  Particle.publish("Base", data, PRIVATE); // payload is ignored anyway
  ...
}

Also your Particle.variable() commands violate the rules:

  • max 12. characters
  • no blanks (and special characters)
  • your syntax is deprecated, you should rather use Particle.variable("WiFiSSID", dspSSID);

The Particle.publish and variable work perfectly sorry.

I and having issues getting a button release to register with the photon. When I run the example it works fine but when I convert to my own code and add it I am not receiving anything.

This is awesome!!!!! it works!!!! Thank you so much

When my solution works it at least suggests that I may be have been right in a way with my assessment that the publish rate limit violation may have contributed to your original issue.
Even if it wasn't the cause it was at least a clear indicator that something must be impeding the expected flow of loop() as there is no way you could call Particle.publish() at the anticipated rate and not run into the limit.

It could be that the SI7021 library was slowing down your loop() in a way that the publishes didn't fail but the nexLoop() calls were impacted instead.

1 Like

Hi @ScruffR,
I like this code snippet for control of my relays with the particle cloud function. I noticed your version and @mbrew345 are very slightly different. I get an error when compiling. Here’s my version, very similar; any ideas?

I get an error when compiling a function-definition is not allowed here before ‘{’ token at the line

int controlRelay(const char* cmd) {

The full section is located in my void setup;

    Particle.function("Control", controlRelay);
    
    int RELAY1_ON  (String command);
    int RELAY1_OFF (String command);
    int RELAY2_ON  (String command);
    int RELAY2_OFF (String command);   
    int RELAY3_ON  (String command);
    int RELAY3_OFF (String command); 
    int RELAY4_ON  (String command);
    int RELAY4_OFF (String command); 

    struct cmd_t {
    char cmd[16];
    int  pin;
    int  state;
    };

    cmd_t cmdList[] = 
    { { "RELAY1_ON"   , relay1  , HIGH }
    , { "RELAY1_OFF"  , relay1  , LOW  }
    , { "RELAY2_ON"   , relay2  , HIGH }
    , { "RELAY2_OFF"  , relay2  , LOW  }
    , { "RELAY3_ON"   , relay3  , HIGH }
    , { "RELAY3_OFF"  , relay3  , LOW  }
    , { "RELAY4_ON"   , relay4  , HIGH }
    , { "RELAY4_OFF"  , relay4  , LOW  }
    };
    
    const int cmdCount = sizeof(cmdList) / sizeof(cmdList[0]); // calculate number of commands

    int controlRelay(const char* cmd) {
      int retVal = -1;
      for (int i = 0; i < cmdCount; i++) {
        if (strcmp(cmd, cmdList[i].cmd) == 0) { // when cmd matches a command in the list
          digitalWrite(cmdList[i].pin, cmdList[i].state);
          retVal = (cmdList[i].pin * 10 + cmdList[i].state);
          break;
        }
      }
      return retVal;

Correction This is in void setup() , rest is in void loop()

    Particle.function("Control", controlRelay);

    int RELAY1_ON  (String command);
    int RELAY1_OFF (String command);
    int RELAY2_ON  (String command);
    int RELAY2_OFF (String command);   
    int RELAY3_ON  (String command);
    int RELAY3_OFF (String command); 
    int RELAY4_ON  (String command);
    int RELAY4_OFF (String command); ```

Have you got the full code and error message (SHOW RAW in WebIDE) too?

However, your function prototypes and function definition are not supposed to be inside setup() but your Particle.function() call has to be - so that’s probably the cause.

OK, Let me see if I can boil it down to just this aspect. The full code is a monster.

Something to do with cmdCount looks defined at

 const int cmdCount = sizeof(cmdList) / sizeof(cmdList[0]); // calculate number of commands```

Raw Error

2022_10_22_part-functions.ino:51:31: warning: unused variable 'cmdCount' [-Wunused-variable]
   51 |                     const int cmdCount = sizeof(cmdList) / sizeof(cmdList[0]); // calculate number of commands
      |                               ^~~~~~~~
make[2]: *** [../build/target/user/platform-13-m2022_10_22_part-functions.o] Error 1
make[2]: Leaving directory `/firmware/user'
make[1]: *** [user] Error 2
make[1]: Leaving directory `/firmware/modules/boron/user-part'
make: *** [modules/boron/user-part] Error 2

Full Code - Boiled down to just this function

//Relays

int relay1 = D3; // RELAY1 is connected to D3
int relay2 = D2; // RELAY2 is connected to D2
int relay3 = D1; // RELAY3 is connected to D1
int relay4 = D0; // RELAY4 is connected to D0

int RELAY1_ON  (String command);
int RELAY1_OFF (String command);
int RELAY2_ON  (String command);
int RELAY2_OFF (String command);   
int RELAY3_ON  (String command);
int RELAY3_OFF (String command); 
int RELAY4_ON  (String command);
int RELAY4_OFF (String command); 

   
void setup() {

Particle.function("Control", controlRelay);
    
pinMode(relay1, OUTPUT);
pinMode(relay2, OUTPUT);
pinMode(relay3, OUTPUT);
pinMode(relay4, OUTPUT);

digitalWrite(relay1, LOW); //set all relays LOW on Startup
digitalWrite(relay2, LOW);
digitalWrite(relay3, HIGH);
digitalWrite(relay4, LOW);
}

void loop() {

                    struct cmd_t {
                    char cmd[16];
                    int  pin;
                    int  state;
                    };

                    cmd_t cmdList[] = 
                    { { "RELAY1_ON"   , relay1  , HIGH }
                    , { "RELAY1_OFF"  , relay1  , LOW  }
                    , { "RELAY2_ON"   , relay2  , HIGH }
                    , { "RELAY2_OFF"  , relay2  , LOW  }
                    , { "RELAY3_ON"   , relay3  , HIGH }
                    , { "RELAY3_OFF"  , relay3  , LOW  }
                    , { "RELAY4_ON"   , relay4  , HIGH }
                    , { "RELAY4_OFF"  , relay4  , LOW  }
                    };
                    const int cmdCount = sizeof(cmdList) / sizeof(cmdList[0]); // calculate number of commands

                    int controlRelay(const char* cmd) {
                    int retVal = -1;
                    for (int i = 0; i < cmdCount; i++) {
                        if (strcmp(cmd, cmdList[i].cmd) == 0) { // when cmd matches a command in the list
                        digitalWrite(cmdList[i].pin, cmdList[i].state);
                        retVal = cmdList[i].pin * 10 + cmdList[i].state);
                        break;
                        }
                    }
                    return retVal;
                    }
}```

Got it to work, here for anyone else who comes down this path. This compiles;

//Relays

int relay1 = D3; // RELAY1 is connected to D3
int relay2 = D2; // RELAY2 is connected to D2
int relay3 = D1; // RELAY3 is connected to D1
int relay4 = D0; // RELAY4 is connected to D0

int RELAY1_ON  (String command);
int RELAY1_OFF (String command);
int RELAY2_ON  (String command);
int RELAY2_OFF (String command);   
int RELAY3_ON  (String command);
int RELAY3_OFF (String command); 
int RELAY4_ON  (String command);
int RELAY4_OFF (String command); 

   
void setup() {

Particle.function("Control", controlRelay);
    
pinMode(relay1, OUTPUT);
pinMode(relay2, OUTPUT);
pinMode(relay3, OUTPUT);
pinMode(relay4, OUTPUT);

digitalWrite(relay1, LOW); //set all relays LOW on Startup
digitalWrite(relay2, LOW);
digitalWrite(relay3, HIGH);
digitalWrite(relay4, LOW);
}

void loop() {

        //Do stuff          
                    
}

  struct cmd_t {
                    char cmd[16];
                    int  pin;
                    int  state;
                    };

                    cmd_t cmdList[] = 
                    { { "RELAY1_ON"   , relay1  , HIGH }
                    , { "RELAY1_OFF"  , relay1  , LOW  }
                    , { "RELAY2_ON"   , relay2  , HIGH }
                    , { "RELAY2_OFF"  , relay2  , LOW  }
                    , { "RELAY3_ON"   , relay3  , HIGH }
                    , { "RELAY3_OFF"  , relay3  , LOW  }
                    , { "RELAY4_ON"   , relay4  , HIGH }
                    , { "RELAY4_OFF"  , relay4  , LOW  }
                    };

const int cmdCount = sizeof(cmdList) / sizeof(cmdList[0]); // calculate number of commands

int controlRelay(const char* cmd) {
  int retVal = -1;
  for (int i = 0; i < cmdCount; i++) {
    if (strcmp(cmd, cmdList[i].cmd) == 0) { // when cmd matches a command in the list
      digitalWrite(cmdList[i].pin, cmdList[i].state);
      retVal = (cmdList[i].pin * 10 + cmdList[i].state);
      break;
    }
  }
  return retVal;
}

This is only a warning which would by no means prevent a successful compile, so the error must have been something different (which is not shown in the error list)

However, glad you got it to compile eventually :+1:

Your later code makes much more sense, since in the previous version you had all the items that should have been globally defined localized in loop() where they'd immediately disappear and hence lose their previous values whenever loop() finished.

Particularly controlRelay() would not be available for the Particle.function() callback that you register in setup().