Problem with "is not captured" error on compile

Hi everyone! Could someone take a look and help me with this error? I’m still pretty new to this programming thing, and this has me scratching my head. I understand that rID is not being passed into server.on, but I’m not sure how to fix it in this case; I’m sure that I’m just missing something simple… Thanks!

Error:
exit status 1
‘rID’ is not captured

void initializeRelay (relay& rID) {
  server.on("/relay" + String(rID.relayID) + "_on", [] (){
    rID.setRelay(1);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay" + String(rID.relayID) + "_off", [] (){
    rID.setRelay(0);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay" + String(rID.relayID) + "_status", [] (){
    server.send(200, "text/plain", int(rID.getRelay()));
  });
  rID.setState(rID.defaultState);
}

Should your function definition be declaring a pointer to a relay?

initializeRelay (relay* rID)

How do you call it?

I’m afraid I do not know. I’m not sure the difference (Google is the only reason I’ve gotten this far, lmao).

I tried your suggestion and ended up with the following error:

"cannot convert ‘relay’ to ‘relay*’ for argument ‘1’ to ‘void initializeRelay(relay*)’

I call it like so:

initializeRelay(relay1);

(where relay1 is an object of a class)

I think I might be on the right track with this? Getting a new error with it.

void initializeRelay (relay& rID) {
  [&, rID] server.on("/relay" + String(rID.relayID) + "_on", [] (){
    rID.setRelay(1);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  [&, rID] server.on("/relay" + String(rID.relayID) + "_off", [] (){
    rID.setRelay(0);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  [&, rID] server.on("/relay" + String(rID.relayID) + "_status", [] (){
    server.send(200, "text/plain", int(rID.getRelay()));
  });
  rID.setState(rID.defaultState);
}

Error:
expected ‘{’ before ‘server’

Try declaring your function this way

void initializeRelay (class relay& rID) {

[&, rID] server.on will not help - your initial code was better

would be good to see the full code as well to understand where all the things come from

what is server?

Getting the original error with that.

'rID' is not captured

can you post your full source code?

looks like you are trying to use Lambda expressions which provide the flexibility of function pointers (Lambdas in the relatively recent C++11 standard are a way of treating functions as objects and can be used where a function requires a function as one of its parameters)

so when you do

  server.on("/relay" + String(rID.relayID) + "_on", [] (){
    rID.setRelay(1);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });

you are trying to pass the function

{
    rID.setRelay(1);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  }

as a parameter of the on method of the server object

but just before that you do

[] ()

so your lambda does not get any local variable and does not have access to rID when you try to execute the function.

You need to pass the necessary parameters in the parenthesis

[] (your params for the function goes here)

The full source is included at the end of this post.

I’ve tried tinkering with your suggestion a few times, but I’m continuing to get similar errors. This is the closest I was able to get:

void initializeRelay (relay& rID) {
  server.on("/relay" + String(rID.relayID) + "_on", [&, rID] (){
    rID.setRelay(1);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay" + String(rID.relayID) + "_off", [&, rID] (){
    rID.setRelay(0);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay" + String(rID.relayID) + "_status", [&, rID] (){
    server.send(200, "text/plain", int(rID.getRelay()));
  });
  rID.setState(rID.defaultState);
}

I’m getting this error:

passing ‘const relay’ as ‘this’ argument of ‘void relay::setRelay(int)’ discards qualifiers [-fpermissive]

Here is the source (sorry if its a bit lengthy and a bit of a mess). I omitted irrelevant functions as it is well over the character limit to post. This is honestly the first thing I’ve programmed in C++, I have a background in PowerShell, VBS, and Bash, but not really anything outside of scripting, so I apologize if anything doesn’t make sense.

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <TimeLib.h>
#include <WiFiUdp.h>
#define ONE_WIRE_BUS 14 //Data wire on physical pin 0

class relay { //Define relay class 
  public:
    int relayID;
    String relayName;
    int pinID;
    int stateDefault;
    int scheduleOn;
    int scheduleOff;
    //Enable or disable the relay. A low pin enables the relay in this case.
    void setRelay(int relayState) {
      if (relayState == 1) {
        digitalWrite(pinID, LOW);
      }
      else if (relayState == 0) {
        digitalWrite(pinID, HIGH);
      }
    }
    
    //Return the relay status. A low pin means that the relay is enabled.
    bool getRelay() {
      if (digitalRead(pinID) == 0) {
        return 1;
      }
      else if (digitalRead(pinID) == 1) {
        return 0;
      }
    }
    
    //getRelayCurrentState and encode the results into html
    String getCurrentStateHTML() {
      if (getRelay() == 1){
        return "<statusON>ON</statusON>";
      }
      else if (getRelay()  == 0){
        return "<statusOFF>OFF</statusOFF>";
      }
    }
    
    //Initial relay creation
    relay(int rID, String rName, int rPin, int sDefault, int schOn, int schOff){

      relayID = rID;
      relayName = rName;
      pinID = rPin;
      stateDefault = sDefault;
      scheduleOn = schOn;
      scheduleOff = schOff;

      pinMode(pinID, OUTPUT); //Enable pin as output
      digitalWrite(pinID, HIGH); //Initial open relay
    }
  
}; //End relay class definition

//Create relay objects
relay relay1(1, "Basking Lamp", 5, 0, 1000, 1900);
relay relay2(2, "UV Lamp", 4, 1, NULL, NULL);
relay relay3(3, "Pump", 0, 1, NULL, NULL);
relay relay4(4, "Siphon Starter", 2, 0, NULL, NULL);

int siphonMonPin = 12; //Define siphon pin

int siphonAutoTime; //Stores the time that the siphon starter was automatically enabled.

//TEMPORARY define SSID/PSK
const char* ssid     = "106NET";
const char* password = "B1u3b3RR!3s69";

//Initialize temperature sensor
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
 
ESP8266WebServer server(80);


// NTP Servers:
//static const char ntpServerName[] = "us.pool.ntp.org";
static const char ntpServerName[] = "time.nist.gov";
//static const char ntpServerName[] = "time-a.timefreq.bldrdoc.gov";
//static const char ntpServerName[] = "time-b.timefreq.bldrdoc.gov";
//static const char ntpServerName[] = "time-c.timefreq.bldrdoc.gov";

//const int timeZone = 1;     // Central European Time
const int timeZone = -5;  // Eastern Standard Time (USA)
//const int timeZone = -4;  // Eastern Daylight Time (USA)
//const int timeZone = -8;  // Pacific Standard Time (USA)
//const int timeZone = -7;  // Pacific Daylight Time (USA)


WiFiUDP Udp;
unsigned int localPort = 8888;  // local port to listen for UDP packets

time_t getNtpTime();

int currentTime; //Stores the current time

void setup(){
    
  Serial.begin(115200);  // Serial connection start

  // Connect to WiFi network
  WiFi.begin(ssid, password);
  Serial.print("\n\r \n\rWorking to connect");
 
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  
  server.on("/", handleRoot);
  
  server.on("/temp", [](){ 
    String temp_f = String(getTemperature());       // read sensor
    server.send(200, "text/plain", temp_f);
  });

  //Initialize Relays
  initializeRelay(relay1);
  initializeRelay(relay2);
  initializeRelay(relay3);
  initializeRelay(relay4);

  pinMode(siphonMonPin, INPUT); //Enable siphon sonitor pin

  sensors.begin(); // initialize temperature sensor  

  server.begin(); //Start web server
  Serial.println("HTTP server started");

  Udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(Udp.localPort());
  Serial.println("waiting for sync");
  setSyncProvider(getNtpTime);
  setSyncInterval(300);

}
 
void loop()
{
  server.handleClient();
  updateTime();
  checkSiphon(relay3, relay4);
  checkSchedule(relay1);
  checkSchedule(relay2);
  checkSchedule(relay3);
  checkSchedule(relay4);
}  

void initializeRelay (relay& rID) {
  server.on("/relay" + String(rID.relayID) + "_on", [&, rID] (){
    rID.setRelay(1);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay" + String(rID.relayID) + "_off", [&, rID] (){
    rID.setRelay(0);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay" + String(rID.relayID) + "_status", [&, rID] (){
    server.send(200, "text/plain", int(rID.getRelay()));
  });
  rID.setState(rID.defaultState);
}

well you write a lambda as

[]  ()  mutable throw() -> type {lambda body}

the [b][][/b] is the lambda-introducer in the C++ specification for the capture clause
the [b]()[/b] is the lambda declarator - basically the parameter list for the function. It’s Optional

** **mutable** **
specification Optional.
[b]throw()[/b] is the exception-specification what to do if there is an exception. It’s Optional.

** **-> type** **
is the trailing-return-type Optional.
[b]{lambda body}[/b] is the function you want to declare and pass as parameter

A lambda can access—or capture–variables from the surrounding scope.

A lambda begins with the capture clause, which specifies which variables are captured, and whether the capture is by value or by reference.

Variables that have the ampersand ‘&’ prefix are accessed by reference
Variables that do not have it are accessed by value.

An empty capture clause, , indicates that the body of the lambda expression accesses no variables in the enclosing scope.

You can use the default capture mode to indicate how to capture any outside variables that are referenced in the lambda: [&] means all variables that you refer to are captured by reference, and [=] means they are captured by value. You can use a default capture mode, and then specify the opposite mode explicitly for specific variables

So if that is clear, then when you write

  server.on("/temp", [](){ 
    String temp_f = String(getTemperature());       // read sensor
    server.send(200, "text/plain", temp_f);
  });

you are telling the server: if you get a request for /temp in the URL, then execute a function that takes no parameters and does not need any environment variable. (that’s the []() part)

When you do

  server.on("/relay" + String(rID.relayID) + "_on", [&, rID] (){
    rID.setRelay(1);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });

(let’s assume String(rID.relayID) is “05”)

you are instructing the server to listen for “/relay05_on” in the URL and in that case execute the function.

Challenge is that your function needs access to the variable rID, so you need to give that one in the capture clause.

with
** **[&, rID]** **
you are instructing the compiler to pass everything by reference, except rID by value.

if you pass rID by value, you hand over a copy of the object in the function, so whatever you do there does not really affect the real original object.

so I would give it a try by just having [&] so that rID is passed by reference.

i can’t really try this at the moment, so just trying to explain how I would think about it.

Thank you for the comprehensive description! I can see how the [&] would make sense in this case, but unfortunately it did not work. I’ve tried three different combinations:

server.on("/relay" + String(rID.relayID) + "_on", [&] (){
    rID.setRelay(1);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });

and then I tried isolating just the rID

server.on("/relay" + String(rID.relayID) + "_on", [&rID] (){
    rID.setRelay(1);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });

both produce the same error:
no matching function for call to ‘ESP8266WebServer::on(StringSumHelper&, initializeRelay(relay&)::__lambda1)’

and I tried this, but I see that I’m just passing the same thing as if I were to have rID (like before) since both pass the value not the reference.

server.on("/relay" + String(rID.relayID) + "_on", [=] (){
    rID.setRelay(1);
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });

produces the same error as before:
passing ‘const relay’ as ‘this’ argument of ‘void relay::setRelay(int)’ discards qualifiers. [-fpermissive]

I might have another idea on why this does not work:

when you call your initializeRelay you pass an argument rID (indeed by reference) but rID is a local variable to that function which scope is limited to the lifetime of the function.

I would suggest 2 things to test:

test 1: remove the call to initializeRelay and paste the code of initializeRelay directly into your setup function after rID initialization.

test2: drop the use of lambda and in your code declare a few functions

  void handleRoot() {
    Serial.println("HTTP GET /"); // assuming Serial is connected to your computer
    String message = "<html><body><h1>Welcome!</h1>";
    server.send(200, "text/html", message);
  }

  void handleLedOn() {
    Serial.println("HTTP /on -> Relay on"); // assuming Serial is connected to your computer
   // do something
    server.send(200, "text/html", "<html><body><h1>Relay On !</h1></html>");
  }

  void handleLedOff() {
    Serial.println("HTTP /on -> Relay off"); // assuming Serial is connected to your computer
   // do something
    server.send(200, "text/html", "<html><body><h1>Relay Off !</h1></html>");
  }

  void handleNotFound(){
    server.send(404, "text/html", "<html><body><h1> Sorry I don't understand!</h1></body></html>");
  }

and in your setup() after activating the IP connection

    // Scanning HTTP requests
    server.on("/", handleRoot);
    server.on("/on", handleLedOn);
    server.on("/off", handleLedOff);
    server.onNotFound(handleNotFound);
    server.begin();
    Serial.println("HTTP server starter"); // assuming Serial is connected to your computer

and test if everything works with this already

Sorry for the delay in getting back to you.

I ended getting this to work by simply writing out each send.on manually in the setup (see below). Its not ideal, because I’m repeating a lot of code, which I know is a no-no, but for now it is working as I would expect. I’m working on some other aspects of the project now and I don’t want this to hold everything else up, so I’ll keep tinkering around with this after everything else is done. I really appreciate all of your help on this, though!

//Relay power controls
  server.on("/relay1_on", [] (){
    relay1.setRelay(1);
    relay1.manualState = 1;
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay1_off", [] (){
    relay1.setRelay(0);
    relay1.manualState = 0;
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay1_status", [] (){
    server.send(200, "text/plain", String(relay1.getRelay()));
  });
  server.on("/relay2_on", [] () {
    relay2.setRelay(1);
    relay2.manualState = 1;
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay2_off", [] () {
    relay2.setRelay(0);
    relay2.manualState = 0;
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay2_status", [] (){
    server.send(200, "text/plain", String(relay2.getRelay()));
  });
  server.on("/relay3_on", [] () {
    relay3.setRelay(1);
    relay3.manualState = 1;
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay3_off", [] () {
    relay3.setRelay(0);
    relay3.manualState = 0;
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay3_status", [] (){
    server.send(200, "text/plain", String(relay3.getRelay()));
  });
  server.on("/relay4_on", [] () {
    relay4.setRelay(1);
    relay4.manualState = 1;
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay4_off", [] () {
    relay4.setRelay(0);
    relay4.manualState = 0;
    server.send(200, "html", "<head><meta http-equiv=\"refresh\" content=\"0; url=/\" /></head>");
  });
  server.on("/relay4_status", [] (){
    server.send(200, "text/plain", String(relay4.getRelay()));
  });