Trouble reading strings with Udp

i need to send strings back and forth between two devices using UDP.
An Arduino UNO with an Adafruit WiFi shield is one of them and right now, my windows PC running SocketTest v 3.0.0 is the other.

I found a sketch for the UNO shown below. I send "aaa" from SocketTest and this is the result on the UNO:

Received packet of size 3
From 192.168.1.36
Contents:

Notice there is nothing shown after "Contents:". The packet buffer data is only 3 chars so there is no string terminator. I tried to add a '\0' (string terminator) with packetBuffer[packetSize + 1] = '\0'; but that didn't help.

What is a good way to receive a string with UDP?

void loop()
{
  // if there's data available, read a packet
  int packetSize = Udp.parsePacket();
  if (packetSize) {
    Serial.print("Received packet of size ");
    Serial.println(packetSize);
    Serial.print("From ");
    Serial.println((IPAddress)Udp.remoteIP());

    // read the packet into packetBufffer
    Udp.read(packetBuffer, packetSize);
    Serial.println("Contents:");
    Serial.println(packetBuffer);

    // send a reply to the IP address and port that sent us the packet we received
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Udp.write("Acknowledged");
    Udp.endPacket();
  }
  delay(10);
}

Notice there is nothing shown after "Contents:".

Notice that there is NOTHING to prove that.

    Serial.println(packetBuffer);

Did you fail to notice that packetBuffer is NOT a string? A string is a NULL terminated array of chars. The data from read is an array. The array is NOT NULL terminated.

Exactly... That is why tried a 0 at the end to make it look like string, but that did not work. I also tried using a NULL at the end.

If I make packetBuffer a string which would seen to make sense, the Udp.Read fails because it wants to read into a character array.

There doesn't seem to be a Udp.readln or a Udp.readline, so the question remains, how does one use Udp to pass strings?

Exactly... That is why tried a 0 at the end to make it look like string, but that did not work. I also tried using a NULL at the end.

Where, in the code you posted, does that happen?

If I make packetBuffer a string which would seen to make sense, the Udp.Read fails because it wants to read into a character array.

A string is a NULL terminated array of chars. A String is something completely different. Which did you try to use?

Where/how is packetBuffer declared?

The the line of code I used to do that is in the comments.

Notice there is nothing shown after “Contents:”. The packet buffer data is only 3 chars so there is no string terminator. I tried to add a ‘\0’ (string terminator) with packetBuffer[packetSize + 1] = ‘\0’; but that didn’t help.

And, the buffer is 255 bytes.

You'll get a lot further if you post all of your code, not just a snippet. How can we test it, if it's not all there?

Perhaps this will help. The entire Example code that was included with the arduino WiFi101 library is shown below. It has not been modified by me at all. It is what I was trying to use as a basis for my project. It is being used with the AdaFruit WiFi shield on an Arduino UNO. It exhibits the problem originally described in my OP above. This code depends on an “arduino_secrets.h” file which has only two lines:
#define SECRET_SSID “”
#define SECRET_PASS “”
Simply add your WiFi name and password.

If someone could help me understand how to make this work with strings, it would be helpful.
BTW: the name of the file is WiFiUdpSendReceiveString.ino. Clearly it was meant to send and receive Strings.

Thanks…

/*
  WiFi UDP Send and Receive String

 This sketch wait an UDP packet on localPort using a WiFi shield.
 When a packet is received an Acknowledge packet is sent to the client on port remotePort

 Circuit:
 * WiFi shield attached

 created 30 December 2012
 by dlf (Metodo2 srl)

 */


#include <SPI.h>
#include <WiFi101.h>
#include <WiFiUdp.h>

int status = WL_IDLE_STATUS;
#include "arduino_secrets.h" 
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;    // your network SSID (name)
char pass[] = SECRET_PASS;    // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0;             // your network key Index number (needed only for WEP)

unsigned int localPort = 2390;      // local port to listen on

char packetBuffer[255]; //buffer to hold incoming packet
char  ReplyBuffer[] = "acknowledged";       // a string to send back

WiFiUDP Udp;

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
  }

  // check for the presence of the shield:
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue:
    while (true);
  }

  // attempt to connect to WiFi network:
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(10000);
  }
  Serial.println("Connected to wifi");
  printWiFiStatus();

  Serial.println("\nStarting connection to server...");
  // if you get a connection, report back via serial:
  Udp.begin(localPort);
}

