[SOLVED] UDP receive not working on Electron?

Hello group,

I am not getting UDP receive to actually receive anything on the Electron. At some level it has to because that is how it talks to the Particle cloud servers. But it does not seem to work for me in my app.

I have reduced my code down to this test. When compiled for, and run on, the Photon it works as expected. But when compiled for the Electron I never received any UDP datagrams.

The code sends a UDP datagram every 10 seconds to the public IP address of a server out at AWS. I use the log from that server to send a UDP datagram back to the Electron.

In order to test with the Photon, I have configured my home router to forward the datagrams that arrive from that AWS server to the Photon’s IP address on my home network. As I say, that all works as expected.

There could be several things that will prevent me from receiving UDP on the Electron: The cellular provider, the code inside the UBLOX module, etc. But all of these have been solved because the Particle cloud services work great on the Electron, right?

Does the cellular provider implement a UDP port white-list that Particle has an entry in that allows the Particle cloud communication to work?

Thinking there might be a conflict inside the firmware or the UBLOX, the test code pasted here uses MANUAL mode in an attempt to keep the firmware from starting up any of the UDP objects/code. But this has not helped.

I’ve since read the UBLOX documentation and it looks like it supports 7 sockets of mixed TCP or UDP type, so perhaps that is not an issue.

Note that I create a #define for “Network” so that the code works on either the Particle or the Electron. I get the same results if I change those references to “Cellular” and compile only for the Electron.

Any ideas?

My test code:


SYSTEM_MODE(MANUAL);

#if defined(SPARK)
  #if defined(PLATFORM_ID)
    #if (PLATFORM_ID == 0)
      //Core
#define Network WiFi
    #elif (PLATFORM_ID == 6)
         //Photon
#define Network WiFi
    #elif (PLATFORM_ID == 10)
         //Electron
#define Network Cellular
    #endif
  #endif  //PLATFORM_ID
#endif  //SPARK

struct myLoc {
    float p_lat;
    float p_long;
    short speed;
    short track;
    short alt;
    unsigned short seq;
    };


#define htons(n) (((((unsigned short)(n) & 0xFF)) << 8) | (((unsigned short)(n) & 0xFF00) >> 8))
#define ntohs(n) (((((unsigned short)(n) & 0xFF)) << 8) | (((unsigned short)(n) & 0xFF00) >> 8))


IPAddress remoteIP(54,0,0,0);  // Address changed to keep my server private
int remotePort = 32001;
UDP udp;

struct myLoc locBuf;

char udpbuf[40];
unsigned long lastCom;


void myUdp()
{
static unsigned short seq = 1;

    locBuf.seq = htons(seq);
    seq++;

    if (udp.sendPacket((char *)&locBuf, sizeof(locBuf), remoteIP, remotePort)<0) {
        digitalWrite(D7, HIGH);
    }
}


void setup()
{
    pinMode(D7, OUTPUT);
    digitalWrite(D7, HIGH);
    Serial.begin(9600); // USB Serial
  
    for(int yy = 0; yy < 25; yy++)
    {
      digitalWrite(D7, LOW);
      delay(200);
      digitalWrite(D7, HIGH);
      delay(200);
    }

    digitalWrite(D7, HIGH);
    Network.on();
    Network.connect();

    while(Network.connecting())
    {
        delay(500);
    }
    delay(700);
    udp.begin(32001);
    digitalWrite(D7, LOW);
    lastCom = millis();
}

void loop()
{
int ulen;
struct myLoc *ml;
 


    ulen = udp.receivePacket(udpbuf, 30);
    if (ulen > 0)
    {
Serial.print("udp length: ");
Serial.println(ulen);

        if(ulen == 16)
        {
            ml = (struct myLoc *)udpbuf;
            locBuf.speed = ntohs(ml->speed);
            locBuf.track = ntohs(ml->track);
            locBuf.alt = ntohs(ml->alt);
            locBuf.p_lat = ml->p_lat;
            locBuf.p_long = ml->p_long;
Serial.print("Got speed: ");
Serial.println(locBuf.speed);
        }
    }

digitalWrite(D7, LOW);

    // Send our request UDP packet every 10 seconds
    if((millis() - lastCom) > 10000)
    {
        locBuf.speed = htons(-2);
        locBuf.track = htons(-2);
        locBuf.alt = htons(-2);
Serial.println("Sending UDP request.");
        myUdp();

        lastCom = millis();
    }

}

I compile via the Particle cli with:

particle compile photon .

or

particle compile electron .

I was able to send a UDP packet from an Electron to a public-facing server and get a response back by UDP to the Electron. I used a node.js server. One thing to beware of - the server must to respond to the port that the packet came from, not the port that you are listening on on the Electron. The reason is that the gateway between cellular and Internet may do port mapping.

Electron code:

#include "Particle.h"

const uint16_t UDP_LOCAL_PORT = 7123;
const unsigned long sendPeriodMs = 30000;
const size_t UDP_BUFFER_SIZE = 512;

UDP udp;
IPAddress remoteAddr(65,19,178,42);
uint16_t remotePort = 7123;
unsigned long lastSend = 0 - 20000;
char udpBuffer[UDP_BUFFER_SIZE+1];
int sendSeq = 0;

void setup() {
	Serial.begin(9600);

	udp.begin(UDP_LOCAL_PORT);
}

void loop() {
	if (millis() - lastSend >= sendPeriodMs) {
		lastSend = millis();

		int len = snprintf(udpBuffer, UDP_BUFFER_SIZE, "send %d", ++sendSeq);
		udp.sendPacket(udpBuffer, len, remoteAddr, remotePort);

		Serial.printlnf("sent packet seq=%d", sendSeq);
	}

	int count = udp.receivePacket(udpBuffer, UDP_BUFFER_SIZE);
	if (count > 0) {
		udpBuffer[count] = 0;
		IPAddress remoteAddr = udp.remoteIP();

		Serial.printlnf("## packet size=%d addr=%s port=%d data=%s", count, remoteAddr.toString().c_str(), udp.remotePort(), udpBuffer);
	}
	else
	if (count == 0) {
		// No packet
	}
	else {
		Serial.printlnf("** receivePacket error %d", count);
		udp.begin(UDP_LOCAL_PORT);
	}

}

Sample Electron serial log:

sent packet seq=7
## packet size=15 addr=65.19.178.42 port=7123 data=response packet
sent packet seq=8
## packet size=15 addr=65.19.178.42 port=7123 data=response packet

Server code (node.js):

const dgram = require('dgram');
const server = dgram.createSocket('udp4');

server.on('error', (err) => {
  console.log(`server error:\n${err.stack}`);
  server.close();
});

server.on('message', (msg, rinfo) => {
  console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
  
  var msg = new Buffer('response packet');
  server.send(msg, 0, msg.length, rinfo.port, rinfo.address);
});

server.on('listening', () => {
  var address = server.address();
  console.log(`server listening ${address.address}:${address.port}`);
});

server.bind(7123);

Sample server log:

node udptest.js 
server listening 0.0.0.0:7123
server got: send 7 from 176.83.209.198:10841
server got: send 8 from 176.83.209.198:10841
1 Like

That would do it. I’ll make the change to my server code (to send to the port the message came from).

-bill

I changed my server code to keep & reply to the port the packet arrived from and it works, of course.

It had not occurred to me that they may well do port translation as well.

Thanks for the help!

-bill

1 Like

Can we mark this solved then?

Yes. Solved.

1 Like