Serial Connection Plugin
The Serial Connection plugin handles RS-232 serial ports, USB-to-Serial adapters, and virtual serial ports. This is one of the most commonly used connection methods in INDI drivers.
When to Use Serial
- RS-232 serial port devices
- USB-to-Serial adapters (FTDI, Prolific, CP2102, etc.)
- Virtual serial ports
- Most legacy astronomy equipment
- Arduino and microcontroller-based devices
Header File
#include <connectionplugins/connectionserial.h>
Basic Setup
class MyDriver : public INDI::DefaultDevice
{
public:
bool initProperties() override
{
INDI::DefaultDevice::initProperties();
// Connection plugins are created in initProperties(), once the device name is known.
// Create the Serial Connection plugin
serialConnection = new Connection::Serial(this);
// Configure defaults (optional)
serialConnection->setDefaultBaudRate(Connection::Serial::B_9600);
serialConnection->setDefaultPort("/dev/ttyUSB0");
// Register handshake function (optional)
serialConnection->registerHandshake([&]() { return Handshake(); });
// Register with the driver
registerConnection(serialConnection);
return true;
}
private:
Connection::Serial *serialConnection = nullptr;
bool Handshake()
{
// Send test command to verify connection
char response[32];
if (sendCommand("*IDN?\n", response, sizeof(response)))
{
if (strstr(response, "MyDevice"))
{
LOG_INFO("Device handshake successful");
return true;
}
}
LOG_ERROR("Handshake failed");
return false;
}
};
Communication Example
bool MyDriver::sendCommand(const char *cmd, char *res, int reslen)
{
// Get the serial port file descriptor
int fd = serialConnection->getPortFD();
if (fd < 0)
{
LOG_ERROR("Serial port not open");
return false;
}
// Write command
int nbytes_written = write(fd, cmd, strlen(cmd));
if (nbytes_written < 0)
{
LOGF_ERROR("Write error: %s", strerror(errno));
return false;
}
// Read response if expected
if (res && reslen > 0)
{
int nbytes_read = read(fd, res, reslen - 1);
if (nbytes_read < 0)
{
LOGF_ERROR("Read error: %s", strerror(errno));
return false;
}
res[nbytes_read] = '\0';
}
return true;
}
Available Baud Rates
The Serial plugin supports standard baud rates:
Connection::Serial::B_1200 // 1200 bps
Connection::Serial::B_2400 // 2400 bps
Connection::Serial::B_4800 // 4800 bps
Connection::Serial::B_9600 // 9600 bps
Connection::Serial::B_19200 // 19200 bps
Connection::Serial::B_38400 // 38400 bps
Connection::Serial::B_57600 // 57600 bps
Connection::Serial::B_115200 // 115200 bps
Connection::Serial::B_230400 // 230400 bps
Advanced: Reading with Timeout
#include <poll.h>
bool MyDriver::readWithTimeout(char *buffer, int len, int timeout_ms)
{
int fd = serialConnection->getPortFD();
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLIN;
int rc = poll(&pfd, 1, timeout_ms);
if (rc < 0)
{
LOGF_ERROR("Poll error: %s", strerror(errno));
return false;
}
else if (rc == 0)
{
LOG_ERROR("Read timeout");
return false;
}
int nbytes = read(fd, buffer, len - 1);
if (nbytes < 0)
{
LOGF_ERROR("Read error: %s", strerror(errno));
return false;
}
buffer[nbytes] = '\0';
return true;
}
Binary Data Transfer
For binary data (images, firmware, etc.):
bool MyDriver::sendBinaryData(const uint8_t *data, size_t len)
{
int fd = serialConnection->getPortFD();
size_t totalWritten = 0;
while (totalWritten < len)
{
int nbytes = write(fd, data + totalWritten, len - totalWritten);
if (nbytes < 0)
{
LOGF_ERROR("Write error: %s", strerror(errno));
return false;
}
totalWritten += nbytes;
}
return true;
}
Automatic Port Selection
USB serial adapters can show up under a different port name each time they are plugged in, so the serial plugin helps users connect without having to pick the right port by hand.
System Ports
When the device connects, the plugin scans the system for serial ports and lists them in the SYSTEM_PORTS property, so users can pick a port with one click. On Linux, it lists the stable /dev/serial/by-id/ links instead of /dev/ttyUSBx names when there is one for every adapter. Identical adapters (same USB vendor and product ID) may not get their own by-id links, and in that case the /dev/ttyUSBx names are listed instead.
If the port saved in the driver’s configuration is no longer available (it is neither among the detected ports nor present on disk) and exactly one serial port is detected, the plugin uses that port instead. Custom or mapped ports that still exist, such as a udev symlink like /dev/mount, are always kept.
Auto Search
The DEVICE_AUTO_SEARCH property lets users enable auto search. When enabled and connecting to the selected port fails, the plugin tries every other detected port, in random order so that several drivers searching at the same time don’t collide, and calls your handshake on each one. On success, it saves the working port and, on Linux, turns auto search off again.
Auto search relies on your handshake to recognize the device, so make sure Handshake() returns false for any device that is not yours.
Matching Ports by Name
Some devices include their name in the USB serial ID, for example /dev/serial/by-id/usb-Pegasus_Astro_FlatMaster_.... For these devices, call setPortMatchPattern() with a case-insensitive regular expression. When the saved port is not available, the plugin selects the first detected port whose path matches the pattern, without having to try a handshake on every port:
bool MyDriver::initProperties()
{
INDI::DefaultDevice::initProperties();
serialConnection = new Connection::Serial(this);
serialConnection->setDefaultBaudRate(Connection::Serial::B_115200);
// Select the port whose ID contains "FlatMaster" if the saved port is gone.
serialConnection->setPortMatchPattern("FlatMaster");
serialConnection->registerHandshake([&]() { return Handshake(); });
registerConnection(serialConnection);
return true;
}
The pattern can match several names, such as "303a|espressif|wheelly|usbmodem". If the pattern is empty (the default), no matching is done.
Best Practices
- Baud Rate: Use the device’s documented baud rate
- Timeouts: Always use timeouts for read operations
- Flow Control: Consider hardware flow control for high-speed transfers
- Error Recovery: Implement retry logic for transient errors
- Buffer Flushing: Flush buffers after errors or unexpected data
- Line Endings: Be consistent with line endings (\r\n, \n, etc.)
Common Issues
Permission Denied
# Add user to dialout group
sudo usermod -a -G dialout $USER
# Log out and log back in
Device Not Found
# List serial devices
ls -l /dev/ttyUSB* /dev/ttyACM*
# Check dmesg for connection messages
dmesg | tail -20
Wrong Baud Rate
Symptoms include:
- Garbled data
- No response from device
- Partial commands
Solution: Verify baud rate in device documentation
Example Drivers
- Most INDI drivers use serial communication
- Check
indi-eqmodfor telescope control - Check
indi-asifor camera control - Check any mount or focuser driver