Inital commit
CI / macOS (push) Has been cancelled
CI / Windows / Clang (push) Has been cancelled
CI / macOS (push) Has been cancelled
CI / Windows / Clang (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
add_library(celrs_crsf STATIC crsf.c)
|
||||
target_include_directories(celrs_crsf PUBLIC "${CMAKE_SOURCE_DIR}")
|
||||
target_compile_features(celrs_crsf PRIVATE c_std_23)
|
||||
|
||||
add_library(celrs_serial STATIC serial.c)
|
||||
target_include_directories(celrs_serial PUBLIC "${CMAKE_SOURCE_DIR}")
|
||||
target_compile_features(celrs_serial PRIVATE c_std_23)
|
||||
|
||||
# Level-filtering logger — calls log_write(); symbol resolved by the final binary
|
||||
add_library(celrs_logger STATIC logger.c)
|
||||
target_include_directories(celrs_logger PUBLIC "${CMAKE_SOURCE_DIR}")
|
||||
target_compile_features(celrs_logger PRIVATE c_std_23)
|
||||
|
||||
# Real log_write implementation — linked into production binaries only
|
||||
add_library(celrs_log_write STATIC log_write.c)
|
||||
target_include_directories(celrs_log_write PUBLIC "${CMAKE_SOURCE_DIR}")
|
||||
target_compile_features(celrs_log_write PRIVATE c_std_23)
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "celrs/crsf.h"
|
||||
#include <string.h>
|
||||
|
||||
/* CRC8-CCITT with polynomial 0x07, init 0x00 (used by CRSF/ELRS) */
|
||||
uint8_t cel_crsf_crc(uint8_t const* data, size_t len) {
|
||||
uint8_t crc = 0x00;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
crc ^= data[i];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
crc = (crc & 0x80) ? (crc << 1) ^ 0x07 : (crc << 1);
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
int cel_crsf_frame_validate(cel_crsf_frame const* frame) {
|
||||
/* Rebuild the data that was CRC'd: dest + src + type + size + payload */
|
||||
uint8_t data[260];
|
||||
size_t offset = 0;
|
||||
data[offset++] = frame->destination;
|
||||
data[offset++] = frame->source;
|
||||
data[offset++] = frame->type;
|
||||
data[offset++] = frame->size;
|
||||
memcpy(data + offset, frame->payload, frame->size);
|
||||
offset += frame->size;
|
||||
|
||||
uint8_t calc_crc = cel_crsf_crc(data, offset);
|
||||
return calc_crc == frame->crc ? 0 : -1;
|
||||
}
|
||||
|
||||
int cel_crsf_frame_parse(cel_crsf_frame* frame, uint8_t const* buf, size_t len) {
|
||||
if (frame == NULL || buf == NULL) return -1;
|
||||
/* Minimum: header(1) + dest(1) + src(1) + type(1) + size(1) = 5 bytes */
|
||||
if (len < 5) return -1;
|
||||
|
||||
/* Verify header */
|
||||
if (buf[0] != CEL_CRSF_FRAME_HEADER) return -1;
|
||||
|
||||
frame->destination = buf[1];
|
||||
frame->source = buf[2];
|
||||
frame->type = buf[3];
|
||||
frame->size = buf[4];
|
||||
|
||||
uint8_t size = buf[4];
|
||||
/* Total: header(1) + dest(1) + src(1) + type(1) + size(1) + payload(N) + crc(1) */
|
||||
size_t total = 6 + size;
|
||||
if (len < total) return -1;
|
||||
|
||||
memcpy(frame->payload, buf + 5, size);
|
||||
frame->crc = buf[5 + size];
|
||||
|
||||
return cel_crsf_frame_validate(frame);
|
||||
}
|
||||
|
||||
size_t cel_crsf_frame_build(uint8_t* dst, uint8_t destination, uint8_t source,
|
||||
uint8_t type, uint8_t const* payload, uint8_t size) {
|
||||
if (dst == NULL) return 0;
|
||||
|
||||
dst[0] = CEL_CRSF_FRAME_HEADER;
|
||||
dst[1] = destination;
|
||||
dst[2] = source;
|
||||
dst[3] = type;
|
||||
dst[4] = size;
|
||||
|
||||
if (payload != NULL && size > 0) {
|
||||
memcpy(dst + 5, payload, size);
|
||||
}
|
||||
|
||||
/* CRC over dest + src + type + size + payload */
|
||||
uint8_t crc = cel_crsf_crc(dst + 1, 3 + 1 + size);
|
||||
dst[5 + size] = crc;
|
||||
|
||||
return 1 + 3 + 1 + size + 1; /* header + 3 fields + size byte + payload + crc */
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* CRSF frame header byte */
|
||||
#define CEL_CRSF_FRAME_HEADER 0xC8
|
||||
|
||||
/* CRSF device addresses */
|
||||
#define CEL_CRSF_ADDRESS_FC_BROADCAST 0x00
|
||||
#define CEL_CRSF_ADDRESS_FC 0x10
|
||||
#define CEL_CRSF_ADDRESS_TBS_GROUND_STATION 0x80
|
||||
#define CEL_CRSF_ADDRESS_CUSTOM_MODULE 0xEA
|
||||
#define CEL_CRSF_ADDRESS_RC_DEVICE 0xDD
|
||||
#define CEL_CRSF_ADDRESS_GPS 0xEC
|
||||
#define CEL_CRSF_ADDRESS_FLIGHT_CONTROLLER 0xED
|
||||
|
||||
/* CRSF frame types */
|
||||
typedef enum {
|
||||
CEL_CRSF_FRAMETYPE_PACKET_LINK_TELEMETRY = 0x02,
|
||||
CEL_CRSF_FRAMETYPE_RC_CHANNELS_PACKED = 0x01,
|
||||
CEL_CRSF_FRAMETYPE_GPS = 0x02,
|
||||
CEL_CRSF_FRAMETYPE_HEARTBEAT = 0x03,
|
||||
CEL_CRSF_FRAMETYPE_VERSION = 0x04,
|
||||
CEL_CRSF_FRAMETYPE_PARAMETER_SETTINGS_ENTRY = 0x05,
|
||||
CEL_CRSF_FRAMETYPE_PARAMETER_READ = 0x06,
|
||||
CEL_CRSF_FRAMETYPE_PARAMETER_WRITE = 0x07,
|
||||
CEL_CRSF_FRAMETYPE_DEVICE_INFO = 0x08,
|
||||
CEL_CRSF_FRAMETYPE_PARAMETER_LIST = 0x09,
|
||||
CEL_CRSF_FRAMETYPE_RC_CHANNELS_RAW = 0x16,
|
||||
CEL_CRSF_FRAMETYPE_MSP_READ = 0x17,
|
||||
CEL_CRSF_FRAMETYPE_MSP_WRITE = 0x18,
|
||||
CEL_CRSF_FRAMETYPE_CURR_VOLTAGE_TEMP = 0x1E,
|
||||
CEL_CRSF_FRAMETYPE_BATTERY_SENSOR = 0x1F,
|
||||
CEL_CRSF_FRAMETYPE_COMPRESSED_SENSORS = 0x28,
|
||||
CEL_CRSF_FRAMETYPE_ARM = 0x0D,
|
||||
CEL_CRSF_FRAMETYPE_SETTING = 0x9E,
|
||||
CEL_CRSF_FRAMETYPE_SUPERBOX = 0xA0,
|
||||
CEL_CRSF_FRAMETYPE_DEVICE_SUPERBOX = 0xA1,
|
||||
} cel_crsf_frame_type;
|
||||
|
||||
/* Parsed CRSF frame */
|
||||
typedef struct {
|
||||
uint8_t destination;
|
||||
uint8_t source;
|
||||
uint8_t type;
|
||||
uint8_t size;
|
||||
uint8_t payload[255];
|
||||
uint8_t crc;
|
||||
} cel_crsf_frame;
|
||||
|
||||
/* CRC8 calculation over CRSF frame data (CCITT poly 0x07) */
|
||||
uint8_t cel_crsf_crc(uint8_t const* data, size_t len);
|
||||
|
||||
/* Validate CRC of a CRSF frame (header already stripped, starts at dest addr) */
|
||||
int cel_crsf_frame_validate(cel_crsf_frame const* frame);
|
||||
|
||||
/* Parse a raw buffer into a cel_crsf_frame. Returns 0 on success, -1 on error.
|
||||
buf should start with 0xC8 header. */
|
||||
int cel_crsf_frame_parse(cel_crsf_frame* frame, uint8_t const* buf, size_t len);
|
||||
|
||||
/* Build a CRSF frame into dst buffer. Returns total bytes written.
|
||||
dst must have space for at least 5 + size bytes (header, addr, src, type,
|
||||
size byte, payload, crc). */
|
||||
size_t cel_crsf_frame_build(uint8_t* dst, uint8_t destination, uint8_t source,
|
||||
uint8_t type, uint8_t const* payload, uint8_t size);
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "celrs/log_write.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void cel_log_write(char const* msg) {
|
||||
printf("%s\n", msg);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void cel_log_write(char const* msg);
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "celrs/logger.h"
|
||||
#include "celrs/log_write.h"
|
||||
#include <stdio.h>
|
||||
|
||||
static cel_log_level s_level = CEL_LOG_DEBUG;
|
||||
|
||||
void cel_logger_set_level(cel_log_level level) { s_level = level; }
|
||||
|
||||
static void emit(char const* prefix, char const* msg) {
|
||||
char buf[512];
|
||||
snprintf(buf, sizeof(buf), "[%s] %s", prefix, msg);
|
||||
cel_log_write(buf);
|
||||
}
|
||||
|
||||
void cel_log_debug(char const* msg) { if (s_level <= CEL_LOG_DEBUG) emit("DEBUG", msg); }
|
||||
void cel_log_info(char const* msg) { if (s_level <= CEL_LOG_INFO) emit("INFO", msg); }
|
||||
void cel_log_warn(char const* msg) { if (s_level <= CEL_LOG_WARN) emit("WARN", msg); }
|
||||
void cel_log_err(char const* msg) { if (s_level <= CEL_LOG_ERROR) emit("ERROR", msg); }
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum { CEL_LOG_DEBUG = 0, CEL_LOG_INFO, CEL_LOG_WARN, CEL_LOG_ERROR, CEL_LOG_NONE } cel_log_level;
|
||||
|
||||
void cel_logger_set_level(cel_log_level level);
|
||||
void cel_log_debug(char const* msg);
|
||||
void cel_log_info(char const* msg);
|
||||
void cel_log_warn(char const* msg);
|
||||
void cel_log_err(char const* msg);
|
||||
@@ -0,0 +1,69 @@
|
||||
#ifdef _MSC_VER
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "celrs/serial.h"
|
||||
|
||||
/*
|
||||
* Platform-agnostic serial port implementation.
|
||||
*
|
||||
* Windows uses Win32 CreateFile/ReadFile/WriteFile.
|
||||
* POSIX uses open/read/write with termios.
|
||||
*
|
||||
* This is a stub implementation that compiles but does nothing.
|
||||
* Real platform-specific code will be added when TDD tests drive it.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct cel_serial_port {
|
||||
char path[256];
|
||||
int baud_rate;
|
||||
int fd; /* platform-specific handle (HANDLE on Win, int on POSIX) */
|
||||
};
|
||||
|
||||
cel_serial_port* cel_serial_open(char const* path, int baud_rate) {
|
||||
if (path == NULL) return NULL;
|
||||
|
||||
cel_serial_port* port = (cel_serial_port*)calloc(1, sizeof(cel_serial_port));
|
||||
if (port == NULL) return NULL;
|
||||
|
||||
strncpy(port->path, path, sizeof(port->path) - 1);
|
||||
port->path[sizeof(port->path) - 1] = '\0';
|
||||
port->baud_rate = baud_rate;
|
||||
port->fd = -1;
|
||||
|
||||
/* TODO: platform-specific open (CreateFile on Win, open+termios on POSIX) */
|
||||
(void)baud_rate;
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
void cel_serial_close(cel_serial_port* port) {
|
||||
if (port == NULL) return;
|
||||
/* TODO: platform-specific close */
|
||||
free(port);
|
||||
}
|
||||
|
||||
size_t cel_serial_read(cel_serial_port* port, unsigned char* buf, size_t len, int timeout_ms) {
|
||||
(void)port;
|
||||
(void)buf;
|
||||
(void)len;
|
||||
(void)timeout_ms;
|
||||
/* TODO: platform-specific read */
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t cel_serial_write(cel_serial_port* port, unsigned char const* buf, size_t len) {
|
||||
(void)port;
|
||||
(void)buf;
|
||||
(void)len;
|
||||
/* TODO: platform-specific write */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cel_serial_flush(cel_serial_port* port) {
|
||||
(void)port;
|
||||
/* TODO: platform-specific flush */
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
|
||||
/* Opaque serial port handle */
|
||||
typedef struct cel_serial_port cel_serial_port;
|
||||
|
||||
/* Open a serial port. path is platform-specific:
|
||||
* Windows: "COM3"
|
||||
* Linux/macOS: "/dev/ttyUSB0"
|
||||
* baud_rate: 400000 is standard for ELRS CRSF
|
||||
* Returns NULL on failure. */
|
||||
cel_serial_port* cel_serial_open(char const* path, int baud_rate);
|
||||
|
||||
/* Close and free the serial port */
|
||||
void cel_serial_close(cel_serial_port* port);
|
||||
|
||||
/* Read up to len bytes. Returns bytes read, 0 on timeout/error. */
|
||||
size_t cel_serial_read(cel_serial_port* port, unsigned char* buf, size_t len, int timeout_ms);
|
||||
|
||||
/* Write data. Returns bytes written, 0 on error. */
|
||||
size_t cel_serial_write(cel_serial_port* port, unsigned char const* buf, size_t len);
|
||||
|
||||
/* Flush output buffer */
|
||||
void cel_serial_flush(cel_serial_port* port);
|
||||
Reference in New Issue
Block a user