UDP String to Hex Array for Nokia PCD8544 Display

Hi there,

I am using Firefly for Grasshopper to send a UDP 'HEX' string representation of an 84x48 pixel bitmap to a Nokia 5110 display.

I believe firefly sends a String and NOT an array.

I can format the string however I want, currently I have '0x' notation as that is what I have found inside the ADAfruit GFX & PCD8544 library.

//A snippet of the string sent from FireFly
//0xFF, 0x7F, 0x7F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x79...   


//Basic UDP read
void loop() {

  
 if (Udp.parsePacket()) {

    int len = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);

    packetBuffer[len] = 0;

    Serial.println(packetBuffer);
}

}


//Serial monitor correctly displays the string
//0xFF, 0x7F, 0x7F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x79

I know it's possible to condense the string to reduce the length to something like: FF7F3F3F3F3F.. which is not a problem, it's just the bitmap for the bootloader inside the ADAfruit library looks like so:

//ADAFruit_PCD8544.cpp
uint8_t pcd8544_buffer[LCDWIDTH * LCDHEIGHT / 8] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ... }

How would I go about splitting/traversing the received UDP string, extracting the pair of characters after the '0x' and putting the HEX value into an array like in the ADAfruit PCD library? I believe the HEX value is simply another way of representing an 8 bit long binary eg 00001111 number and is the same thing?

Any help would be greatly appreciated!

Send it as raw binary in the UDP packet. Why complicate it with a ASCII string format?

You seem a bit confused about data representation in a program vs data representation in actual memory.

This is not a string once your program is compiled...

 uint8_t pcd8544_buffer[LCDWIDTH * LCDHEIGHT / 8] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ... };

It is a sequence of raw bytes with the indicated values in the computers memory. That’s as compact as that data gets (without using a compression scheme). As they are raw bytes they don’t strictly have a “format”, other than perhaps “raw”.

Format is something which we, as humans, assign to some data when it is represented as sting of ASCII characters. Those characters are themselves represented inside the computers memory as a sequence of raw bytes.

This would be a string representation of the above data...

char pcd8544_string[] = "0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ...";

But will only be a hinderance to you in solving this problem since you will need to write code to translate from that ASCII representation back into a sequence of raw bytes.

In the first example the compiler does that for you, but using the compiler isn’t an option once your program is running!

What program is sending the UDP packet? That might be key in deciding if you need a format or if you can send the bytes raw.

I've had a thought, if I omit the "0x" and take the pair of chars, say, "FF" and use a lookup table, the result would be 11111111. However I'd need to compare each char separately and then concatenate 1111 & 1111 together.

I know this doesn't work like a string so I'm guessing the theory goes something like:

int A = 1111;
int B = 1111;


int c = int A * 10000 + int B

//should return 11111111, the equivalent of 0xFF

I have been reading about how much memory strings can use up on microcontrollers and I have no idea if this is an efficient way of going about it.

Should I stay away from substrings and tokenising?

// example lookup table

char convertCharToHex(char ch)
{
  char returnType;
  switch(ch)
  {
    case '0':
    returnType = 0000;
    break;
    case  '1' :
    returnType = 0001;
    break;
    case  '2':
    returnType = 0010;
    break;
    case  '3':
    returnType = 0011;
    break;
    case  '4' :
    returnType = 0100;
    break;
    case  '5':
    returnType = 0101;
    break;
    case  '6':
    returnType = 0110;
    break;
    case  '7':
    returnType = 0111;
    break;
    case  '8':
    returnType = 1000;
    break;
    case  '9':
    returnType = 1001;
    break;
    case  'A':
    returnType = 1010;
    break;
    case  'B':
    returnType = 1011;
    break;
    case  'C':
    returnType = 1100;
    break;
    case  'D':
    returnType = 1101;
    break;
    case  'E':
    returnType = 1110;
    break;
    case  'F' :
    returnType = 1111;
    break;
    default:
    returnType = 0;
    break;
  }
  return returnType;
}

