Fading Coder

An Old Coder’s Final Dance

Home > Tech > Content

Working with RS232 in CAPL: Open, Configure, Send, Receive, and Close

Tech 2

This guide summarizes the CAPL RS232 API used for serial communication and shows how to open, confiugre, transmit, receive, and close a serial connection.

Key functions:

  • RS232Open(port)
  • RS232Configure(port, baudrate, dataBits, stopBits, parity)
  • RS232Send(port, buffer[], count)
  • RS232Receive(port, buffer[], size)
  • RS232Close(port)
  • RS232SetHandshake(port, hendshake, XonLimit, XoffLimit, XonChar, XoffChar)

If you need local testing without hardware, create a virtual COM-port pair in your OS or with a third-party tool (e.g., COM5 <-> COM6), then bind CAPL to one side.

Opening a serial port

Returns 1 on success and 0 on failure (e.g., port does not exist or is in use).

on key 'o' // Open COM6
{
  dword com = 6;
  if (RS232Open(com))
  {
    write("COM%u opened", com);
  }
  else
  {
    write("Failed to open COM%u (busy or not present)", com);
  }
}

Configuring the port

Set baud rate, data bits, stop bits, and parity. Default is (9600, 8, 1, 0).

on key 'c' // Configure COM6: 9600-8N1
{
  dword com = 6;
  if (!RS232Open(com))
  {
    write("Open failed for COM%u", com);
    return;
  }

  if (RS232Configure(com, 9600, 8, 1, 0))
  {
    write("COM%u configured: 9600-8N1", com);
  }
  else
  {
    write("COM%u configuration failed", com);
  }
}

Sending data (with send/error callbacks)

RS232Send returns 1 on success. On completion, RS232OnSend is called. On failure, RS232OnError is invoked.

/*@!Encoding:ASCII*/

on key 's' // Open, configure, and send a message
{
  dword com = 6;
  dword ok;

  ok = RS232Open(com);
  if (!ok)
  {
    write("Open failed for COM%u", com);
    return;
  }

  ok = RS232Configure(com, 9600, 8, 1, 0);
  if (!ok)
  {
    write("Configure failed for COM%u", com);
    return;
  }

  // Prepare payload (do not send the terminating NUL)
  char msg[] = "Hello, World!";
  dword n = (dword)strlen(msg);
  byte out[32];
  int i;
  for (i = 0; i < n; i++)
  {
    out[i] = (byte)msg[i];
  }

  ok = RS232Send(com, out, n);
  if (ok)
  {
    write("Queued %u bytes on COM%u", n, com);
  }
  else
  {
    write("Send failed on COM%u", com);
  }
}

RS232OnSend(dword port, byte data[], dword len)
{
  // Log transmitted bytes in hex with a timestamp
  char ts[32];
  char line[128];
  int i;
  line[0] = 0;

  for (i = 0; i < len; i++)
  {
    char tmp[8];
    snprintf(tmp, elCount(tmp), "%02X ", data[i]);
    snprintf(line, elCount(line), "%s%s", line, tmp);
  }

  getLocalTimeString(ts);
  write("%s COM%u TX: %s", ts, port, line);
}

RS232OnError(dword port, dword flags)
{
  if (flags & 1)
  {
    writeLineEx(0, 3, "COM%u transmit error", port);
  }
  if (flags & 2)
  {
    writeLineEx(0, 3, "COM%u receive error", port);
  }
}

Receiving data (arming receive and receive callback)

RS232Receive returns 1 when arming succeeds. On data arrival, RS232OnReceive is invoked. Re-arm as needed if your environment requires it.

/* Arm receive and log incoming data */

on key 'r'
{
  dword com = 6;
  byte rx[64];

  if (!RS232Open(com))
  {
    write("Open failed for COM%u", com);
    return;
  }

  if (!RS232Configure(com, 9600, 8, 1, 0))
  {
    write("Configure failed for COM%u", com);
    return;
  }

  if (RS232Receive(com, rx, elCount(rx)))
  {
    write("COM%u receive armed for %u bytes", com, elCount(rx));
  }
  else
  {
    write("COM%u receive arm failed", com);
  }
}

RS232OnReceive(dword port, byte data[], dword len)
{
  // Render both a text preview and a hex dump
  int i;
  char text[128];
  char hex[256];
  text[0] = 0;
  hex[0] = 0;

  // Copy as printable string (truncate and NUL-terminate)
  for (i = 0; i < len && i < (elCount(text) - 1); i++)
  {
    text[i] = (char)data[i];
  }
  text[i < elCount(text) ? i : elCount(text) - 1] = '\0';

  // Hex dump
  for (i = 0; i < len; i++)
  {
    char tmp[8];
    snprintf(tmp, elCount(tmp), "%02X ", data[i]);
    snprintf(hex, elCount(hex), "%s%s", hex, tmp);
  }

  write("COM%u RX (%u bytes): '%s' | %s", port, len, text, hex);
}

Closing the port

Always release the port when your measurement stops.

on preStop
{
  dword com = 6;
  RS232Close(com);
}

Handshake configuration

Use RS232SetHandshake to configure flow control. The handshake parameter is implementation-specific (for example: 0=None, 1=RTS/CTS, 2=DSR/DTR, 3=XON/XOFF). Xon/Xoff characters typically use 0x11/0x13.

on key 'h'
{
  dword com = 6;
  dword handshake = 3;      // Example: XON/XOFF (check your documentation)
  dword xonLimit  = 100;    // Resume when buffer below this threshold
  dword xoffLimit = 10;     // Pause when buffer above this threshold
  dword xonChar   = 0x11;   // XON
  dword xoffChar  = 0x13;   // XOFF

  if (RS232SetHandshake(com, handshake, xonLimit, xoffLimit, xonChar, xoffChar))
  {
    write("COM%u handshake configured", com);
  }
  else
  {
    write("COM%u handshake configuration failed", com);
  }
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.