Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GIGA R1, WiFiUDP::endPacket() fails after release 4.1.3 #944

Open
giorgiogallina opened this issue Aug 22, 2024 · 1 comment
Open

GIGA R1, WiFiUDP::endPacket() fails after release 4.1.3 #944

giorgiogallina opened this issue Aug 22, 2024 · 1 comment
Assignees
Labels
bug Something isn't working

Comments

@giorgiogallina
Copy link

Dear developers,

following issue #858, which is still making me struggle, I am now facing the following issue:
If I try to send an UDP packet in Access Point mode, opened after having unsuccesfully tried to connect to some networks, the function endPacket() always fails after updating to 4.1.3 release of Arduino Mbed OS GIGA Boards. The problem persists in release 4.1.5.

A bit of background: in my project, I need to:

  1. try to connect to some WiFi network,
  2. If/when the connection fails, open Access Point and, through a captive portal, ask user to insert SSID and pwd to their WiFi connection (needed on first start and later, in case of credentials changes, for instance),
  3. Connect to the WiFi with the new credentials.

I am attaching a simplified version of my code below, but I guess you can easily build up one on your own.

Many thanks for your contribution!

/*
  Adaptation of examples created by Tom Igoe and modified by Karl Söderby
 */

#include <WiFi.h>
#include <WiFiUdp.h>
char ssid[] = "arduinoAP";  // your network SSID (name)
char pass[] = "qwerty123";  // your network password (use for WPA, or use as key for WEP)


#define DBGON  // Debug option  -serial print
#define DBGONP
#define DBGON_X   // Debug option - incl UDP (DNS) packets

// Define UDP settings for DNS 
#define UDP_PACKET_SIZE 1024          // UDP packet size time out, preventign too large packet reads
#define DNS_HEADER_SIZE 12     // DNS Header
#define DNS_ANSWER_SIZE 16     // DNS Answer = standard set with Packet Compression
#define DNS_MAX_REQUESTS 32    // trigger first DNS requests, to redirect to own web-page
#define AP_DNS_PORT  53        // local port to listen for UDP packets
#define AP_CHANNEL  5          // AP wifi channel


WiFiUDP udp;       // A UDP instance to let us send and receive packets over UDP
IPAddress ap_ip;          // Global Acces Point IP adress

int status = WL_IDLE_STATUS;
WiFiServer server(80);


bool AP_DNSRedirect(void)
{
  int t = 0;     // loop index
  int flag = 0;  // conditions check
  uint16_t packet_size = 0;
  uint16_t reply_size = 0;
  IPAddress client_ip;
  int client_port;
  byte packet_buffer[UDP_PACKET_SIZE + DNS_ANSWER_SIZE];   // buffer to hold incoming and outgoing packets

  // get packet size, if available
  packet_size = udp.parsePacket();

  // something went wrong, return with error
  if (packet_size > UDP_PACKET_SIZE)
  {
    Serial.println("DNS INFO: too large packet Error, size " + String(packet_size));
    return false;
  }

  // packet available, read it
  if (packet_size > 0)
  {
    // read packet from client
    int plen = udp.read(packet_buffer, packet_size);
    if (plen > 0  &&  plen < UDP_PACKET_SIZE)   // just to be sure
    { packet_buffer[plen] = 0;
    }

    // get client address
    client_ip = udp.remoteIP();
    client_port = udp.remotePort();

    // skip own requests (ntp-pool time request from WiFi module)
    if (client_ip != ap_ip)
    {
      // print debug info
        Serial.println("DNS INFO: received " + String(packet_size) + "bytes from " + String(client_ip) + "-" + String(client_port));
        Serial.print("    ");
        for (t = 0; t < packet_size; ++t) {
          Serial.print(String(packet_buffer[t], HEX));
          Serial.print(":");
        }
        Serial.println();

      // ----------  BUILD REPLY ON REQUEST  ---------- //
      // change HEADER
      packet_buffer[2]  = 0x81;   // answare header code
      packet_buffer[3]  = 0x80;   // answare header code
      packet_buffer[4]  = 0x00;   // QDCOUNT = 1 question
      packet_buffer[5]  = 0x01;   // QDCOUNT = 1 question
      packet_buffer[6]  = 0x00;   // ANCOUNT = 1 answer
      packet_buffer[7]  = 0x01;   // ANCOUNT = 1 answer
      packet_buffer[8]  = 0x00;   // NSCOUNT / ignore
      packet_buffer[9]  = 0x00;   // NSCOUNT / ignore
      packet_buffer[10] = 0x00;   // ARCOUNT / ignore
      packet_buffer[11] = 0x00;   // ARCOUNT / ignore
      // retain end of question plus Qtype and Qclass 5 octets
      for (reply_size = 12;  packet_buffer[reply_size] != 0;  reply_size++);
      reply_size += 5;
      // add answer to reply
      packet_buffer[reply_size++] = 0xc0;      // pointer to pos 12 : NAME Labels
      packet_buffer[reply_size++] = 0x0c;      // pointer to pos 12 : NAME Labels
      packet_buffer[reply_size++] = 0x00;      // TYPE
      packet_buffer[reply_size++] = 0x01;      // TYPE
      packet_buffer[reply_size++] = 0x00;      // CLASS
      packet_buffer[reply_size++] = 0x01;      // CLASS
      packet_buffer[reply_size++] = 0x00;      // TTL
      packet_buffer[reply_size++] = 0x00;      // TTL
      packet_buffer[reply_size++] = 0x18;      // TLL 2 days
      packet_buffer[reply_size++] = 0x4c;      // TLL 2 days
      packet_buffer[reply_size++] = 0x00;      // RDLENGTH = 4
      packet_buffer[reply_size++] = 0x04;      // RDLENGTH = 4
      packet_buffer[reply_size++] = ap_ip[0]; // AP ip address
      packet_buffer[reply_size++] = ap_ip[1]; // AP ip address
      packet_buffer[reply_size++] = ap_ip[2]; // AP ip address
      packet_buffer[reply_size++] = ap_ip[3]; // AP ip address

      // print debug info
        Serial.println("DNS INFO: reply " + String(reply_size) + " bytes from " + String(ap_ip) + "-" + String(AP_DNS_PORT));
        Serial.print("    ");
        for (t = 0; t < reply_size; ++t) {
          Serial.print(String(packet_buffer[t], HEX));
          Serial.print(":");
        }
        Serial.println();
        Serial.println("    " + String((char*)packet_buffer));


      // ----------  START SENDING REPLY  ---------- //
      flag = udp.beginPacket(client_ip, client_port);
      Serial.println("DNS INFO: beginPacket() -> " + String(flag));

      udp.write(packet_buffer, reply_size);

      flag = udp.endPacket();
      Serial.println("DNS INFO: endPacket() -> " + String(flag));

      if (flag <= 0)
      {
        Serial.println("DNS ERROR: endPacket() Failed.");
        return false;
      }
    }
  }

  return true;
}


