Overview
Lifecycle methods control the USB streaming session, including initialization, connection management, and shutdown. These methods must be called in the correct order to ensure proper operation.
start
Starts USB streaming with pre-configured settings. The driver creates internal FreeRTOS tasks to handle USB data from the stream pipe and executes user callbacks when new frames are ready.
Parameters
This method takes no parameters.
Return Value
This method returns void. The underlying C function returns esp_err_t, but the C++ wrapper does not expose error codes.
Example
#include "USB_STREAM.h"
USB_STREAM usb;
void setup() {
Serial.begin(115200);
// Configure UVC camera (example)
uint8_t *xferBufferA = (uint8_t *)malloc(55 * 1024);
uint8_t *xferBufferB = (uint8_t *)malloc(55 * 1024);
uint8_t *frameBuffer = (uint8_t *)malloc(55 * 1024);
usb.uvcConfiguration(
640, 480, // width, height
FRAME_INTERVAL_FPS_15, // frame interval
55 * 1024, // transfer buffer size
xferBufferA, xferBufferB, // transfer buffers
55 * 1024, // frame buffer size
frameBuffer // frame buffer
);
// Register callbacks before starting
usb.uvcCamRegisterCb(onFrameReady, NULL);
// Start streaming
usb.start();
Serial.println("USB streaming started");
}
void onFrameReady(uvc_frame_t *frame, void *ptr) {
// Handle frame
Serial.printf("Frame: %dx%d\n", frame->width, frame->height);
}
Notes
You must configure video/audio parameters using uvcConfiguration() and/or uacConfiguration() before calling start(). Starting without configuration may lead to undefined behavior.
Register state change and frame callbacks before calling start() to ensure you don’t miss any initial events.
The method creates internal FreeRTOS tasks that run continuously until stop() is called. These tasks handle USB data transfer and callback execution.
stop
Stops current USB streaming. Internal FreeRTOS tasks are deleted and related resources are freed.
Parameters
This method takes no parameters.
Return Value
This method returns void. The underlying C function returns esp_err_t, but the C++ wrapper does not expose error codes.
Example
#include "USB_STREAM.h"
USB_STREAM usb;
bool streaming = false;
void setup() {
Serial.begin(115200);
// Configure and start streaming
// ... configuration code ...
usb.start();
streaming = true;
Serial.println("Streaming started");
}
void loop() {
// Stop streaming after 30 seconds
if (streaming && millis() > 30000) {
Serial.println("Stopping stream...");
usb.stop();
streaming = false;
Serial.println("Stream stopped");
}
}
Example with Error Handling
void stopStreaming() {
if (isStreamingActive) {
Serial.println("Stopping USB streaming...");
usb.stop();
// Wait a moment for cleanup
delay(100);
// Free allocated buffers
free(xferBufferA);
free(xferBufferB);
free(frameBuffer);
isStreamingActive = false;
Serial.println("Cleanup complete");
}
}
Notes
Always call stop() before destroying the USB_STREAM object or deallocating buffers used in the configuration. Failing to do so may cause memory leaks or crashes.
The method blocks until all internal tasks are properly terminated. This may take a short moment to complete.
After calling stop(), you can reconfigure parameters and call start() again to restart streaming with different settings.
connectWait
Waits for a USB device to be connected, with a specified timeout.
void connectWait(int timeoutMs);
Parameters
Timeout value in milliseconds. The method will block for up to this duration waiting for a device connection.
- Set to
0 to return immediately
- Set to
-1 or large value for extended wait
- Typical values:
5000 (5 seconds) to 30000 (30 seconds)
Return Value
This method returns void. The underlying C function returns esp_err_t with the following values:
ESP_OK: Device connected successfully
ESP_ERR_TIMEOUT: Timeout expired without connection
ESP_ERR_INVALID_STATE: Streaming not started
Example
#include "USB_STREAM.h"
USB_STREAM usb;
void setup() {
Serial.begin(115200);
// Configure streaming parameters
// ... configuration code ...
// Start streaming
usb.start();
Serial.println("Waiting for USB device...");
// Wait up to 10 seconds for device connection
usb.connectWait(10000);
Serial.println("Device connected or timeout reached");
}
Example with User Feedback
void waitForDevice() {
const int TIMEOUT_MS = 15000;
Serial.println("Please connect a USB camera...");
Serial.printf("Waiting up to %d seconds\n", TIMEOUT_MS / 1000);
usb.start();
usb.connectWait(TIMEOUT_MS);
// You may want to check connection state after this
Serial.println("Proceeding with streaming");
}
Example with State Callback
volatile bool deviceConnected = false;
void onStateChange(usb_stream_state_t state, void *arg) {
if (state == STREAM_CONNECTED) {
deviceConnected = true;
Serial.println("Device connected!");
} else {
deviceConnected = false;
Serial.println("Device disconnected!");
}
}
void setup() {
Serial.begin(115200);
// Register state callback
usb.registerState(onStateChange, NULL);
// Configure and start
// ... configuration code ...
usb.start();
// Wait for connection
Serial.println("Waiting for device...");
usb.connectWait(10000);
if (deviceConnected) {
Serial.println("Ready to stream!");
} else {
Serial.println("No device detected");
}
}
Notes
This method must be called after start(). Calling it before starting streaming will result in an invalid state error.
The method blocks the calling task for the specified timeout duration or until a device connects, whichever comes first.
This method is useful for ensuring a device is connected before attempting to stream data, preventing errors from operations on a disconnected device.
registerState
Registers a callback function that will be invoked when the USB streaming state changes (device connection/disconnection).
void registerState(StateChangeCallback newFunction, void *usr_data);
Parameters
newFunction
StateChangeCallback
required
A pointer to a callback function with the signature:void callback(usb_stream_state_t event, void *arg)
The event parameter will be:
STREAM_CONNECTED (0): USB device connected
STREAM_DISCONNECTED: USB device disconnected
A user-defined pointer that will be passed to the callback function as the second argument. Use NULL if not needed.This can point to:
- Application context/state
- Configuration structures
- Class instances (for C++ callbacks)
- Any user data needed in the callback
Return Value
This method returns void.
Example
#include "USB_STREAM.h"
USB_STREAM usb;
void onConnectionChange(usb_stream_state_t state, void *arg) {
if (state == STREAM_CONNECTED) {
Serial.println("✓ USB device connected");
// Device is ready, can start operations
} else if (state == STREAM_DISCONNECTED) {
Serial.println("✗ USB device disconnected");
// Handle disconnection, pause operations
}
}
void setup() {
Serial.begin(115200);
// Register state callback before starting
usb.registerState(onConnectionChange, NULL);
// Configure and start streaming
// ... configuration code ...
usb.start();
}
Example with User Data
struct AppState {
int frameCount;
bool readyToStream;
unsigned long lastConnectTime;
};
AppState appState = {0, false, 0};
void onConnectionChange(usb_stream_state_t state, void *arg) {
AppState *app = (AppState *)arg;
if (state == STREAM_CONNECTED) {
Serial.println("Device connected");
app->readyToStream = true;
app->frameCount = 0;
app->lastConnectTime = millis();
} else {
Serial.println("Device disconnected");
app->readyToStream = false;
Serial.printf("Total frames captured: %d\n", app->frameCount);
}
}
void setup() {
Serial.begin(115200);
// Pass application state to callback
usb.registerState(onConnectionChange, &appState);
// Configure and start
// ... configuration code ...
usb.start();
}
Example with Class Method
class CameraManager {
private:
USB_STREAM usb;
bool connected;
static void stateCallback(usb_stream_state_t state, void *arg) {
CameraManager *manager = (CameraManager *)arg;
manager->handleStateChange(state);
}
void handleStateChange(usb_stream_state_t state) {
connected = (state == STREAM_CONNECTED);
Serial.printf("Connection state: %s\n", connected ? "UP" : "DOWN");
}
public:
CameraManager() : connected(false) {}
void begin() {
// Register with 'this' pointer
usb.registerState(stateCallback, this);
// Configure and start
// ... configuration code ...
usb.start();
}
bool isConnected() { return connected; }
};
CameraManager camera;
void setup() {
Serial.begin(115200);
camera.begin();
}
Notes
Only one state callback can be registered at a time. Registering a new callback will overwrite any previously registered callback.
The callback is executed in the context of an internal FreeRTOS task. Keep the callback execution time minimal and avoid blocking operations like:
- Long delays
- Heavy computations
- Blocking I/O operations
- Serial.print() in high-frequency callbacks (use sparingly)
Register the state callback before calling start() to ensure you receive the initial connection event if a device is already connected.
The callback is useful for:
- Updating UI/LED indicators
- Pausing/resuming application logic
- Logging connection events
- Triggering reconnection attempts
Typical Lifecycle Sequence
Here’s a complete example showing the recommended order of operations:
#include "USB_STREAM.h"
USB_STREAM usb;
uint8_t *xferBufferA, *xferBufferB, *frameBuffer;
bool streaming = false;
void onStateChange(usb_stream_state_t state, void *arg) {
Serial.printf("State: %s\n",
state == STREAM_CONNECTED ? "CONNECTED" : "DISCONNECTED");
}
void onFrame(uvc_frame_t *frame, void *ptr) {
Serial.printf("Frame: %dx%d, %d bytes\n",
frame->width, frame->height, frame->data_bytes);
}
void setup() {
Serial.begin(115200);
// Step 1: Allocate buffers
xferBufferA = (uint8_t *)malloc(55 * 1024);
xferBufferB = (uint8_t *)malloc(55 * 1024);
frameBuffer = (uint8_t *)malloc(55 * 1024);
// Step 2: Register callbacks
usb.registerState(onStateChange, NULL);
usb.uvcCamRegisterCb(onFrame, NULL);
// Step 3: Configure streaming
usb.uvcConfiguration(
640, 480,
FRAME_INTERVAL_FPS_15,
55 * 1024,
xferBufferA, xferBufferB,
55 * 1024,
frameBuffer
);
// Step 4: Start streaming
usb.start();
streaming = true;
Serial.println("Started");
// Step 5: Wait for device
usb.connectWait(10000);
Serial.println("Ready");
}
void loop() {
// Your application logic
delay(100);
}
void cleanup() {
if (streaming) {
// Step 6: Stop streaming
usb.stop();
streaming = false;
// Step 7: Free buffers
free(xferBufferA);
free(xferBufferB);
free(frameBuffer);
}
}