rcbench
Servo and ESC test bench: panel, IOmcu and shared C
Loading...
Searching...
No Matches
openyge.h File Reference

The OpenYGE ESC (electronic speed controller) protocol: framing, telemetry, status and parameters. More...

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

Go to the source code of this file.

Data Structures

struct  openyge_header_t
struct  openyge_frame_t
 One decoded frame: header and payload. More...
struct  openyge_decoder_t
 Byte buffer and counters of the resynchronising decoder. More...
struct  openyge_telemetry_t
struct  openyge_status_t
 Decoded status byte. More...
struct  openyge_params_t

Macros

#define OPENYGE_SYNC   0xA5u
#define OPENYGE_VERSION   3u
#define OPENYGE_HDR_V3   6u
#define OPENYGE_HDR_LEGACY   4u
#define OPENYGE_CRC_BYTES   2u
#define OPENYGE_TELEMETRY_BYTES   26u
#define OPENYGE_CONTROL_BYTES   4u
#define OPENYGE_MIN_FRAME   (OPENYGE_HDR_LEGACY + OPENYGE_CRC_BYTES)
 The shortest possible frame: legacy header plus CRC (cyclic redundancy check).
#define OPENYGE_MAX_FRAME   140u
#define OPENYGE_CRC_INIT   0x0000u
 CRC-16/XMODEM: polynomial 0x1021, no reflection, no final XOR, seed 0x0000.
#define OPENYGE_CRC_CHECK   0x31C3u
#define OPENYGE_DEV_MASTER   0x80u
 Address byte: bit 7 set means the sender is the master.
#define OPENYGE_DEV_ADDR_MASK   0x7Fu
#define OPENYGE_DEV_BROADCAST   0x00u
#define OPENYGE_TEMP_OFFSET   40
 Temperatures travel as °C + 40, so −40 is representable in a uint8.
#define OPENYGE_MAX_PARAMS   64
 The bitmap that tracks which indices have been seen is 64 bits wide.
#define OPENYGE_P_COUNT   0
 Indices this project relies on.
#define OPENYGE_P_MOTOR_POLES   20
#define OPENYGE_P_PINION_TEETH   21
#define OPENYGE_P_MAIN_TEETH   22

Enumerations

enum  openyge_frame_type_t {
  OPENYGE_FT_TELE_AUTO = 0x00 , OPENYGE_FT_TELE_RESP = 0x02 , OPENYGE_FT_TELE_REQ = 0x03 , OPENYGE_FT_WRITE_PARAM_RESP = 0x04 ,
  OPENYGE_FT_WRITE_PARAM_REQ = 0x05
}
 Frame type codes, the type byte of the header. More...
enum  openyge_subject_t { OPENYGE_SUBJECT_ESC = 0 , OPENYGE_SUBJECT_BEC }
 Which device the warning bits are talking about.
enum  openyge_state_t {
  OPENYGE_ST_DISARMED = 0x0 , OPENYGE_ST_POWER_CUT = 0x1 , OPENYGE_ST_FAST_START = 0x2 , OPENYGE_ST_ALIGN_FOR_POS = 0x4 ,
  OPENYGE_ST_BRAKING_NORM_FINI = 0x6 , OPENYGE_ST_BRAKING_SYNC_FINI = 0x7 , OPENYGE_ST_STARTING = 0x8 , OPENYGE_ST_BRAKING_NORM = 0x9 ,
  OPENYGE_ST_BRAKING_SYNC = 0xA , OPENYGE_ST_WINDMILLING = 0xC , OPENYGE_ST_RUNNING_NORM = 0xE
}
 The motor states. More...

Functions

uint16_t openyge_crc (uint16_t crc, const void *data, size_t len)
 Fold len bytes at data into crc and return the new accumulator.
void openyge_decoder_reset (openyge_decoder_t *d)
 Clear a decoder, buffer and counters.
bool openyge_decode_byte (openyge_decoder_t *d, uint8_t byte, openyge_frame_t *out)
 Feed one received byte; returns true when out holds a whole good frame.
bool openyge_frame_has_telemetry (const openyge_frame_t *f)
 True if this frame is one an ESC sends, i.e. one carrying telemetry.
