No description
  • JavaScript 91.4%
  • CSS 6.6%
  • HTML 2%
Find a file
dosch 1d0bf7353f Rebuild MeshCore KaiOS client from scratch
Ground-up rewrite applying lessons from the sibling MeshGarmin port,
replacing the previous multi-file app.

- BLE (bluetooth.js): mozBluetooth scan + PIN pairing/bonding + GATT over the
  bonded (encrypted) link, so the app works with STOCK MeshCore firmware. This
  is the structural advantage over Garmin, whose Connect IQ apps can only open
  unencrypted BLE and thus cannot talk to a stock node.
- Protocol (protocol.js): APP_START/SELF_INFO, DEVICE_QUERY/DEVICE_INFO,
  GET_CONTACTS/CONTACT, GET_CHANNEL/CHANNEL_INFO, SEND_TXT_MSG (direct),
  SEND_CHANNEL_TXT_MSG, SYNC_NEXT_MESSAGE, msg recv v0/v3, battery.
- Paced retrying write queue + quiet-gap frame reassembly (carried from Garmin).
- Receive via SYNC polling, with BLE notifications as an accelerator.
- UI (app.js): 240x320 D-pad; connect/PIN/menu/channels/contacts/conversation/
  compose/info screens; localStorage persistence (store.js).
- mock.js fake node for full browser testing (no hardware).
- Verified in browser mock mode; ESLint clean; no post-FF48 APIs.
- Docs refreshed (README, CLAUDE, research note); stale old-app docs/logs removed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 09:39:18 +02:00
css Rebuild MeshCore KaiOS client from scratch 2026-06-08 09:39:18 +02:00
icons Rebuild MeshCore KaiOS client from scratch 2026-06-08 09:39:18 +02:00
js Rebuild MeshCore KaiOS client from scratch 2026-06-08 09:39:18 +02:00
screenshots Rebuild MeshCore KaiOS client from scratch 2026-06-08 09:39:18 +02:00
.gitignore Rebuild MeshCore KaiOS client from scratch 2026-06-08 09:39:18 +02:00
CLAUDE.md Rebuild MeshCore KaiOS client from scratch 2026-06-08 09:39:18 +02:00
eslint.config.mjs Rebuild MeshCore KaiOS client from scratch 2026-06-08 09:39:18 +02:00
index.html Rebuild MeshCore KaiOS client from scratch 2026-06-08 09:39:18 +02:00
manifest.webapp Rebuild MeshCore KaiOS client from scratch 2026-06-08 09:39:18 +02:00
MESHCORE_KAIOS_RESEARCH.md Rebuild MeshCore KaiOS client from scratch 2026-06-08 09:39:18 +02:00
README.md Rebuild MeshCore KaiOS client from scratch 2026-06-08 09:39:18 +02:00

MeshCore for KaiOS

A vanilla-JS KaiOS app (Nokia 2720 Flip / 8110) that connects to a MeshCore companion radio (e.g. SenseCAP T1000-E) over Bluetooth LE and lets you chat on an off-grid LoRa mesh. Built fresh, applying the hard-won lessons from the sibling MeshGarmin project.

Nokia (KaiOS)  ──BLE / Nordic UART (bonded, PIN)──  T1000-E  ──LoRa──  MeshCore mesh
 (BLE central)                                      (companion fw)

Why KaiOS succeeds where Garmin couldn't

