Page 1 of 1

Sending HEX values via serial port

Posted: Wed Nov 27, 2024 4:35 pm
by oldgeezer2
Hello,

I retired last year and needed something to keep me busy (or my wife was going to divorce me). Thinking they would be interesting to play with I purchased some robotic items at an auction. I was able to learn enough to use some of the parts to make an animated Halloween display using an Arduino for control. This took a long time since I had no mechanical experience, my electronics knowledge is limited and the last programming I had done was in the mid 1970's using assembly on an 8080 based system.

Among the items were 2 arms that use Robotis Dynamixel MX servos. These servos are different since they are controlled by sending commands via the serial (COM) port and the status can be obtained by reading the port. I did some testing and I am able to send the command to set the speed to 40 by using the following code:

Code: Select all

Port$ = "COM4"

 If OpenSerialPort(0, Port$, 57600, NoParity, 8, 1, NoHandshake, 1024, 1024)
  MessageRequester("Information", Port$+" opened.")  
Else
  MessageRequester("Error", "Can't open "+Port$+". Ending program.")
  End
  EndIf 

OpenConsole()
PrintN("Press return to continue.")
Input()

;   Set speed to 40
SetSpd:
Dim sendBuffer.a(8)
 sendBuffer(0) = $FF
 sendBuffer(1) = $FF
 sendBuffer(2) = $01
 sendBuffer(3) = $05
 sendBuffer(4) = $03
 sendBuffer(5) = $20
 sendBuffer(6) = $28
 sendBuffer(7) = $00
 sendBuffer(8) = $AE
 WriteSerialPortData(COM4, @sendBuffer(), 9)


This works, but I thought there might be a more efficient way. The problem I am seeing is that the motors expect the HEX value, but other than what I show above, everything I have tried converts this the to the ASCII value for each element. For example $FF is 11111111, but the output I see is the ASCII representation of FF 00011111 00011111. Other than writing each command in the format above, Is there a better / more efficient way of coding this?

Thank you in advance for any assistance. And please take it easy on this old man since I am new at this.

Re: Sending HEX values via serial port

Posted: Wed Nov 27, 2024 7:02 pm
by spikey
Hello oldgeezer2, and welcome!
oldgeezer2 wrote: Wed Nov 27, 2024 4:35 pm Is there a better / more efficient way of coding this?
I can't think of anything that qualifies as significantly more efficient. The data to be sent with WriteSerialPortData() needs to be in a contiguous buffer. There are a limited number of ways of arranging this.

However, there are some problems with the code as presented which, if it currently works, may not stay that way and could cause trouble later...

In this line:

Code: Select all

OpenSerialPort(0, Port$, 57600, NoParity, 8, 1, NoHandshake, 1024, 1024)
The constants should be '#PB_SerialPort_NoParity' and '#PB_SerialPort_NoHandshake'. What you are actually doing here is creating two integer variables with zero values on the fly. See Variables and Types and OpenSerialPort().

You have a similar problem on this line:

Code: Select all

WriteSerialPortData(COM4, @sendBuffer(), 9)
This time you're creating a variable called COM4 with a zero value. Coincidentally, in this case, it happens to work with the OpenSerialPort because you opened it with a value of zero (the first parameter) but it's not ... right.

I would suggest that something like this would be 'better':

Code: Select all

#COM4 = 0
Port$ = "COM4"