bool openyge_telemetry_parse (const openyge_frame_t *f, openyge_telemetry_t *out)
 Parse the telemetry payload.
bool openyge_control_parse (const openyge_frame_t *f, uint16_t *index, uint16_t *param)
 Parse the 4-byte control payload of a request.
size_t openyge_encode (uint8_t *out, size_t cap, uint8_t type, uint8_t device, uint8_t seq, const void *payload, size_t payload_len)
 Build a frame.
size_t openyge_build_telemetry_request (uint8_t *out, size_t cap, uint8_t device, uint8_t seq)
 A telemetry request: the control payload, both words zero.
size_t openyge_build_param_write (uint8_t *out, size_t cap, uint8_t device, uint8_t seq, uint16_t index, uint16_t value)
 A single parameter write.
void openyge_status_decode (uint8_t status1, openyge_status_t *out)
 Decode the status byte of a telemetry frame.
const char * openyge_state_name (uint8_t state)
 A short name for a motor state, or "?" for a reserved value.
void openyge_params_reset (openyge_params_t *p)
 Clear the parameter cache.
void openyge_params_observe (openyge_params_t *p, uint16_t index, uint16_t value)
 Record one (index, value) pair from a telemetry frame.
void openyge_params_begin_writes (openyge_params_t *p)
 Mark writes outstanding, which withdraws the table until it is re-read.
void openyge_params_end_writes (openyge_params_t *p)
 Clear the outstanding-writes mark.
bool openyge_params_complete (const openyge_params_t *p)
 True once every index 0..count-1 has been seen and no write is pending.
bool openyge_params_get (const openyge_params_t *p, uint16_t index, uint16_t *out)
 Read one.
float openyge_params_progress (const openyge_params_t *p)
 0..1, for a progress bar.
bool openyge_motor_rpm (const openyge_params_t *p, uint32_t erpm, float *motor_rpm)
 Mechanical and head RPM (revolutions per minute) from eRPM (electrical RPM) and the gear train.
bool openyge_head_rpm (const openyge_params_t *p, uint32_t erpm, float *head_rpm)
 Head rpm from eRPM and the gear train.

Detailed Description

The OpenYGE ESC (electronic speed controller) protocol: framing, telemetry, status and parameters.

Implements the OpenYGE telemetry and parameter protocol of YGE ESCs. YGE and OpenYGE are names or trademarks of their respective owners; they are used here only to say which ESCs this module works with (NOTICE, "Trademarks").

Written from docs/OpenYGE.md, the specification of record. Anything that page marks as inferred is inferred here too, and each such place carries a comment.

Boundaries of this module:

  • No UART (universal asynchronous receiver-transmitter) access. Bytes in, decoded frames out, so the host suite feeds it truncated and corrupted frames without an ESC on the bench.
  • No struct cast over the receive buffer. The wire is little-endian and packed; a host need be neither, and an unaligned 16-bit read is undefined behaviour on a strict-alignment target. Every field is assembled byte by byte.
  • No parameter writes. Reading is safe; writing is not until the parameter indices are confirmed. Building a write request is here; deciding to send one is not.

SPDX-License-Identifier: MIT

Macro Definition Documentation

◆ OPENYGE_CONTROL_BYTES

#define OPENYGE_CONTROL_BYTES   4u

control payload length

◆ OPENYGE_CRC_BYTES

#define OPENYGE_CRC_BYTES   2u

CRC trailer, little-endian

◆ OPENYGE_CRC_INIT

#define OPENYGE_CRC_INIT   0x0000u

CRC-16/XMODEM: polynomial 0x1021, no reflection, no final XOR, seed 0x0000.

The check value is over the 9 bytes of the ASCII string "123456789"; an implementation at the far end is verified against it before it is trusted.

◆ OPENYGE_HDR_LEGACY

#define OPENYGE_HDR_LEGACY   4u

pre-v3: no seq, no device

◆ OPENYGE_HDR_V3

#define OPENYGE_HDR_V3   6u

header bytes, version 3 and up

◆ OPENYGE_MAX_FRAME

#define OPENYGE_MAX_FRAME   140u

largest frame, in bytes

◆ OPENYGE_P_COUNT

