Esp8266 webserver restart after IP changed - webserver

I'm trying to access a webserver after I've changed IP addresses on my ESP8266 arduino sketch, but it's not responding (but a ping is).
The sketch starts in WIFI_AP mode (for device testing purposes), but if a network is discovered after an asynchronous WiFi scan, the call back code disconnects the WiFi and then creates a connection in WIFI_AP_STA mode (acting as a secondary network access point).
ESP8266WebServer webServer(80);
void myWifiScanCompleted( int networksFound ) {
IPAddress ip(192,168,1,2); // fixed IP address of this access point
IPAddress gateway(192,168,1,254);
IPAddress subnet(255,255,255,0);
int count;
int quality;
networkConnection = NET_CONNECT_NONE; // initialise as not found
for(count = 0; count < networksFound; count++) {
quality = getRSSIasQuality( WiFi.RSSI(count) );
if(strcmp(WiFi.SSID(count).c_str(), ap_ssid ) == 0) {
debug_print("found network %s (%d\%)", ap_ssid, quality);
networkConnection = NET_CONNECT_FOUND;
break;
} else {
debug_print("ignoring network %s (%d\%)", WiFi.SSID(count).c_str(), quality);
}
}
if(networkConnection == NET_CONNECT_FOUND) { // found network = try to connect
// reset any existing connection
webServer.stop();
WiFi.disconnect();
delay(100);
WiFi.mode(WIFI_AP_STA); // need both modes to join the network to get IP address, and then create access point
WiFi.begin(ap_ssid, ap_password);
count = 0;
while (WiFi.status() != WL_CONNECTED) {
toggle_led(); // Blink the LED
if(++count > (15 * 10)) {
debug_print("error connecting to network %s", ap_ssid);
networkConnection = NET_CONNECT_NONE;
break; // failed to connect
}
delay(100);
}
if(networkConnection != NET_CONNECT_NONE) {
networkConnection = NET_CONNECT_CONNECTED; // we've successfully connected to the network, now set up the access point
ip = WiFi.localIP();
gateway = (uint32_t)0;
kev_softAPConfig(ip, gateway, subnet, false); // no DHCP server
if(! kev_softAP(ap_ssid, ap_password, MY_WIFI_CHANNEL, MY_WIFI_HIDDEN, MY_WIFI_MAX_CONNECTIONS)) {
debug_print("create %s softAP failed - performing network reset", ap_ssid);
WiFi.disconnect();
networkConnection = NET_CONNECT_NONE;
} else {
// access point started = restart server
webServer.begin(); // start web server on the new IP address
debug_print("create %s softAP OK", ap_ssid);
}
}
}
}
The functions kev_softAPConfig() and kev_softAP() are modified versions of the API that do NOT start the dhcp service (which already exists on the network).
I've proved these functions work, I've just changed the order of things so that I can provide device testing features.
Has anyone achieved this, or am I a pioneer? :-)

I found that I had to use a different IP range for the access point from the WiFi.localIP() allocated to the station by the network DHCP server, which meant having to write some proxy code to communicate across networks.

Related

Failure to send data from client to server in ESP-01 WiFi