pcbbc:
Send it as raw binary in the UDP packet. Why complicate it with a ASCII string format?

What program is sending the UDP packet? That might be key in deciding if you need a format or if you can send the bytes raw.

I am using Firefly for Grasshopper, which runs inside a cad program, Rhino 3d. http://www.fireflyexperiments.com/

I don't have the option to change how the UDP packet is sent, from the documentation I believe it is sent as String.

michaelkreft:
I don't have the option to change how the UDP packet is sent, from the documentation I believe it is sent as String.

Seems rather limiting. A UDP packet doesn’t have a format, it’s just a stream of raw data bytes.
But you are correct, if it is ASCII data, you will need to convert it.

uint8_t char_to_nibble(char c)
{
  if (c >= '0' && c <= '9') return c - '0';
  if (c >= 'A' && c <= 'F') return c - 'A' + 10;
  if (c >= ’a' && c <= 'f') return c - 'a' + 10;
  return 0;
}

uint8_t str_hex_to_byte(char* s)
{
  uint8_t hi = char_to_nibble(s[0]);
  uint8_t lo = char_to_nibble(s[1]);
  return (hi << 4) + lo;
}

pcbbc:
Seems rather limiting. A UDP packet doesn’t have a format, it’s just a stream of raw data bytes.
But you are correct, if it is ASCII data, you will need to convert it.

uint8_t char_to_nibble(char c)

{
 if (c >= '0' && c <= '9') return c - '0';
 if (c >= 'A' && c <= 'F') return c - 'A' + 10;
 if (c >= ’a' && c <= 'f') return c - 'a' + 10;
 return 0;
}

uint8_t str_hex_to_byte(char* s)
{
 uint8_t hi = char_to_nibble(s[0]);
 uint8_t lo = char_to_nibble(s[1]);
 return (hi << 4) + lo;
}

Thank you for your reply, I am having trouble trying to implement your solution. On further inspection I am able to loop through the array packetBuffer[]; I have attached the code I am using to read the UDP packets

//UDP data sent from FireFly = "0123456789ABCDEF"


void loop() {
  
  int packetSize = Udp.parsePacket();
  if (packetSize) {
    Serial.printf("Received packet of size %d from %s:%d\n    (to %s:%d, free heap = %d B)\n",
                  packetSize,
                  Udp.remoteIP().toString().c_str(), Udp.remotePort(),
                  Udp.destinationIP().toString().c_str(), Udp.localPort(),
                  ESP.getFreeHeap());

    // read the packet into packetBufffer
    int n = Udp.read((unsigned char*)&packetBuffer, UDP_TX_PACKET_MAX_SIZE);
    packetBuffer[n] = 0;
    Serial.println("Contents:");
    Serial.println(packetBuffer);

    }

    for(int i = 0; i < packetSize; i++) {
      Serial.print("packetBuffer[");
      Serial.print(i);
      Serial.print("] = ");
      Serial.println(packetBuffer[i]);
      }

}


/* Serial Monitor Output = 

Received packet of size 16 from 192.168.1.100:49407
    (to 192.168.1.11:2390, free heap = 42048 B)
Contents:
0123456789ABCDEF
packetBuffer[0] = 0
packetBuffer[1] = 1
packetBuffer[2] = 2
packetBuffer[3] = 3
packetBuffer[4] = 4
packetBuffer[5] = 5
packetBuffer[6] = 6
packetBuffer[7] = 7
packetBuffer[8] = 8
packetBuffer[9] = 9
packetBuffer[10] = A
packetBuffer[11] = B
packetBuffer[12] = C
packetBuffer[13] = D
packetBuffer[14] = E
packetBuffer[15] = F
*/

Does this mean I need to concatenate packetBuffer & packetBuffer[i+1] and then perform your functions on the string?

if (Udp.parsePacket()) {
   int len = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
   //packetBuffer[len] = 0;
     packetBuffer[len] = '\0'; //more explicit null terminator
   Serial.println(packetBuffer);
}
//Serial monitor correctly displays the string
//0xFF, 0x7F, 0x7F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x79

You have a comma separated hex character array which has been null terminated. You can use strtol() to convert it to integers.
http://www.cplusplus.com/reference/cstdlib/strtol/

strtol will work with or without the 0x prefix.

void setup() {
  Serial.begin(115200);
  //Null terminated hex character array
  //char packetBuffer[] = "0xFF,0x7F,0x7F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x79";
  char packetBuffer[] = "FF,7F,7F,3F,3F,3F,3F,3F,3F,3F,79";
  char* endPtr;
  //the endPtr will be to the ',' after strtol operates
  Serial.println(strtol(packetBuffer,&endPtr,16));
  while(*endPtr != NULL)//dereference endPtr is not pointing to Null, but at ','
  {
  Serial.println(strtol(endPtr + 1, &endPtr, 16));//move past ','
  }
}
void loop() {}

cattledog, it's not comma separated (execpt in the OPs original post). See the debug output supplied in post 5...

Received packet of size 16 from 192.168.1.100:49407
    (to 192.168.1.11:2390, free heap = 42048 B)
Contents:
0123456789ABCDEF

I'm going to assume the Firefly sends ASCII strings, and the OP is just typing in a string in hex format. That's fine I suppose.

To convert the received string "back" into raw bytes...

for (int i = 0; i < len; i+=2)
{
   uint8_t b = str_hex_to_byte(packetBuffer[i]);
   //Do something with byte 'b'
}

I can format the string however I want
/A snippet of the string sent from FireFly
//0xFF, 0x7F, 0x7F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x79...

Perhaps the OP can clarify what FireFly is sending.

cattledog:
You have a comma separated hex character array which has been null terminated. You can use strtol() to convert it to integers.
http://www.cplusplus.com/reference/cstdlib/strtol/

strtol will work with or without the 0x prefix.

Thank you very much cattledog for looking at my problem, your suggestion has solved it! PM me your paypal account and I'll buy you a coffee! :slight_smile:

Your thank you is all I need. I'm pleased I could help you solve your problem.

For my own interest, what are you imaging on the Nokia 5110? Can you attach an image of the display after you send a frame? How frequently is the frame transfer? Do you send a complete 504 byte frame or just a partial? How does the image relate to firefly?

Are you absolutely certain that Firefly can not send the raw bytes instead of the acsii?

Inquiring minds want to know?

cattledog:
For my own interest, what are you imaging on the Nokia 5110? Can you attach an image of the display after you send a frame?

It was a bit tricky to catch this shot but you can see my hand being shown in the screen in the foreground and background. I wanted to see if I could turn the Nokia 5110 into a live video monitor. I plan to write a diffusion algorithm in grasshopper next to handle some shading.

cattledog:
How frequently is the frame transfer? Do you send a complete 504 byte frame or just a partial?

Seems to run fairly ok at 4fps, I send a 504 byte frame although it also works with a 4032 byte frame.

cattledog:
How does the image relate to firefly?

Firefly is able to sample the image and will be able to handle a few diffusion iterations in between frames. Firefly can combine video streams, process video, add video effects and many many other possibilities all on the fly.

cattledog:
Are you absolutely certain that Firefly can not send the raw bytes instead of the acsii?

I can send a 1 or 0 for every pixel, if that's what you mean? There is very limited documentation and my understanding of udp is limited. UDP Sender - Firefly - Component for Grasshopper | Grasshopper Docs

It's clear from the documentation you referenced that the UDP Sender component in Firefly transmits the data as text. Sending the raw data bytes would be faster as there is less to send. For example AA is two characters(two bytes), but 170 (10101010) as a number is only one byte. If your frame rate is OK, then faster may not be important.

If you have access to "under the hood" of Firefly you might be able to modify that. Perhaps there is a Firefly forum where you could ask about sending raw bytes.

I can send a 1 or 0 for every pixel, if that's what you mean? There is very limited documentation and my understanding of udp is limited.

No, sending the text as binary text on a pixel by pixel basis is even more data to send.

This is an interesting project. Good luck.