UDP string

Hi

I want to strip some data/bytes from a UDP string

With Sharkwire I looked at the sting and the red tekst has the needed info:

0000 ff ff ff ff ff ff 00 22 68 73 15 3e 08 00 45 00 ÿÿÿÿÿÿ."hs.>…E.
0010 00 53 30 dd 00 00 80 11 83 3a c0 a8 02 33 c0 a8 .S0Ý…:À¨.3À¨
0020 02 ff 26 8f 26 8f 00 3f 86 d3 53 54 41 54 55 53 .ÿ&.&…?.ÓSTATUS
0030 3a 20 22 6e 61 6d 65 2d 31 22 20 22 22 20 30 20 : “name-1” “” 0
0040 33 20 30 20 30 20 37 30 32 31 30 20 22 30 22 20 3 0 0 70210 “0”
0050 30 20 22 31 22 20 20 37 30 33 35 30 20 22 22 de 0 “1” 70350 ""Þ
0060 00 .

On the arduino serial monitor it looks like:
STATUS: “name-1” “” 0 3 0 0 70210 “0” 0 “1” 70350 “”⸮

I want to read the “name-1” that can be up to 10 charaters long.
I used the code of UDPSendReceiveString and modufied it a bit to read the the string
I have no clue what will be the correct way to do this

Peter

#include <SPI.h>         // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h>         // UDP library from: bjoern@cs.stanford.edu 12/30/2008


byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 2, 177);

#define UDP_TX_PACKET_MAX_SIZE 100

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

// buffers for receiving and sending data
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,

// An EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;

void setup() {
  // start the Ethernet and UDP:
  Ethernet.begin(mac, ip);
  Udp.begin(localPort);

  Serial.begin(9600);
}

void loop() {
  // if there's data available, read a packet
  int packetSize = Udp.parsePacket();
  if (packetSize)
  {
     // read the packet into packetBufffer
    Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
      Serial.println(packetBuffer);
  }
}

You can use strstr() to find a text in a text. That will give you a pointer to "name-1". Next you can move the pointer a number of positions forward to where the actual data starts and copy that using memcpy() to an other array . You will need to keep space for a terminating nul character.

On a cell phone at the moment so can't work out an example.

I assume the field "name-1" will change giving you the data required
using strstr(), as @sterretje suggested, search for "STATUS" and the "name-1" field follows it
e.g. the code searches for STATUS then print the name-1 field looking for the terminaling "

  Serial.begin(115200);
  char packetBuffer[]={"  .ÿ&.&..?.ÓSTATUS:\"name-1\" \"\" 0 3 0 0 70210 \"0\" 0 \"1\"  70350 \"\""};
  Serial.println(packetBuffer);
  char * ptr=strstr(packetBuffer, "STATUS");
  for (int i=0;*(ptr+8+i) != '\"';i++)
      Serial.print(*(ptr+8+i));

prints

  .ÿ&.&..?.ÓSTATUS:"name-1" "" 0 3 0 0 70210 "0" 0 "1"  70350 ""
name-1

however, your packets have 0's which will cause problems as strstr() looks for null terminated strings

This might be a heresy but can you read the packet bytes as they arrive instead of waiting for the whole set?

It's not too hard to watch for a series of bytes arrive and then buffer only the bytes you want.

char lookFor = { 53, 54, 41, 54, 55, 53, 3a, 20, 22 }; // STATUS: "

Use a state machine to keep track of what's been matched (you might read 'S' and the next arrived not be 'T') until the lookFor string is matched and then you know that until the next ", every char you get is that file name.

Do that and you will have your data before the packet finishes sending but do get the rest to not just clear the input but also to be sure the packet is not broken or contains errors.

And a PS, set your Serial.begin() rate high so the print output buffer clears quicker. 9600 baud is very slow, if you print much then the buffer will fill and your sketch will slow down. I use 115200 baud at least for most sketches.

… use strstr

Like Horace noted, a UDP packet is binary, so it can contain NUL characters. strstr will stop looking at that byte. You have to use memmem. You can’t print the packet for the same reason.

For testing, you need to define the test packet with its byte values, thoughtfully provided by Wireshark.
Then the test program is:

