Skip to content

Commit 180d732

Browse files
committed
builds (?)
1 parent d671a93 commit 180d732

File tree

10 files changed

+969
-6
lines changed

10 files changed

+969
-6
lines changed
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
# PayloadHandler Buffer Strategy
2+
3+
## Overview
4+
5+
The PayloadHandler uses a **two-buffer strategy** to efficiently handle both protocol/header data and large image data:
6+
7+
1. **Small static buffer** (2 KB) for protocol headers/commands
8+
2. **Large dynamic buffer** (256 KB) from BufferManager for image data
9+
10+
## Why This Approach?
11+
12+
### Problem with Static Buffers for Images
13+
- **Memory waste**: 100s of KB allocated permanently even when not receiving
14+
- **Stack overflow**: Embedded systems have limited stack/static memory
15+
- **Component bloat**: Every component instance would have huge buffer
16+
17+
### Solution: BufferManager
18+
- Allocates memory **only when needed**
19+
- Returns memory when done
20+
- Memory pooling for efficiency
21+
- Multiple components can share memory pool
22+
23+
## Architecture
24+
25+
```
26+
UART Driver (64 byte chunks)
27+
28+
PayloadHandler
29+
30+
┌─────┴─────┐
31+
↓ ↓
32+
Protocol Image
33+
Buffer Buffer
34+
(2 KB) (256 KB)
35+
static dynamic
36+
```
37+
38+
## Buffer Details
39+
40+
### Protocol Buffer
41+
- **Size**: 2048 bytes (static allocation)
42+
- **Purpose**: Parse commands, headers, metadata
43+
- **Lifecycle**: Always present
44+
- **Location**: `m_protocolBuffer[PROTOCOL_BUFFER_SIZE]`
45+
46+
### Image Buffer
47+
- **Size**: 256 KB (dynamic allocation)
48+
- **Purpose**: Accumulate complete image data
49+
- **Lifecycle**: Allocated when receiving, deallocated when done
50+
- **Location**: `m_imageBuffer` (Fw::Buffer from BufferManager)
51+
52+
## Data Flow
53+
54+
### 1. Idle State (No Image)
55+
```
56+
Incoming data → Protocol Buffer → Parse for commands
57+
```
58+
59+
### 2. Image Header Detected
60+
```
61+
Parse "<IMG_START>\n" → allocateImageBuffer()
62+
→ m_receiving = true
63+
→ Start accumulating
64+
```
65+
66+
### 3. Receiving Image
67+
```
68+
Incoming data → Image Buffer (accumulate) → Check each chunk for "<IMG_END>"
69+
```
70+
71+
### 4. Image Complete
72+
```
73+
Detect "<IMG_END>\n" → processCompleteImage() → Write to file
74+
→ deallocateImageBuffer()
75+
→ m_receiving = false
76+
```
77+
78+
## Camera Protocol
79+
80+
The Nicla Vision camera sends images using this protocol:
81+
82+
```
83+
<IMG_START>\n
84+
[raw JPEG data in 512-byte chunks]
85+
\n<IMG_END>\n
86+
```
87+
88+
Example:
89+
```
90+
<IMG_START>
91+
ÿØÿà...JFIF...image data...ÿÙ
92+
<IMG_END>
93+
```
94+
95+
## Key Functions
96+
97+
### Buffer Allocation
98+
```cpp
99+
bool allocateImageBuffer()
100+
```
101+
- Requests 256 KB buffer from BufferManager
102+
- Returns true on success, false on failure
103+
- Logs event if allocation fails
104+
105+
### Buffer Deallocation
106+
```cpp
107+
void deallocateImageBuffer()
108+
```
109+
- Returns buffer to BufferManager
110+
- Called automatically when image complete or on error
111+
112+
### Data Accumulation
113+
```cpp
114+
bool accumulateProtocolData(const U8* data, U32 size) // Small chunks
115+
bool accumulateImageData(const U8* data, U32 size) // Large chunks
116+
```
117+
118+
## Configuration
119+
120+
### BufferManager Setup (instances.fpp)
121+
```fpp
122+
instance payloadBufferManager: Svc.BufferManager {
123+
// 256 KB buffers, 2 buffers = 512 KB total
124+
bins[0].bufferSize = 256 * 1024
125+
bins[0].numBuffers = 2
126+
}
127+
```
128+
129+
### Topology Connections (topology.fpp)
130+
```fpp
131+
payload.allocate -> payloadBufferManager.bufferGetCallee
132+
payload.deallocate -> payloadBufferManager.bufferSendIn
133+
```
134+
135+
## Memory Usage
136+
137+
### Static (Always Allocated)
138+
- Protocol buffer: 2 KB
139+
- Component overhead: ~1 KB
140+
- **Total: ~3 KB**
141+
142+
### Dynamic (Only When Receiving)
143+
- Image buffer: 256 KB (when allocated)
144+
- **Total: 0-256 KB**
145+
146+
### Pool Configuration
147+
- 2 buffers of 256 KB = **512 KB total pool**
148+
- Allows receiving 2 images simultaneously or buffering one while processing another
149+
150+
## Customization
151+
152+
### To Change Buffer Sizes
153+
154+
1. **Protocol buffer** (PayloadHandler.hpp):
155+
```cpp
156+
static constexpr U32 PROTOCOL_BUFFER_SIZE = 2048; // Change this
157+
```
158+
159+
2. **Image buffer** (PayloadHandler.hpp):
160+
```cpp
161+
static constexpr U32 IMAGE_BUFFER_SIZE = 256 * 1024; // Change this
162+
```
163+
164+
3. **BufferManager pool** (instances.fpp):
165+
```cpp
166+
bins[0].bufferSize = 256 * 1024; // Must match IMAGE_BUFFER_SIZE
167+
bins[0].numBuffers = 2; // Number of buffers in pool
168+
```
169+
170+
### To Add Different Buffer Sizes
171+
172+
You can configure multiple buffer bins in the BufferManager:
173+
```cpp
174+
bins[0].bufferSize = 256 * 1024; // 256 KB for large images
175+
bins[0].numBuffers = 2;
176+
bins[1].bufferSize = 64 * 1024; // 64 KB for small images
177+
bins[1].numBuffers = 4;
178+
```
179+
180+
BufferManager will allocate the smallest buffer that fits the request.
181+
182+
## Implementation Details
183+
184+
### Protocol Parsing (Implemented)
185+
186+
The `processProtocolBuffer()` function:
187+
1. Scans for newline-terminated lines
188+
2. Checks if line matches `<IMG_START>`
189+
3. If match, calls `allocateImageBuffer()` and sets `m_receiving = true`
190+
4. Generates filename: `/mnt/data/img_NNN.jpg`
191+
192+
### Image Reception (Implemented)
193+
194+
The `in_port_handler()` function:
195+
- When `m_receiving == true`, accumulates data into image buffer
196+
- Each chunk is checked for `<IMG_END>` marker using `findImageEndMarker()`
197+
- When marker found, extracts image data (before marker) and calls `processCompleteImage()`
198+
199+
### End Marker Detection
200+
201+
The camera sends:
202+
```
203+
[image data]\n<IMG_END>\n
204+
```
205+
206+
The handler searches each incoming chunk for `<IMG_END>` and stops accumulation when found. Image data before the marker is written to file.
207+
208+
## Error Handling
209+
210+
### Allocation Failure
211+
- Logs `BufferAllocationFailed` event
212+
- Continues processing protocol buffer
213+
- Can retry later if needed
214+
215+
### Buffer Overflow
216+
- Image buffer overflow: Logs `ImageDataOverflow` event, deallocates buffer
217+
- Protocol buffer overflow: Clears buffer and restarts
218+
219+
### Component Destruction
220+
- Destructor automatically deallocates any active image buffer
221+
- Prevents memory leaks
222+
223+
## Performance Notes
224+
225+
- **Allocation overhead**: ~microseconds (one-time per image)
226+
- **Copy overhead**: memcpy for each 64-byte UART chunk (~100 cycles)
227+
- **Memory efficiency**: Only allocates when receiving, 256x better than static
228+
- **Latency**: No impact on UART receive rate (64 bytes @ 10Hz = 640 bytes/sec)
229+
230+
## Testing Checklist
231+
232+
- [ ] Receive small image (< 256 KB)
233+
- [ ] Receive large image (> 256 KB) - should fail gracefully
234+
- [ ] Receive multiple images sequentially
235+
- [ ] Handle allocation failure (simulate by allocating 2 buffers)
236+
- [ ] Handle protocol buffer overflow
237+
- [ ] Handle image buffer overflow
238+
- [ ] Component cleanup (destructor)
239+

0 commit comments

Comments
 (0)