Using the ESP8266WiFi library, I have two ESP-01's/ESP8266's connected over WiFi. It works perfectly when the client sends a request (all non HTML!) to the server (using port 5000 - to prevent any confusion with HTTP, FTP etc.). But I cannot get the client to receive an answer back from the server. Now, in the ESP8266WiFi library (3.0.2) there is a note that server.write() is not implemented, and that I should use server.accept() instead of server.available(); though I did not see any applicable examples using server.accept(), but I see many examples using client.print() so I try to follow those - to no avail, yet. What I am doing is the following: 1. establish connectivity to the WiFi; 2. have the client connect to the server and send two bytes to the server. 3. Do a digital write to a pin of the server-ESP8266.(this toggles a relay, which works fine) 4. write back from server to client that the digital write has been done. On the client side, after writing to the server, I run in a loop for some 10 seconds trying to receive something from the server, which never comes. Then I cycle back to the beginning, and the client asks to toggle the relay again - this runs nicely for hours.
Any insights here on what I should do differently are highly appreciated. I really want to be able to get some acknowledgement back to the client once the server has toggled the relay. Or if someone has a working example with server.accept() - I would try that too.
Client side code:
int pin_value;
uint8_t ip[4];
void setup()
{
Serial.begin(115200);
ip[0]=10;
ip[1]=0;
ip[2]=0;
ip[3]=6;
//We connect to the WiFi network
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
//Wait until connected
while (WiFi.status() != WL_CONNECTED){
delay(500);
Serial.print(".");
}
Serial.print("Client - ");
Serial.println("WiFi connected");
}
void loop(){
//Variable that we will use to connect to the server
WiFiClient client;
//if not able to connect, return.
if (!client.connect(ip, SERVER_PORT)){ return; }
// We create a buffer to put the send data
uint8_t buffer[Protocol::BUFFER_SIZE];
//We put the pin number in the buffer
// whose state we want to send
buffer[Protocol::PIN] = RELAY;
//put the current state of the pin in the send buffer
buffer[Protocol::VALUE] = pin_value;
//We send the data to the server
client.write(buffer, Protocol::BUFFER_SIZE);
// try to read the answer from the server for about 10 seconds
int nr_of_tries = 10000;
while (client.connected() && nr_of_tries > 0)
{if (client.available())
{ String line = client.readStringUntil('\n');
nr_of_tries = 0;
Serial.print("line= ");
Serial.println(line);
}
else
{delay(1);
nr_of_tries=nr_of_tries-1;
}
}
Serial.print("nr of tries= ");
Serial.println(nr_of_tries);
Serial.print("connected: ");
Serial.println(client.connected());
client.flush();
client.stop();
Serial.println(" change sent");
if (pin_value == 0)
{pin_value =1;
Serial.println("Pin_value set to 1");
}
else
{pin_value=0;
Serial.println("Pin_value set to 0");}
delay(10000);
}
Server side code:
WiFiServer server(SERVER_PORT);
void setup()
{
Serial.begin(115200); // must have the same baud rate as the serial monitor
pinMode(RELAY,OUTPUT);
digitalWrite(RELAY, LOW);
// Connect to the WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED){
delay(500);
Serial.print(".");
}
Serial.println("Server - ");
Serial.println("WiFi connected");
// Set this ESP to behave as a WiFi Access Point
// WiFi.mode(WIFI_AP);
// set SSID and Password to connect to this ESP
// WiFi.softAP(SSID, PASSWORD);
// Start the server
server.begin();
Serial.println("Server started");
// Output of the IP address
Serial.print("Use this IP to connect: ");
Serial.println(WiFi.localIP());
}
void loop()
{
// Check if there is any client connecting
WiFiClient client = server.available();
if (client)
{
//Serial.println("Client detected");
//If the client has data he wants to send us
//check for a second or so as transmission can take time
int nr_of_tries = 1000;
while(!client.available() && nr_of_tries > 0)
{ nr_of_tries=nr_of_tries-1;
delay(1);
}
if (client.available())
{
// Serial.println(" Client data");
// create a buffer to put the data to be received
uint8_t buffer[Protocol::BUFFER_SIZE];
// We put the data sent by the client in the buffer
// but do not read more than the buffer length.
int len = client.read(buffer, Protocol::BUFFER_SIZE);
// retrieve which pin number the client sent
int pinNumber = buffer[Protocol::PIN];
Serial.print("Pin Number: ");
Serial.println(pinNumber);
// retrieve the value of this pin
int value = buffer[Protocol::VALUE];
Serial.print("Value: ");
Serial.println(value);
// Set the pin indicated by the received pin number in output mode
// but only if the pin is the GPIO0 pin!
if (pinNumber == RELAY)
{ pinMode(pinNumber, OUTPUT);
// Set the pin indicated by the received pin number to the passed value
digitalWrite(pinNumber, value);
}
// tell the client that the relay has been set or reset.
size_t i;
if (value == 0) {
i=server.println("Set");
Serial.print("i= ");
Serial.println(i);
}
else {
i=server.println("Reset");
Serial.print("i= ");
Serial.println(i);
}
}
}
//Close the connection with the client
//client.stop();
}
Common definitions:
#include <ESP8266WiFi.h>
const char* ssid = "blablabla";
const char* password = "blublublu";
#define SERVER_PORT 5000
#define RELAY 0
//Protocol that the Server and Client will use to communicate
enum Protocol{
PIN, // Pin whose state you want to change
VALUE, // State to which the pin should go (HIGH = 1 or LOW = 0)
BUFFER_SIZE // The size of our protocol. IMPORTANT: always leave it as the last item of the enum
};
Solved! By changing server.println("Set"); into client.println("Set") and doing the same for the transmission of "Reset" a few lines lower in the server side code it works!

