GIGA R1, conflict between Access Point and common WiFi connection

Hello everyone,

I am facing some issues while trying, in a larger project, to accomplish the following:

  1. Try to connect to the internet via WiFi
  2. If connection fails, open Access Point (AP) and let user insert WiFi credentials (through captive portal)
  3. 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.