Serial Communications Package Compatibility Issue

Hello!

I’m trying to write a program that can accept an input when run (for integration into LabVIEW) to control a pump (Cole Parmer Masterflex L/S). This pump accepts serial communications through an RS-232 cable, which I have been interfacing with using GNAT.Serial_Communications. The issue I run into is when I attempt to issue a control command (The ControlInput variable), the pump returns an Invalid Data error. This does not entirely make sense, however, because it does accept the enquiry, numeration, and status request commands. In addition, I also wrote a Python script that executes the same functions, but does not return this error.

Is this a known issue with the library, potentially an issue with the pump’s controller itself, or an issue with how I’ve defined or executed something?

Here’s my code:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Streams;
with GNAT.Serial_Communications;
with Ada.Command_Line;

procedure ctlr is
use Ada.Streams;
use GNAT;

arg1	: constant String := Ada.Command_Line.Argument(1); -- Takes the first argument from the command line when executed (RPM)
RPM 	: Stream_Element_Array (1 .. arg1'Length);	-- Defines the stream element array for the RPM to be input into ControlInput

EndPrompt		: String (1 .. 3);
EndPromptLength : Natural;

subtype Message is Stream_Element_Array (1 .. 11); -- Defines a stream element array subtype for reading the buffer in the Serial_Communications.Read call



Buffer	: Message; -- Sets up the buffer for reading the StatusCheck
Last	: Stream_Element_Offset; -- Sets the endpoint for the Buffer

S_Port	: constant Natural := 1; -- defines the serial port name ("COM<S_Port>") 

begin

for I in arg1'Range loop
	RPM (Stream_Element_Offset(I)) := Stream_Element(Character'Pos(arg1(I))); -- Transforms String type from the first argument to Stream_Element type
end loop;


declare
	Port_Name 		: constant Serial_Communications.Port_Name := Serial_Communications.Name (S_Port); -- Defines the serial port name
	Port      		: Serial_Communications.Serial_Port; -- Defines Serial_Port from the Serial_Communications package as "Port"
	
	ControlInput   	: constant Stream_Element_Array  := ( -- Sets control parameters
		1 => 16#02#, -- STX (start text)
		2 => 16#50#, -- P
		3 => 16#30#, -- 0
		4 => 16#31#, -- 1
		5 => 16#53#, -- S (speed)
		6 => 16#2B#, -- + (clockwise rotation)
		7 => 16#30#, -- 0
		8 => RPM (1), -- input digit 1
		9 => RPM (2), -- input digit 2
		10 => RPM (3), -- input digit 3
		11 => RPM (4), -- input decimal
		12 => RPM (5), -- input digit 4
		13 => 16#47#, -- G
		14 => 16#0D# -- CR
	);
	
	Enquiry			: constant Stream_Element_Array := ( -- Sends an enquiry (checks if the pump is connected)
		1 => 16#02#, -- STX
		2 => 16#05#, -- ENQ
		3 => 16#0D# -- CR
	);
	
	
	Numeration   	: constant Stream_Element_Array  := ( -- Sets the pump's number (P01)
		1 => 16#02#, -- STX
		2 => 16#50#, -- P
		3 => 16#30#, -- 0
		4 => 16#31#, -- 1
		5 => 16#0D# -- CR
	);
	
	
	
	StatusCheck		: constant Stream_Element_Array := ( -- Checks the pump's status after running controls
		1 => 16#02#, -- STX
		2 => 16#50#, -- P
		3 => 16#30#, -- 0
		4 => 16#31#, -- 1
		5 => 16#49#, -- I
		6 => 16#0D# -- CR
	);
	
	StopSignal		: constant Stream_Element_Array := ( -- Tells the pump to stop
		1 => 16#02#, -- STX
		2 => 16#50#, -- P
		3 => 16#30#, -- 0
		4 => 16#31#, -- 1
		5 => 16#48#, -- H
		6 => 16#0D# -- CR
	);

begin
	Serial_Communications.Open -- Opens the serial port
	 (Port => Port,
	  Name => Port_Name);

	Serial_Communications.Set -- Sets the details for the serial port
	 (Port      => Port, 
	  Rate      => Serial_Communications.B4800,
	  Bits      => Serial_Communications.CS7,
	  Stop_Bits => Serial_Communications.One,
	  Parity    => Serial_Communications.Odd);

	Serial_Communications.Write -- Sends the inquiry
	 (Port   => Port,
	  Buffer => Enquiry);
	  
	  delay 0.1;
	  
	Serial_Communications.Write -- Sets the pump's number
	 (Port   => Port,
	  Buffer => Numeration);

	  delay 0.1;
	  
	Serial_Communications.Write -- Sets the control input (RPM, # revs, etc)
	 (Port   => Port,
	  Buffer => ControlInput);

	  delay 0.1;
	  
	Serial_Communications.Write -- Checks the pump's status
	 (Port   => Port,
	  Buffer => StatusCheck);
	  
	  delay 0.1;

	Serial_Communications.Read -- Reads the pump's response to the StatusCheck
	(Port => Port,
	 Buffer => Buffer,
	 Last => Last);
	
	for I in 1 .. Last loop
		Put (Stream_Element'Image(Buffer(I))); -- Prints the output from Read into the cmd prompt
		New_Line;
	end	loop;
	
	
	--~ delay 5.0; -- Length the pump runs
	Put ("Type 'end' to end program: ");
	Get_Line (EndPrompt, EndPromptLength);
	
	if EndPrompt = "end" then
	Serial_Communications.Write -- Sends the pump the stop signal
	 (Port   => Port,
	  Buffer => StopSignal);

	delay 1.0;
	
	Serial_Communications.Close -- Closes the serial port
	 (Port => Port);
	end if;
end;

end ctlr;

Thanks in advance!

I used that package 10 years ago, using a RPI.

I think it had some issue on Arm - but not on intel - that a friend (jsa - Jacob Sparre Andersen ) fixed.

This was on Linux. What platform are you on ?

I see I got the fixed version here

the changes (2 I think) is commented - - jsa

(I also see name change in function Name)
-- return Port_Name (“/dev/ttyS” & N_Img (N_Img’First + 1 .. N_Img’Last));
return Port_Name (“/dev/ttyAMA” & N_Img (N_Img’First + 1 .. N_Img’Last));
for an RPI

Anyway - googling the API - I used https://www.masterflex.nl/assets/uploads/2017/09/07551-xx.pdf

page 3-37 I see this

PnnS+0130.0

Your code inserts a ‘G’ before the . Is that intended? (byte 13 in ControlInput)

I was kinda wondering about that too. The manual I found for that pump (https://www.masterflex.nl/assets/uploads/2017/09/07551-xx.pdf) shows only the following parameters for that command:

+xxx.x, -xxx.x, +xxxx, -xxxx, +=CW, –=CCW

I couldn’t find any mention of the G for that command, but I also don’t use the pump, so maybe I missed something.

I’m using Windows 11.

The G command is the “turn on” command for the pump (page 3-36). To add some context, the control command allows you to string multiple commands together. This is the example listed in the manual on page 3-32:

More than one command can be put in a command string as shown following: P09S+0500.0V08255.37G The above multiple command string example would set the speed at pump satellite 09 to 500.0 rpm, clockwise direction, set 8255.37 revolutions and start the drive. The maximum number of characters allowed in one pump drive string is 38, including , Pnn and .

I found the solution, turns out it was not an Ada package issue. Turns out in order to run continuously, the G command requires the argument 0, thus making the command <STX>P01S+0100.0G0<CR>. Thanks everyone for the help!

1 Like

Good that you solved it.

In addition, I also wrote a Python script that executes the same functions, but does not return this error

But I wonder if you sent the same string via Python

When I reviewed the Python script, it appears I actually had defined the number of revolutions, P01S+0100.0V300.0G before the G command, so the 0 parameter was not necessary and thus did not throw an error.