Opening a UDP connection in Veins toward external server

I'm using Veins 4.4 and I need to store some results in an outer server, so I would like to open a UDP connection toward it.
I've read several posts about using a TCP connection for the mobility in Veins,and I understood I should resort to the Inet module to open a connection.
Although I don't need it for the mobility, but to send data to an external server.
Is there any suggestion?
I was trying to use the method processCommandFromApp method from inet/src/transport/UDP.cc class:
void UDP::processCommandFromApp(cMessage *msg)
{
switch (msg->getKind())
{
case UDP_C_BIND: {
UDPBindCommand *ctrl = check_and_cast<UDPBindCommand*>(msg->getControlInfo());
bind(ctrl->getSockId(), msg->getArrivalGate()->getIndex(), ctrl->getLocalAddr(), ctrl->getLocalPort());
break;
}
case UDP_C_CONNECT: {
UDPConnectCommand *ctrl = check_and_cast<UDPConnectCommand*>(msg->getControlInfo());
connect(ctrl->getSockId(), msg->getArrivalGate()->getIndex(), ctrl->getRemoteAddr(), ctrl->getRemotePort());
break;
}
case UDP_C_CLOSE: {
UDPCloseCommand *ctrl = check_and_cast<UDPCloseCommand*>(msg->getControlInfo());
close(ctrl->getSockId());
break;
}
case UDP_C_SETOPTION: {
UDPSetOptionCommand *ctrl = check_and_cast<UDPSetOptionCommand *>(msg->getControlInfo());
SockDesc *sd = getOrCreateSocket(ctrl->getSockId(), msg->getArrivalGate()->getIndex());
if (dynamic_cast<UDPSetTimeToLiveCommand*>(ctrl))
setTimeToLive(sd, ((UDPSetTimeToLiveCommand*)ctrl)->getTtl());
else if (dynamic_cast<UDPSetTypeOfServiceCommand*>(ctrl))
setTypeOfService(sd, ((UDPSetTypeOfServiceCommand*)ctrl)->getTos());
else if (dynamic_cast<UDPSetBroadcastCommand*>(ctrl))
setBroadcast(sd, ((UDPSetBroadcastCommand*)ctrl)->getBroadcast());
else if (dynamic_cast<UDPSetMulticastInterfaceCommand*>(ctrl))
setMulticastOutputInterface(sd, ((UDPSetMulticastInterfaceCommand*)ctrl)->getInterfaceId());
else if (dynamic_cast<UDPSetMulticastLoopCommand*>(ctrl))
setMulticastLoop(sd, ((UDPSetMulticastLoopCommand*)ctrl)->getLoop());
else if (dynamic_cast<UDPSetReuseAddressCommand*>(ctrl))
setReuseAddress(sd, ((UDPSetReuseAddressCommand*)ctrl)->getReuseAddress());
else if (dynamic_cast<UDPJoinMulticastGroupsCommand*>(ctrl))
{
UDPJoinMulticastGroupsCommand *cmd = (UDPJoinMulticastGroupsCommand*)ctrl;
std::vector<IPvXAddress> addresses;
std::vector<int> interfaceIds;
for (int i = 0; i < (int)cmd->getMulticastAddrArraySize(); i++)
addresses.push_back(cmd->getMulticastAddr(i));
for (int i = 0; i < (int)cmd->getInterfaceIdArraySize(); i++)
interfaceIds.push_back(cmd->getInterfaceId(i));
joinMulticastGroups(sd, addresses, interfaceIds);
}
else if (dynamic_cast<UDPLeaveMulticastGroupsCommand*>(ctrl))
{
UDPLeaveMulticastGroupsCommand *cmd = (UDPLeaveMulticastGroupsCommand*)ctrl;
std::vector<IPvXAddress> addresses;
for (int i = 0; i < (int)cmd->getMulticastAddrArraySize(); i++)
addresses.push_back(cmd->getMulticastAddr(i));
leaveMulticastGroups(sd, addresses);
}
else
throw cRuntimeError("Unknown subclass of UDPSetOptionCommand received from app: %s", ctrl->getClassName());
break;
}
default: {
throw cRuntimeError("Unknown command code (message kind) %d received from app", msg->getKind());
}
}
delete msg; // also deletes control info in it
}
I included the inet path as follows:
#include <inet/src/transport/udp/UDP.h>
and I call it as follows, by passing as input UDP_C_CONNECT message.:
cMessage *UDP_C_CONNECT;
void Inet::UDP::processCommandFromApp(UDP_C_CONNECT);
But when I run the simulation, it crashes, returning this error:
Errors occurred during the build.
Errors running builder 'OMNeT++ Makefile Builder' on project 'veins'.
java.lang.NullPointerException
1) Is there the correct way to set up the required connection?
2) Why I'm getting this error as soon as I include the inet path?
UPDATE
I also tried another way to establish the connection:
std::string host;
host = "16777343";
int port = 5144;
Veins::TraCIConnection* connection;
connection = TraCIConnection::connect(host.c_str(), port);
but as soon as it load the plugin, then it's like it is waiting for something at time 0.0 without starting the generation of the nodes.
Thanks for helping
Simulations using OMNeT++ are C++ programs, so you can use the full range of libraries and system calls available to any C++ program. If you want to open a UDP connection to some other computer on your network, just create a UDP socket as you would in any C++ program, then send the data whenever needed.
Maybe the easiest way to go about writing this is to
start with a plain C++ program that has nothing to do with OMNeT++
move the part of the program that has to run before everything else into the initialize method of a module in your simulation, the rest to a handleMessage method.