void loop() {

  // if there's data available, read a packet
  int packetSize = Udp.parsePacket();
  if (packetSize)
  {
    Serial.print("Received packet of size ");
    Serial.println(packetSize);
    Serial.print("From ");
    IPAddress remoteIp = Udp.remoteIP();
    Serial.print(remoteIp);
    Serial.print(", port ");
    Serial.println(Udp.remotePort());

    // read the packet into packetBufffer
    int len = Udp.read(packetBuffer, 255);
    if (len > 0) packetBuffer[len] = 0;
    Serial.println("Contents:");
    Serial.println(packetBuffer);

    // send a reply, to the IP address and port that sent us the packet we received
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Udp.write(ReplyBuffer);
    Udp.endPacket();
  }
}


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 the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}
    int len = Udp.read(packetBuffer, 255);
    if (len > 0) packetBuffer[len] = 0;
    Serial.println("Contents:");
    Serial.println(packetBuffer);

Before treating the array as a string, it might be useful to see what, exactly, is in the array. Perhaps the array contains nothing but non-printable characters.

   for(byte i=0; i<len; i++)
   {
       Serial.print("packetBuffer[");
       Serial.print(i);
       Serial.print("] = 0x");
       Serial.println(packetBuffer[i], HEX);
   }

Now we are getting somewhere. As you can see below, your suggestion shows there is nothing printable in the packet buffer. The Udp.read looks okay and it correctly determines that 3 chars were sent. So what’s going on?

Here is what I did:

// read the packet into packetBufffer
    int len = Udp.read(packetBuffer, 255);
    if (len > 0) packetBuffer[len] = 0;
    Serial.println("Contents:");
    for (int i = 0; i < len; i++)
    {
      Serial.print(packetBuffer[i], HEX);
      Serial.print(" ");
    }

And here is the result of sending “aaa”:

Received packet of size 3
From 192.168.1.36, port 62166
Contents:
0 0 FFFFFFAB

I have found a temporary workaround. If the packetBuffer size is 197 or less, it works fine. Anything larger than 197 breaks it. So the workaround is keeping the buffer size less than 198.

I suspect the problem is in the WiFi101 library because 197/198 is a really odd boundary. If it was 127/128 then I would understand.

When the sketch compiles, there is plenty of room for program space and local variables.

Sketch uses 24426 bytes (75%) of program storage space. Maximum is 32256 bytes.
Global variables use 1484 bytes (72%) of dynamic memory, leaving 564 bytes for local variables. Maximum is 2048 bytes.

Can someone verify this issue by duplicating it? If so, I'll figure out how to report it.

One of the issues with your code is that Udp.read() reads an array of bytes, not chars.

Per your suggestion, I tried to change the packetBuffer to byte but then the Serial.println(packetBuffer) fails. I also tried to change the packetBuffer to unsigned char and the Serial.println fails that also.

I wanted to be certain what type the read is supposed to return, so I took a peek into the WiFi101.cpp library file and the code for the read is below. Isn’t that saying it wants a char buffer? There is no other read routine in there that use a byte buffer that I can see.

int WiFiUDP::read(unsigned char* buf, size_t size)
{
	// sizeof(size_t) is architecture dependent
	// but we need a 16 bit data type here
	uint16_t size_tmp = available();

	if (size_tmp == 0) {
		return -1;
	}

	if (size < size_tmp) {
		size_tmp = size;
	}

	int result = WiFiSocket.read(_socket, buf, size);

	if (result > 0) {
		_parsedPacketSize -= result;
	}

	return result;
}

but then the Serial.println(packetBuffer) fails.

It IS possible to print the array, IF you know what you are doing.

Fails how?

Isn't that saying it wants a char buffer?

No. Careful reading is important. It says that it expects an unsigned char array.

Your homework assignment is now to determine the relationship between a byte and a char, and between a byte and an unsigned char. Hint: Is char signed or unsigned, by default?

In the code below, I moved the packetBuffer into the loop routine. The print can no longer work as Serial.print(packetBuffer); so I converted the packet buffer into a string for printing. I hope the byte/char/unsigned char question is now resolved.