The MeshGarmin port hit a hard wall: MeshCore companion firmware demands an MITM-encrypted (bonded) BLE link (SerialBLEInterface.cpp: setMITM(true) + SECMODE_ENC_WITH_MITM). Garmin Connect IQ apps can only open an Open / unencrypted connection (encrypted links are reserved for Garmin's own accessories), so the watch's notify-enable write was rejected (d18) and no messages flowed. The only Garmin fix was custom node firmware with open BLE.

KaiOS does not have that limit. navigator.mozBluetooth performs system-level PIN pairing/bonding, and this privileged app then runs GATT over that bonded (encrypted) link. So Nokia talks to stock MeshCore firmware — no firmware changes needed. That is exactly goal #1: connect to a node using PIN verification.

Features

  • Connect with PIN — scan for MeshCore nodes, pair with the node's BLE PIN (default 123456), bond, and connect over GATT.
  • Channels — lists channel slots (GET_CHANNEL 07); send/receive group messages.
  • Contacts — syncs the node's contact list (GET_CONTACTS); send/receive direct messages.
  • Conversations — per-channel / per-contact history with unread badges, persisted in localStorage.
  • Device info — model, firmware, radio (freq/SF/CR), battery, storage, BLE PIN (DEVICE_QUERY + GET_BATT_AND_STORAGE).

Screen real estate is tight (240×320), so the UI is list-first: a single content pane, 3 softkeys, D-pad navigation, and a native input for keypad/T9 text entry.

File structure

KaiOS/
├── manifest.webapp     privileged app, "bluetooth" permission
├── index.html          header + content pane + softkey bar
├── css/app.css         240x320, D-pad, compact lists & bubbles
└── js/
    ├── utils.js        bytes/UTF-8/hex/LE helpers
    ├── protocol.js     MeshCore frame build + parse (commands/responses/pushes)
    ├── bluetooth.js    mozBluetooth: scan, PIN pair, GATT, notify+poll,
    │                   paced write queue, frame reassembly. Mock-aware.
    ├── mock.js         in-browser fake node (auto-used when no mozBluetooth)
    ├── store.js        localStorage cache: contacts, channels, messages
    └── app.js          orchestrator: session flow, screens, keys, rendering

Carry-overs from the working Garmin code

  • Paced, retrying write queue (WRITE_PACE_MS, retries) — avoids the "not enough resources" failure seen on fast/early writes.
  • Frame reassembly via quiet-gap timer (FRAME_GAP_MS = 40) — BLE delivers frames in MTU-sized notification chunks; the firmware spaces whole frames ≥60 ms apart, so a <60 ms gap marks a frame boundary. (148-byte contact frames need this on small-MTU links.)
  • Command-response sequencingAPP_START is sent first; the rest of the session (time, device query, contacts, channels) starts only after SELF_INFO arrives.
  • Polling for receive — KaiOS BLE notifications are unreliable on some builds, so the app polls SYNC_NEXT_MESSAGE every 2.5 s; a MSG_WAITING (0x83) push just triggers an immediate sync when notifications do work.

Run in a browser (mock mode)

No hardware needed — when navigator.mozBluetooth is absent the app uses a fake node (mock.js).

cd KaiOS
python3 -m http.server 8000
# open http://localhost:8000
# F1 = left softkey, Enter = center, F3 = right, arrows = D-pad

Mock mode exercises the full flow: scan → pair → session sync (2 contacts + Public channel) → open a conversation → write → send. An incoming message is injected ~8 s after connect.

Captured mock screens are in screenshots/ — connect, menu, contacts, conversation (send + receive), compose, channels, and device info.

Deploy to a Nokia device

Uses the kdeploy tool (Firefox Remote Debugging Protocol over ADB):

# 1. Enable debugging on the phone: dial *#*#33284#*#*
# 2. Connect USB, then:
adb forward tcp:6000 localfilesystem:/data/local/debugger-socket
# 3. from a kdeploy checkout:
node main.js install "/path/to/this/repo"    # manifest.webapp is at the repo root
node main.js list
node main.js launch "app://<app-id-from-list>/manifest.webapp"

(install may log a callback error but still install — verify with list.) Alternatively use Firefox WebIDE → Open Packaged App → select this folder.

Pairing note: if the GATT connect is rejected for security, pair the node once via the phone's Settings → Bluetooth (enter the BLE PIN) so the bond exists, then connect from the app.

Protocol reference

  • MeshCore firmware (authoritative): https://github.com/meshcore-dev/MeshCoredocs/companion_protocol.md and command codes / contact frame in examples/companion_radio/MyMesh.cpp.
  • Frame layouts cross-checked against the MeshGarmin Protocol.mc.
  • Full protocol notes also in CLAUDE.md and MESHCORE_KAIOS_RESEARCH.md.

Status

Builds and runs fully in browser mock mode (verified: protocol round-trips, frame reassembly, all screens render and navigate, send/receive stored). On-device BLE (scan / PIN pair / GATT / notify) is written against the mozBluetooth API but not yet tested on hardware — that is the next step.