If OpenSerialPort(#COM4, Port$, 57600, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)
  MessageRequester("Information", Port$+" opened.")  
Else
  MessageRequester("Error", "Can't open "+Port$+". Ending program.")
  End
EndIf 

OpenConsole()
PrintN("Press return to continue.")
Input()

;   Set speed to 40
SetSpd:
Dim sendBuffer.a(8)
sendBuffer(0) = $FF
sendBuffer(1) = $FF
sendBuffer(2) = $01
sendBuffer(3) = $05
sendBuffer(4) = $03
sendBuffer(5) = $20
sendBuffer(6) = $28
sendBuffer(7) = $00
sendBuffer(8) = $AE
WriteSerialPortData(#COM4, @sendBuffer(), 9)

Re: Sending HEX values via serial port

Posted: Wed Nov 27, 2024 8:00 pm
by oldgeezer2
spikey,

Thank you for the prompt reply and advice. The code was my interpretation of what I found on various searches so I am not surprised that there are better ways, but I am glad it worked for testing. I will incorporate your suggestions as I go along.

By efficient I was hoping for less typing since it is another skill I am not very good at.

Thanks again for the advice.

Re: Sending HEX values via serial port

Posted: Wed Nov 27, 2024 8:34 pm
by spikey
oldgeezer2 wrote: Wed Nov 27, 2024 8:00 pm ...I was hoping for less typing...
:D I've been giving out the same advice on that subject for 25 years - "There really is no substitute for proper typing practice". An hour a day spent at something like https://www.typingtest.com/ for a couple of weeks will make a huge difference.

Re: Sending HEX values via serial port

Posted: Wed Nov 27, 2024 9:51 pm
by infratec
I would use:

Code: Select all

WriteSerialPortData(#COM4, @sendBuffer(0), 9)

Re: Sending HEX values via serial port

Posted: Thu Nov 28, 2024 1:15 pm
by NicTheQuick
I would suggest to write some helper functions to write instructions. This way you can automate the length and checksum calculations, give the instructions proper names using Enumeration and make you life easier in general when communicating with the device.

Here's an idea:

Code: Select all

#COM4 = 0
Port$ = "COM4"

If OpenSerialPort(#COM4, Port$, 57600, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)
	MessageRequester("Information", Port$+" opened.")  
Else
	MessageRequester("Error", "Can't open "+Port$+". Ending program.")
	End
EndIf 

OpenConsole()
PrintN("Press return to continue.")
Input()

Structure Instruction
	header1.a
	header2.a
	packet_id.a
	length.a
	instruction.a
	params.a[0]
EndStructure

Enumeration Instruction
	#PING = 1
	#READ
	#WRITE
	#REG_WRITE
	#ACTION
	#FACTORY_RESET
	#REBOOT
	#SYNC_WRITE
	#BULK_READ
EndEnumeration

Procedure write_instruction(port.i, id.a, instruction.a, Array params.a(1))
	Protected i.i, param_length = ArraySize(params())
	Protected sum.a, instruction_size.i = SizeOf(Instruction) + param_length + 1
	Protected *instruction.Instruction = AllocateMemory(instruction_size)
	With *instruction
		\header1 = $FF
		\header2 = $FF
		\packet_id = id
		\length = param_length + 2
		\instruction = instruction
		sum = \packet_id + \length + \instruction
		For i = 0 To param_length - 1
			\params[i] = params(i)
			sum + params(i)
		Next
		; Used as checksum field
		\params[param_length] = ~sum
	EndWith
	
	Protected bytes_written.i, bytes_written_sum.i = 0
	Protected result.i = #True
	Repeat
		bytes_written = WriteSerialPortData(port, *instruction + bytes_written, instruction_size - bytes_written)
		; If the return value is 0 some error occured
		If bytes_written = 0
			result = #False
			Break
		EndIf
		bytes_written_sum + bytes_written
	Until bytes_written_sum = instruction_size
	FreeMemory(*instruction)
	ProcedureReturn result
EndProcedure

Procedure set_speed(port.i, speed.a)
	Protected Dim params.a(2)
	params(0) = $20
	params(1) = speed
	params(2) = $00
	ProcedureReturn write_instruction(port, 1, #WRITE, params())
EndProcedure

set_speed(#COM4, 40)
Source: https://emanual.robotis.com/docs/en/dxl/protocol1/

Re: Sending HEX values via serial port

Posted: Sun Dec 01, 2024 10:18 am
by infratec
If you want to transmit fixed hex sequences, you can also use the DataSection:

Code: Select all

#Port$ = "COM4"

If OpenSerialPort(0, #Port$, 57600, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)
  MessageRequester("Information", #Port$ + " opened.")  
Else
  MessageRequester("Error", "Can't open " + #Port$ + ". Ending program.")
  End
EndIf

OpenConsole()
PrintN("Press return to continue.")
Input()

WriteSerialPortData(0, ?SetSpd, ?SetSpdEnd - ?SetSpd)

CloseSerialPort(0)

DataSection
  SetSpd:
  Data.a $FF, $FF, $01, $05, $03, $20, $28, $00, $AE
  SetSpdEnd:
EndDataSection