[Solved] Trouble Utilizing Null Character

I am using Automation Direct's P1AM Arduino PLC to try and send commands to a TCP/IP stepper motor driver. Using Wireshark I was able to see the data that needed to be in the packet to control the motor.
image

I wrote the following program to try and send this same packet to the stepper driver but from the Arduino instead of my computer:

#include <SPI.h>
#include <Ethernet.h>

String Control, Command;

byte mac[] = { //Mac address on side of P1AM Eth module
  0x60, 0x52, 0xD0, 0x07, 0x41, 0x95
};

IPAddress ip(192, 168, 1, 2);//IP Address of the P1AM-ETH module.
IPAddress stepperDriver(192, 168, 1, 1); //IP Address for stepper motor driver

EthernetClient client;

char Null = 0;  //Ascii value of null character
char Bell = 7;  //Ascii value of bell character

char Ret = 13; //Ascii value of carriage return
char Lf = 10;  //Ascii value of line feed

void setup() {
  Control = String("FL10000");  //step motor 10000 steps
  Command = String(Null + Bell + Control + Ret + Lf); //append header and footer to command string

  Ethernet.begin(mac, ip);
  Serial.begin(9600);

  delay(2000); //sanity delay

  Serial.println("Connecting...");

  if (client.connect(stepperDriver, 49153)) { //Connect to device
    Serial.println("Connected");
  } else {
    Serial.println("Connection Failed");  
  }
}

void loop() {
  delay(3000);
  client.print(Command);  //send packet
  Serial.print(Command);  //print packet in serial monitor
}

Running this and using Wireshark again gives me this:
PacketBad

However if I change the line:

Command = String(Null + Bell + Control + Ret + Lf);

To:

Command = String(Bell + Control + Ret + Lf);

I get closer to my expected result but still do not have the null character at the start:
PacketBetter

Any help would be greatly appreciated,

Thank you!

Welcome to the forum

Command = String(Null + Bell + Control + Ret + Lf);

You possible meant

Command = String(Null) + String(Bell) + String(Control) + String(Ret) + String(Lf);

Thank you for the help. I tried that and got the same result as when I simply removed Null from the Command string.
image

maybe try..

#include <SPI.h>
#include <Ethernet.h>

String Control, Command;

byte mac[] = { //Mac address on side of P1AM Eth module
  0x60, 0x52, 0xD0, 0x07, 0x41, 0x95
};


byte command[] = {0x00, 0x07, 0x46, 0x4c, 0x31, 0x30, 0x30, 0x30, 0x30, 0x0d, 0x0a};

IPAddress ip(192, 168, 1, 2);//IP Address of the P1AM-ETH module.
IPAddress stepperDriver(192, 168, 1, 1); //IP Address for stepper motor driver

EthernetClient client;

char Null = 0;  //Ascii value of null character
char Bell = 7;  //Ascii value of bell character

char Ret = 13; //Ascii value of carriage return
char Lf = 10;  //Ascii value of line feed

void setup() {
  Control = String("FL10000");  //step motor 10000 steps
  Command = String(Null + Bell + Control + Ret + Lf); //append header and footer to command string

  Ethernet.begin(mac, ip);
  Serial.begin(9600);

  delay(2000); //sanity delay

  Serial.println("Connecting...");

  if (client.connect(stepperDriver, 49153)) { //Connect to device
    Serial.println("Connected");
  } else {
    Serial.println("Connection Failed");
  }
}

void loop() {
  delay(3000);
  client.print(Command);  //send packet
  client.write(command, sizeof(command));
  Serial.print(Command);  //print packet in serial monitor
}

good luck.. ~q

Do you understand the difference between your code and mine ?

What do you see when you print the Command variable after building it ?

As an experiment why not just set the String to the required value ?

if the motor is expecting a command line terminated with a return and linefeed, why not just create such a string and send it

output

FL1234
 46 4C 31 32 33 34 D A 0

char buf [90];
char s   [90];

