Skip to content

PicoFramework User Guide

Pico-Framework: A web-first embedded framework for C++

Pico-Framework is a modern, modular framework designed for embedded systems like the Raspberry Pi Pico W. It is engineered to bring order, structure, and maintainability to applications that might otherwise be a tangled web of global variables, interrupt handlers, and ad-hoc state machines. Drawing inspiration from web frameworks and real-time design principles, Pico-Framework provides a clean separation of concerns and a foundation for building robust, feature-rich embedded systems.

Event-Driven Architecture

At its heart, Pico-Framework is based on an event-driven architecture. Rather than tightly coupling logic into monolithic loops or callback chains, it introduces the idea of framework tasks and controllers that communicate via a shared event bus. This approach mirrors patterns used in desktop and distributed systems, where separation and encapsulation improve clarity and testing.

Applications built on Pico-Framework subclass FrameworkApp. This base class is responsible for initializing the system, configuring core services, and starting the task scheduler. Developers define routes, register services, and manage global models here. FrameworkApp acts as the glue that binds together the HTTP server, router, logging system, time manager, and storage backend. It also owns the startup sequence and is the natural place to define application-level routes or respond to global events like network availability.

Services

Supporting this core is a collection of services that provide persistence, scheduling, communication, and diagnostics. For example, the StorageManager abstraction allows the application to read and write files without knowing whether the underlying storage is an SD card (via FatFsStorageManager) or internal flash memory (via LittleFsStorageManager). This separation is critical in embedded systems, where different platforms may impose different constraints. The storage API is simple but powerful: read and write files, stream data, append logs, and list directories — all through a unified interface. The JsonService builds on this by exposing a clean JSON-based persistence layer, while FrameworkModel enables object collections (e.g., zone definitions, schedules) to be manipulated like in-memory databases with automatic saving.

Logging, too, is elevated beyond simple printf calls. The Logger component provides structured logging with timestamps, log levels, and the ability to persist logs to storage. This enables diagnostics that survive reboots and can be viewed remotely via the web interface. Logs can be rotated manually and displayed through simple routes or HTML views.

Time Management

Time is another foundational concern in real-world embedded applications, and Pico-Framework addresses this with the TimeManager class. Upon startup, the device syncs with an NTP server and sets the system time. TimeManager can also detect the correct timezone via a public geolocation API and convert timestamps to local time for logging, scheduling, or UI display. It provides human-readable strings, UTC conversion, and broadcasts system events when time becomes valid or synced. This greatly simplifies tasks like showing the current clock on a dashboard or triggering jobs at a local time.

Once time is valid, TimerService provides a powerful mechanism for scheduling. Whether you need one-shot timers, repeated intervals, or structured daily events (like irrigation schedules), TimerService allows you to describe your intent declaratively. Events are posted to subscribers at the right time — there are no callbacks to manage or timer handles to track. Duration-based scheduling (start/stop pairs) is supported directly, enabling robust time-based automation patterns.

Networking

Networking is managed by the Network utility class, which handles Wi-Fi startup, reconnection, and retry logic. It ensures that the system boots in a network-ready state before attempting tasks that depend on connectivity. The Tcp class builds on this, wrapping low-level socket operations in a clean, reusable client-side API. It handles TLS setup, DNS resolution, and connection management, allowing developers to implement secure protocol clients (e.g., HTTP, MQTT) without depending on high-level libraries.

Interaction with the outside world is enabled through an HTTP server integrated into the framework. Developers define routes in each controller and can serve static files or generate responses dynamically. Views can be rendered with variable substitution using FrameworkView, allowing simple embedded UIs that can be served directly from device storage. Multipart form uploads are supported for file transfers, and JSON APIs can be built effortlessly using the renderJson() helpers.

Framework Controllers

All of these features are organized around the core concept of controllers, which are long-lived, independently scheduled tasks derived from FrameworkController. Each controller can define its own routes, subscribe to events, poll periodically, and respond to system changes. This modularity makes large applications easier to maintain and extend. For example, one controller might manage a file browser, another a weather integration, and another a scheduler — all running concurrently and responding to events.

In sum, Pico-Framework is not just a set of utilities — it is an architecture. It promotes maintainability through abstraction, testability through modularity, and clarity through structured task design. It brings web-level development concepts like routing, event-driven logic, and service layering into the embedded domain without sacrificing performance or control. Whether you're building an IoT sprinkler controller, a smart sensor gateway, or an automation hub, Pico-Framework offers the foundation to build confidently and cleanly.


