Serial protocol translator: Laser <-> Machine

Hello everybody

I have a challenging first Arduino project where I would greatly appreciate some guidance.
Here is a short introduction to the problem and situation:

We have a machine that works with a laser. The machine is a bit older, no longer supported and should be considered as given (no possibility to access the control program).
The problem: it came without the laser and so we had to buy a new one to make it work again. Even though the new laser is also controlled (parametrized) via RS232 it doesn’t work as a drop-in replacement. Of course the new laser has a different command protocol than the original one :confused:
I now have to realize some protocol translation between the machine and the laser:

[machine] ============[laser]

as translator I am using an Arduino Mega with appropriate RS232<>TTL converter on the hardware serial ports 1 and 2. The serial settings for all devices are: 57600 Baud, 8 bits data length,
1 stop bit, no even/parity check,

There is a two-way communication i.e. the machine polls the laser status / laser sends its status, the machine sets output power / laser confirms, laser sends error message / machine shuts down laser

The (new) laser accepts and sends commands and replies as ASCII strings via RS232, from the manual:
“All commands and responses will consist of printable ASCII characters. Commands are typically three or four letter mnemonic codes followed by a parameter, if required. All commands and responses will be terminated with a (CR, 0x0D, \r) character”

The machine sends and expects HEX codes via RS232, from the manual:
"The command from computer consists of operation code (1 byte) and optional parameter
(ASCII-string), terminated by the “carriage return” (“CR”) byte (0x0D)."
an example is given in the manual:
Command | Operation code (hex)| example
“Read pump current value” | 0x82 | 82 0D Laser answers by the CR-terminated
ASCII-string, current value is in Amperes.

Using a terminal program like hterm I can pretend to be the laser or the machine and talk to the other side. Here is a part of the hterm communication log (received hex characters exported to text, commented with signification by me; with timestamps).

10:44:15.487:
840D //read temperature
10:44:17.806:
8F0D //turn emission OFF
10:44:17.931:
80302E3030303030300D85300D //set current 0.000000; set op. mode ACC
10:44:18.025:
900D //switch ON aiming beam
10:44:18.478:
860D //read status
10:44:19.181:
840D //read temperature

The Arduino Mega now has to translate commands and answers to the protocol used by the other side.
I think, with these explanations you can more or less see, what I am trying to do.

Questions:

  1. How would you organize the program (divide in sub-tasks) and what kind of basic program structure would be apropriate? My approach now is to divide into the parts a) communication with machine, b) communication with laser, c) translation between the two protocols.

  2. I am stuck with mainly a) and to some extent b): the machine seems to use some mixture of hex command byte and optional ascii strings. I have some troubles translating this into chosing the right code for reading the serial port and transferring the received data to something useable in c).

  3. Testing code variants was made impossible by the fact that I was not able to receive anything when the Arduino was connected to laser and machine. I found that Serial1.available() and Serial2.available() always return “0” when I print them to the serial monitor. At the same time I am sure that the machine was constantly polling status and laser temperature as verified by connection to the PC and hterm.
    I am also sure that the Arduino ports work correctly as I tested them simultaneously connecterd to 3 RS232 ports on a PC.

Thank you for reading until here

have a nice weekend!

I would start with code to read and log messages from both ports:

const int BUFFER_SIZE = 100;
char ControllerBuffer[BUFFER_SIZE];
unsigned int ControllerIndex;
char LaserBuffer[BUFFER_SIZE];
unsigned int LaserIndex;

void setup() {
  Serial1.begin(57800); // Controller
  Serial2.begin(57600); // Laser
}

void loop() {
  ControllerPoll();
  LaserPoll();
}


void ControllerPoll() {
  if (Serial1.available()) {
    char c = Serial1.read();
    if (c == '\r') {
      ControllerInput();
      ControllerIndex = 0; // Reset buffer
    } else {
      ControllerBuffer[ControllerIndex++] = c;
      ControllerBuffer[ControllerIndex] = '\0'; // Null Terminator.
    }
  }
}

void ControllerInput() {
  //  For now we just log the message
  Serial.print(F("C: "));
  if (ControllerIndex > 0)
    Serial.print(ControllerBuffer[0], HEX);
  if (ControllerIndex > 1) {
    Serial.print(' ');
    Serial.print(&ControllerBuffer[1]);
  }
  Serial.println();
}


void LaserPoll() {
  if (Serial2.available()) {
    char c = Serial2.read();
    if (c == '\r') {
      LaserInput();
      LaserIndex = 0; // Reset buffer
    } else {
      LaserBuffer[LaserIndex++] = c;
      LaserBuffer[LaserIndex] = '\0'; // Null Terminator.
    }
  }
}

void LaserInput() {
  //  For now we just log the message
  Serial.print(F("L: "));
  if (LaserIndex > 0) {
    Serial.print(LaserBuffer);
  }
  Serial.println();
}

@johnwasser thank you for the quick reply and your effort! I will definitely try what you suggested and post the experience here.
I can only access the machine next week so I am forced to take a break for now. In the meantime I try to find out why SerialX.available() is returning 0 all the time.

burnito:
In the meantime I try to find out why SerialX.available() is returning 0 all the time.

If you have the RX and TX connections swapped you will not receive anything. Usually RX is an input and TX is an output and you would connect the TX from one device to the RX of another. SOMETIMES those labels get swapped. Try both ways.

Thank you for the hint with RX/TX - they are always good for troubles...
finally it was a defective gender changer I had to use to connect the machine whereas the the PC connection worked without it. :confused:

I tried johnwasser's code and made some minor modifications to produce output:

  1. added a call of laser status "STA" in the Setup, which is answered by the laser as STA: ....
  2. added a Serial.print[ControllerBuffer[0]) in ControllerInput() (without the HEX keyword) to see the actually transmitted character in the terminal program used for Monitoring on Serial.
    here is the Output:
14:22:56.814:
L: STA: 136644608

14:22:58.908:
C: FFFFFF86
†

14:22:59.611:
C: FFFFFF84
„

14:23:02.392:
C: FFFFFF86
†

As mentioned, the laser sends his status after my call in setup(), then there are the regular polls by the machine for laser status and temperature 0x860D and 0x840D.

Since there are also longer commands like 0x80302E3030303030300D85300D as shown in the original post, I plan to replace the 'if (Serial1.available())' by a 'while (Serial.available()) in ControllerPoll(). So I should end up with a character array that can be processed further.

Now I will have to compare the two command protocols and implement a two-way translation.
I would probably solve this with a lookup table for larger or more homogeneously structured command sets. However, here I am not sure what the most efficient way would be, lookup table for the command and postprocessing for the optional Argument where needed or write a long switch structure/individual if Statements, that do the entire command translation case by case. Any suggestions?

Thank you very much for your support!