I'm having some trouble while putting my ESP8266WebServer standard connection procedure to its own class. I'm not able to pass the object server to my handleRoot function via bind::std.... I have tried multiple approaches, but right now, nothing succeeds. So maybe you could help me. Right now, the code compiles, I have commented out the corresponding lines. But in order to peform some actions on a client request, I need to have access to the server class methods in the functions handleRoot and handleForm. Here are the corresponding sketches. Thank you for your help.
Arduino sketch:
#include "WiFiHandler.h"
#include <ESP8266WebServer.h>
ESP8266WebServer server(80);
WiFiHandler myWiFiHandler;
void setup(){
Serial.begin(115200);
myWiFiHandler.setupWiFi(server); // Setup WiFi
}
void loop(){
myWiFiHandler.clientHandler(server); //Handle client requests
}
Header file:
#ifndef WiFiHandler_h
#define WiFiHandler_h
#include <WiFiSetup.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include "handleHTML.h"
class WiFiHandler
{
private:
WiFiSetup myWiFiSetup; // Create object myWiFiSetup
handleHTML myHTMLhandler; // Create object myHTMLHandler
char* _ssid;
char* _password;
void handleRoot();
void handleForm();
public:
WiFiHandler();
~WiFiHandler();
void setupWiFi(ESP8266WebServer&);
void clientHandler(ESP8266WebServer&);
};
#endif
Source file:
#include <WiFiSetup.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include "handleHTML.h"
#include "Arduino.h"
#include "WiFiHandler.h"
WiFiHandler::WiFiHandler()
: _ssid(myWiFiSetup.ssid()), _password(myWiFiSetup.passcode())
{
}
WiFiHandler::~WiFiHandler(){/*Nothing to destruct*/}
void WiFiHandler::setupWiFi(ESP8266WebServer& server_)
{
WiFi.begin(_ssid, _password);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print("Status: \n");
Serial.println(WiFi.status());
}
//If connection successful show IP address in serial monitor
Serial.print("Connected to ");
Serial.println(_ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP()); //IP address assigned to your ESP
server_.on("/", std::bind(&WiFiHandler::handleRoot, this)); // Which routine to handle at root location
server_.on("/", std::bind(&WiFiHandler::handleForm, this)); // Form action is handled here
server_.begin(); //Start server
Serial.println("HTTP server started");
}
void WiFiHandler::handleRoot()
{
Serial.print("WiFi-Request received");
// server_.send(200, "text/html", myHTMLhandler.getHTML()); //Send web page
}
void WiFiHandler::handleForm()
{
// String buttonState = server.arg("State");
Serial.print("State: ");
// Serial.println(buttonState);
String s = "<a href='/'> Go Back </a>";
// server_.send(200, "text/html", s); //Send web page
}
void WiFiHandler::clientHandler(ESP8266WebServer& server_)
{
server_.handleClient();
}
Changing the callback finally did the job.
server_.on("/", [&]() { handleRoot(server_); });
server_.on("/action_page", [&]() { handleForm(server_); });
and the corresponding function:
void WiFiHandler::handleRoot(ESP8266WebServer& server_)
{
server_.send(200, "text/html", myHTMLhandler.getHTML()); //Send web page
}
I do it the following way, the principle works by the way also with async webserver:
class MY_WebServer : public WebServer { // inherit from the lib class
public:
MY_WebServer();
void begin() {
MDNS.addService("http", "tcp", 80);
WebServer::begin();
}
void startWebServer(); // this function initializes wifi, all handlers and params
private:
void handleFileList();
//.... some 30 handlers ...
}
Works in a quite complex application stable for months.
Related
ESP32S NodeMCU
VSCode with PlatformIO
Hey all,
Apologies if I get terms wrong, this is my first time with an ESP32 and webservers. I'm developing code for an amateur ESP32 project that involves connection to an http page. I have set up an if-else loop that reads the ending of the webserver url to set the waterState variable. The ESP32 will connect to my WiFi network with no issue. However, my computer will not connect to the url. The result should print out the state chosen (ex: morning) and the terminal will indicate that the function has completed execution.
I tried moving around the WiFiClient and WiFiServer instances but that hasn't worked. I was able to get this program working once before when I kept the ScheduleProt code inside the WiFi connect. I tried that replicating that again but it now isn't working.
WiFiConnect.h
#ifndef WIFICONNECT_H
#define WIFICONNECT_H
#include <WiFiClient.h>
#include "Wifi.h"
class WiFiConnect
{
private:
#define WIFI_NETWORK "NetworkName"
#define WIFI_PASSWORD "Password"
#define WIFI_TIMEOUT 20000
public:
void wifiConnect();
void wifiDisconnect();
};
#endif
WiFiConnect.cpp - Handles connection to WiFi
#include <WiFi.h>
#include <WiFiConnect.h>
void WiFiConnect::wifiConnect() {
WiFiServer server(80);
Serial.print("Connecting to WiFi");
WiFi.mode(WIFI_AP);
WiFi.begin(WIFI_NETWORK, WIFI_PASSWORD);
unsigned long startAttemptTime = millis();
//Connection Protocol
while(WiFi.status() != WL_CONNECTED && (millis() - startAttemptTime) < WIFI_TIMEOUT) {
Serial.println("...");
delay(100);
}
if(WiFi.status() != WL_CONNECTED) {
Serial.println("Failed, attempting again...");
delay(5000);
}
else{
Serial.print("Connected: ");
Serial.println(WiFi.localIP());
server.begin();
}
}
void WiFiConnect::wifiDisconnect() {
Serial.println("Disconnecting from WiFi...");
WiFi.disconnect();
}
ScheduleProt.h
#ifndef SCHEDULEPROT_H
#define SCHEDULEPROT_H
class ScheduleProt {
public:
int waterState = -1;
void scheduleWatering();
};
#endif
ScheduleProt.cpp - Reads the URL from the http server
#include <WiFi.h>
#include <Arduino.h>
#include "WiFiConnect.h"
#include "ScheduleProt.h"
void ScheduleProt::scheduleWatering() {
WiFiServer server(80);
WiFiClient client = server.available();
WiFiConnect newConnection;
newConnection.wifiConnect();
while(waterState = -1){
if (client) {
String req = client.readStringUntil('\r');
Serial.println("Waiting for user input...");
//Watering Times
if(req.indexOf("/morning/") != -1){
client.print("Morning");
waterState = 0;
}
else if(req.indexOf("/noon/") != -1){
waterState = 1;
client.print("Noon");
}
else if(req.indexOf("/evening/") != -1){
waterState = 2;
client.print("Evening");
}
else if(req.indexOf("/midnight/") != -1){
waterState = 3;
client.print("Midnight");
}
}
}
Serial.println("User input recieved, Huzzah!" + waterState);
newConnection.wifiDisconnect();
}
Here is the terminal
�Connecting to WiFi...
...
...
...
...
...
...
...
...
Connected: 192.168.1.100
If it helps, here is the main.cpp code
#include <Arduino.h>
#include <time.h>
#include "Wifi.h"
#include "ScheduleProt.h"
#include "WaterProt.h"
#include "CurrentTime.h"
#define DEEPSLEEPTIME 86400000
#define WATER_DURATION 10000
#define MOTORPIN 0
WaterProt waterProtocol;
CurrentTime currentTime;
ScheduleProt newSchedule;
void setup() {
Serial.begin(9600);
newSchedule.scheduleWatering();
}
void loop() {
if (waterProtocol.getWateringHour(newSchedule.waterState) == currentTime.getCurrentHour()){
waterProtocol.waterPlant(MOTORPIN, WATER_DURATION);
}
else {
esp_sleep_enable_timer_wakeup(1800000);
esp_deep_sleep_start();
}
esp_sleep_enable_timer_wakeup(DEEPSLEEPTIME);
esp_deep_sleep_start();
}
The webpage error
[1]: https://i.stack.imgur.com/HLIaH.png
Any help would be appreciated! Thank you!
It looks like you're actually creating two separate WiFiServers, and only calling begin() on one of them.
One WiFiServer is created inside WiFiConnect::wifiConnect(). This is a local variable and so it goes away when that function completes.
The other WiFiServer is created inside ScheduleProt::scheduleWatering(). This one is probably the one you want to keep, but you'll need to add a call to server.begin() before the call to server.available().
My suggestion is:
Remove all references to WiFiServer from WiFiConnect::wifiConnect().
Move the two lines inside ScheduleProt::scheduleWatering() that relate to WiFiConnect to the top of the function, so that they happen before the WiFiServer is initialized.
Add a call to server.begin() just before the call to server.available().
I also notice that ScheduleProt::scheduleWatering() is called from your setup() function, which means that it will only run once (i.e. accept one HTTP connection), and the loop() function will only run after that connection has completed. It looks like this might be intended, but I wasn't sure so thought I should point it out just in case you were unaware.
Update: I just had another look at the docs and it looks like WiFiServer.available() doesn't actually wait for a client to connect, it just returns the connection if there is one already. If that's the case, you really need to call it repeatedly until it returns a connection (or use a blocking function instead - I haven't looked into what's available in terms of that). Probably the easiest way to do this with your current code is to move the WiFiClient client = server.available(); inside the while loop.
It might also be a good idea to add a delay(1); inside that loop, so that it isn't running the processor at 100% load. I can't say this for sure without refreshing my memory on how the ESP32 does power management, but it's likely that adding the delay will save a significant amount of power. Either way, it won't hurt.
This question already has an answer here:
expected unqualified-id before '.' token arduino library
(1 answer)
Closed 1 year ago.
When i try to call any function from my library, it gives the following error:
In function 'void setup()':
error: expected unqualified-id before '.' token
netti.SetupWifi();
^
Yesterday it worked fine and i'm pretty sure that i haven't changed anything so i have no idea what's wrong with it. The library is for making wifi connection and telnet monitoring easier.
Adruino code:
#include <netti.h>
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
netti.SetupWifi(); //error here
}
void loop() {
// put your main code here, to run repeatedly:
netti.ReconnectWifi(); //same error here
Serial.println("runs"); // test so it's running atleast some code
delay(1000);
}
netti.h:
#ifndef netti_h
#define netti_h
#if (ARDUINO >= 100)
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <ESP8266WiFi.h>
class netti{
public:
//constructor
netti(bool displayMsg=false);
//methods
void SetupWifi();
void ReconnectWifi();
void SetupMobile(); // for different wifi
void ReconnectMobile(); //for different wifi
WiFiClient Telnet;
private:
};
#endif
and netti.cpp:
#include <ESP8266WiFi.h>
#include "netti.h"
#include "Arduino.h"
#include <PubSubClient.h>
WiFiClient espClient;
PubSubClient client(espClient);
IPAddress ip(192, 168, 1, 100);
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);
WiFiServer TelnetServer(23);
WiFiClient Telnet;
netti::netti(bool displayMsg){
Serial.println("netti kirjasto käytössä");
}
void(* resetFunc) (void) = 0;
void handleTelnet(){
Telnet.println("test");
Serial.println("telnet handler called");
if (TelnetServer.hasClient()){
if (!Telnet || !Telnet.connected()){
if(Telnet) Telnet.stop();
Serial.println("telnet available");
Telnet =TelnetServer.available();
}else {
TelnetServer.available().stop();
Serial.println("telnet in use");
}
}
}
void netti::SetupWifi() {
delay(100);
Serial.println();
Serial.print("Connecting to ");
Serial.println("wifi");
WiFi.hostname("ESP_host");
WiFi.config(ip, gateway, subnet);
WiFi.begin("wifi name","pass place");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
};
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
TelnetServer.begin();
TelnetServer.setNoDelay(true);
};
void netti::ReconnectWifi() {
int i=0;
handleTelnet();
Telnet.println("test2"); // does nothing, why??
if(WiFi.status() != WL_CONNECTED){
delay(5000);
SetupWifi();
};
};
void netti::SetupMobile() {
delay(100);
Serial.println();
Serial.print("Connecting to ");
Serial.println("mobile");
WiFi.hostname("ESP_host");
WiFi.begin("mobile_place","pass place");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
};
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
TelnetServer.begin();
TelnetServer.setNoDelay(true);
};
void netti::ReconnectMobile() {
int i=0;
handleTelnet();
if(WiFi.status() != WL_CONNECTED){
delay(5000);
SetupMobile();
}
};
This is my first attempt at making my own library and i have no idea why it's not working.
Thanks in advance for all the help
PS. I'm having trouble with Telnet, trying to work the solution, which is hard, when the library doesn't run
The error is the following. You have defined a class called netti but you have not created an instance of the class. Therefore you cannot call instance methods on this class. For example if you have
class netti{
public:
//constructor
netti(bool displayMsg=false);
//methods
void SetupWifi();
void ReconnectWifi();
void SetupMobile(); // for different wifi
void ReconnectMobile(); //for different wifi
WiFiClient Telnet;
private:
};
you need to write
netti netti_instance(true);
netti_instance.SetupWifi();
If you want to be able to call methods on a class without an instance you need to declare them as static methods.
I suggest you look into some basic tutorials on how to use C++ classes.
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/bind.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/asio.hpp>
#include <queue>
class ClassA
{
public:
bool _isok = false;
};
class Session
{
public:
const char _buffer[1024];
void SendHandler();
void ReadHandler();
};
void Session::SendHandler()
{
//do something (Send handler)
}
void Session::ReadHandler()
{
// do something (Read handler)
_socket->async_read_some(boost::asio::buffer(_buffer, 1024), Session::ReadHandler);
//
}
int main(void)
{
// Connect
// Assuming the connection is complete
// ......connect.......
// completed Connect and Init _socket;
ClassA classA;
//async Send
boost::asio::async_write(_socket.,
boost::asio::buffer(buffer, 1024),Session::SendHandler);
memset(&_buffer, '\0', sizeof(_buffer));
//async Read
_socket->async_read_some(boost::asio::buffer(_buffer, 1024), Session::ReadHandler);
// I wanna must do something( a = 1;) after finish ReadHandler() ( not using polling )
if (classA._isok == true) //while(classA._isok == false){} (I don't wanna using this)
{
int a = 1; //after Readhandler but I don't know what should I do not using infinity loop
}
}
I wanna must run a=1; on ensure 1 time after Readhandler
(_isok == true after Readhandler)
how to use synchrouse not using polling?
I wanna wait untill (_isok = true;) ,not using polling
I don't want to run any subsequent lines until it is done by interrupt mode.
but I don't know best synchrouse way
I'm getting an exception (see below) when I try to use NTPClient in my custom class RealTimeService. Please advise why and how to fix it. Thank you.
File RealTimeService.h:
#ifndef RealTimeLib
#define RealTimeLib
#include <NTPClient.h>
#include <WiFiUdp.h>
class RealTimeService {
private:
NTPClient _ntp;
NTPClient _createNtpClient();
void _update();
public:
RealTimeService();
void begin();
};
#endif
File RealTimeService.cpp:
#include "RealTimeService.h"
RealTimeService::RealTimeService() :
_ntp(_createNtpClient())
{
}
NTPClient RealTimeService::_createNtpClient() {
WiFiUDP udp;
NTPClient ntp(udp, "pool.ntp.org", 3600, 86400000);
return ntp;
}
void RealTimeService::begin() {
_update();
}
void RealTimeService::_update() {
_ntp.begin(); // Throws an exception
if(_ntp.update()) {
long time = _ntp.getEpochTime();
} else {
Serial.print("Failed to get time from server.\n");
}
}
File WebServerSecure.ino:
#include <ESP8266WiFi.h>
#include "RealTimeService.h"
#ifndef STASSID
#define STASSID "Sedmikraska"
#define STAPSK "38098246"
#endif
const char* ssid = STASSID;
const char* password = STAPSK;
RealTimeService realTime;
void setup(void) {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
realTime.begin();
}
void loop(void) {
delay(1000);
}
It throws an exception in function RealTimeService::_update() at line _ntp.begin();
User exception (panic/abort/assert)
And the decoded stack is:
0x4020714a: HardwareSerial::begin(unsigned long, SerialConfig, SerialMode, unsigned char, bool) at C:\Users\user\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.4\cores\esp8266\HardwareSerial.cpp line 51
0x4020804c: __unhandled_exception_cpp() at C:\Users\user\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.4\cores\esp8266\core_esp8266_main.cpp line 229
0x4020a07c: NTPClient::begin(int) at C:\Users\user\Documents\Arduino\libraries\NTPClient\NTPClient.cpp line 61
0x40202544: NTPClient::begin() at C:\Users\user\Documents\Arduino\libraries\NTPClient\NTPClient.cpp line 53
0x40201028: RealTimeService::_update() at C:\Users\user\AppData\Local\Temp\arduino_build_506039\sketch\RealTimeService.cpp line 20
0x40201b04: ESP8266WiFiSTAClass::status() at C:\Users\user\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.4\libraries\ESP8266WiFi\src\ESP8266WiFiSTA.cpp line 634
0x40201058: RealTimeService::begin() at C:\Users\user\AppData\Local\Temp\arduino_build_506039\sketch\RealTimeService.cpp line 16
0x4020115c: setup() at C:\Users\user\Documents\Arduino\WebServerSecure/WebServerSecure.ino line 23
0x40208224: loop_wrapper() at C:\Users\user\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.7.4\cores\esp8266\core_esp8266_main.cpp line 194
You define udp in RealTimeService::_createNtpClient() and pass it to NTPClient. NTPClient will continue to need it after RealTimeService::_createNtpClient() returns, but udp will become invalid. Even if NTPClient copies it, its destructor is called, invalidating the resources it used.
You need to change udp to be an instance variable so that it will survive as long as _ntp does.
class RealTimeService {
private:
WiFiUDP _udp;
NTPClient _ntp;
...
and
NTPClient RealTimeService::_createNtpClient() {
NTPClient ntp(_udp, "pool.ntp.org", 3600, 86400000);
Your code is also calling _ntp.begin() repeatedly. You should ensure that the NTP library you're using allows for this; sometimes libraries are written so that their begin() method can only be called once.
In addition to #romkey's answer, you could probably skip _createNtpClient altogether and move the initialization of the fields in the constructor. Something more compact, like
#ifndef RealTimeLib
#define RealTimeLib
#include <WiFiUdp.h>
#include <NTPClient.h>
class RealTimeService {
private:
WiFiUDP _udp;
NTPClient _ntp;
...
public:
RealTimeService();
...
};
#endif
#include "RealTimeService.h"
RealTimeService::RealTimeService() :
_udp(),
_ntp(_udp, "pool.ntp.org", 3600, 86400000)
{
}
...
I have been trying to get my ESP32 connected to Wifi with C++. I have a small issue which I don't understand at all. And will eventually getting it connected to AWS IOT. I have it all working perfectly with the Arduino native language with no issues, but when trying to get it running with C++ I can't even get the wifi connecting.
When I read the localIP back in the loop when running the connectWifi() function (arduino code) it reads back the IP easily and I already have this with other code working with AWS IOT perfectly. When trying to run the initialiseWifi() function (C++) it doesn't seem to work and it returns an IP of 0.0.0.0.
I got this code from a ESP32 AWS IOT tutorial using C++ found on Visual Studio PlatformIO, and I also couldn't get the tutorial working either.
I feel like this is a simple error and I am new to C++, so am hoping someone can point it out so I can fix it up please!
#include "secrets.h"
#include <WiFiClientSecure.h>
#include <MQTTClient.h>
#include <ArduinoJson.h>
#include "WiFi.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_vfs_fat.h"
#include "driver/sdmmc_host.h"
#include "lwip/inet.h"
#include "lwip/dns.h"
const int CONNECTED_BIT = BIT0;
WiFiClientSecure net = WiFiClientSecure();
MQTTClient client = MQTTClient(256);
void messageHandler(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch(event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_GOT_IP:
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently
auto-reassociate. */
esp_wifi_connect();
break;
default:
break;
}
return ESP_OK;
}
void initialise_wifi(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
//Allocate storage for the struct
wifi_config_t wifi_config = {};
//Assign ssid & password strings
strcpy((char*)wifi_config.sta.ssid, "XXXXXXXXXX");
strcpy((char*)wifi_config.sta.password, "XXXXXXXXXXX");
wifi_config.sta.bssid_set = false;
// ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_start() );
}
void connectWifi() {
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.println("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED){
delay(500);
Serial.print(".");
}
printf("\n");
printf("Hello world!\n");
printf("Local IP: ");
Serial.println(WiFi.localIP());
}
void setup() {
Serial.begin(9600);
//connectWifi();
initialise_wifi();
}
void loop() {
Serial.println(WiFi.localIP());
delay(1000);
}