Skip to content

ayltai/espine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Espine

Monorepo for controlling MicroPython/ESP32 devices over a lightweight HTTP + WebSocket API, plus a React client library.

Purpose: provide the core infrastructure and reusable building blocks for developing ESP32-based remote-controlled actuators (e.g. remote controlled cars), including connectivity, command dispatch, device handlers, and optional camera streaming.

This repo contains three projects:

  • espine-node: MicroPython device runtime (commands, handlers, WiFi, WebSocket).
  • espine-cam: MicroPython camera streaming runtime (MJPEG over HTTP).
  • espine-react: React hook + transport/store helpers for controlling an Espine device from React UI.

Repo layout

Device API (common concepts)

Health check

Both device runtimes expose:

  • GET /health200 JSON
    • { "status": "ok", "environment": "esp32" | "unix" }

WebSocket (espine-node)

  • GET /ws (WebSocket)
    • Incoming text messages must be JSON of shape:
      • { "type": string, "payload"?: object }
    • type: "heartbeat" is treated as a keepalive.
    • type: "connection_closed" triggers a failsafe (all handlers stop()).
    • Incoming binary messages are passed to CommandDispatcher.consume(...) (currently a stub).
    • Outgoing binary messages can be produced by handlers via CommandDispatcher.produce(...).

MJPEG camera stream (espine-cam)

  • GET /video/<frame_size>?quality=<int>
    • Streams multipart/x-mixed-replace frames (image/jpeg).
    • frame_size is a key from the built-in FRAME_SIZES mapping (examples: 320x240, 640x480, 1280x720).
    • quality defaults to 15.

espine-node

MicroPython-first runtime for a remote-controlled device. It follows an event-driven, stateless-actor-ish pattern:

  • DeviceController hosts HTTP routes and the /ws WebSocket.
  • CommandDispatcher routes commands to registered handlers.
  • BaseHandler defines the handler interface (handle() and stop()).
  • WiFiManager configures STA/AP mode on-device (simulated on unix).

Code lives under:

Development (library)

From the repo root:

  • Install/update Python deps: cd espine-node && make upgrade
  • Lint: cd espine-node && make lint
  • Test: cd espine-node && make test

Using espine-node in a firmware/app project

This monorepo does not include a device application main.py. The intended workflow is:

  1. Create your own firmware/app project that contains:
    • main.py
    • optionally an app folder such as src/ containing your custom handlers
  2. Use the template Makefile to copy espinenode/ and the vendored microdot/ into your project and deploy via mpremote.

See the template at: espine-node/Makefile.template

A minimal sketch of a MicroPython main.py (your project) typically:

  • brings up WiFi with WiFiManager
  • registers handlers on a CommandDispatcher
  • starts CommandDispatcher.start() and DeviceController.start()

espine-cam

MicroPython camera streaming runtime (ESP32-CAM style setups).

  • DeviceController serves GET /health and GET /video/<frame_size>
  • Uses the MicroPython camera module (JPEG capture).
  • Includes a WiFi manager similar to espine-node.

Code lives under:

Development (library)

  • Install/update Python deps: cd espine-cam && make upgrade
  • Lint: cd espine-cam && make lint

Using espine-cam in a firmware/app project

Like espine-node, the included espine-cam/Makefile.template is meant to be copied into your device project to:

  • flash a camera-capable MicroPython firmware image
  • deploy espinecam/ + microdot/ + your app src/ + your main.py

espine-react

React library for connecting to an espine device over WebSocket.

Public API:

  • useDevice(url, initialState, onStream?, heartbeatInterval?, webSocket?)

It composes three internal modules:

  • DeviceLink: manages the WebSocket connection (JSON + binary)
  • DeviceStore: minimal reactive store with patch updates
  • Commander: sends commands and emits periodic heartbeats

Code lives under: espine-react/src/

Install / build / test

Prereqs (as declared in the project):

  • Node.js >= 22
  • pnpm >= 10

Commands:

  • Install: cd espine-react && pnpm i
  • Lint: cd espine-react && pnpm lint
  • Test: cd espine-react && pnpm test
  • Build: cd espine-react && pnpm build

Basic usage

import { useDevice } from 'espine-react';

export function MyComponent() {
  const { status, store, execute } = useDevice(
    'ws://192.168.4.1/ws',
    { count: 0 },
    (pcm) => {
      // Binary frames arrive as Int16Array
      console.log('stream samples', pcm.length);
    },
    500,
  );

  const state = store.getState();

  return (
    <div>
      <div>Status: {status}</div>
      <div>Count: {state.count}</div>
      <button onClick={() => execute?.({ type: 'increment', payload: { amount: 1 } })}>
        Increment
      </button>
    </div>
  );
}

Notes:

  • JSON messages received from the device are treated as patches and merged into the store.
  • Binary messages received from the device are surfaced via onStream(Int16Array).

License

MIT. See LICENSE.

About

Monorepo for controlling MicroPython/ESP32 devices over a lightweight HTTP + WebSocket API, plus a React client library.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Contributors