×

INDI Library v2.0.7 is Released (01 Apr 2024)

Bi-monthly release with minor bug fixes and improvements

Writing simulators for serial devices (e.g. /dev/ttyUSB0) with socat

  • Posts: 111
  • Thank you received: 41
Hi there,

I have seen that there is an approach of writing simulators for serial (USB) devices such as focuser, etc.. by developing
an Arduino program and using e.g. the Arduino Beetle as the simulation hardware. However, it turns out, one can do that even simpler with command socat:
sudo socat PTY,link=/dev/ttyDMFC,raw,group-late=dialout,mode=660,echo=0,b19200,cs8,parenb=0,cstopb=0,clocal=0 EXEC:./dmfcsim
creates a virtual serial link to program
dmfcsim
and communicates via STD_IN and STD_OUT with the program.
I created for fun a simple C program (see below) called
dmfcsim.c
with simulates PegasusAstro FocusCube DMFC-Serial-Command-Table.pdf . The Simulator is not finally done, however as a simple proof of principle it works.
Note, one could use any (simple) script languages (Python, etc...) for creating INDI serial device simulators with this ansatz.

Cheers
Thomas
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
 
#ifndef STDIN_FILENO
#define STDIN_FILENO	0
#endif
 
#ifndef STDOUT_FILENO
#define STDOUT_FILENO	1
#endif
 
#define R_CMD(c)	(rbuf[0] == c && rbuf[1] == 0xa)
#define RW_CMD(c)	(rbuf[0] == c && rbuf[1] == ':')
 
/* Start socat with serial connection setting: 19200 8N1,
   $ socat PTY,link=/dev/ttyDMFC,raw,group-late=dialout,mode=660,echo=0,b19200,cs8,parenb=0,cstopb=0,clocal=0 EXEC:./dmfcsim
*/
 
struct dmfc_s {
	char status[9];
	float version;
	uint8_t motor_mode;
	float temperature;
	uint16_t position;
	uint8_t moving_status;
	uint8_t led_status;
	uint8_t reverse;
	uint8_t disable_encoder;
	uint16_t backlash_value;
 
	uint16_t motor_maxspeed;
	uint32_t rotary_enc_pos;
};
 
int main(int argc, char *argv[])
{
	struct dmfc_s dmfc	 = {
		.status          = "OK_DMFCN",
		.version	 = 1.2,
		.motor_mode	 = 1,	/* Stepper. */
		.temperature	 = 22.4,
		.position	 = 10000,
		.moving_status	 = 0,	/* Idle. */
		.led_status	 = 0,	/* Off. */
		.reverse	 = 0,	/* Normal (not reverse). */
		.disable_encoder = 0,	/* Encoder is on. */
		.backlash_value	 = 0,
		.motor_maxspeed  = 400,
		.rotary_enc_pos  = 5000
	};
 
	while (1) {
 
		char rbuf[32];
		char wbuf[64];
 
		memset(rbuf, 0, sizeof(rbuf));
		memset(wbuf, 0, sizeof(wbuf));
 
		ssize_t rsize = read(STDIN_FILENO, rbuf, sizeof(rbuf));
		if (rsize < 0)
			perror("stdin");
 
		if (R_CMD('#'))
			snprintf(wbuf, sizeof(wbuf), "%s\n", dmfc.status);
		else if (R_CMD('A')) {
			/* E.g. "OK_DMFCN:2.6:1:22.4:50:0:1:1:0:100" */
			snprintf(wbuf, sizeof(wbuf), "%s:%2.2f:%d:%2.2f:%d:%d:%d:%d:%d:%d\n",
				 dmfc.status,
				 dmfc.version,
				 dmfc.motor_mode,
				 dmfc.temperature,
				 dmfc.position,
				 dmfc.moving_status,
				 dmfc.led_status,
				 dmfc.reverse,
				 dmfc.disable_encoder,
				 dmfc.backlash_value);
		} else if (R_CMD('B'))
			snprintf(wbuf, sizeof(wbuf), "%3.2f\n", 400.0);
		else if (RW_CMD('C'))
			dmfc.backlash_value = atof(rbuf + 2);
		else if (RW_CMD('E')) {
			dmfc.disable_encoder = atoi(rbuf + 2);
			snprintf(wbuf, sizeof(wbuf), "E:%d\n", dmfc.disable_encoder);
		} else if (R_CMD('V'))
			snprintf(wbuf, sizeof(wbuf), "%1.1f\n", dmfc.version);
		else if (R_CMD('T'))
			snprintf(wbuf, sizeof(wbuf), "%2.2f\n", dmfc.temperature);
		else if (R_CMD('P'))
			snprintf(wbuf, sizeof(wbuf), "%d\n", dmfc.position);
		else if (R_CMD('I'))
			snprintf(wbuf, sizeof(wbuf), "%d\n", dmfc.moving_status);
		else if (RW_CMD('M'))
			dmfc.position = atoi(rbuf + 2);
		else if (RW_CMD('G'))
			dmfc.position += atoi(rbuf + 2);
		else if (RW_CMD('S'))
			dmfc.motor_maxspeed = atoi(rbuf + 2);
		else if (R_CMD('L'))
			snprintf(wbuf, sizeof(wbuf), "%d\n", dmfc.led_status);
		else if (RW_CMD('L')) {
			uint8_t v = atoi(rbuf + 2);
			if (v == 2)
				dmfc.led_status = 1;
			if (v == 1)
				dmfc.led_status = 0;
			if (v == 2 || v == 1)
				snprintf(wbuf, sizeof(wbuf), "%d\n", dmfc.led_status);
		} else if (RW_CMD('R')) {
			dmfc.motor_mode = atoi(rbuf + 2);
			snprintf(wbuf, sizeof(wbuf), "%d\n", dmfc.motor_mode);
		} else if (RW_CMD('N')) {
			dmfc.reverse = atoi(rbuf + 2);
			snprintf(wbuf, sizeof(wbuf), "N:%d\n", dmfc.reverse);
		} else if (RW_CMD('W'))
			dmfc.position = atoi(rbuf + 2);
		else if (R_CMD('X'))
			snprintf(wbuf, sizeof(wbuf), "N:%u\n", dmfc.rotary_enc_pos);
		else
			continue;
 
		ssize_t wsize = write(STDOUT_FILENO, wbuf, strlen(wbuf));
		if (wsize < 0)
			perror("stdout");
	}
 
	return 0;
}
4 years 5 months ago #46909

Please Log in or Create an account to join the conversation.

Time to create page: 0.202 seconds