Search code examples
javaandroidnetwork-programmingarduinoesp32

Sending UDP Packets from Android App to ESP32 does not work


I'm building an android app that should talk to an ESP32. The setup is the following:

  • The ESP32 configures itself as an access point
  • The ESP32 regularly sends a connection request via UDP broadcast
  • The APP connects the phone to the WiFi of the ESP32 (Credentials hardcoded for the moment)
  • The APP receives the connection request

Here is where I am stuck:

  • The APP answers to the sender (The ESP32) with an acknowledgement. Now ESP32 and APP should know each other.

I joined the WiFi with my laptop and watch the traffic via WireShark. All broadcast messages (and lots of other stuff) are visible, however, the APP never sends out an UDP packet.

I am very sure that the ESP32 side works, everytime I send udp packets from my laptop via python, they arrive at the ESP. I setup the Accespoint like this:

const IPAddress gateWay(192, 168, 1, 1);
const IPAddress localIP(192, 168, 1, 1);
const IPAddress subnet(0, 0, 0, 0);

void setup(){
  Serial.begin(115200);
  // sdUtils.begin();
  imu.begin(FILTER_UPDATE_RATE_HZ);

  // configure esp as access point
  WiFi.softAP(SSID, PASS);
  //WiFi.config(localIP, gateWay, subnet);
  WiFi.softAPConfig(localIP, gateWay, subnet);
  Serial.println(udp.begin(2255));
}

void loop(){
  // initiate connection
  while (!client.connected()) {
    Serial.println("Initializing connection");
    // broadcast IP via UDP broadcast
    String ipAddress = WiFi.localIP().toString();
    udp.beginPacket(WiFi.broadcastIP().toString().c_str(), 2255);
    udp.println(ipAddress);
    udp.endPacket();
      
    // parse answer
    int packetSize;
    unsigned long startTime = millis();
    do {
      packetSize = udp.parsePacket();
    } while (!packetSize && (millis() - startTime) < 5000);
    if (packetSize) {
      // read answer which is IP adress
      char buffer[packetSize];
      Serial.println(udp.remoteIP().toString());
      udp.read(buffer, packetSize);
      String phoneIP = buffer;
      Serial.println(phoneIP); 

      // Establish a TCP connection to the phone
      client.connect(phoneIP.c_str(), 8080);

      // Send data to the phone
      client.println("Hello from the ESP32!");
    } else {
      Serial.println("Server dindn't answer...");
    }
  }
}

This is how I try to send UDP packages in my android app:

public class Connectivity {
    private final Context context;
    private WifiManager wifiManager;
    private Handler handler;

    private DatagramSocket socket;


    public Connectivity(Context context) {

        this.context = context;
        this.handler = new Handler(Looper.getMainLooper());

        try {
            wifiManager = (WifiManager) this.context.getSystemService(Context.WIFI_SERVICE);
            socket = new DatagramSocket(2255);
            socket.setBroadcast(true);
        } catch (IOException e) {
            Log.e(TAG, "failed to create UDP socket", e);
        }
    }


    public void connectSensor(String ssid, String password) {

        // make sure wifi is enabled
        if (!wifiManager.isWifiEnabled()) {
            wifiManager.setWifiEnabled(true);
        }

        establishConAfterQ(ssid, password);
    }

    public void disconnectSensor() {
        if (wifiManager.isWifiEnabled()) {
            wifiManager.disconnect();
            wifiManager.setWifiEnabled(false);
        }
    }

    public void start() {
        new Thread(this::receive).start();
    }

    public void receive() {
        while (true) {
            byte[] buffer = new byte[128];
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

            try {
                socket.receive(packet);
            } catch (IOException e) {
                Log.e(TAG, "Failed to receive UDP packet", e);
                continue;
            }

            String message = new String(buffer, 0, packet.getLength());
            Log.d(TAG, "Received UDP packet: " + message.replace("\n", "") + " from IP address: " + packet.getAddress().toString() + " at port: " + packet.getPort());

            // Send the answer
            Log.i(TAG, "Sending Answer");
            String answer = "Heureka it works!!";
            byte[] data = answer.getBytes();
            DatagramPacket response = new DatagramPacket(data, data.length, packet.getAddress(), packet.getPort());

            try {
                socket.send(response);
            } catch (IOException e) {
                Log.e(TAG, "Failed to send UDP packet", e);
            }
        }
    }

The function where I connect to the wifi looks like the following:

private void establishConAfterQ(String ssid, String password) {
        WifiEnterpriseConfig config = new WifiEnterpriseConfig();
        config.enableTrustOnFirstUse(true);
        config.setPassword(password);
        WifiNetworkSpecifier specifier = new WifiNetworkSpecifier.Builder()
                .setSsid(ssid)
                .setWpa2Passphrase(password)
                .build();
        NetworkRequest networkRequest = new NetworkRequest.Builder()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .setNetworkSpecifier(specifier)
                .build();

        ConnectivityManager connectivityManager = (ConnectivityManager) this.context.getSystemService(Context.CONNECTIVITY_SERVICE);

        // Register a callback to listen for the connection result
        ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(android.net.Network network) {
                super.onAvailable(network);
                Log.d(TAG, "Connected to WiFi network: " + ssid);
            }

            @Override
            public void onUnavailable() {
                super.onUnavailable();
                Log.d(TAG, "Failed to connect to WiFi network: " + ssid);
            }
        };

        // Request the network connection
        connectivityManager.requestNetwork(networkRequest, networkCallback);
    }

Weirdly enough, everything worked nicely for a brief time, when I disconnected the phone from the computer and logged into the wifi hotspot manually. However, I could not reproduce this behaviouir thereafter.

Am I missing something like a configuration for the connection so that I cannot send UDP commands? I have searched Google quite intensively, however I haven't found anything.

UPDATE I found out that I can send udp packages from android to the broadcast address (255.255.255.255) or to the IP address of the phone hosting the APP. However, I cannot reach any IP other than those (or at least I haven't found one). So is there maybe a hidden firewall built into android?

Thanks for any help!


Solution

  • There seems no hidden firewall in android.

    I use the broadcast, but not to (255.255.255.255).

    Just use the localIP from the wifiudp class object, and set the 4th octet to 255, i.e. pertaining only to the current segment.