I am making a simple homebrew IoT solution using the esp8266, using the build in libraries for web server routes. These lib functions don't like using functions with arguments so my solution was to use lambdas and capture the variables I need. The problem is when trying to operate on the same instance of an object from two different lambdas, when using the pinPower setter methods that update an int storing the pin power (1 or 0, yes I know a bool would be better) a change in one lambda does not appear in the other.
I think this has to do with lambdas capturing variables by value and it creating a new instance of my class; I have tried capturing outputPinArray[i] as a reference but this just meant that the getter function failed to get the correct value and so failed to do so much as toggle the value (class uses the getter internally). Using a static variable and getter function worked but I want to be able to use multiple instances of the class and so don't want a shared variable like this. I have seen various posts around that seem to point to capturing using the extern keyword but I haven't found much documentation for using this in lambdas and wasn't able to figure out how to use it properly.
This is the code I am working with:
for(int i = 0; i < 1; i++) {
server.on(path, HTTP_POST, [outputPinArray , argToCheck, i]() mutable {
// Sets pinNumber variable and turns on or off the arduino pin using outputPinArray[i].setPinPower or .togglePinPower
});
server.on(path, HTTP_GET, [outputPinArray,i](){
// Send integer value using outputPinArray[i].getPinPower()
});
}
outputPin class code:
int OutputPin::getPinPower(){
return pinPower;
}
void OutputPin::setPinPower(int value){
if(value == HIGH || value == LOW){
pinPower = value;
digitalWrite(getPinNum(), pinPower);
}
Any help is appreciated, Thank you!
If I understand your problem correctly, all you need to do is
server.on(path, HTTP_POST, [&outputPinArray , argToCheck, i]() {
Fail on my part, the array went out of scope while the routes were the esp8266webserver object was declared globally. Solved with help with #obamator
Related
I'm trying to create simple game in C++. At one point I want to have some setting, save and load from config file.
The config file should be read from the beginning, and should be accessible anywhere it needed.
So far I only see Singleton pattern as a solution.
Another way is to create an object an pass it down, but it can mess
up the current code.
I've also search and found something called Dependency Injection.
Is dependency injection useful in C++
Which design patterns can be applied to the configuration settings problem?
But I don't quite understand it, you still have to create an object in main and pass it down, right?
Singleton is quite simple, but some consider it antipattern, while pass it down the tree can mess up my current code. Is there any other Patterns?
P/S: I'm also curious how games load their setting.
I would suggest something simple as the following example, which circumvents any singleton-related or initialization order issue:
struct global_state
{
config _config;
};
struct game_state
{
global_state& _global_state;
};
int main()
{
global_state globals{load_config_from_file()};
game_state game{globals};
game.run();
}
Since _global_state is a member of game_state, it can be used in member functions without the need of explicitly passing it as a parameter:
void game_state::update_ui()
{
const float text_size = _global_state._config.get_float("text_size");
_some_text.set_size(text_size);
}
I am trying to initialize a server to look as specific inputs based on the request it gets. there are a lot of them so I want to initialize it with a loop as follows:
void serverInit() {
for (int i = 1; i <= qty; i++) {
String s = "/readBatt" + i;
server.on(s, runTest(i));
}
server.begin();
Serial.println("Server started.");
}
It's telling me that server.on(s, runTest(i)); is an invalid use of void expression. I know it wants it formatted as server.on(s, runTest) but the function runTest(int n) takes a parameter. How can i pass this parameter through to the function?
It seems you are using the WebServer class from the ESP32 Arduino libraries. As you have gleaned already, the callback specified in the on() method does not accept any arguments.
You have an alternative, however. You can specify a 'placeholder' in the URL path - using curly brackets - {}. In the callback, then, the corresponding argument can be retrieved by using the pathArg() method - which accepts the argument index as parameter.
Example ...
You could define your API endpoint as /readBatt/<battery number>. To configure the server to handle requests to this endpoint, then, you would use something like
#include <uri/UriBraces.h>
server.on(UriBraces("/readBatt/{}"), runTest);
In your callback, you would retrieve the first argument as follows ...
static void runTest() {
String batteryNumber = server.pathArg(0);
Serial.println("Request to read battery");
String response = "You attempted to read battery " + batteryNumber;
response += ".\nThis endpoint is a placeholder. Check again soon!";
server.send(200, "text/plain", response);
}
Finally ... Suppose your ESP8266 was running on local IP address 192.168.1.9. You could access your new API endpoint by opening
http://192.168.1.9/readBatt/1
in your browser. (Replace 1 with the relevant battery number.)
I don't think there are versions of the pathArg() which return an integer, unfortunately, so you may have to perform a conversion at some point.
You can use what's called a "closure". A closure lets you compose a function which retains access to variables outside of its scope.
A closure is written using a "lambda expression" - basically an anonymous function. C++'s syntax for lambda expressions looks like this:
[capture variable list](argument list) { body}
The capture variable list is a list of variables you want to be made available inside the body. The argument list is the normal function argument list that would get passed in by the caller. You'd write the lambda expression you need like this:
[i]() { runTest(i); }
and use it like this:
server.on(s, [i]() { runTest(i); });
To be clear, #David Collins' answer is the better way to write the web server. Using a parameter in the URL is better than creating several URLs with the parameter embedded in them. I'm just answering the question of how to pass a parameter to a function that gets called without arguments. If you write the web server code the better way, you won't need to do this (although I would do a bounds check on the value passed in the URL to make sure you're getting a valid battery number).
I added the following lines in onData method of my TracIDemoRSU11p.cc file:
void TraCIDemoRSU11p::onData(WaveShortMessage* wsm) {
findHost()->getDisplayString().updateWith("r=16,green");
annotations->scheduleErase(1, annotations->drawLine(wsm->getSenderPos(), mobi->getCurrentPosition(), "blue"));
static int count=0;
count++;
if(count>100)
{
wsm->setCount(count);
sendMessage(wsm->getWsmData());
}
}
What I want is to get the vehicle's address which has sent the data as well.
Also, is there any problem in using the static variables in the method?
I have defined a count variable in wsm message and I want to send the count value along with the vehicleId in the message.
Amending the message definition to include a vehicle id is allowed. So is using static variables in C++. I don't think it is a good idea though.
The static variable will be shared among all instances of that class (that is, across nodes and - possibly - simulation runs). I would recommend using a regular member variable of some class.
I'm updating my V1 cocos2d-x app to V3 and I'm stuck on callbacks.
I can do call backs with lambdas like this, works fine -
auto mcb = CallFunc::create([this](){
this->doCallback(kEVENT_MENU_IS_ONSCREEN);
});
hex->runAction(Sequence::create(somethingthattakestime, mcb, NULL) );
However, I want to pass a CallFunc variable into a function, store it in my object, then use/call it at some point in the future.
In a class I define -
CallFunc * callfunc;
Set with a simple -
item->callfunc = callfunc;
Within a function -
void LBMenuAddMenuItemName( CallFunc * callfunc );
I also declare the CallFunc variable as static so it hangs around -
static auto doSoloPlay = CallFunc::create([this](){
CCLOG("doSoloPlay variable");
this->menuSoloPlay();
});
Later when I wish to use this, I do -
Sequence * seq = Sequence::create(callfunc,NULL);
somesprite->runAction(seq);
However, this ends badly with a SIGSEGV (GLThread).
Using typeid(callfunc).name() shows that callfunc is a CallFunc. Using setTag(69) in declaration and then getTag() before SIGSEGV does not return a sensible value.
Can someone explain what I'm doing wrong and the correct method for delayed callbacks?
Thank you!
LB
Tried many things then found this error goes away when I do a 'retain()' after the declaration -
doSoloPlay->retain();
This increases the reference count.
I don't quite know why I need this. My guess is that cocos2d-x classes may do some clever garbage collection?
If the function is called later as a callback, you need to retain the callback when you save it and release it at the right time.
The static declaration only save the address of your CCCallFunc but the class is destroyed before you wish to execute it.
I have a program that uses plug-ins. As I'm in development, these plug-ins are currently just .h and .cpp files that I add or remove from my project before re-compiling, but eventually they will be libraries.
Each plug-in contains lists of data in vectors, and I need to dynamically load data from the plug-ins without knowing which plug-ins are present. For instance:
// plugin1.h
extern vector<int> plugin1Data;
// plugin2.h
extern vector<int> plugin2Data;
// main.cpp
vector<vector<int>> pluginDataList;
int CountPlugins () {
// Some function that counts how many plug-ins are present, got this bit covered ;)
}
int main() {
int numPlugins = CountPlugins();
for (int i = 0; i < numPlugins; i++) {
vector<int> newPluginData = /***WAY TO ADD PLUGIN DATA!!!***/;
pluginDataList.push_back(newPluginData);
}
}
I already access the names of each plugin present during my CountPlugins() function, and have a list of names, so my first gut feeling was to use the name from each plugin to create a variable name like:
vector<string> pluginNames = /*filled by CountPlugins*/;
string pluginDataName = pluginNames.at(i) + "Data";
// Use pluginDataName to locate plugin1Data or plugin2Data
That's something I've done before in c# when I used to mess around with unity, but I've read a few stackoverflow posts clearly stating that it's not possible in c++. It's also a fairly messy solution in C# anyway as far as I remember.
If each plugin was a class instead of just a group of vectors, I could access the specific data doing something like plugin2.data... but then I still need to be able to reference the object stored within each plugin, and that'll mean that when I get round to compiling the plugins as libraries, I'll always have to link to class declaration and definition, which isn't ideal (though not out of the question if it'll give a nicer solution over all).
I'm all out of ideas after that, any help you can offer will be most welcome!
Thanks! Pete
Why dont you save the data as JSON between the application and the plugins ? That way you will also allow other types of tech to plug-into your app, like javascript based plugins via an embedded version of v8 or c#/.net plugins via mono.'