void wifiCleanup(void)
{
  udp.stop();  // should be ok even on non-initialized UDP
  delay(2);

  WiFi.disconnect();
  WiFi.end();
  delay(20);

  // WiFi = WiFiClass(WiFiInterface::get_default_instance());
}


void printWiFiStatus(void)
{
  Serial.println("-- WIFI INFO ----------------------------");
  Serial.print  ("      SSID: ");   Serial.println(WiFi.SSID());
  Serial.print  ("   IP addr: ");   Serial.println(WiFi.localIP());
  Serial.print  ("  IP gatew: ");   Serial.println(WiFi.gatewayIP());
  Serial.print  ("      RSSI: ");   Serial.print(WiFi.RSSI());   Serial.println("dBm");
  Serial.println("-----------------------------------------");
}




void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(115200);
  while (!Serial) {
    ;  // wait for serial port to connect. Needed for native USB port only
  }

  Serial.println("Access Point Web Server");


  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true)
      ;
  }

  // start the web server on port 80
  server.begin();

  // -----    TRY TO CONNECT TO SOME NETWORK...  ----- //
  Serial.println("Connecting to inexistent network...");
  status = WiFi.begin("unkwnown", "password1");
  Serial.print("WIFI STATUS: ");   Serial.println(status);
  printWiFiStatus();

  // delay(5000);
  wifiCleanup();
  Serial.print("WIFI STATUS: ");   Serial.println(WiFi.status());
  printWiFiStatus();

  // connection failed, now open access point
  // -----  OPEN ACCESS POINT  ----- //

  // print the network name (SSID);
  Serial.print("Creating access point named: ");
  Serial.println(ssid);

  ap_ip = IPAddress((char)random(11, 172), (char)random(0, 255), (char)random(0, 255), 0x01);  // Generate random IP adress in Private IP range
  WiFi.config(ap_ip, ap_ip, ap_ip, IPAddress(255, 255, 255, 0));      // Setup config


  // Create open network. Change this line if you want to create an WEP network:
  status = WiFi.beginAP(ssid, pass);
  if (status != WL_AP_LISTENING) {
    Serial.println("Creating access point failed");
    // don't continue
    while (true);
  }

  // wait some seconds for connection:
  delay(6000);

  // you're connected now, so print out the status
  printWiFiStatus();



  int udp_err = udp.begin(AP_DNS_PORT);
  Serial.println("UDP begin(" + String(AP_DNS_PORT) + "): " + String((int)udp_err));


  Serial.println("Setup completed. Ready for connection.");
}





void loop() {
  // 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");
    }
  }

  // use UDP to hijack client to our captive portal
  AP_DNSRedirect();

  // listen for incoming clients
  WiFiClient client = server.available();

  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 are 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");
  }
}
@facchinm facchinm added the bug Something isn't working label Aug 28, 2024
@giorgiogallina
Copy link
Author

Some further details that might help.

I was debugging my code and discovered that even on MBED OS Release 4.1.1 WiFiUDP::endPacket() fails if it was preceded by some attempts to connect with a wrong password to an exisiting network. If I try to connect to some networks that does not exist, on the other hand, everything works fine.

I was tempted to open a new issue and link it here, but I guess it can be treated as a single issue.

Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants