Skip to content

Progressive Pipeline API

Header: main/cam_pjpeg.h

A FreeRTOS task that captures RGB565 frames, encodes progressive JPEG via esp_pjpeg, fragments into MTU-sized binary packets via pjpeg_lora, and transmits each packet with lora_send_binary(). This is a parallel alternative to cam_lora.h — selected at compile time with USE_PROGRESSIVE_JPEG.

Progressive structure means the receiver sees a recognizable image after just the DC scans (~3 KB), with detail added by later scans.

FieldTypeDefaultDescription
lora_destuint16_t0Base station LoRa address
periodic_msuint32_t0Periodic capture interval in ms (0 = disabled)
motion_enabledboolfalseReserved for future use
jpeg_qualityuint8_t75JPEG quality 1–100 (pjpeg encoder scale)
scan_presetpjpeg_scan_preset_tPJPEG_SCAN_LORAScan organization (LORA preset optimized for early visual payoff)
mtuuint16_t0Packet MTU (0 = LORA_MAX_PAYLOAD 240, use 184 for MeshCore)
use_ackboolfalseEnable windowed ACK/retransmit via pjpeg_lora_ack
use_crc8booltrueAppend CRC8 integrity byte to each data packet
esp_err_t cam_pjpeg_start(const cam_pjpeg_config_t *config);

Initialize and start the progressive pipeline. Creates a FreeRTOS task (cam_pjpeg, 12KB stack, priority 5) that:

  1. Initializes the camera (fake or real) in RGB565 mode
  2. Polls for LoRa commands every 100ms
  3. Checks periodic and on-demand triggers
  4. Captures, encodes, and transmits progressive JPEG images

Returns: ESP_OK on success, ESP_ERR_NO_MEM if task creation fails, ESP_ERR_INVALID_STATE if already running.

void cam_pjpeg_stop(void);

Signal the pipeline task to stop. Deinitializes the ACK sender if it was active. The task self-deletes on its next iteration.

void cam_pjpeg_trigger_capture(void);

Request an immediate capture from outside the pipeline task. Sets an internal flag that the task checks on its next poll cycle.

uint32_t cam_pjpeg_get_capture_count(void);

Returns the total number of successful captures since pipeline start.

bool cam_pjpeg_is_running(void);

Check if the pipeline task is currently running.

When use_ack is true, the pipeline initializes a pjpeg_lora_ack_sender_t that wraps the packetizer with windowed acknowledgement:

  1. All packets for a segment (JPEG headers or a single scan) are captured into a PSRAM buffer
  2. Packets are transmitted in windows of 8
  3. After each window, cam_pjpeg_rx_callback calls lora_receive_binary() to wait for a 4-byte ACK bitmap
  4. Missing packets are retransmitted (up to 3 retries per window)
  5. On NACK or max retries, the segment is marked failed and the next segment begins

When use_ack is false (default), packets are sent fire-and-forget. CRC8 is still appended (controlled by use_crc8) so the receiver can detect corruption even without ACK.

The task supports two capture triggers, evaluated in priority order:

A remote C:CAPTURE command or call to cam_pjpeg_trigger_capture() sets the capture_pending flag. The task processes this immediately on its next poll cycle.

When periodic_ms > 0, the task captures at fixed intervals. An on-demand capture resets the periodic timer.

The task listens for LoRa packets and handles these commands:

CommandAction
C:CAPTURESet capture pending flag
C:STATUSReply with S:HEAP=N:UP=N:RSSI=N:CAP=N:VER=X:MODE=PJPEG
C:PINGReply with S:PONG
C:CONFIG:period=NSet periodic interval (ms)
C:CONFIG:quality=NSet JPEG quality (1–100)
flowchart TD
    A["cam_pjpeg_capture_and_send()"] --> B["fake_camera_fb_get()<br/>Get RGB565 framebuffer"]
    B --> C["Copy pixels to PSRAM<br/>Free camera buffer early"]
    C --> D["fake_camera_fb_return()"]
    D --> E["pjpeg_encoder_create()"]
    E --> F["pjpeg_encode_coefficients()<br/>DCT + quantize → PSRAM"]
    F --> G["free(pixels)"]
    G --> H["pjpeg_lora_ack_send_image()<br/>Packetize + ACK + lora_send_binary()"]
    H --> I["pjpeg_encoder_destroy()"]

The camera framebuffer is released before encoding begins. The encoder stores DCT coefficients in PSRAM, then the packetizer reads them scan-by-scan — this keeps peak memory usage lower than holding both pixels and JPEG data simultaneously.

Featurecam_lora.h (legacy)cam_pjpeg.h (progressive)
Input formatJPEG (from OV2640)RGB565 (software encode)
EncodingHardware JPEGProgressive JPEG via esp_pjpeg
Wire formatASCII Base64 + CRC16Binary packets + CRC8
Fragmentationlora_transfer.hpjpeg_lora.h + pjpeg_lora_ack.h
Send functionlora_send()lora_send_binary()
ACK protocolWindowed (8-frag bitmap)Windowed (8-frag bitmap)
Progressive displayNoYes (DC scans first)
Motion detectionYesReserved (not yet wired)
Compile flagDefaultUSE_PROGRESSIVE_JPEG