uint8_t packet[] =
{
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x22, 
  0x68, 0x73, 0x15, 0x3e, 0x08, 0x00, 0x45, 0x00,
  0x00, 0x53, 0x30, 0xdd, 0x00, 0x00, 0x80, 0x11,
  0x83, 0x3a, 0xc0, 0xa8, 0x02, 0x33, 0xc0, 0xa8,
  0x02, 0xff, 0x26, 0x8f, 0x26, 0x8f, 0x00, 0x3f,
  0x86, 0xd3, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53,
  0x3a, 0x20, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x2d,
  0x31, 0x22, 0x20, 0x22, 0x22, 0x20, 0x30, 0x20,
  0x33, 0x20, 0x30, 0x20, 0x30, 0x20, 0x37, 0x30,
  0x32, 0x31, 0x30, 0x20, 0x22, 0x30, 0x22, 0x20,
  0x30, 0x20, 0x22, 0x31, 0x22, 0x20, 0x20, 0x37,
  0x30, 0x33, 0x35, 0x30, 0x20, 0x22, 0x22, 0xde,
  0x00,
};
size_t packetLen = sizeof(packet);

const uint8_t pattern[] = "STATUS: \"";
const size_t  patternLen = sizeof(pattern)-1; // don't include NUL char

void setup()
{
  Serial.begin( 9600 );
  Serial.println( F("dummyload test") );
  
  uint8_t *found = (uint8_t *) memmem( packet, packetLen, pattern, patternLen );
  
  if (found)  {
    size_t patternStart = (found - packet);
    size_t nameStart    = patternStart + patternLen;
    size_t remaining    = packetLen - nameStart;

    size_t NAME_SIZE = 32;
    char   name[ NAME_SIZE ];
    size_t nameLen = 0;

    // Don't look at more characters than we can save.
    if (remaining > NAME_SIZE-1)
      remaining = NAME_SIZE-1;

    while (nameLen < remaining) {
      char c = (char) packet[ nameStart + nameLen ];
      if (c == '"')
        break;
      name[ nameLen++ ] = c;
    }
    name[ nameLen ] = '\0'; // NUL-terminate
    
    Serial.print( F("Found STATUS field at position ") );
    Serial.println( nameStart );
    
    Serial.print( F("STATUS field value = \"") );
    Serial.print( name );
    Serial.println( '\"' );
  } else {
    Serial.println( F("STATUS field not found") );
  }
}

void loop() {}

replaced strstr() in post #2 with memmem() as recommended by @-dev

  Serial.begin(115200);
  byte packetBuffer[]={0x02,0xff,0x26,0x8f,0x26,0x8f,0x00,0x3f,0x86,0xd3,0x53,0x54,0x41,0x54,0x55,0x53,0x3a,0x20,0x22,0x6e,0x61,0x6d,0x65,0x2d,0x31,0x22,0x20,0x22,0x22,0x20,0x30,0x20 };
  byte * ptr=(byte *)memmem(packetBuffer, sizeof(packetBuffer),"STATUS",6);
  if(ptr==NULL)Serial.println("NULL"); 
  else {
     Serial.println("found");
     do { ptr++; } while(*ptr != '\"');  // search for starting "
     while(*(++ptr) != '\"')             // print until terminating " found
        Serial.print((char)*ptr);
     }

a run gives

found
name-1

Hi all

Thanks for the feedback but…

First
When I make a faster serial then 9K6 I got garbage on the serial monitor

Second
The name-1 isn’t changed when I put a other name in de UDP string. The “decoder” still came up with name-1
This is with both sketches, while wireshark show a different name…

0000 ff ff ff ff ff ff 00 22 68 73 15 3e 08 00 45 00 ÿÿÿÿÿÿ."hs.>…E.
0010 00 59 56 e3 00 00 80 11 5d 2e c0 a8 02 33 c0 a8 .YVã…].À¨.3À¨
0020 02 ff 26 8f 26 8f 00 45 86 d9 53 54 41 54 55 53 .ÿ&.&…E.ÙSTATUS
0030 3a 20 22 6e 65 74 77 65 72 6b 2d 69 64 22 20 22 : “netwerk-id” "
0040 22 20 30 20 36 20 30 20 30 20 31 38 30 36 38 30 " 0 6 0 0 180680
0050 20 22 30 22 20 30 20 22 31 22 20 20 31 38 30 36 “0” 0 “1” 1806
0060 38 30 20 22 22 d1 00 80 ""Ñ.

The code below gives “name-1found” as output on the serial monitor.

Peter

#include <SPI.h>         // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h>         // UDP library from: bjoern@cs.stanford.edu 12/30/2008


byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 2, 177);

#define UDP_TX_PACKET_MAX_SIZE 100

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

// buffers for receiving and sending data
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,

// An EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;




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

