Servo Movement
Client → Server Servo Movement Query Format
The servo movement query (which uses the 4-byte code SRVP
) is used by the client to issue movement commands for the selected mcu. The query has the structure:
[NUMBER_OF_MOVEMENTS]
→ A singleuint8_t
indicating how many servo movements the query contains. Since it cannot be zero, it's not affected by the offset.[SERVO_ID:SERVO_POS]
→ The information pairSERVO_ID
, the id of the servo the client wants to move; andSERVO_POS
, the position the servo has to take. These two values are separated by the ASCII character:
(uint8_t c=58
), and for multiple movements, each information pairSERVO_ID:SERVO_POS
is separated by a dash-
(uint8_t c=45
) from the information pair of the following servo movement. BothSERVO_ID
andSERVO_POS
are singleuint8_t
values, and must have the positive +1 offset applied, as they can take value zero.
[NUMBER_OF_MOVEMENTS]
→ A singleuint8_t
indicating how many servo movements the query contains. Since it cannot be zero, it's not affected by the offset.[SERVO_ID:PWM_SIGNAL]
→ The information pairSERVO_ID
, the id of the servo the client wants to move; andPWM_SIGNAL
, the PWM signal for the given servo ID. These two values are separated by the ASCII character:
(uint8_t c=58
), and for multiple movements, each information pairSERVO_ID:PWM_SIGNAL
is separated by a dash-
(uint8_t c=45
) from the information pair of the following servo movement. BothSERVO_ID
(a singleuint8_t
value) andPWM_SIGNAL
(auint16_t
value) must have the positive +1 offset applied, as they can take value zero.
Due to the server function of query parameter validation, the response for this query can take two forms:
If during validation, the server rejects a query for any reason, it will return a single NACK control query to the client, ending the SRVP process. The defined possible NACK codes for this first reponse are:
_NACK_InvalidQuery
(uint8_t c=255
) → If the query format is invalid: contains null-bytes, invalid header or tail sequences, or the query function code is not recognized._NACK_NoActiveMCU
(uint8_t c=254
) → If client has not selected a mcu before issuing the movement command._NACK_InvalidParameter
(uint8_t c=252
) → If the query parameters are out of range: a servo id exceeds the selected mcu servo count, or a servo position out of the range [1→180]._NACK_ServoCountMissmatch
(uint8_t c=251
) → If the movement order tries to move more servos than the selected mcu has._NACK_NoMCUInfo
(uint8_t c=250
) → If the selected mcu is a "DumbMCU", and there's no PWM information available in the server database._NACK_MCUOffline
(uint8_t c=249
) → If the selected mcu is not currently connected to the server.
On the other hand, if the server validates the parameters, it will return a first ACK control query to the client and forward the SRVP query to the target mcu. Once the mcu performs the movement, it will reply with a control query to the server, which is then be relayed back to the client as a second control response. For this second control query, the defined possible NACK codes are:
_NACK_InvalidQuery
(uint8_t c=255
) → Should never happen: If the query format is invalid: contains null-bytes, invalid header or tail sequences, or the query function code is not recognized._NACK_NoActiveMCU
(uint8_t c=254
) → If client has not selected a mcu before issuing the movement command. Would happen if another client selects the same mcu as the current client._NACK_MCUOffline
(uint8_t c=249
) → If the selected mcu is not currently connected to the server. Would happen if the mcu disconnects between the client query validation and the server message to the mcu._NACK_ErrorContactingMCU
(uint8_t c=248
) → If the selected mcu is not currently connected to the server.
During the second control response, both _NACK_NoActiveMCU
and _NACK_MCUOffline
represent time-of-check time-of-use conditions catched by the server during query dispatch.
Server → MCU Servo Movement Query Format
The servo movement query sent by the client must be translated from the client → server into the server → mcu format before forwarding it to the mcu, which makes the query take the form:
Where the query parameters [NUMBER_OF_MOVEMENTS]
, [SERVO_ID:SERVO_POS]
and [SERVO_ID:PWM_SIGNAL]
remain unchanged and adhere to the same rules as the client → server format.
PWM Signal codification
Since the PWM signal can take various bit resolution levels, usually ranging from 8-bit to 16-bit, with >16bit being niche or application-specific territory, the procotol allocates 2 bytes (up to 15-bit pwm resolution) for PWM signals on SRVP queries.
This means that in order to follow the protocol no-zero convention, each individual byte needs to incorporate this offset. This is required because, for values of PWM_SIGNAL<255
, the upper-byte will always be zero, and for the specific value PWM_SIGNAL=256
, the lower-byte will be zero.
How is offset applied
The offset is introduced by fixing the most significant bit of the uint16_t PWM_SIGNAL
to 1
, and then adding 1 to the normal PWM signal value. This results in a 15-bit signal with one fewer representable PWM value, a range of [0 → 32766]. On PWM servos that use the full 15-bit resolution over a 180º linear range, this results in a precision loss of approximately 5.493 millidegrees reduction in total range, and 0.1676 microdegrees decrease in angular resolution per step, which is effectively negligible for most use-cases.
To introduce this offset, the formula PWM_SIGNAL = pwm + (1+(0b1<<15))
is used; and the formula pwm = PWM_SIGNAL - (1+(0b1<<15))
is used on the receiving end to undo the offset applied.
Endianness
To avoid the endianness problem, the protocol introduces a normalized implementation through which uint16_t
values are split into two uint8_t
values in a set order (the first byte will be the upper-byte, followed by the lower-byte). The visual representation of this transformation would be:
And the functions used for this are:
divideIntoBytes() | |
---|---|
restore16int() | |
---|---|
Examples: SRVP Communication Overview
The servo movement query takes on a different communication path depending on whether the server validation passes or fails. The possible schemas are:
For instance, the movement query !s-SRVP-2-9:13-7:18-e!
would translate to "Perform 2 servo movements: move servo ID 8 to position 12, and servo ID 6 to position 17". Assuming the parameters are valid, and the selected mcu is online, the query validation would succeed, returning the first ACK to the client while forwarding the SRVP query to the mcu. Once the mcu completes the movements, it will reply with a positive control query, which will be relayed back to the client as a second ACK.
On the other hand, if the user sends the query !s-SRVP-1-3:187-e!
the movement order (move servo id=2, to position 186) would be invalid, as the servo position is out-of-range. In this case, the server validation would catch it, returning a negative control code (_NACK_InvalidParameter
) to the client, and ending the SRVP query "process".
The final option is that, once validated by the server, the mcu deems the movement order invalid. This would lead to a positive first control response, followed by a negative control query. In this case, similar to the SRVP rejection by server, no movement is performed.