Winsock Connect function is taking too long when Windows firewall is on

I have made a client that, when the windows firewall is OFF on the server then it will connect instantly to the server but when the firewall is ON then it takes around 30-35 secs to connect with server. I have tried adding firewall exceptions on server for my application and for ports as well but nothing is working out, its still taking the same amount of time.
here is my client piece of code where I am trying to connect with server :
bool connectSocket()
{
_connectSocket = socket(PF_INET, SOCK_STREAM,0);
if(_connectSocket == INVALID_SOCKET)
{
LOG_ERROR("Failed intiating socket with partner.");
return false;
}
struct sockaddr_in thataddr;
memset(&thataddr,0,sizeof(thataddr));
thataddr.sin_addr.s_addr = inet_addr(_partnerIP.c_str());
thataddr.sin_family = AF_INET;
thataddr.sin_port = htons(_partnerPort);
int i = ::connect(_connectSocket,(struct sockaddr *)&thataddr,sizeof(thataddr));
if(i<0)
{
//i = WSAGetLastError();
LOG_WARNING("Failed connecting to partner. Check partner status.");
return false;
}
}
When Firewall is ON on the server ::connect function will take 30-35 secs to give error.
Any help in this direction is highly appreciated.
Thanks

Arduino: turn on and off ethernet server availability

