diff --git a/examples/platformio/serial_transmitter_prototype.cpp b/examples/platformio/serial_transmitter_prototype.cpp new file mode 100644 index 00000000..69e2cfc0 --- /dev/null +++ b/examples/platformio/serial_transmitter_prototype.cpp @@ -0,0 +1,255 @@ +#if __has_include("Arduino.h") +#include "Arduino.h" +#define ARDUINO_IS_INCLUDED 1 +#else +#define ARDUINO_IS_INCLUDED 0 +#endif + +/* Packet rate enumeration. */ +typedef enum packet_rate_index_e +{ + PACKET_RATE_4HZ, + PACKET_RATE_25HZ, + PACKET_RATE_50HZ, + PACKET_RATE_100HZ, + PACKET_RATE_150HZ, + PACKET_RATE_200HZ, + PACKET_RATE_250HZ, + PACKET_RATE_333HZ, + PACKET_RATE_500HZ, + PACKET_RATE_1000HZ, + PACKET_RATE_COUNT +} packet_rate_index_t; + +/* Selected packet rate. */ +const packet_rate_index_t selected_packet_rate = PACKET_RATE_50HZ; +uint32_t *packet_rate_us = nullptr; + +/* Time structure for the packet rate. */ +typedef struct software_realtime_counter_s +{ + uint32_t time_us = 0; + uint32_t time_us_last = 0; + uint32_t time_us_delta = 0; + int32_t time_us_error = 0; + const int32_t time_us_max_allowed_error = 2; +} software_realtime_counter_t; + +/* Packed 11-bit RC Channels. */ +struct rc_channels_packed_s +{ + uint16_t ch1 : 11; + uint16_t ch2 : 11; + uint16_t ch3 : 11; + uint16_t ch4 : 11; + uint16_t ch5 : 11; + uint16_t ch6 : 11; + uint16_t ch7 : 11; + uint16_t ch8 : 11; + uint16_t ch9 : 11; + uint16_t ch10 : 11; + uint16_t ch11 : 11; + uint16_t ch12 : 11; + uint16_t ch13 : 11; + uint16_t ch14 : 11; + uint16_t ch15 : 11; + uint16_t ch16 : 11; +} __attribute__((packed)); + +typedef struct rc_channels_packed_s rc_channels_packed_t; + +/* CRSF Frame structure and union. */ +typedef struct crsf_frame_s +{ + uint8_t sync; + uint8_t length; + uint8_t type; + uint8_t payload[60]; + uint8_t crc; +} crsf_frame_t; + +const size_t crsf_frame_size = sizeof(crsf_frame_t); + +typedef union crsf_tx_frame_u +{ + crsf_frame_t frame; + uint8_t buffer[crsf_frame_size]; +} crsf_tx_frame_t; + +/* Time structure instance. */ +software_realtime_counter_t *sw_timer = nullptr; + +crsf_tx_frame_t crsf_tx_frame; + +/* CRC8-DVB-S2. */ +uint8_t crc8_dvb_s2(uint8_t crc, const uint8_t data) +{ + crc ^= data; + for (uint8_t i = 0; i < 8; i++) + { + if (crc & 0x80) + { + crc = (crc << 1) ^ 0xD5; + } + else + { + crc <<= 1; + } + } + return crc; +} + +/* This function calculates the CRC for the CRSF frame +from the type to the end of the payload. */ +uint8_t calculate_crc(const crsf_tx_frame_t *crsf_tx_frame) +{ + uint8_t crc = 0; + crc = crc8_dvb_s2(crc, crsf_tx_frame->frame.type); + for (uint8_t i = 0; i < crsf_tx_frame->frame.length - 2; i++) + { + crc = crc8_dvb_s2(crc, crsf_tx_frame->frame.payload[i]); + } + return crc; +} + +/* Exit handlers. */ +void exit_success_handler() +{ + /* Clean up and stop. */ + delete[] packet_rate_us; + delete sw_timer; + + /* Print a message to the serial monitor. */ + Serial.println("Program has ended successfully."); +} + +void exit_time_max_allowed_error_handler() +{ + /* Print a message to the serial monitor. */ + Serial.print("Program has ended with an error: "); + Serial.println("Time error is greater than the maximum allowed error."); + + /* Print how far the sw_timer error is from the packet rate in microseconds. */ + Serial.print("Time Error: "); + Serial.print(sw_timer->time_us_error); + Serial.println(" us"); + + /* Clean up and stop. */ + delete[] packet_rate_us; + delete sw_timer; +} + +#if ARDUINO_IS_INCLUDED == 1 +void setup() +{ + Serial.begin(115200); + while (!Serial) + { + delay(10); + } + + /* Dynamically allocate memory for the packet rate, + and calculate the sw_timer in microseconds for each packet rate. */ + packet_rate_us = new uint32_t[PACKET_RATE_COUNT]; + packet_rate_us[PACKET_RATE_4HZ] = (1000000UL / 4UL); + packet_rate_us[PACKET_RATE_25HZ] = (1000000UL / 25UL); + packet_rate_us[PACKET_RATE_50HZ] = (1000000UL / 50UL); + packet_rate_us[PACKET_RATE_100HZ] = (1000000UL / 100UL); + packet_rate_us[PACKET_RATE_150HZ] = (1000000UL / 150UL); + packet_rate_us[PACKET_RATE_200HZ] = (1000000UL / 200UL); + packet_rate_us[PACKET_RATE_250HZ] = (1000000UL / 250UL); + packet_rate_us[PACKET_RATE_333HZ] = (1000000UL / 333UL); + packet_rate_us[PACKET_RATE_500HZ] = (1000000UL / 500UL); + packet_rate_us[PACKET_RATE_1000HZ] = (1000000UL / 1000UL); + + /* Initialize the sw_timer structure. */ + sw_timer = new software_realtime_counter_t; + + /* Initialise Serial1 with 1.87M baud rate. */ + Serial1.begin(1875000); + memset(&crsf_tx_frame, 0, crsf_frame_size); + + /* Initialise the RC Channels structure. */ + rc_channels_packed_t rc_channels_packed; + rc_channels_packed.ch1 = 992; + rc_channels_packed.ch2 = 992; + rc_channels_packed.ch3 = 992; + rc_channels_packed.ch4 = 992; + rc_channels_packed.ch5 = 178; + rc_channels_packed.ch6 = 992; + rc_channels_packed.ch7 = 992; + rc_channels_packed.ch8 = 992; + rc_channels_packed.ch9 = 992; + rc_channels_packed.ch10 = 992; + rc_channels_packed.ch11 = 992; + rc_channels_packed.ch12 = 992; + rc_channels_packed.ch13 = 992; + rc_channels_packed.ch14 = 992; + rc_channels_packed.ch15 = 992; + rc_channels_packed.ch16 = 992; + + /* Prepare the CRSF RC Channels Packed frame. */ + crsf_tx_frame.frame.sync = 0xC8; // NB: EdgeTX uses 0xEE which is incorrect. + crsf_tx_frame.frame.length = 24; + crsf_tx_frame.frame.type = 0x16; + memcpy(crsf_tx_frame.frame.payload, &rc_channels_packed, sizeof(rc_channels_packed)); + crsf_tx_frame.frame.crc = calculate_crc(&crsf_tx_frame); + + /* Print a message to the serial monitor. */ + Serial.println("Testing CRSF Serial Transmitter Prototype..."); + + /* Initialise Serial1 with 1.87M baud rate. */ + Serial1.begin(1875000); + memset(&crsf_tx_frame, 0, crsf_frame_size); + + /* Set the time in microseconds. */ + sw_timer->time_us = micros(); + sw_timer->time_us_last = sw_timer->time_us; +} + +void loop() +{ + + static uint32_t iteration = 0; + + /* Calculate the number of iterations based on the selected packet rate and the equivalent total execution time of three seconds. */ + static const uint32_t iterations = (3000000UL / packet_rate_us[selected_packet_rate]); + + if (iteration < iterations) + { + /* Calculate the time delta in microseconds. */ + sw_timer->time_us = micros(); + sw_timer->time_us_delta = sw_timer->time_us - sw_timer->time_us_last; + + /* Calculate the time error in microseconds. */ + sw_timer->time_us_error = sw_timer->time_us - (sw_timer->time_us_last + packet_rate_us[selected_packet_rate]); + + /* If the time delta is greater than or equal to the packet rate in microseconds and the time error is less than the maximum allowed error. */ + if (sw_timer->time_us_delta >= packet_rate_us[selected_packet_rate] && sw_timer->time_us_error < sw_timer->time_us_max_allowed_error) + { + /* Set the last time in microseconds. */ + sw_timer->time_us_last = sw_timer->time_us; + + /* Write 64 bytes to Serial1. */ + Serial1.write(crsf_tx_frame.buffer, crsf_tx_frame.frame.length + 1); + + /* Increment the iteration. */ + iteration++; + } + + /* If the time error is greater than or equal to the maximum allowed error. */ + else if (sw_timer->time_us_error >= sw_timer->time_us_max_allowed_error) + { + /* Exit the program. */ + atexit(exit_time_max_allowed_error_handler); + exit(EXIT_FAILURE); + } + } + else + { + /* Exit the program. */ + atexit(exit_success_handler); + exit(EXIT_SUCCESS); + } +} +#endif diff --git a/library.json b/library.json index 9dfd96e2..01d379a4 100644 --- a/library.json +++ b/library.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json", "name": "CRSFforArduino", - "version": "1.1.0-1.0.0", + "version": "1.1.0-2024.9.3", "description": "An Arduino Library for communicating with ExpressLRS and TBS Crossfire receivers.", "keywords": "arduino, remote-control, arduino-library, protocols, rc, radio-control, crsf, expresslrs", "repository": diff --git a/library.properties b/library.properties index 648bedb0..bba2adef 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=CRSFforArduino -version=1.1.0-1.0.0 +version=1.1.0-2024.9.3 author=Cassandra Robinson maintainer=Cassandra Robinson sentence=CRSF for Arduino brings the Crossfire Protocol to the Arduino ecosystem. diff --git a/platformio.ini b/platformio.ini index a0883b29..25c75a8e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,7 +13,7 @@ core_dir = .pio/core default_envs = ; defect_detector development - ; ${build.all} + ; ${build.commonly_used} extra_configs = targets/common.ini targets/quality_control.ini @@ -31,9 +31,41 @@ test_dir = [env:development] board = adafruit_metro_m4 +build_flags = +build_src_flags = + ; Force GNU C++ 23: + -std=gnu++23 + ; Compile hardening flags: + -O2 + -Wall + -Wextra + -Wformat + -Wformat=2 + ; -Wconversion ; Disabled due to spamming conversion issues in packages outside the scope of CRSF for Arduino. + -Wimplicit-fallthrough + -U_FORTIFY_SOURCE + -D_FORTIFY_SOURCE=3 + -D_GLIBCXX_ASSERTIONS + ; -fsrict-flex-arrays=3 ; Not available in GCC Arm Compiler v12.3.1 + -fstack-clash-protection + -fstack-protector-strong build_src_filter = - +<../examples/platformio/main.cpp> + +<../examples/platformio/serial_transmitter_prototype.cpp> +<*/*/*.cpp> +<*.cpp> build_type = debug +build_unflags = + -c++11 + -Os +check_flags = + --enable=all + --disable=missingInclude + --disable=unusedFunction + --language=c++ + --std=c++23 +check_severity = low, medium, high +check_skip_packages = yes +check_tool = cppcheck extends = env_common_samd51 +platform_packages = + platformio/toolchain-gccarmnoneeabi @ 1.120301.0 ; PlatformIO's latest GCC Compiler version diff --git a/src/CFA_Config.hpp b/src/CFA_Config.hpp index 2c6425f9..d52d0c24 100644 --- a/src/CFA_Config.hpp +++ b/src/CFA_Config.hpp @@ -46,11 +46,11 @@ See https://semver.org/ for more information. */ // These are the pre-release version details which are only used if CRSFFORARDUINO_VERSION_IS_PRERELEASE is set to 1. // NOTE: Pre-release versions are not recommended for production use. #if CRSFFORARDUINO_VERSION_IS_PRERELEASE == 1 -#define CRSFFORARDUINO_VERSION_PRE "1.0.0" -#define CRSFFORARDUINO_VERSION_BUILD_DATE "2024-7-21" -#define CRSFFORARDUINO_VERSION_BUILD_MAJOR 1 -#define CRSFFORARDUINO_VERSION_BUILD_MINOR 0 -#define CRSFFORARDUINO_VERSION_BUILD_PATCH 0 +#define CRSFFORARDUINO_VERSION_PRE "2024.9.3" +#define CRSFFORARDUINO_VERSION_BUILD_DATE "2024-9-3" +#define CRSFFORARDUINO_VERSION_BUILD_YEAR 2024 +#define CRSFFORARDUINO_VERSION_BUILD_MONTH 9 +#define CRSFFORARDUINO_VERSION_BUILD_DAY 3 #endif /* Failsafe Options diff --git a/src/SerialReceiver/CRSF/CRSF.hpp b/src/SerialReceiver/CRSF/CRSF.hpp index 4805c523..17ae4ecf 100644 --- a/src/SerialReceiver/CRSF/CRSF.hpp +++ b/src/SerialReceiver/CRSF/CRSF.hpp @@ -24,7 +24,7 @@ #pragma once -#include "../CRC/CRC.hpp" +#include "../common/CRC/CRC.hpp" #include "CRSFProtocol.hpp" namespace serialReceiverLayer diff --git a/src/SerialReceiver/SerialReceiver.cpp b/src/SerialReceiver/SerialReceiver.cpp index 2a279a30..483be571 100644 --- a/src/SerialReceiver/SerialReceiver.cpp +++ b/src/SerialReceiver/SerialReceiver.cpp @@ -233,12 +233,22 @@ namespace serialReceiverLayer CRSF_DEBUG_SERIAL_PORT.println(); CRSF_DEBUG_SERIAL_PORT.flush(); +#if CRSF_DEBUG_ENABLE_VERSION_OUTPUT > 0 CRSF_DEBUG_SERIAL_PORT.print("Version: "); +#if CRSF_VERSION_IS_PRERELEASE > 0 + CRSF_DEBUG_SERIAL_PORT.print(CRSFFORARDUINO_VERSION); + CRSF_DEBUG_SERIAL_PORT.print("-"); + CRSF_DEBUG_SERIAL_PORT.println(CRSFFORARDUINO_VERSION_PRERELEASE); + CRSF_DEBUG_SERIAL_PORT.print("Build date: "); + CRSF_DEBUG_SERIAL_PORT.println(CRSFFORARDUINO_VERSION_BUILD_DATE); +#else CRSF_DEBUG_SERIAL_PORT.println(CRSFFORARDUINO_VERSION); CRSF_DEBUG_SERIAL_PORT.print("Build date: "); CRSF_DEBUG_SERIAL_PORT.println(CRSFFORARDUINO_VERSION_DATE); +#endif CRSF_DEBUG_SERIAL_PORT.println(); CRSF_DEBUG_SERIAL_PORT.flush(); +#endif for (int i = 0; i < 93; i++) { diff --git a/src/SerialReceiver/Telemetry/Telemetry.hpp b/src/SerialReceiver/Telemetry/Telemetry.hpp index e12b5a40..e56f82e5 100644 --- a/src/SerialReceiver/Telemetry/Telemetry.hpp +++ b/src/SerialReceiver/Telemetry/Telemetry.hpp @@ -26,7 +26,7 @@ #include "Arduino.h" -#include "../CRC/CRC.hpp" +#include "../common/CRC/CRC.hpp" #include "../CRSF/CRSFProtocol.hpp" #include "../SerialBuffer/SerialBuffer.hpp" diff --git a/src/SerialTransmitter/SerialTransmitter.cpp b/src/SerialTransmitter/SerialTransmitter.cpp new file mode 100644 index 00000000..b147a58b --- /dev/null +++ b/src/SerialTransmitter/SerialTransmitter.cpp @@ -0,0 +1,101 @@ +/** + * @file SerialReceiver.cpp + * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) + * @brief The Serial Transmitter layer for the CRSF for Arduino library. + * + * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. + * + * @section License GNU General Public License v3.0 + * This source file is a part of the CRSF for Arduino library. + * CRSF for Arduino is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CRSF for Arduino is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CRSF for Arduino. If not, see . + * + */ + +#include "SerialTransmitter.hpp" +#include "../hal/CompatibilityTable/CompatibilityTable.hpp" +#include "Arduino.h" + +using namespace hal; + +namespace serialTransmitterLayer +{ + SerialTransmitter::SerialTransmitter() + { + #if defined(ARDUINO_ARCH_STM32) +#if defined(HAVE_HWSERIAL1) + _uart = &Serial1; +#elif defined(HAVE_HWSERIAL2) + _uart = &Serial2; +#elif defined(HAVE_HWSERIAL3) + _uart = &Serial3; +#endif +#elif defined(ARDUINO_ARCH_ESP32) + _uart = &Serial1; + +#if defined(D0) + _rxPin = D0; +#else + _rxPin = 0; +#endif + +#if defined(D1) + _txPin = D1; +#else + _txPin = 1; +#endif +#else + _uart = &Serial1; +#endif + } + + SerialTransmitter::SerialTransmitter(HardwareSerial *hwUartPort) + { + _uart = hwUartPort; + +#if defined(ARDUINO_ARCH_ESP32) +#if defined(D0) + _rxPin = D0; +#else + _rxPin = 0; +#endif + +#if defined(D1) + _txPin = D1; +#else + _txPin = 1; +#endif +#endif + } + + SerialTransmitter::SerialTransmitter(HardwareSerial *hwUartPort, int8_t rxPin, int8_t txPin) + { + _uart = hwUartPort; + +#if defined(ARDUINO_ARCH_ESP32) + _rxPin = rxPin; + _txPin = txPin; +#else + (void)rxPin; + (void)txPin; +#endif + } + + SerialTransmitter::~SerialTransmitter() + { + _uart = nullptr; + + _rxPin = -1; + _txPin = -1; + } +} // namespace serialTransmitterLayer diff --git a/src/SerialTransmitter/SerialTransmitter.hpp b/src/SerialTransmitter/SerialTransmitter.hpp new file mode 100644 index 00000000..4e8b69d7 --- /dev/null +++ b/src/SerialTransmitter/SerialTransmitter.hpp @@ -0,0 +1,48 @@ +/** + * @file SerialTransmitter.hpp + * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) + * @brief The Serial Transmitter layer for the CRSF for Arduino library. + * + * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. + * + * @section License GNU General Public License v3.0 + * This source file is a part of the CRSF for Arduino library. + * CRSF for Arduino is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CRSF for Arduino is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CRSF for Arduino. If not, see . + * + */ + +#pragma once + +#ifndef ENV_DEFECT_DETECTOR +#include "../CFA_Config.hpp" +#endif +#include "Arduino.h" + +namespace serialTransmitterLayer +{ + class SerialTransmitter + { + public: + SerialTransmitter(); + explicit SerialTransmitter(HardwareSerial *hwUartPort); + SerialTransmitter(HardwareSerial *hwUartPort, int8_t rxPin, int8_t txPin); + virtual ~SerialTransmitter(); + + private: + HardwareSerial *_uart; + + int8_t _rxPin = -1; + int8_t _txPin = -1; + }; +} // namespace serialTransmitterLayer diff --git a/src/SerialReceiver/CRC/CRC.cpp b/src/common/CRC/CRC.cpp similarity index 100% rename from src/SerialReceiver/CRC/CRC.cpp rename to src/common/CRC/CRC.cpp diff --git a/src/SerialReceiver/CRC/CRC.hpp b/src/common/CRC/CRC.hpp similarity index 100% rename from src/SerialReceiver/CRC/CRC.hpp rename to src/common/CRC/CRC.hpp