Pico-Framework Overview

Pico-Framework is a modern, event-driven application framework designed for embedded systems like the Raspberry Pi Pico W. It brings high-level structure to bare-metal and FreeRTOS-based systems, while remaining modular, portable, and efficient. This document provides a high-level overview of all key components and how they fit together, based on the complete suite of user guides.


Core Concepts

At the heart of Pico-Framework is the idea of structured, service-oriented embedded programming. Applications are composed of tasks, controllers, services, and views—all coordinated through FreeRTOS and a lightweight router/event system.

The framework promotes: - Loose coupling via service registration - Modular design using FrameworkController - Fully event-driven flow using EventManager - Persistent storage and structured logging - Declarative scheduling and clean routing

Everything is designed to work in resource-constrained environments with optional features enabled via modular configuration.


Application Structure

Your main application derives from FrameworkApp, which: - Sets up the HTTP server and router - Registers services like StorageManager, JsonService, and Logger - Starts system components like FrameworkManager - Initializes all user-defined routes

Controllers (FrameworkController) implement discrete logic (e.g., GPIO control, scheduling, or file management). They handle requests, events, and background polling in an isolated task context.


Key Services and Subsystems

Storage System

The StorageManager interface abstracts persistent file access: - FatFsStorageManager for SD cards (large, removable, compatible) - LittleFsStorageManager for flash (always available, small-footprint)

On top of this: - JsonService persists flat JSON config and settings - FrameworkModel stores and retrieves structured object arrays (e.g., zones or programs)

This architecture allows your application to remain independent of the storage medium.

HTTP Server and Routing

The framework includes a minimalist HTTP server with routing, request/response objects, and multipart/form parsing.

Routes are registered via:

router.addRoute("GET", "/status", [](HttpRequest& req, HttpResponse& res, const RouteMatch&) {
    res.send("OK");
});

Static file serving and uploads are supported when StorageManager is enabled.

Event and Notification System

The EventManager provides indexed task notifications via onEvent() handlers in controllers. It supports: - System notifications (e.g., network ready, time valid) - User-defined event enums - Publish/subscribe model via subscribe() and postEvent()

This separates concerns cleanly and avoids direct task-to-task coupling.

Time and Scheduling

The TimeManager handles: - SNTP time sync - Timezone detection via Open-Meteo - AON/RTC initialization - Time formatting and validity checks

The TimerService lets you schedule one-shot, recurring, or daily events using: - scheduleAt() - scheduleEvery() - scheduleDailyAt() - scheduleDuration()

All timers post events—not callbacks—maintaining the event-driven design.

Logging

The Logger provides timestamped, level-based logging: - Console + optional file logging - Thread-safe - Compatible with all storage types - Viewable via web routes

Advanced use includes manual log rotation and embedded log viewers.

Networking and TCP

Networking is initialized by the Network class with: - Retry logic - Status polling - LED feedback - Reconnection helpers

The Tcp class abstracts both TLS and plain sockets for client-side communication, enabling: - Secure APIs - MQTT or FTP-style protocols - Lightweight, testable client code

User Interface and Views

The FrameworkView supports rendering HTML files with variable substitution or returning structured JSON using:

res.send(FrameworkView::render("/index.html", context));
res.send(FrameworkView::renderJson(object));

Combined with static file support, this enables lightweight frontends hosted entirely from the device.


Development Approach

  • Register all services and models at startup using AppContext::register<T>()
  • Define routes in initRoutes()
  • Use onEvent() to respond to notifications
  • Use poll() in each controller for periodic logic
  • Keep logging minimal but structured

The framework is event-first, file-aware, and task-structured by default.


Summary

Pico-Framework gives embedded developers:

  • A clean and testable architecture
  • Structured persistence and logging
  • A full HTTP server and JSON stack
  • Safe task scheduling and timer-driven events
  • Web-capable APIs and file hosting

It is ideal for connected, sensor-driven, or user-interfaced devices like automation controllers, monitoring systems, or IoT prototypes.


Feature Checklist

Pico-Framework Feature Checklist

This document outlines the current features of Pico-Framework as of the latest development state. All features are grouped by subsystem. Checkmarks (✓) indicate completed features, and future plans are noted where relevant. Designed to be used in HTML-rendered markdown without advanced bullet formatting.


Core Architecture

