Hello everyone,
I am facing some issues while trying, in a larger project, to accomplish the following:
- Try to connect to the internet via WiFi
- If connection fails, open Access Point (AP) and let user insert WiFi credentials (through captive portal)
- Go back to point 1
Every time I close a connection, I call WiFi.disconnect()
and WiFi.end()
; Of course, I call WiFiClient::stop()
when needed. My WiFiClient
object is global and obtained through WiFiServer::available()
.
For the HTTP communication, I use a different WiFiClient object.
After closing AP connection, I could connect to the WiFi but any connection (e.g., to www.google.com) failed by a time-out. I kind of solved the problem through the following code I invoke every time I switch between point 1 and 2 of the algorithm.
WiFi = * ((WiFiClass*) (new WiFiClass(WiFiInterface::get_default_instance())));
However I do not understand such a behaviour.
Now, if WiFi connection fails after AP was accomplished once, AP is opened again, but this time communication fails. If I close and open AP again, it works again.
In order to replicate the situation, I modified the AP Example in GIGA R1's documentation so to restart AP every ~80 seconds and accomplish a captive portal. It succeeds and fails alternatively.
I therefore restart AP twice in my code to make things work, despite not smoothly. Obviously this is not a good solution.
Any help would be greatly appreciated
apparent SOLUTION:
WifiServer::begin()
must be called only once at the beginning- do not change AP IP address
- when switching from AP to normal WiFi, call
WiFi = * ((WiFiClass*) (new WiFiClass(WiFiInterface::get_default_instance())));
#include <SPI.h>
#include <WiFi.h>
#include <WiFiServer.h>
#include <WiFiClient.h>
#include <WiFiUdp.h>
char ssid[] = "ArduinoAP";
char pass[] = "Qwerty123";
int status = WL_IDLE_STATUS;
#define UDP_PACKET_SIZE 1024
#define DNSHEADER_SIZE 12
#define DNSANSWER_SIZE 16
WiFiServer* server = new WiFiServer(80); // Global Acces Point Web Server
WiFiUDP* G_UDPAP_DNS = new WiFiUDP(); // A UDP instance to let us send and receive packets over UDP
IPAddress G_APip; // Global Acces Point IP adress
byte G_UDPPacketbuffer[UDP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
byte G_DNSReplyheader[DNSHEADER_SIZE] = {
0x00, 0x00, // ID, to be filled in #offset 0
0x81, 0x80, // answer header Codes
0x00, 0x01, //QDCOUNT = 1 question
0x00, 0x01, //ANCOUNT = 1 answer
0x00, 0x00, //NSCOUNT / ignore
0x00, 0x00 //ARCOUNT / ignore
};
byte G_DNSReplyanswer[DNSANSWER_SIZE] = {
0xc0, 0x0c, // pointer to pos 12 : NAME Labels
0x00, 0x01, // TYPE
0x00, 0x01, // CLASS
0x00, 0x00, // TTL
0x18, 0x4c, // TLL 2 days
0x00, 0x04, // RDLENGTH = 4
0x00, 0x00, // IP adress octets to be filled #offset 12
0x00, 0x00 // IP adress octeds to be filled
};
/* DNS Routines via UDP, act on DSN requests on Port 53*/
/* assume wifi UDP connection has been set up */
bool APDNSScan() {
int t = 0; // generic loop counter
int r, p; // reply and packet counters
int flag = 0;
unsigned int packetSize = 0;
unsigned int replySize = 0;
byte G_DNSReplybuffer[UDP_PACKET_SIZE]; // buffer to hold the send DNS reply
bool dns_flag = true;
IPAddress G_APDNSclientip;
int G_DNSClientport;
packetSize = G_UDPAP_DNS->parsePacket();
if (packetSize > 1023)
{
return false;
}
if (packetSize) { // We've received a packet, read the data from it
int plen = G_UDPAP_DNS->read(G_UDPPacketbuffer, packetSize); // read the packet into the buffer
if (plen > 0 && plen < 1024)
{
// G_UDPPacketbuffer[plen] = 0;
}
G_APDNSclientip = G_UDPAP_DNS->remoteIP();
G_DNSClientport = G_UDPAP_DNS->remotePort();
// if ( (G_APDNSclientip != G_APip) && (G_DNSRqstcounter<=DNSMAXREQUESTS) ) // skip own requests - ie ntp-pool time requestfrom Wifi module
if ((G_APDNSclientip != G_APip)) // skip own requests - ie ntp-pool time requestfrom Wifi module
{
Serial.println("DNS-packets (" + String(packetSize) + ") from " + String(G_APDNSclientip) + " port " + String(G_DNSClientport));
for (t = 0; t < packetSize; ++t) {
Serial.print(String(G_UDPPacketbuffer[t], HEX));
Serial.print(":");
}
Serial.println(" ");
for (t = 0; t < packetSize; ++t) {
Serial.print(String((char)G_UDPPacketbuffer[t])); //Serial.print("");
}
Serial.println("");
//Copy Packet ID and IP into DNS header and DNS answer
G_DNSReplyheader[0] = G_UDPPacketbuffer[0];
G_DNSReplyheader[1] = G_UDPPacketbuffer[1]; // Copy ID of Packet offset 0 in Header
G_DNSReplyanswer[12] = G_APip[0];
G_DNSReplyanswer[13] = G_APip[1];
G_DNSReplyanswer[14] = G_APip[2];
G_DNSReplyanswer[15] = G_APip[3]; // copy AP Ip adress offset 12 in Answer
r = 0; // set reply buffer counter
p = 12; // set packetbuffer counter @ QUESTION QNAME section
// copy Header into reply
for (t = 0; t < DNSHEADER_SIZE; ++t) G_DNSReplybuffer[r++] = G_DNSReplyheader[t];
// copy Question into reply: Name labels till octet=0x00
while (G_UDPPacketbuffer[p] != 0) G_DNSReplybuffer[r++] = G_UDPPacketbuffer[p++];
// copy end of question plus Qtype and Qclass 5 octets
for (t = 0; t < 5; ++t) G_DNSReplybuffer[r++] = G_UDPPacketbuffer[p++];
//copy Answer into reply
for (t = 0; t < DNSANSWER_SIZE; ++t) G_DNSReplybuffer[r++] = G_DNSReplyanswer[t];
replySize = r;
Serial.println("* DNS-Reply (" + String(replySize) + ") from " + String(G_APip) + " port " + String(53));
for (t = 0; t < replySize; ++t) {
Serial.print(String(G_DNSReplybuffer[t], HEX));
Serial.print(":");
}
Serial.println();
for (t = 0; t < replySize; ++t) {
Serial.print(String((char)G_DNSReplybuffer[t])); //Serial.print("");
}
Serial.println("\n");
// Send DSN UDP packet
dns_flag &= (G_UDPAP_DNS->beginPacket(G_APDNSclientip, G_DNSClientport) > 0); //reply DNSquestion
uint8_t dns_count = 6;
G_UDPAP_DNS->write(G_DNSReplybuffer, replySize);
dns_flag &= (G_UDPAP_DNS->endPacket() > 0);
} // end loop correct IP
} // end loop received packet
return dns_flag;
}
void connectAP()
{
G_APip = IPAddress((char)random(11, 172), (char)random(0, 255), (char)random(0, 255), 0x01); // Generate random IP adress in Private IP range
WiFi.config(G_APip, G_APip, G_APip);
status = WiFi.beginAP(ssid, pass);
Serial.println(" beginAP status: " + String(status));
if (status != WL_AP_LISTENING) {
Serial.println("Creating access point failed");
do {
delay(1000);
status = WiFi.beginAP(ssid, pass);
Serial.println("status: " + String(status));
} while (status != WL_AP_LISTENING);
}
G_UDPAP_DNS->begin(53);
}
void setup() {
//Initialize serial and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("Access Point Web Server");
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
// check for the WiFi module:
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Communication with WiFi module failed!");
// don't continue
while (true)
;
}
// print the network name (SSID);
Serial.print("Creating access point named: ");
Serial.println(ssid);
// Create open network. Change this line if you want to create an WEP network:
connectAP();
// wait 6 seconds for connection:
delay(6000);
// start the web server on port 80
server->begin();
// you're connected now, so print out the status
printWiFiStatus();
}
void loop() {
/* ------ RESET CONNECTION AFTER 1m 15s ------ */
static uint64_t timer = millis();
if (millis() - timer > 75000)
{
Serial.println("\n----------- RECONNECTING -----------");
// clean-up
G_UDPAP_DNS->stop();
WiFi.disconnect();
WiFi.end();
delay(2000);
Serial.println(" end() status: " + String(WiFi.status()));
// deeper clean-up
delete G_UDPAP_DNS;
// delete server;
WiFi = * ((WiFiClass*) (new WiFiClass(WiFiInterface::get_default_instance())));
delay(100);
// server = new WiFiServer(80);
G_UDPAP_DNS = new WiFiUDP();
Serial.println(" new status: " + String(WiFi.status()));
// try to connect to inexistent network
status = WiFi.begin(ssid, pass);
Serial.println(" begin() status: " + String(status));
delay(500);
status = WiFi.begin(ssid, pass);
Serial.println(" begin() status: " + String(status));
delay(2000);
// clean-up
WiFi.disconnect();
WiFi.end();
delay(2000);
Serial.println(" end() status: " + String(WiFi.status()));
//deeper clean-up
WiFi = * ((WiFiClass*) (new WiFiClass(WiFiInterface::get_default_instance())));
delay(100);
Serial.println(" new status: " + String(WiFi.status()));
// create Access Point
connectAP();
delay(5000);
Serial.println(" AP ok status: " + String(WiFi.status()));
Serial.println("--------------------------------------\n");
//reset timer
timer = millis();
}
/* ----- DNS REDIRECTION FOR CAPTIVE PORTAL (UDP) ----- */
APDNSScan();
/* ------------ REST COMMUNICATION (TCP?) ------------- */
// compare the previous status to the current status
if (status != WiFi.status()) {
// it has changed update the variable
status = WiFi.status();
if (status == WL_AP_CONNECTED) {
// a device has connected to the AP
Serial.println("Device connected to AP");
} else {
// a device has disconnected from the AP, and we are back in listening mode
Serial.println("Device disconnected from AP");
}
}
WiFiClient client = server->available(); // listen for incoming clients
if (client) { // if you get a client,
Serial.println("new client"); // print a message out the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
delayMicroseconds(10); // This is required for the Arduino Nano RP2040 Connect - otherwise it will loop so fast that SPI will never be served.
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
// the content of the HTTP response follows the header:
client.print("Click <a href=\"/HR\">here</a> turn the RED LED on<br>");
client.print("Click <a href=\"/LR\">here</a> turn the RED LED off<br>");
client.print("Click <a href=\"/HG\">here</a> turn the GREEN LED ON<br>");
client.print("Click <a href=\"/LG\">here</a> turn the GREEN LED off<br>");
client.print("Click <a href=\"/BH\">here</a> turn the BLUE LED on<br>");
client.print("Click <a href=\"/BL\">here</a> turn the BLUE LED off<br>");
// The HTTP response ends with another blank line:
client.println();
// break out of the while loop:
break;
} else { // if you got a newline, then clear currentLine:
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
// Check to see if the client request (turns ON/OFF the different LEDs)
if (currentLine.endsWith("GET /HR")) {
digitalWrite(LED_RED, LOW);
}
if (currentLine.endsWith("GET /LR")) {
digitalWrite(LED_RED, HIGH);
}
if (currentLine.endsWith("GET /HG")) {
digitalWrite(LED_GREEN, LOW);
}
if (currentLine.endsWith("GET /LG")) {
digitalWrite(LED_GREEN, HIGH);
}
if (currentLine.endsWith("GET /BH")) {
digitalWrite(LED_BLUE, LOW);
}
if (currentLine.endsWith("GET /BL")) {
digitalWrite(LED_BLUE, HIGH);
}
}
}
// close the connection:
client.stop();
Serial.println("client disconnected");
}
}
void printWiFiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print where to go in a browser:
// Serial.print("To see this page in action, open a browser to http://");
// Serial.println(ip);
}
Hardware: Just the Arduino GIGA with its antenna.