diff --git a/FprimeZephyrReference/Components/CMakeLists.txt b/FprimeZephyrReference/Components/CMakeLists.txt index 26405d0..e9d6171 100644 --- a/FprimeZephyrReference/Components/CMakeLists.txt +++ b/FprimeZephyrReference/Components/CMakeLists.txt @@ -10,3 +10,4 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Burnwire/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/BootloaderTrigger/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/AntennaDeployer/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FsSpace/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PayloadHandler/") diff --git a/FprimeZephyrReference/Components/PayloadHandler/CMakeLists.txt b/FprimeZephyrReference/Components/PayloadHandler/CMakeLists.txt new file mode 100644 index 0000000..15cabfd --- /dev/null +++ b/FprimeZephyrReference/Components/PayloadHandler/CMakeLists.txt @@ -0,0 +1,36 @@ +#### +# F Prime CMakeLists.txt: +# +# SOURCES: list of source files (to be compiled) +# AUTOCODER_INPUTS: list of files to be passed to the autocoders +# DEPENDS: list of libraries that this module depends on +# +# More information in the F´ CMake API documentation: +# https://fprime.jpl.nasa.gov/latest/docs/reference/api/cmake/API/ +# +#### + +# Module names are derived from the path from the nearest project/library/framework +# root when not specifically overridden by the developer. i.e. The module defined by +# `Ref/SignalGen/CMakeLists.txt` will be named `Ref_SignalGen`. + +register_fprime_library( + AUTOCODER_INPUTS + "${CMAKE_CURRENT_LIST_DIR}/PayloadHandler.fpp" + SOURCES + "${CMAKE_CURRENT_LIST_DIR}/PayloadHandler.cpp" +# DEPENDS +# MyPackage_MyOtherModule +) + +### Unit Tests ### +# register_fprime_ut( +# AUTOCODER_INPUTS +# "${CMAKE_CURRENT_LIST_DIR}/PayloadHandler.fpp" +# SOURCES +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/PayloadHandlerTestMain.cpp" +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/PayloadHandlerTester.cpp" +# DEPENDS +# STest # For rules-based testing +# UT_AUTO_HELPERS +# ) diff --git a/FprimeZephyrReference/Components/PayloadHandler/PayloadHandler.cpp b/FprimeZephyrReference/Components/PayloadHandler/PayloadHandler.cpp new file mode 100644 index 0000000..575306d --- /dev/null +++ b/FprimeZephyrReference/Components/PayloadHandler/PayloadHandler.cpp @@ -0,0 +1,344 @@ +// ====================================================================== +// \title PayloadHandler.cpp +// \author robertpendergrast +// \brief cpp file for PayloadHandler component implementation class +// ====================================================================== +#include "Os/File.hpp" +#include "Fw/Types/Assert.hpp" +#include "Fw/Types/BasicTypes.hpp" +#include "FprimeZephyrReference/Components/PayloadHandler/PayloadHandler.hpp" +#include +#include + +namespace Components { + +// ---------------------------------------------------------------------- +// Component construction and destruction +// ---------------------------------------------------------------------- + +PayloadHandler ::PayloadHandler(const char* const compName) + : PayloadHandlerComponentBase(compName), + m_protocolBufferSize(0), + m_imageBufferUsed(0) { + // Initialize protocol buffer to zero + memset(m_protocolBuffer, 0, PROTOCOL_BUFFER_SIZE); +} + +PayloadHandler ::~PayloadHandler() { + // Clean up any allocated image buffer + deallocateImageBuffer(); +} + + +// ---------------------------------------------------------------------- +// Handler implementations for typed input ports +// ---------------------------------------------------------------------- + + +void PayloadHandler ::in_port_handler(FwIndexType portNum, Fw::Buffer& buffer, const Drv::ByteStreamStatus& status) { + + this->log_ACTIVITY_LO_UartReceived(); + + // Check if we received data successfully + if (status != Drv::ByteStreamStatus::OP_OK) { + // TODO - log error event? + return; + } + + // Check if buffer is valid + if (!buffer.isValid()) { + return; + } + + // Get the data from the incoming buffer + const U8* data = buffer.getData(); + const U32 dataSize = static_cast(buffer.getSize()); + + // Unclear if this works as intended if data flow is interrupted + + if (m_receiving && m_imageBuffer.isValid()) { + // Currently receiving image data - accumulate into large buffer + + // First accumulate the new data + if (!accumulateImageData(data, dataSize)) { + // Image buffer overflow + this->log_WARNING_HI_ImageDataOverflow(); + deallocateImageBuffer(); + m_receiving = false; + return; + } + + // Check if we've received all expected data + if (m_expected_size > 0 && m_imageBufferUsed >= m_expected_size) { + // We have all the data! Trim to exact size (in case we got too) + m_imageBufferUsed = m_expected_size; + + // Image is complete + processCompleteImage(); + } + } else { + // Not receiving image - accumulate protocol data + if (!accumulateProtocolData(data, dataSize)) { + // Protocol buffer overflow - clear and retry + clearProtocolBuffer(); + accumulateProtocolData(data, dataSize); + } + + // Process protocol buffer to detect image headers/commands + processProtocolBuffer(); + } +} + +// ---------------------------------------------------------------------- +// Handler implementations for commands +// ---------------------------------------------------------------------- + +void PayloadHandler ::SEND_COMMAND_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, const Fw::CmdStringArg& cmd) { + + // Append newline to command to send over UART + Fw::CmdStringArg tempCmd = cmd; + tempCmd += "\n"; + Fw::Buffer commandBuffer( + reinterpret_cast(const_cast(tempCmd.toChar())), + tempCmd.length() + ); + + // Send command over output port + Drv::ByteStreamStatus sendStatus = this->out_port_out(0, commandBuffer); + + Fw::LogStringArg logCmd(cmd); + + // Log success or failure + if (sendStatus != Drv::ByteStreamStatus::OP_OK) { + this->log_WARNING_HI_CommandError(logCmd); + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR); + return; + } + else { + this->log_ACTIVITY_HI_CommandSuccess(logCmd); + } + + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + +// ---------------------------------------------------------------------- +// Helper method implementations +// ---------------------------------------------------------------------- + +bool PayloadHandler ::accumulateProtocolData(const U8* data, U32 size) { + // Check if we have space for the new data + if (m_protocolBufferSize + size > PROTOCOL_BUFFER_SIZE) { + return false; + } + + // Copy data into protocol buffer + memcpy(&m_protocolBuffer[m_protocolBufferSize], data, size); + m_protocolBufferSize += size; + + return true; +} + +void PayloadHandler ::processProtocolBuffer() { + // Protocol: [4-byte little-endian uint32][image data] + + if (m_protocolBufferSize >= HEADER_SIZE) { + // Check for at the beginning + if (!isImageStartCommand(m_protocolBuffer, m_protocolBufferSize)) { + return; // Not an image header + } + + // Check for tag after + const char* sizeTag = ""; + bool hasSizeTag = true; + + for (U32 i = 0; i < SIZE_TAG_LEN; ++i) { + if (m_protocolBuffer[SIZE_TAG_OFFSET + i] != static_cast(sizeTag[i])) { + hasSizeTag = false; + break; + } + } + + if (!hasSizeTag) { + return; // Invalid header + } + + // Extract 4-byte size (little-endian) + U32 imageSize = 0; + imageSize |= static_cast(m_protocolBuffer[SIZE_VALUE_OFFSET + 0]); + imageSize |= static_cast(m_protocolBuffer[SIZE_VALUE_OFFSET + 1]) << 8; + imageSize |= static_cast(m_protocolBuffer[SIZE_VALUE_OFFSET + 2]) << 16; + imageSize |= static_cast(m_protocolBuffer[SIZE_VALUE_OFFSET + 3]) << 24; + + // Verify tag + const char* closeSizeTag = ""; + bool hasCloseSizeTag = true; + + for (U32 i = 0; i < SIZE_CLOSE_TAG_LEN; ++i) { + if (m_protocolBuffer[SIZE_CLOSE_TAG_OFFSET + i] != static_cast(closeSizeTag[i])) { + hasCloseSizeTag = false; + break; + } + } + + if (!hasCloseSizeTag) { + return; // Invalid header + } + + // Valid header! Allocate buffer + if (allocateImageBuffer()) { + m_receiving = true; + m_bytes_received = 0; + m_expected_size = imageSize; + + // Generate filename + char filename[64]; + snprintf(filename, sizeof(filename), "/mnt/data/img_%03d.jpg", m_data_file_count++); + m_currentFilename = filename; + + this->log_ACTIVITY_LO_ImageHeaderReceived(); + + // Remove header + U32 remainingSize = m_protocolBufferSize - HEADER_SIZE; + if (remainingSize > 0) { + memmove(m_protocolBuffer, + &m_protocolBuffer[HEADER_SIZE], + remainingSize); + } + m_protocolBufferSize = remainingSize; + + // Transfer any remaining data (image data) to image buffer + if (m_protocolBufferSize > 0) { + if (!accumulateImageData(m_protocolBuffer, m_protocolBufferSize)) { + this->log_WARNING_HI_ImageDataOverflow(); + deallocateImageBuffer(); + m_receiving = false; + } + clearProtocolBuffer(); + } + } else { + // Buffer allocation failed + this->log_WARNING_HI_BufferAllocationFailed(IMAGE_BUFFER_SIZE); + } + } +} + +void PayloadHandler ::clearProtocolBuffer() { + m_protocolBufferSize = 0; + memset(m_protocolBuffer, 0, PROTOCOL_BUFFER_SIZE); +} + +bool PayloadHandler ::allocateImageBuffer() { + // Request buffer from BufferManager + m_imageBuffer = this->allocate_out(0, IMAGE_BUFFER_SIZE); + + // Check if allocation succeeded + if (!m_imageBuffer.isValid() || m_imageBuffer.getSize() < IMAGE_BUFFER_SIZE) { + this->log_WARNING_HI_BufferAllocationFailed(IMAGE_BUFFER_SIZE); + deallocateImageBuffer(); + return false; + } + + m_imageBufferUsed = 0; + return true; +} + +void PayloadHandler ::deallocateImageBuffer() { + if (m_imageBuffer.isValid()) { + this->deallocate_out(0, m_imageBuffer); + m_imageBuffer = Fw::Buffer(); // Reset to invalid buffer + } + m_imageBufferUsed = 0; +} + +bool PayloadHandler ::accumulateImageData(const U8* data, U32 size) { + FW_ASSERT(m_imageBuffer.isValid()); + + // Check if we have space + if (m_imageBufferUsed + size > m_imageBuffer.getSize()) { + return false; + } + + // Copy data into image buffer + memcpy(&m_imageBuffer.getData()[m_imageBufferUsed], data, size); + m_imageBufferUsed += size; + m_bytes_received += size; + + return true; +} + +void PayloadHandler ::processCompleteImage() { + FW_ASSERT(m_imageBuffer.isValid()); + + // Write image to file + Os::File::Status status = m_file.open(m_currentFilename.c_str(), Os::File::OPEN_WRITE); + + if (status == Os::File::OP_OK) { + // Os::File::write expects FwSizeType& for size parameter + FwSizeType sizeToWrite = static_cast(m_imageBufferUsed); + status = m_file.write(m_imageBuffer.getData(), sizeToWrite, Os::File::WaitType::NO_WAIT); + m_file.close(); + + if (status == Os::File::OP_OK) { + // Success! sizeToWrite now contains actual bytes written + Fw::LogStringArg pathArg(m_currentFilename.c_str()); + this->log_ACTIVITY_HI_DataReceived(m_imageBufferUsed, pathArg); + } else { + // TODO - log write error + } + } else { + // TODO - log open error + } + + // Clean up + deallocateImageBuffer(); + m_receiving = false; + m_bytes_received = 0; +} + +I32 PayloadHandler ::findImageEndMarker(const U8* data, U32 size) { + // Looking for "\n" or "" + const char* marker = ""; + + if (size < IMG_END_LEN) { + return -1; + } + + // Search for the marker + for (U32 i = 0; i <= size - IMG_END_LEN; ++i) { + bool found = true; + for (U32 j = 0; j < IMG_END_LEN; ++j) { + if (data[i + j] != static_cast(marker[j])) { + found = false; + break; + } + } + if (found) { + // Found marker at position i + // If preceded by newline, back up to before newline + if (i > 0 && data[i - 1] == '\n') { + return static_cast(i - 1); + } + return static_cast(i); + } + } + + return -1; // Not found +} + +bool PayloadHandler ::isImageStartCommand(const U8* line, U32 length) { + const char* command = ""; + + if (length < IMG_START_LEN) { + return false; + } + + for (U32 i = 0; i < IMG_START_LEN; ++i) { + if (line[i] != static_cast(command[i])) { + return false; + } + } + + return true; +} + +} // namespace Components diff --git a/FprimeZephyrReference/Components/PayloadHandler/PayloadHandler.fpp b/FprimeZephyrReference/Components/PayloadHandler/PayloadHandler.fpp new file mode 100644 index 0000000..1d28fa1 --- /dev/null +++ b/FprimeZephyrReference/Components/PayloadHandler/PayloadHandler.fpp @@ -0,0 +1,86 @@ +module Components { + @ Manager for Nicla Vision + passive component PayloadHandler { + + # One async command/port is required for active components + # This should be overridden by the developers with a useful command/port + @ TODO + sync command SEND_COMMAND(cmd: string) # Command to send data over UART + + event CommandError(cmd: string) severity warning high format "Failed to send {} command over UART" + + event CommandSuccess(cmd: string) severity activity high format "Command {} sent successfully" + + event DataReceived( data: U8, path: string) severity activity high format "Stored {} bytes of payload data to {}" + + event ByteReceived( byte: U8) severity activity low format "Received byte: {}" + + event ImageHeaderReceived() severity activity low format "Received image header" + + event UartReceived() severity activity low format "Received UART data" + + event BufferAllocationFailed(buffer_size: U32) severity warning high format "Failed to allocate buffer of size {}" + + event ImageDataOverflow() severity warning high format "Image data overflow - buffer full" + + output port out_port: Drv.ByteStreamSend + + sync input port in_port: Drv.ByteStreamData + + @ Port for allocating buffers for image data + output port allocate: Fw.BufferGet + + @ Port for deallocating buffers + output port deallocate: Fw.BufferSend + + ############################################################################## + #### Uncomment the following examples to start customizing your component #### + ############################################################################## + + # @ Example async command + # async command COMMAND_NAME(param_name: U32) + + # @ Example telemetry counter + # telemetry ExampleCounter: U64 + + # @ Example event + # event ExampleStateEvent(example_state: Fw.On) severity activity high id 0 format "State set to {}" + + # @ Example port: receiving calls from the rate group + # sync input port run: Svc.Sched + + # @ Example parameter + # param PARAMETER_NAME: U32 + + ############################################################################### + # Standard AC Ports: Required for Channels, Events, Commands, and Parameters # + ############################################################################### + @ Port for requesting the current time + time get port timeCaller + + @ Port for sending command registrations + command reg port cmdRegOut + + @ Port for receiving commands + command recv port cmdIn + + @ Port for sending command responses + command resp port cmdResponseOut + + @ Port for sending textual representation of events + text event port logTextOut + + @ Port for sending events to downlink + event port logOut + + @ Port for sending telemetry channels to downlink + telemetry port tlmOut + + @ Port to return the value of a parameter + param get port prmGetOut + + @Port to set the value of a parameter + param set port prmSetOut + + } +} diff --git a/FprimeZephyrReference/Components/PayloadHandler/PayloadHandler.hpp b/FprimeZephyrReference/Components/PayloadHandler/PayloadHandler.hpp new file mode 100644 index 0000000..caba8e1 --- /dev/null +++ b/FprimeZephyrReference/Components/PayloadHandler/PayloadHandler.hpp @@ -0,0 +1,127 @@ +// ====================================================================== +// \title PayloadHandler.hpp +// \author robertpendergrast +// \brief hpp file for PayloadHandler component implementation class +// ====================================================================== + +#ifndef FprimeZephyrReference_PayloadHandler_HPP +#define FprimeZephyrReference_PayloadHandler_HPP + +#include +#include +#include "Os/File.hpp" +#include "FprimeZephyrReference/Components/PayloadHandler/PayloadHandlerComponentAc.hpp" + +namespace Components { + +class PayloadHandler final : public PayloadHandlerComponentBase { + public: + // ---------------------------------------------------------------------- + // Component construction and destruction + // ---------------------------------------------------------------------- + + //! Construct PayloadHandler object + PayloadHandler(const char* const compName //!< The component name + ); + + //! Destroy PayloadHandler object + ~PayloadHandler(); + + U8 m_data_file_count = 0; + bool m_receiving = false; + U32 m_bytes_received = 0; + + U8 m_lineBuffer[128]; + size_t m_lineIndex = 0; + Os::File m_file; + std::string m_currentFilename; + + // Small protocol buffer for commands/headers (static allocation) + static constexpr U32 PROTOCOL_BUFFER_SIZE = 2048; + U8 m_protocolBuffer[PROTOCOL_BUFFER_SIZE]; + U32 m_protocolBufferSize = 0; + + // Large image buffer (dynamic allocation via BufferManager) + Fw::Buffer m_imageBuffer; + U32 m_imageBufferUsed = 0; // Bytes used in image buffer + static constexpr U32 IMAGE_BUFFER_SIZE = 256 * 1024; // 256 KB for images + + // Protocol constants for image transfer + // Protocol: [4-byte uint32][image data] + static constexpr U32 IMG_START_LEN = 11; // strlen("") + static constexpr U32 SIZE_TAG_LEN = 6; // strlen("") + static constexpr U32 SIZE_VALUE_LEN = 4; // 4-byte little-endian uint32 + static constexpr U32 SIZE_CLOSE_TAG_LEN = 7; // strlen("") + static constexpr U32 IMG_END_LEN = 9; // strlen("") + + // Derived constants + static constexpr U32 HEADER_SIZE = IMG_START_LEN + SIZE_TAG_LEN + SIZE_VALUE_LEN + SIZE_CLOSE_TAG_LEN; // 28 bytes + static constexpr U32 SIZE_TAG_OFFSET = IMG_START_LEN; // 11 + static constexpr U32 SIZE_VALUE_OFFSET = IMG_START_LEN + SIZE_TAG_LEN; // 17 + static constexpr U32 SIZE_CLOSE_TAG_OFFSET = SIZE_VALUE_OFFSET + SIZE_VALUE_LEN; // 21 + + U32 m_expected_size = 0; // Expected image size from header + + private: + // ---------------------------------------------------------------------- + // Handler implementations for typed input ports + // ---------------------------------------------------------------------- + + //! Handler implementation for in_port + //! Handler implementation for in_port + void in_port_handler(FwIndexType portNum, //!< The port number + Fw::Buffer& buffer, + const Drv::ByteStreamStatus& status); + + private: + // ---------------------------------------------------------------------- + // Handler implementations for commands + // ---------------------------------------------------------------------- + + //! Handler implementation for command SEND_COMMAND + //! + //! TODO + void SEND_COMMAND_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq, //!< The command sequence number + const Fw::CmdStringArg& cmd); + + // ---------------------------------------------------------------------- + // Helper methods for data accumulation + // ---------------------------------------------------------------------- + + //! Accumulate protocol data (headers, commands) + //! Returns true if data was successfully accumulated, false on overflow + bool accumulateProtocolData(const U8* data, U32 size); + + //! Process protocol buffer to detect commands/image headers + void processProtocolBuffer(); + + //! Clear the protocol buffer + void clearProtocolBuffer(); + + //! Allocate image buffer from BufferManager + //! Returns true on success + bool allocateImageBuffer(); + + //! Deallocate image buffer + void deallocateImageBuffer(); + + //! Accumulate image data into dynamically allocated buffer + //! Returns true on success, false on overflow + bool accumulateImageData(const U8* data, U32 size); + + //! Process complete image (write to file, send event, etc.) + void processCompleteImage(); + + //! Check if buffer contains image end marker + //! Returns position of marker start, or -1 if not found + I32 findImageEndMarker(const U8* data, U32 size); + + //! Parse line for image start command + //! Returns true if line is "" + bool isImageStartCommand(const U8* line, U32 length); +}; + +} // namespace Components + +#endif diff --git a/FprimeZephyrReference/Components/PayloadHandler/docs/BUFFER_USAGE.md b/FprimeZephyrReference/Components/PayloadHandler/docs/BUFFER_USAGE.md new file mode 100644 index 0000000..84c1e95 --- /dev/null +++ b/FprimeZephyrReference/Components/PayloadHandler/docs/BUFFER_USAGE.md @@ -0,0 +1,249 @@ +# PayloadHandler Buffer Strategy + +## Overview + +The PayloadHandler uses a **two-buffer strategy** to efficiently handle both protocol/header data and large image data: + +1. **Small static buffer** (2 KB) for protocol headers/commands +2. **Large dynamic buffer** (256 KB) from BufferManager for image data + +## Why This Approach? + +### Problem with Static Buffers for Images +- **Memory waste**: 100s of KB allocated permanently even when not receiving +- **Stack overflow**: Embedded systems have limited stack/static memory +- **Component bloat**: Every component instance would have huge buffer + +### Solution: BufferManager +- Allocates memory **only when needed** +- Returns memory when done +- Memory pooling for efficiency +- Multiple components can share memory pool + +## Architecture + +``` +UART Driver (64 byte chunks) + ↓ + PayloadHandler + ↓ + ┌─────┴─────┐ + ↓ ↓ +Protocol Image +Buffer Buffer +(2 KB) (256 KB) +static dynamic +``` + +## Buffer Details + +### Protocol Buffer +- **Size**: 2048 bytes (static allocation) +- **Purpose**: Parse commands, headers, metadata +- **Lifecycle**: Always present +- **Location**: `m_protocolBuffer[PROTOCOL_BUFFER_SIZE]` + +### Image Buffer +- **Size**: 256 KB (dynamic allocation) +- **Purpose**: Accumulate complete image data +- **Lifecycle**: Allocated when receiving, deallocated when done +- **Location**: `m_imageBuffer` (Fw::Buffer from BufferManager) + +## Data Flow + +### 1. Idle State (No Image) +``` +Incoming data → Protocol Buffer → Parse for commands +``` + +### 2. Image Header Detected +``` +Parse "...bytes..." → Extract image size (m_expected_size) + → allocateImageBuffer() + → m_receiving = true + → Start accumulating +``` + +### 3. Receiving Image +``` +Incoming data → Image Buffer (accumulate) → Check if m_imageBufferUsed >= m_expected_size +``` + +### 4. Image Complete +``` +Received m_expected_size bytes → processCompleteImage() → Write to file + → deallocateImageBuffer() + → m_receiving = false +``` + +## Camera Protocol + +The Nicla Vision camera sends images using this protocol: + +``` +[4-byte little-endian uint32][raw JPEG data in 512-byte chunks] +``` + +Example: +``` +\x00\xC8\x00\x00ÿØÿà...JFIF...image data...ÿÙ + ^^^^^^^^^^ = 51200 bytes (little-endian) +``` + +**Key Features:** +- **No newlines** - All data is sent continuously +- **Binary size** - 4-byte little-endian uint32 embedded in header +- **Deterministic** - Exact image size known before data arrives + +## Key Functions + +### Buffer Allocation +```cpp +bool allocateImageBuffer() +``` +- Requests 256 KB buffer from BufferManager +- Returns true on success, false on failure +- Logs event if allocation fails + +### Buffer Deallocation +```cpp +void deallocateImageBuffer() +``` +- Returns buffer to BufferManager +- Called automatically when image complete or on error + +### Data Accumulation +```cpp +bool accumulateProtocolData(const U8* data, U32 size) // Small chunks +bool accumulateImageData(const U8* data, U32 size) // Large chunks +``` + +## Configuration + +### BufferManager Setup (instances.fpp) +```fpp +instance payloadBufferManager: Svc.BufferManager { + // 256 KB buffers, 2 buffers = 512 KB total + bins[0].bufferSize = 256 * 1024 + bins[0].numBuffers = 2 +} +``` + +### Topology Connections (topology.fpp) +```fpp +payload.allocate -> payloadBufferManager.bufferGetCallee +payload.deallocate -> payloadBufferManager.bufferSendIn +``` + +## Memory Usage + +### Static (Always Allocated) +- Protocol buffer: 2 KB +- Component overhead: ~1 KB +- **Total: ~3 KB** + +### Dynamic (Only When Receiving) +- Image buffer: 256 KB (when allocated) +- **Total: 0-256 KB** + +### Pool Configuration +- 2 buffers of 256 KB = **512 KB total pool** +- Allows receiving 2 images simultaneously or buffering one while processing another + +## Customization + +### To Change Buffer Sizes + +1. **Protocol buffer** (PayloadHandler.hpp): +```cpp +static constexpr U32 PROTOCOL_BUFFER_SIZE = 2048; // Change this +``` + +2. **Image buffer** (PayloadHandler.hpp): +```cpp +static constexpr U32 IMAGE_BUFFER_SIZE = 256 * 1024; // Change this +``` + +3. **BufferManager pool** (instances.fpp): +```cpp +bins[0].bufferSize = 256 * 1024; // Must match IMAGE_BUFFER_SIZE +bins[0].numBuffers = 2; // Number of buffers in pool +``` + +### To Add Different Buffer Sizes + +You can configure multiple buffer bins in the BufferManager: +```cpp +bins[0].bufferSize = 256 * 1024; // 256 KB for large images +bins[0].numBuffers = 2; +bins[1].bufferSize = 64 * 1024; // 64 KB for small images +bins[1].numBuffers = 4; +``` + +BufferManager will allocate the smallest buffer that fits the request. + +## Implementation Details + +### Protocol Parsing (Implemented) + +The `processProtocolBuffer()` function: +1. Waits for minimum 28 bytes (header size) +2. Validates `` tag (11 bytes) +3. Validates `` tag (6 bytes) +4. Extracts 4-byte little-endian size +5. Validates `` tag (7 bytes) +6. Calls `allocateImageBuffer()` and sets `m_receiving = true`, `m_expected_size = imageSize` +7. Generates filename: `/mnt/data/img_NNN.jpg` + +### Image Reception (Implemented) + +The `in_port_handler()` function: +- When `m_receiving == true`, accumulates data into image buffer +- After each chunk, checks if `m_imageBufferUsed >= m_expected_size` +- When complete, trims to exact size and calls `processCompleteImage()` + +### Size-Based Completion + +The camera sends the exact image size in the header, so we know precisely when we've received all data: + +```cpp +if (m_expected_size > 0 && m_imageBufferUsed >= m_expected_size) { + m_imageBufferUsed = m_expected_size; // Trim any extra (e.g., ) + processCompleteImage(); +} +``` + +No need to search for end markers in every chunk! + +## Error Handling + +### Allocation Failure +- Logs `BufferAllocationFailed` event +- Continues processing protocol buffer +- Can retry later if needed + +### Buffer Overflow +- Image buffer overflow: Logs `ImageDataOverflow` event, deallocates buffer +- Protocol buffer overflow: Clears buffer and restarts + +### Component Destruction +- Destructor automatically deallocates any active image buffer +- Prevents memory leaks + +## Performance Notes + +- **Allocation overhead**: ~microseconds (one-time per image) +- **Copy overhead**: memcpy for each 64-byte UART chunk (~100 cycles) +- **Memory efficiency**: Only allocates when receiving, 256x better than static +- **Latency**: No impact on UART receive rate (64 bytes @ 10Hz = 640 bytes/sec) + +## Testing Checklist + +- [ ] Receive small image (< 256 KB) +- [ ] Receive large image (> 256 KB) - should fail gracefully +- [ ] Receive multiple images sequentially +- [ ] Handle allocation failure (simulate by allocating 2 buffers) +- [ ] Handle protocol buffer overflow +- [ ] Handle image buffer overflow +- [ ] Component cleanup (destructor) + diff --git a/FprimeZephyrReference/Components/PayloadHandler/docs/USAGE.md b/FprimeZephyrReference/Components/PayloadHandler/docs/USAGE.md new file mode 100644 index 0000000..f971cf7 --- /dev/null +++ b/FprimeZephyrReference/Components/PayloadHandler/docs/USAGE.md @@ -0,0 +1,319 @@ +# PayloadHandler Usage Guide + +## Overview + +The PayloadHandler receives images from the Nicla Vision camera over UART and saves them to the filesystem. + +## Camera Commands + +### Take a Snapshot + +Send this command to the camera over UART: +``` +snap +``` + +The camera will: +1. Take a photo +2. Save it locally as `images/img_NNNN.jpg` +3. Send it back over UART with the protocol: + ``` + [4-byte size][JPEG data] + ``` + +## F' Commands + +### Send Command to Camera + +Use the F' command interface to send commands to the camera: + +``` +SEND_COMMAND("snap") +``` + +This forwards the command over UART to the camera. + +## Expected Behavior + +### Successful Image Reception + +1. **Command Sent** + - Event: `CommandSuccess` - "Command snap sent successfully" + +2. **Image Start** + - PayloadHandler receives `...bytes...` + - Parses image size from 4-byte little-endian value + - Allocates 256 KB buffer from BufferManager + - Event: `ImageHeaderReceived` - "Received image header" + +3. **Image Data** + - Receives 512-byte chunks from camera + - Accumulates in image buffer + - Event: `UartReceived` - "Received UART data" (multiple times) + +4. **Image Complete** + - Receives all expected bytes (size from header) + - Writes image to `/mnt/data/img_NNN.jpg` + - Event: `DataReceived` - "Stored NNNNN bytes of payload data to /mnt/data/img_NNN.jpg" + - Returns buffer to BufferManager + - Note: Camera also sends `` but it's trimmed off + +### Image Filenames + +Images are saved with sequential filenames: +- `/mnt/data/img_000.jpg` +- `/mnt/data/img_001.jpg` +- `/mnt/data/img_002.jpg` +- etc. + +Counter increments each time a valid `...` header is received. + +## Error Cases + +### Buffer Allocation Failed + +**Event:** `BufferAllocationFailed` - "Failed to allocate buffer of size 262144" + +**Cause:** +- BufferManager pool exhausted (both 256 KB buffers in use) +- Previous image reception didn't complete/cleanup + +**Recovery:** +- Wait for current image to complete +- Retry command + +### Image Data Overflow + +**Event:** `ImageDataOverflow` - "Image data overflow - buffer full" + +**Cause:** +- Image larger than 256 KB +- Camera sent more data than buffer can hold + +**Recovery:** +- Increase `IMAGE_BUFFER_SIZE` in PayloadHandler.hpp +- Update BufferManager configuration in instances.fpp + +### Command Error + +**Event:** `CommandError` - "Failed to send snap command over UART" + +**Cause:** +- UART driver not ready +- UART send failed + +**Recovery:** +- Check UART connection +- Retry command + +## Monitoring + +### Key Telemetry (from BufferManager) + +Check these telemetry points to monitor buffer usage: + +- `payloadBufferManager.CurrBuffs` - Currently allocated buffers (0-2) +- `payloadBufferManager.TotalBuffs` - Total buffers available (2) +- `payloadBufferManager.HiBuffs` - High water mark +- `payloadBufferManager.NoBuffs` - Count of allocation failures +- `payloadBufferManager.EmptyBuffs` - Count of empty buffer returns + +### Expected Values During Operation + +**Idle:** +``` +CurrBuffs = 0 +TotalBuffs = 2 +``` + +**Receiving Image:** +``` +CurrBuffs = 1 +TotalBuffs = 2 +``` + +**If NoBuffs > 0:** +- Indicates allocation failures occurred +- Check if images are completing properly +- May need to increase buffer count + +## Protocol Details + +### UART Settings + +- Baud rate: 115200 +- Data bits: 8 +- Stop bits: 1 +- Parity: None + +### Camera → Flight Software Protocol + +``` +Command from FS: "snap\n" +Camera response: "[4-byte little-endian uint32]" + [JPEG file bytes - exact size from header] + "" +``` + +**Header format:** +- `` - 11 bytes ASCII +- `` - 6 bytes ASCII +- Size value - 4 bytes binary (little-endian uint32) +- `` - 7 bytes ASCII +- **Total:** 28 bytes + +**Example header for 51200 byte image:** +``` +\x00\xC8\x00\x00 +``` + +### Timing + +- Command → Image start: ~2-3 seconds (camera capture time) +- Image transmission: depends on size + - At 115200 baud ≈ 11.5 KB/s + - 50 KB image ≈ 4-5 seconds + - 200 KB image ≈ 17-18 seconds + +## Testing + +### Basic Test + +1. Ensure camera is powered and UART connected +2. Send command: `SEND_COMMAND("snap")` +3. Monitor events for successful completion +4. Check file exists: `/mnt/data/img_000.jpg` +5. Verify file size is reasonable (typically 30-100 KB for VGA JPEG) + +### Stress Test + +Send multiple consecutive `snap` commands: +``` +SEND_COMMAND("snap") +# Wait for DataReceived event +SEND_COMMAND("snap") +# Wait for DataReceived event +SEND_COMMAND("snap") +# etc. +``` + +Should handle at least 10+ images sequentially without errors. + +### Buffer Exhaustion Test + +Send 3 `snap` commands rapidly (don't wait): +``` +SEND_COMMAND("snap") +SEND_COMMAND("snap") +SEND_COMMAND("snap") # This should fail with BufferAllocationFailed +``` + +First two should succeed, third should fail gracefully. + +## Filesystem Requirements + +### Mount Point + +The handler expects `/mnt/data/` to be a writable filesystem. + +If using a different mount point, update in `processProtocolBuffer()`: +```cpp +snprintf(filename, sizeof(filename), "/your/path/img_%03d.jpg", m_data_file_count++); +``` + +### Space Requirements + +Each VGA JPEG is typically 30-100 KB. + +With 2 MB filesystem, you can store ~20-60 images. + +Monitor filesystem usage with the `fsSpace` component. + +## Troubleshooting + +### No Images Received + +1. **Check UART connection** + - Verify peripheralUartDriver is configured + - Check baud rate matches camera (115200) + +2. **Check camera is responding** + - Send any command and check for `UartReceived` events + - Verify camera power + +3. **Check events** + - Should see `CommandSuccess` when command sent + - Should see `ImageHeaderReceived` when camera responds + +### Images Corrupted + +1. **Check for buffer overflow** + - Look for `ImageDataOverflow` events + - Increase buffer size if needed + +2. **Check UART errors** + - Verify no data corruption on UART + - Check for timing issues + +3. **Verify complete reception** + - Image size in `DataReceived` event should match expected + - Compare with camera's saved file size + +### Images Not Saving + +1. **Check filesystem mounted** + - Verify `/mnt/data/` exists and is writable + +2. **Check disk space** + - Monitor `fsSpace` telemetry + +3. **Check file operations** + - Add error events for file open/write failures + - Check file permissions + +## Advanced Configuration + +### Changing Buffer Pool Size + +To support larger images or more simultaneous captures, edit `instances.fpp`: + +```fpp +// Support 4 images of 512 KB each = 2 MB pool +bins[0].bufferSize = 512 * 1024; +bins[0].numBuffers = 4; +``` + +Must also update `PayloadHandler.hpp`: +```cpp +static constexpr U32 IMAGE_BUFFER_SIZE = 512 * 1024; +``` + +### Verifying Image Integrity + +Since the protocol now includes size, you can verify completeness: + +```cpp +// In processCompleteImage() +if (m_imageBufferUsed != m_expected_size) { + this->log_WARNING_HI_ImageSizeMismatch(m_expected_size, m_imageBufferUsed); +} +``` + +This catches truncation or corruption during transmission. + +### Multiple Image Formats + +To support different image sizes/formats, configure multiple buffer bins: + +```fpp +// Small images (thumbnail) +bins[0].bufferSize = 32 * 1024; +bins[0].numBuffers = 4; + +// Large images (full resolution) +bins[1].bufferSize = 256 * 1024; +bins[1].numBuffers = 2; +``` + +BufferManager will automatically select the appropriate bin. + diff --git a/FprimeZephyrReference/Components/PayloadHandler/docs/sdd.md b/FprimeZephyrReference/Components/PayloadHandler/docs/sdd.md new file mode 100644 index 0000000..6cca511 --- /dev/null +++ b/FprimeZephyrReference/Components/PayloadHandler/docs/sdd.md @@ -0,0 +1,66 @@ +# FprimeZephyrReference::CameraManager + +Manager for Nicla Vision + +## Usage Examples +Add usage examples here + +### Diagrams +Add diagrams here + +### Typical Usage +And the typical usage of the component here + +## Class Diagram +Add a class diagram here + +## Port Descriptions +| Name | Description | +|---|---| +|---|---| + +## Component States +Add component states in the chart below +| Name | Description | +|---|---| +|---|---| + +## Sequence Diagrams +Add sequence diagrams here + +## Parameters +| Name | Description | +|---|---| +|---|---| + +## Commands +| Name | Description | +|---|---| +|---|---| + +## Events +| Name | Description | +|---|---| +|---|---| + +## Telemetry +| Name | Description | +|---|---| +|---|---| + +## Unit Tests +Add unit test descriptions in the chart below +| Name | Description | Output | Coverage | +|---|---|---|---| +|---|---|---|---| + +## Requirements +Add requirements in the chart below +| Name | Description | Validation | +|---|---|---| +|---|---|---| + +## Change Log +| Date | Description | +|---|---| +|---| Initial Draft | \ No newline at end of file diff --git a/FprimeZephyrReference/ReferenceDeployment/Main.cpp b/FprimeZephyrReference/ReferenceDeployment/Main.cpp index 705a7f2..89de7cf 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Main.cpp +++ b/FprimeZephyrReference/ReferenceDeployment/Main.cpp @@ -12,6 +12,7 @@ const struct device* serial = DEVICE_DT_GET(DT_NODELABEL(cdc_acm_uart0)); const struct device* lora = DEVICE_DT_GET(DT_NODELABEL(lora0)); +const struct device* peripheral_uart = DEVICE_DT_GET(DT_NODELABEL(uart0)); int main(int argc, char* argv[]) { // ** DO NOT REMOVE **// @@ -26,6 +27,10 @@ int main(int argc, char* argv[]) { inputs.uartDevice = serial; inputs.baudRate = 115200; + // For the uart peripheral config + inputs.peripheralBaudRate = 115200; // Minimum is 19200 + inputs.peripheralUart = peripheral_uart; + // Setup, cycle, and teardown topology ReferenceDeployment::setupTopology(inputs); ReferenceDeployment::startRateGroups(); // Program loop diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi index 43f0bbf..03d20d8 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi +++ b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi @@ -17,6 +17,8 @@ telemetry packets ReferenceDeploymentPackets { ComCcsds.commsBufferManager.EmptyBuffs ComCcsdsUart.commsBufferManager.NoBuffs ComCcsdsUart.commsBufferManager.EmptyBuffs + payloadBufferManager.NoBuffs + payloadBufferManager.EmptyBuffs ReferenceDeployment.rateGroup10Hz.RgCycleSlips ReferenceDeployment.rateGroup1Hz.RgCycleSlips } @@ -29,6 +31,9 @@ telemetry packets ReferenceDeploymentPackets { ComCcsdsUart.commsBufferManager.TotalBuffs ComCcsdsUart.commsBufferManager.CurrBuffs ComCcsdsUart.comQueue.buffQueueDepth + payloadBufferManager.TotalBuffs + payloadBufferManager.CurrBuffs + payloadBufferManager.HiBuffs CdhCore.tlmSend.SendLevel } diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp index ccef907..d2d70ce 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp @@ -87,6 +87,9 @@ void setupTopology(const TopologyState& state) { // for over-the-air communications. lora.start(state.loraDevice, Zephyr::TransmitState::DISABLED); comDriver.configure(state.uartDevice, state.baudRate); + + // UART from the board to the payload + peripheralUartDriver.configure(state.peripheralUart, state.peripheralBaudRate); } void startRateGroups() { diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopologyDefs.hpp b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopologyDefs.hpp index 81064f6..1df28b3 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopologyDefs.hpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopologyDefs.hpp @@ -74,6 +74,8 @@ struct TopologyState { U32 baudRate; //!< Baud rate for UART communication CdhCore::SubtopologyState cdhCore; //!< Subtopology state for CdhCore ComCcsds::SubtopologyState comCcsds; //!< Subtopology state for ComCcsds + const device* peripheralUart; + U32 peripheralBaudRate; }; namespace PingEntries = ::PingEntries; diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp b/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp index 26eebbf..668b755 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp @@ -89,4 +89,31 @@ module ReferenceDeployment { instance antennaDeployer: Components.AntennaDeployer base id 0x10029000 instance fsSpace: Components.FsSpace base id 0x10030000 + + instance payload: Components.PayloadHandler base id 0x10031000 + + instance peripheralUartDriver: Zephyr.ZephyrUartDriver base id 0x10032000 + + instance payloadBufferManager: Svc.BufferManager base id 0x10033000 \ + { + phase Fpp.ToCpp.Phases.configObjects """ + Svc::BufferManager::BufferBins bins; + """ + phase Fpp.ToCpp.Phases.configComponents """ + memset(&ConfigObjects::ReferenceDeployment_payloadBufferManager::bins, 0, sizeof(ConfigObjects::ReferenceDeployment_payloadBufferManager::bins)); + // Configure for 256 KB image buffers, 2 buffers + ConfigObjects::ReferenceDeployment_payloadBufferManager::bins.bins[0].bufferSize = 256 * 1024; + ConfigObjects::ReferenceDeployment_payloadBufferManager::bins.bins[0].numBuffers = 2; + ReferenceDeployment::payloadBufferManager.setup( + 1, // manager ID + 0, // store ID + ComCcsds::Allocation::memAllocator, // Reuse existing allocator from ComCcsds subtopology + ConfigObjects::ReferenceDeployment_payloadBufferManager::bins + ); + """ + phase Fpp.ToCpp.Phases.tearDownComponents """ + ReferenceDeployment::payloadBufferManager.cleanup(); + """ + } + } diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp b/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp index ec3bcfb..2757383 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp @@ -44,6 +44,9 @@ module ReferenceDeployment { # For UART sideband communication instance comDriver instance fsSpace + instance payload + instance peripheralUartDriver + instance payloadBufferManager # ---------------------------------------------------------------------- @@ -124,6 +127,7 @@ module ReferenceDeployment { rateGroup10Hz.RateGroupMemberOut[0] -> comDriver.schedIn rateGroup10Hz.RateGroupMemberOut[1] -> ComCcsdsUart.aggregator.timeout rateGroup10Hz.RateGroupMemberOut[2] -> ComCcsds.aggregator.timeout + rateGroup10Hz.RateGroupMemberOut[3] -> peripheralUartDriver.schedIn # Slow rate (1Hz) rate group rateGroupDriver.CycleOut[Ports_RateGroups.rateGroup1Hz] -> rateGroup1Hz.CycleIn @@ -137,6 +141,7 @@ module ReferenceDeployment { rateGroup1Hz.RateGroupMemberOut[7] -> burnwire.schedIn rateGroup1Hz.RateGroupMemberOut[8] -> antennaDeployer.schedIn rateGroup1Hz.RateGroupMemberOut[9] -> fsSpace.run + rateGroup1Hz.RateGroupMemberOut[10] -> payloadBufferManager.schedIn } @@ -161,5 +166,14 @@ module ReferenceDeployment { imuManager.magneticFieldGet -> lis2mdlManager.magneticFieldGet imuManager.temperatureGet -> lsm6dsoManager.temperatureGet } + + connections PayloadHandler { + payload.out_port -> peripheralUartDriver.$send + peripheralUartDriver.$recv -> payload.in_port + + # Buffer management for image data + payload.allocate -> payloadBufferManager.bufferGetCallee + payload.deallocate -> payloadBufferManager.bufferSendIn + } } } diff --git a/FprimeZephyrReference/project/config/AcConstants.fpp b/FprimeZephyrReference/project/config/AcConstants.fpp new file mode 100644 index 0000000..c90c5e1 --- /dev/null +++ b/FprimeZephyrReference/project/config/AcConstants.fpp @@ -0,0 +1,32 @@ +# ====================================================================== +# AcConstants.fpp +# F Prime configuration constants (project override) +# ====================================================================== + +# Override: Increase from default 10 to accommodate all 1Hz rate group members +constant ActiveRateGroupOutputPorts = 15 + +# Defaults +constant PassiveRateGroupOutputPorts = 10 +constant RateGroupDriverRateGroupPorts = 3 +constant CmdDispatcherComponentCommandPorts = 30 +constant CmdDispatcherSequencePorts = 5 +constant SeqDispatcherSequencerPorts = 2 +constant CmdSplitterPorts = CmdDispatcherSequencePorts +constant StaticMemoryAllocations = 4 +constant HealthPingPorts = 25 +constant FileDownCompletePorts = 1 +constant ComQueueComPorts = 2 +constant ComQueueBufferPorts = 1 +constant BufferRepeaterOutputPorts = 10 +constant DpManagerNumPorts = 5 +constant DpWriterNumProcPorts = 5 +constant FileNameStringSize = 200 +constant FwAssertTextSize = 120 +constant AssertFatalAdapterEventFileSize = FileNameStringSize + +# Hub connections +constant GenericHubInputPorts = 10 +constant GenericHubOutputPorts = 10 +constant GenericHubInputBuffers = 10 +constant GenericHubOutputBuffers = 10 diff --git a/FprimeZephyrReference/project/config/CMakeLists.txt b/FprimeZephyrReference/project/config/CMakeLists.txt index cf115d7..0e62ab4 100644 --- a/FprimeZephyrReference/project/config/CMakeLists.txt +++ b/FprimeZephyrReference/project/config/CMakeLists.txt @@ -5,6 +5,7 @@ #### register_fprime_config( CONFIGURATION_OVERRIDES + "${CMAKE_CURRENT_LIST_DIR}/AcConstants.fpp" "${CMAKE_CURRENT_LIST_DIR}/CdhCoreConfig.fpp" "${CMAKE_CURRENT_LIST_DIR}/CdhCoreTlmConfig.fpp" "${CMAKE_CURRENT_LIST_DIR}/CdhCoreFatalHandlerConfig.fpp" diff --git a/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5-pinctrl.dtsi b/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5-pinctrl.dtsi index 34fd8f3..ac0b4ee 100644 --- a/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5-pinctrl.dtsi +++ b/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5-pinctrl.dtsi @@ -11,6 +11,26 @@ input-enable; }; }; + uart0_default: uart0_default { + group1 { + pinmux = ; + }; + + group2 { + pinmux = ; + input-enable; + }; + }; + uart1_default: uart1_default { + group1 { + pinmux = ; + }; + + group2 { + pinmux = ; + input-enable; + }; + }; spi1_default: spi1_default { group1 { pinmux = , ; diff --git a/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5.dtsi b/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5.dtsi index 11db77d..21d5c6d 100644 --- a/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5.dtsi +++ b/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5.dtsi @@ -100,6 +100,20 @@ zephyr_udc0: &usbd { status = "okay"; }; +&uart0 { + status = "okay"; + pinctrl-0 = <&uart0_default>; + current-speed = <115200>; + pinctrl-names = "default"; +}; + +&uart1 { + status = "okay"; + pinctrl-0 = <&uart1_default>; + current-speed = <115200>; + pinctrl-names = "default"; +}; + &spi0 { status = "okay"; cs-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>;