✓ Modular MVC-inspired structure
FrameworkApp base class for main application
FrameworkController base class for logic and routes
FrameworkTask abstraction for FreeRTOS tasks
FrameworkManager for ordered startup and service registration
✓ Application-defined initRoutes() and poll() support
✓ Multicore-safe service access and task startup


HTTP Server

✓ Lightweight HTTP server with request parsing
✓ REST-style routing with method/path match
✓ Route parameters and wildcards
✓ Middleware support (authentication, logging, etc.)
HttpRequest and HttpResponse abstractions
✓ Static file serving from SD or flash
✓ Multipart file upload support
✓ MIME type detection
✓ Automatic fallback to embedded assets if file not found
✓ Optional TLS support via socket wrapper


HTTP Client

HttpClient abstraction
✓ Fluent API via HttpRequest::setUri(), headers, body, etc.
✓ Full TLS with root certificate verification
✓ Handles chunked responses and file writes
✓ Designed for weather, cloud API, or OTA fetches
✓ Uses Tcp abstraction to support multiple implementations


Application Services

AppContext for service registration and lookup
✓ Structured StorageManager abstraction
JsonService for simple config persistence
FrameworkModel for object storage (arrays of items)
FrameworkView for templated HTML or JSON rendering
✓ JWT authentication (middleware + route protection)
✓ GPIO event handler and GpioEventManager
✓ Embedded asset support for no-SD fallback


Event System

Event struct with type, payload, and source
Notification enum system with system/user distinction
EventManager with pub/sub task model
✓ Per-task onEvent() delivery
✓ Indexed task notifications using enums
✓ ISR-safe event posting
✓ Task event masks for filtering


Timer and Scheduling

TimerService for one-shot timers
✓ Recurring interval events
✓ Daily scheduling by time and weekday
✓ Duration start/stop scheduling
✓ Job cancellation by ID
✓ Thread-safe job registry
✓ Planned: Persistent jobs via JSON
✓ Planned: Missed job replay after reboot


Time and RTC Support

TimeManager with SNTP sync
✓ Time validity tracking and retries
✓ AON/RTC setup on RP2040 and RP2350
✓ Timezone detection via Open-Meteo
✓ Human-readable formatting for timestamps
✓ UTC/local conversion via PicoTime


Storage Support

✓ Abstract StorageManager interface
FatFsStorageManager for SD card
LittleFsStorageManager for internal flash
✓ File API: read, write, append, delete, mkdir
✓ Thread-safe access
✓ Safe formatting and fallback mounting
✓ Optional file streaming (read/write in chunks)


Logging

Logger with INFO/WARN/ERROR levels
✓ Timestamps in YYYY-MM-DD HH:MM:SS format
✓ Console + file logging (SD or flash)
✓ Log truncation warning
✓ Manual log rotation logic
✓ Web route integration for log viewing
✓ Safe from multiple tasks


Debug Tracing

✓ Lightweight macro-based trace system
✓ Per-module enable/disable via flags
✓ Optional timestamping
✓ Output to console or file
✓ Controlled via framework_config.h


Utilities and Diagnostics

✓ MIME type mapping
✓ URL parsing/encoding helpers
✓ Task stats reporting
✓ Heap/stack diagnostics
✓ TCP memory diagnostics
✓ Idle memory tracking
✓ Memory-safe file uploads
✓ Framework memory reporting planned for dashboard


Testing and QA

✓ CppUTest integration
✓ Embedded tests for core features
✓ Route + HTTP end-to-end testing
✓ File and model persistence tests
✓ TLS + chunked stream test coverage
✓ Mocha tests for web UI integration
✓ Planned: Mock-based unit tests for client code


Documentation

✓ Full Doxygen coverage (headers only)
✓ Custom style and GitHub integration
✓ HTML rendering with graphs and hierarchy
✓ Markdown reference guides per subsystem
✓ GitHub Pages support
✓ Feature checklists and architecture overview


Build System and Structure

✓ Modular CMake build for app and framework
✓ Configurable build flags
✓ Optional feature toggles (auth, storage, client)
✓ Single-task or multi-task HTTP server modes
✓ LittleFS flash linker support
✓ Directory-mirrored header layout for organization


Example Apps

✓ SD or flash-backed website with login
✓ JWT-authenticated routes
✓ Event driven sprinkler programming with persistance via JSON
✓ Uploads and file browsing
✓ GPIO control via API
✓ Weather forecast integration
✓ Fully portable between SD and flash
✓ Interactive web dashboard and logs