I need to setup an ethernet (web) server that have to be turned on and off depending on some conditions on the Arduino UNO.
I read the docs of the Server class in the Ethernet library and it seems there is no chance to stop the server once you started, i.e. there is no EthernetServer.begin() counterpart.
I thought then to setup the server in the setup section and serve incoming connections depending on when the given condition:
EthernetServer server = EthernetServer(80);
void setup() {
Ethernet.begin(mac, ip);
server.begin();
}
void loop() {
if (condition) {
EthernetClient client = server.available();
if (client == true) {
// serve the client...
}
} else {
// do something else
}
}
This indeed works, but the client is not properly rejected: it is just leaved pending. In the browser one can see the web page loading idefinitely, and if the condition turns to true the client will eventually be served for the request issued when the condition was false.
I see no methods for rejecting the request (there is no counterpart of EthernetServer.available()). The only thing that comes to my mind is to perform a
server.available().stop();
in the beginning of the else block. This prevent to serve requests issued while the condition was false, but doesn't prevent the connection between the client and the server to take place (it's like opening a connection and shut it down immediately).
How could I avoid to establish connections at all while the condition is false?
I'm guessing here since I don't have my Arduino collection handy, but from memory and reading the reference you could try something like
void loop()
{
EthernetClient client = server.available();
if ( !condition )
{
client.stop(); // break connection and do something else
}
else
{
// serve the client...
}
}
Hope that may at least help you in the right direction.
Cheers,
Could you just return a 404 header when you want the server disabled?
if(!condition)
{
client.println("HTTP/1.1 404 OK");
client.println("Content-Type: text/html");
client.println("Connnection: close");
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html><body>404</body></html>");
}
else
{
// serve client
}
I am writing this answer here as it is the only post still active or that hasn't been closed regarding this topic. Despite countless researches regarding being able to switch the EthernetServer on or off at will, this is not possible. The only thing you can do is use some functions defined "public" in the classes of the Ethernet/W5100/W5200/W5500 libraries.
The features I've noticed that actually impact the reliability of the network card are:
#include <Ethernet.h>
#include <utility/w5100.h>
W5100.setRetransmissionTime(milliseconds);
W5100.setRetransmissionCount(number);
(helps to shorten waiting times in case of Wiznet W5100/W5200/W5500 network card timeout)
EthernetClient::setConnectionTimeout(CONNECTION_TIMEOUT);
EthernetClient::setTimeout(CONNECTION_INPUT_STREAMING_TIMEOUT);
(they help to shorten waiting times in case of timeout of the client connected to the EthernetServer)
More tips:
when EthernetServer::available() returns false consider using EthernetServer::flush() to flush server buffers;
when using EthernetClient::write() also use EthernetClient::flush() to ensure that all data has been sent;
use EthernetClient::close() on dead/useless clients to free sockets easely.
Consider implementing a function to force-close network sockets, using the following code:
#include <SPI.h>
#include <utility/w5100.h>
void closeAllSockets()
{
for (int i = 0; i < MAX_SOCK_NUM; i++)
{
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.execCmdSn(i, Sock_CLOSE);
SPI.endTransaction();
}
}
void printAllSockets()
{
for (int i = 0; i < MAX_SOCK_NUM; i++)
{
Serial.print(F("Socket #"));
Serial.print(i);
uint8_ts = W5100.readSnSR(i);
Serial.print(F(": 0x"));
Serial.print(s, 16);
Serial.print(F(" "));
Serial.print(W5100.readSnPORT(i));
Serial.print(F(" D:"));
uint8_t dip[4];
W5100.readSnDIPR(i, dip);
for (int j = 0; j < 4; j++)
{
Serial.print(dip[j], 10);
if (j < 3)
Serial.print(".");
}
Serial.print(F("("));
Serial.print(W5100.readSnDPORT(i));
Serial.println(F(")"));
}
}
MAX_SOCK_NUM changes according to the network card, the Wiznet W5100 has a maximum of 4 sockets, the W5200 and W5500 has a maximum of 8 sockets.
Hope this helps someone.