void loop() {
  byte packetBuffer[]={0x02,0xff,0x26,0x8f,0x26,0x8f,0x00,0x3f,0x86,0xd3,0x53,0x54,0x41,0x54,0x55,0x53,0x3a,0x20,0x22,0x6e,0x61,0x6d,0x65,0x2d,0x31,0x22,0x20,0x22,0x22,0x20,0x30,0x20 };
  byte * ptr=(byte *)memmem(packetBuffer, sizeof(packetBuffer),"STATUS",6);
  if(ptr==NULL)Serial.println("NULL"); 
  else {
     Serial.println("found");
     do { ptr++; } while(*ptr != '\"');  // search for starting "
     while(*(++ptr) != '\"')             // print until terminating " found
        Serial.print((char)*ptr);
     }
}

dummyload:
First
When I make a faster serial then 9K6 I got garbage on the serial monitor

On the lower right corner of serial monitor you can set the baud rate of the monitor to match.

Just left of the monitor baud rate is end-of-input-line options.
When you send text from the monitor to Arduino you can have it add nothing, or newline or carriage return or both.
Default is to add nothing, usually you want to add a newline (char == 10) so your code knows end of line is reached.

And you define packetBuffer twice, once as a char array and in void loop() as a byte array.
Noooooooooooooooooooooooooooooo!

Comment the line
// char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
like this so it is not compiled.

Move the line
byte packetBuffer={0x02,0xff,0x26,0x8f,0x26,0x8f,0x00,0x3f,0x86,0xd3,0x53,0x54,0x41,0x54,0x55,0x53,0x3a,0x20,0x22,0x6e,0x61,0x6d,0x65,0x2d,0x31,0x22,0x20,0x22,0x22,0x20,0x30,0x20 };
up above loop() and setup() so that the array is global.

While you're at it, these are the bytes for the word netwerk-id
6e 65 74 77 65 72 6b 2d 69 64

And these are the bytes you put in byte packetBuffer that spell name-1
0x6e,0x61,0x6d,0x65,0x2d,0x31,

dummyload:
The name-1 isn't changed when I put a other name in de UDP string. The "decoder" still came up with name-1

running the code of post #5 with the new UDP packaket data

byte packetBuffer[]={0x02,0xff,0x26,0x8f,0x26,0x8f,0x00,0x45,0x86,0xd9,0x53,0x54,0x41,0x54,0x55,0x53,0x3a,0x20,0x22,0x6e,0x65,0x74,0x77,0x65,0x72,0x6b,0x2d,0x69,0x64,0x22,0x20,0x22};

I get

found
netwerk-id

Ok Thanks again!

Perhaps I wasn't clear with my question :o

After the string STATUS:" there is a network ID.
I want to use this network ID.
The network ID can be random letters + numbers or - and up to 10 lenght.

With:

byte packetBuffer={0x02,0xff,0x26,0x8f,0x26,0x8f,0x00,0x45,0x86,0xd9,0x53,0x54,0x41,0x54,0x55,0x53,0x3a,0x20,0x22,0x6e,0x65,0x74,0x77,0x65,0x72,0x6b,0x2d,0x69,0x64,0x22,0x20,0x22};

its pre defined (?) and the code looks if this ID is in the string

Peter

horace:
however, your packets have 0's which will cause problems as strstr() looks for null terminated strings

OOPS, overlooked that :frowning:

you couldread the UDP packet into a byte array such as packetBuffer and then process it, e.g.

#include <SPI.h>         // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h>         // UDP library from: bjoern@cs.stanford.edu 12/30/2008


// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(192, 168, 1, 177);

unsigned int localPort = 999;//8888;      // local port to listen on

EthernetUDP Udp;