// -----------------------------------------------------------------------------
void
loop (void)
{
    if (Serial.available ()) {
        int n = Serial.readBytesUntil ('\n', buf, sizeof(buf)-1);
        buf [n] = '\0';

        int val;
        sscanf (buf, "%d", &val);

        sprintf (s, "FL%d\r\n", val);
        Serial.print (s);
        for (unsigned n = 0; n < 1+strlen (s); n++)  {
            Serial.print (" ");
            Serial.print (s [n], HEX);
        }
        Serial.println ();
    }
}

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

I see a problem…
You can’t ‘print’ a null within a string…
You need to ‘write’ it.

Will the String library let you put a null inside a String like that? I'm pretty sure that will confuse some of the functions?

Why do you need to assemble the command first? Are you using it elsewhere? If you do want to assemble it, then assemble it in a char array instead of a String.

char command[5] = {Null, Bell, Control, Ret, Lf};

in loop:

for (int i=0; i<5; i++){
   client.write(command[i]);
   Serial.write(command[i]);
}

Or the more concise:

client.write(command, sizeof(command));

This is correct. You definitely won't be able to do it using print. You'll have to use write one way or the other if there's a null in it.

Serial.print('\0');

(N'est-ce pas?)

That will work for a single character. Internally it just calls write:

From Print.cpp:

size_t Print::print(char c)
{
  return write(c);
}

But if you put the null in the middle of a string then the print that takes a string calls this:

size_t write(const char *str) {
      if (str == NULL) return 0;
      return write((const uint8_t *)str, strlen(str));
    }

And strlen will stop when it hits the null character.

So yes you can print a null, but you can't really do what the OP wants to do and put it in the middle of a String.

I didn't realize Control was a whole String. I thought it was a byte earlier. So let me rephrase my earlier response:

char command[] = {Null, Bell, 'F', 'L', '1', '0', '0', '0', '0', '0', Ret, Lf};

with

client.write(command, sizeof(command));

Although I doubt this is the solution that the OP is looking for. I bet that String in the middle is supposed to change. In which case you'd best to just break it up into separate statements.

char control[] = "FL10000";

and send to client with:


client.write(Null);
client.write(Bell);
client.write(control, sizeof(control));
client.write(Ret);
client.write(Lf);

maybe: char command[] = "\000\007FL100000\r\n";
(be careful - the \0nn syntax expects an octal number. Doesn't matter for null and bell, but...)

Took me a second to realize why you chose octal instead of hex. Nice :slight_smile:

it seems strange to me that an ASCII command expecting a \r and \n termination would require a NUL prefix.

Hi all,

Thank you all very much for the help, I ended up solving the issue by using memcpy and .c_str() to copy the contents of the header, command, and footer into a byte array and sending that to the server using the client.write function rather than client.print.

For those interested here is my code:

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0x60, 0x52, 0xD0, 0x07, 0x41, 0x95 };
int port = 49153;

int time = 5000;

IPAddress ip(192, 168, 1, 2);//IP Address of the P1AM-ETH module.
IPAddress stepperDriver(192, 168, 1, 1); //IP Address for stepper motor driver

EthernetClient client;

byte header[2] = {0x00, 0x07};
byte footer[2] = {0x0d, 0x0a};

String Control = "FL10000";

void setup() {
  Ethernet.begin(mac, ip);
  Serial.begin(9600);

  delay(2000); //sanity delay

  Serial.println("Connecting...");

  if (client.connect(stepperDriver, port)) { //Connect to device
    Serial.println("Connected");
  } else {
    Serial.println("Connection Failed");
  }
}

void loop() {
  if(client.connected()){
    int commandSize = sizeof(header) + sizeof(footer) + Control.length();
    byte Command[commandSize];
    
    memcpy(Command, header, sizeof(header));
    memcpy(Command + sizeof(header), Control.c_str(), Control.length());
    memcpy(Command + sizeof(header) + Control.length(), footer, sizeof(footer));


    client.write(Command, commandSize); //send command
    
  }
  else{
    Serial.println("Connection lost, attempting to reconnect.");
    client.connect(stepperDriver, port);  //attempt to reconnect
  }
  delay(time);
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.