The sketch works just fine as long as the packetBuffer is less than 198 unsigned chars in length. Anything longer still causes it to fail as described in the OP.

Any ideas on what you think might be the problem?

/*
  WiFi UDP Send and Receive String

  This sketch wait an UDP packet on localPort using a WiFi shield.
  When a packet is received an Acknowledge packet is sent to the client on port remotePort

  Circuit:
   WiFi shield attached

  created 30 December 2012
  by dlf (Metodo2 srl)

*/

#include <SPI.h>
#include <WiFi101.h>
#include <WiFiUdp.h>

int status = WL_IDLE_STATUS;
#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;    // your network SSID (name)
char pass[] = SECRET_PASS;    // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0;             // your network key Index number (needed only for WEP)

unsigned int localPort = 2390;      // local port to listen on
char ReplyBuffer[] = "acknowledged";       // a string to send back

WiFiUDP Udp;

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
  }

  // check for the presence of the shield:
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue:
    while (true);
  }

  // attempt to connect to WiFi network:
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(10000);
  }
  Serial.println("Connected to wifi");
  printWiFiStatus();

  Serial.println("\nStarting connection to server...");
  // if you get a connection, report back via serial:
  Udp.begin(localPort);
}

void loop()
{
  unsigned char packetBuffer[197];
  // if there's data available, read a packet
  int packetSize = Udp.parsePacket();
  if (packetSize)
  {
    Serial.print("Received packet of size ");
    Serial.println(packetSize);
    Serial.print("From ");
    IPAddress remoteIp = Udp.remoteIP();
    Serial.print(remoteIp);
    Serial.print(", port ");
    Serial.println(Udp.remotePort());

    // read the packet into packetBufffer
    int len = Udp.read(packetBuffer, 197);
    if (len > 0) packetBuffer[len] = 0;
    Serial.println("Contents:");
    String str = packetBuffer;
    Serial.println(str);
    // send a reply, to the IP address and port that sent us the packet we received
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Udp.write(ReplyBuffer);
    Udp.endPacket();
  }
}

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 the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}
  unsigned char packetBuffer[197];

Otherwise known as a byte.

    String str = packetBuffer;

You can NOT pass a non-NULL terminated char array to a function that expects a string, no matter how badly you want to.

The equal operator IS a function.

I thought it was terminated with the line if (len > 0) packetBuffer[len] = 0;

PickyBiker:
I thought it was terminated with the line if (len > 0) packetBuffer[len] = 0;

I missed that, because you are not following the convention of ONE STATEMENT PER LINE.

There is NO reason to use the String class. Why are you?

I suspect that your UDP or wifi library has a size restriction. Wikipedia tells me that UDP and IPV4 add 28 bytes to your payload to make up the bytes that go on the wire, so maybe that limit is 225.

UDP packets can supposedly be up to 65,507 bytes, but clearly that can't be buffered on an Uno, so somewhere there's likely an arbitrary cutoff. You'll need to dig into the libraries to find it.

If you're unlucky, that limit will be implicit, not enforced and large packets will have the potential to crash your system. I've seen that with ethernet libraries before.

PaulS, As I mentioned above, "The print can no longer work as Serial.print(packetBuffer); so I converted the packet buffer into a string for printing."

I also mentioned this print problem earlier in our discussion. I would like to continue the conversation focusing on why doesn't this work if the buffer is larger than 197 unsigned chars?

Can we please do that?

wildbill, thank you for addressing this.

UDP packets can supposedly be up to 65,507 bytes, but clearly that can't be buffered on an Uno, so somewhere there's likely an arbitrary cutoff. You'll need to dig into the libraries to find it.

I agree, this is likely something in the library. But, I am definitely not an expert and when I looked into the library, there was much code in there that is too sophisticated for me. I was hoping to find someone who could duplicate this problem and then I would be confident enough to raise an issue on the library.

I will also note here that if you google "send strings using udp on Arduino" you will find several examples and all of them, including the one on the official arduino website use 255 for a buffer size. I conclude from that it must have worked at one time or another.

If there are no comments suggesting other possible causes in the next day or two, I will go to github and submit an issue on this for the WiFi101 library.

Thanks again.