void setup() {
    Serial.begin(115200);
    Serial.println("UDP  program");
    // start the Ethernet and UDP:
   if(Ethernet.begin(mac))Serial.println("Configured Ethernet using DHCP OK");
   else Serial.println("Failed to configure Ethernet using DHCP");
     // print your local IP address:
    Serial.print("My IP address: ");
    for (byte thisByte = 0; thisByte < 4; thisByte++) {
       // print the value of each byte of the IP address:
       Serial.print(Ethernet.localIP()[thisByte], DEC);
       Serial.print(".");
       }
   Serial.println();
   if(Udp.begin(localPort))Serial.println("UDP begin() OK");
   else Serial.println("UDP begin() failed!");
}

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 remote = Udp.remoteIP();
    for (int i = 0; i < 4; i++) {
      Serial.print(remote[i], DEC);
      if (i < 3) {
        Serial.print(".");
      }
    }
    Serial.print(", port ");
    Serial.println(Udp.remotePort());
    // read the packet into packetBufffer
    byte packetBuffer[packetSize]={0};
    for(int i=0;i<packetSize;i++)
       packetBuffer[i]=Udp.read();   // read the packet into array
    Serial.print("Contents: ");
    for(int i=0;i<packetSize;i++)    // print contents
      {Serial.print(packetBuffer[i]&0xff,HEX); Serial.print(" "); }
    // search for STATUS
    byte * ptr=(byte *)memmem(packetBuffer, sizeof(packetBuffer),"STATUS",6);
    if(ptr==NULL)Serial.println("NULL"); 
    else {                           // if STATUS found print data
       Serial.println("found");
       do { ptr++; } while(*ptr  != '\"');  // search for starting "
       while(*(++ptr)  != '\"')             // print until terminating " found
          Serial.print((char)*ptr);
      }
  }
}

when the following byte array is transmitted

   int port=999;                            // port to send/receive datagrams on
   String remoteIPaddress= "192.168.1.177";     // IP to send datagrams
    byte[] packetBuffer={(byte)0x02,(byte)0xff,(byte)0x26,(byte)0x8f,(byte)0x26,(byte)0x8f,(byte)0x00,(byte)0x45,(byte)0x86,(byte)0xd9,
      (byte)0x53,(byte)0x54,(byte)0x41,(byte)0x54,(byte)0x55,(byte)0x53,(byte)0x3a,(byte)0x20,(byte)0x22,(byte)0x6e,(byte)0x65,(byte)0x74,
      (byte)0x77,(byte)0x65,(byte)0x72,(byte)0x6b,(byte)0x2d,(byte)0x69,(byte)0x64,(byte)0x22,(byte)0x20,(byte)0x22};

the Serial Monitor displays

UDP  program
Configured Ethernet using DHCP OK
My IP address: 192.168.1.177.
UDP begin() OK
Received packet of size 32
From 192.168.1.96, port 59398
Contents: 2 FF 26 8F 26 8F 0 45 86 D9 53 54 41 54 55 53 3A 20 22 6E 65 74 77 65 72 6B 2D 69 64 22 20 22 found
netwerk-id

an alternative and probably simpler technique was suggested in post #3 by @GoForSmoke where you look at the bytes as they arrive for the sequence “STATUS” and the required data then follows

byte packetBuffer={0x02,0xff,0x26,0x8f,0x26,0x8f,0x00,0x45,0x86,0xd9,0x53,0x54,0x41,0x54,0x55,0x53,0x3a,0x20,0x22,0x6e,0x65,0x74,0x77,0x65,0x72,0x6b,0x2d,0x69,0x64,0x22,0x20,0x22};

its pre defined (?) and the code looks if this ID is in the string

Peter

It's defined right there in that code. Those hexadecimal digits (base 16 digits begin with 0x) are the text.

Arduino uses text codes from the ASCII (American Standard Code for Information Interchange) table.

ASCII codes

Letter S is character 0x53 (== decimal 83) for instance.

Letters A to Z are 0x41 to 0x5A and letters a to z are 0x61 to 0x7A. The difference between upper and lower case is 0x20 (32 decimal).

But for printable characters you don't have to spell with ASCII values, just put the char in single quotes, easier to read!

byte packetBuffer={0x02,0xff,0x26,0x8f,0x26,0x8f,0x00,0x45,0x86,0xd9,'S','T','A','T','U','S',':',' ',0x22,'c','e','t','w','e','r','k','.','i','d',0x22,0x20,0x22};

horace:
an alternative and probably simpler technique was suggested in post #3 by @GoForSmoke where you look at the bytes as they arrive for the sequence "STATUS" and the required data then follows

As a kind of work-up to that, the code can buffer the text (know you have a whole line, serial has no guarantees) and the "walk through the array" using an index to address it a char at a time. With decently optimized code the speed that you could that will be very close to the speed of string.h functions like strstr().

You gain inside knowledge of what otherwise may be a "black box" function that you call with no understanding of how it works. It will appear mysteriously complex and you won't know so well how it deals with your data which means you can easily miss shortcuts/tricks that can simplify and speed your code and how fast you get the code done.

I've done both ways for more hours than I'd like to try counting. I prefer to not need string.h functions but when I do, I'm glad they're there. I spent time writing my own versions of some string.h commands just to know better, time well wasted as I used what I learned to not need string.h functions!