#define OPENYGE_P_COUNT   0

Indices this project relies on.

Every other index is carried, not used. These are unconfirmed too; confirm them before anything is written.

◆ OPENYGE_SYNC

#define OPENYGE_SYNC   0xA5u

first byte of every frame

◆ OPENYGE_TELEMETRY_BYTES

#define OPENYGE_TELEMETRY_BYTES   26u

telemetry payload length

◆ OPENYGE_VERSION

#define OPENYGE_VERSION   3u

the version this speaks; a frame of version 3 or above is read with the 6-byte header

Enumeration Type Documentation

◆ openyge_frame_type_t

Frame type codes, the type byte of the header.

Enumerator
OPENYGE_FT_TELE_AUTO 

ESC pushes, unsolicited

OPENYGE_FT_TELE_RESP 

ESC answers a request

OPENYGE_FT_TELE_REQ 

master asks

OPENYGE_FT_WRITE_PARAM_RESP 

ESC acknowledges a write

OPENYGE_FT_WRITE_PARAM_REQ 

master writes one parameter

◆ openyge_state_t

The motor states.

Reserved values are not listed and must not be mapped onto a neighbour; show the number.

Function Documentation

◆ openyge_build_param_write()

size_t openyge_build_param_write ( uint8_t * out,
size_t cap,
uint8_t device,
uint8_t seq,
uint16_t index,
uint16_t value )

A single parameter write.

One parameter per frame; there is no page write.

Parameters
outBuffer to write the frame into.
capSize of out in bytes.
deviceDevice address, without the master bit.
seqSequence number.
indexParameter index.
valueValue to write.
Returns
Length of the frame, or 0 if it does not fit.

◆ openyge_build_telemetry_request()

size_t openyge_build_telemetry_request ( uint8_t * out,
size_t cap,
uint8_t device,
uint8_t seq )

A telemetry request: the control payload, both words zero.

Parameters
outBuffer to write the frame into.
capSize of out in bytes.
deviceDevice address, without the master bit.
seqSequence number.
Returns
Length of the frame, or 0 if it does not fit.

◆ openyge_control_parse()

bool openyge_control_parse ( const openyge_frame_t * f,
uint16_t * index,
uint16_t * param )

Parse the 4-byte control payload of a request.

Parameters
fDecoded frame.
indexReceives the parameter index; NULL to skip.
paramReceives the parameter value; NULL to skip.
Returns
true when parsed; false for a telemetry frame or a payload of the wrong length.

◆ openyge_crc()

uint16_t openyge_crc ( uint16_t crc,
const void * data,
size_t len )

Fold len bytes at data into crc and return the new accumulator.

A frame's CRC is openyge_crc(OPENYGE_CRC_INIT, frame, total - 2), stored little-endian in the frame's last 2 bytes. Incremental: folding a buffer in pieces gives what folding it whole gives. A len of 0 returns crc and reads nothing.

Parameters
crcAccumulator from an earlier call, or OPENYGE_CRC_INIT.
dataBytes to fold.
lenNumber of bytes in data.
Returns
The new accumulator.

◆ openyge_decode_byte()

bool openyge_decode_byte ( openyge_decoder_t * d,
uint8_t byte,
openyge_frame_t * out )

Feed one received byte; returns true when out holds a whole good frame.

Resynchronising: noise carrying a plausible sync byte in front of a real frame must not swallow the real one. Every sync byte in the buffer is a candidate and the earliest complete candidate wins.

Parameters
dDecoder state.
byteReceived byte.
outReceives the frame when true is returned.
Returns
true when a whole good frame was decoded.

◆ openyge_decoder_reset()

void openyge_decoder_reset ( openyge_decoder_t * d)

Clear a decoder, buffer and counters.

Parameters
dDecoder to clear; NULL does nothing.

◆ openyge_encode()

size_t openyge_encode ( uint8_t * out,
size_t cap,
uint8_t type,
uint8_t device,
uint8_t seq,
const void * payload,
size_t payload_len )

Build a frame.

Returns its length, or 0 if it would not fit or the type is not one this speaks.

The length byte is written from what was assembled, after the bounds check, so a frame that does not fit leaves the buffer untouched.