gethostbyaddr() has error 11001

I'm writing simple program to communicate between smart devices and I receive 11001 when calling gethostbyaddr(). Both devices show they are connected to the same network, but from msdn document 11001 error is Host not found. No such host is known. Does anybody have any suggestion, thanks? My code is below.
void InitializeSocket()
{
WORD socketVersion;
WSADATA wsaData;
SOCKADDR_IN serverInfo;
int returnVal;
LPHOSTENT remotHost;
socketVersion = MAKEWORD(2,2);
WSAStartup(socketVersion, &wsaData);
in_addr iaHost;
//iaHost.s_addr = inet_addr("120.15.22.14");
iaHost.S_un.S_un_b.s_b1 = 120;
iaHost.S_un.S_un_b.s_b2 = 15;
iaHost.S_un.S_un_b.s_b3 = 22;
iaHost.S_un.S_un_b.s_b4 = 14;
remotHost = gethostbyaddr((const char *)&iaHost, sizeof(struct in_addr),
AF_INET);
if(iaHost.s_addr == INADDR_NONE)
{
MessageBox(NULL, TEXT("inet_addr has invalid address"),
TEXT("inet_addr"), MB_OK);
WSACleanup();
closesocket(theSocket);
return;
}
if(!remotHost)
{
returnVal = WSAGetLastError();
WSACleanup();
closesocket(theSocket);
return;
}
theSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(theSocket == INVALID_SOCKET)
{
returnVal = WSAGetLastError();
WSACleanup();
closesocket(theSocket);
return;
}
serverInfo.sin_family = AF_INET;
serverInfo.sin_addr = *((LPIN_ADDR)*remotHost->h_addr_list);
serverInfo.sin_port = htons(8888);
//Connect to the server
returnVal = connect(theSocket, (LPSOCKADDR)&serverInfo,
sizeof(struct sockaddr));
if(returnVal == SOCKET_ERROR)
{
returnVal = WSAGetLastError();
WSACleanup();
closesocket(theSocket);
return;
}
}
Error 11001 is a generic DNS lookup error so I have only one question. Does the 120.15.22.14 address actually exist in DNS?
You can find this out by typing
nslookup 120.15.22.14
from the command line.
If you get an error, it's because DNS doesn't know anything about those addresses, so a gethostbyaddr() will not be able to give you any information.
Update:
Answering your points:
I just type nslookup 120.15.22.14, but it returns back as cant find 120.15.22.14. Non-existent domain.
Since nslookup 120.15.22.14 returns an error, there is no entry in DNS for that IP address. That's your problem.
I look at my device settings for the DNS and it sets to 120.20.32.10 which is different 120.15.22.14 so does it mean I haven't configured the DNS?
If 120.20.32.10 is what's in DNS for your device, and 120.15.22.14 is the actual address, then DNS is wrong. Plain and simple. Fix DNS.
will it produce the same result if I use gethostbyname() instead of gethostbyaddr()?
If you want to turn a DNS name into an IP address, use gethostbyname(). To turn an IP address into a DNS name, use gethostbyaddr().
For connecting to a remote host, you would normally use the gethostbyname() call since the IP address of the host could change at any time. Provided DNS always accurately represents the IP address of that host, that's the preferred way.
I just type nslookup 120.15.22.14, but it returns back as cant find 120.15.22.14. Non-existent domain.
I look at my device settings for the DNS and it sets to 120.20.32.10 which is different 120.15.22.14 so does it mean I haven't configured the DNS?