Parameters
outBuffer to write the frame into.
capSize of out in bytes.
typeOne of openyge_frame_type_t.
deviceDevice address, without the master bit.
seqSequence number.
payloadPayload bytes.
payload_lenPayload length; must match the type's payload size.
Returns
Length of the frame, or 0 if it would not fit or the type is not one this speaks.

◆ openyge_frame_has_telemetry()

bool openyge_frame_has_telemetry ( const openyge_frame_t * f)

True if this frame is one an ESC sends, i.e. one carrying telemetry.

Parameters
fDecoded frame.
Returns
true for an ESC frame with a telemetry-length payload.

◆ openyge_head_rpm()

bool openyge_head_rpm ( const openyge_params_t * p,
uint32_t erpm,
float * head_rpm )

Head rpm from eRPM and the gear train.

Parameters
pCache with the pole count and both gear teeth counts.
erpmElectrical rpm.
head_rpmReceives the head rpm.
Returns
true when the value was written.

◆ openyge_motor_rpm()

bool openyge_motor_rpm ( const openyge_params_t * p,
uint32_t erpm,
float * motor_rpm )

Mechanical and head RPM (revolutions per minute) from eRPM (electrical RPM) and the gear train.

False unless the table is complete and the pole count is at least 2. Head speed needs the pinion and main gear teeth too; when either is zero the head figure is not written.

Parameters
pCache with the pole count.
erpmElectrical rpm.
motor_rpmReceives the motor rpm.
Returns
true when the value was written.

◆ openyge_params_begin_writes()

void openyge_params_begin_writes ( openyge_params_t * p)

Mark writes outstanding, which withdraws the table until it is re-read.

Parameters
pCache to update; NULL does nothing.

◆ openyge_params_complete()

bool openyge_params_complete ( const openyge_params_t * p)

True once every index 0..count-1 has been seen and no write is pending.

Parameters
pCache to test.
Returns
true when the whole table is in the cache.

◆ openyge_params_end_writes()

void openyge_params_end_writes ( openyge_params_t * p)

Clear the outstanding-writes mark.

Parameters
pCache to update; NULL does nothing.

◆ openyge_params_get()

bool openyge_params_get ( const openyge_params_t * p,
uint16_t index,
uint16_t * out )

Read one.

False unless the whole table is complete: a half-read table is not the ESC's settings.

Parameters
pCache to read.
indexParameter index, below the table count.
outReceives the value.
Returns
true when the value was read.

◆ openyge_params_observe()

void openyge_params_observe ( openyge_params_t * p,
uint16_t index,
uint16_t value )

Record one (index, value) pair from a telemetry frame.

Ignored while writes are outstanding: a frame already in flight from before a write carries the old value and would overwrite the new one.

Parameters
pCache to update.
indexParameter index, below OPENYGE_MAX_PARAMS; other values are ignored.
valueParameter value.

◆ openyge_params_progress()

float openyge_params_progress ( const openyge_params_t * p)

0..1, for a progress bar.

The table fills in about 1.6s at 20Hz and 32 parameters.

Parameters
pCache to read.
Returns
0..1; 0 while writes are pending, or while the count is 0 or above OPENYGE_MAX_PARAMS.

◆ openyge_params_reset()

void openyge_params_reset ( openyge_params_t * p)

Clear the parameter cache.

Parameters
pCache to clear; NULL does nothing.

◆ openyge_state_name()

const char * openyge_state_name ( uint8_t state)

A short name for a motor state, or "?" for a reserved value.

Parameters
stateState value; only the low nibble is used.
Returns
The name, or "?" for a reserved value.

◆ openyge_status_decode()

void openyge_status_decode ( uint8_t status1,
openyge_status_t * out )

Decode the status byte of a telemetry frame.

Parameters
status1The status byte.
outReceives the decoded status; NULL does nothing.

◆ openyge_telemetry_parse()

bool openyge_telemetry_parse ( const openyge_frame_t * f,
openyge_telemetry_t * out )

Parse the telemetry payload.

False if the frame does not carry one.

Parameters
fDecoded frame.
outReceives the telemetry.
Returns
true when parsed; false if the frame does not carry telemetry or out is NULL.