Put live KIP dashboards on cheap ESP32 touchscreens around the boat.
KIP dashboards on the 4" 480×480 screen.
A Signal K plugin on the Raspberry Pi runs KIP in headless Chromium and streams it as JPEG frames over WiFi to Waveshare touchscreens: the ESP32-S3 7" and 4.3" (800×480) and 4" (480×480), and the ESP32-P4 10.1" (1280×800). Screens of different sizes can be used at once. Touches on the screen are sent back and injected into KIP as real touch events, so tapping, swiping between dashboards and KIP's menus all work as they do on a tablet.
Raspberry Pi (Signal K) ESP32 display
┌─────────────────────────────────┐ JPEG ┌────────────────────┐
│ signalk-kipcast │ frames │ kipcast-display │
│ headless Chromium │ ─────────▶ │ JPEGDEC → RGB LCD │
│ one window per display id, │ TCP 3051 │ │
│ each running KIP │ ◀───────── │ GT911 touch │
│ │ touches └────────────────────┘
│ browser test viewer, port 3050 │
└─────────────────────────────────┘
Each display has an id (for example saloon or helm). Each id gets its own KIP, drawn at that screen's size, with its own login and settings, so swiping to another dashboard on one screen doesn't change the others. Two displays with the same id show the same thing.
Status: tested on a Pi 5 running Signal K with KIP 4.8, with a Waveshare 7", a 4" Rev4 and a 10.1" ESP32-P4 connected at the same time.
| Folder | What it is |
|---|---|
signalk-kipcast/ |
Signal K plugin (Node.js). Also runs on its own with node standalone.js. |
kipcast-display/ |
PlatformIO firmware for the Waveshare ESP32-S3-Touch-LCD-7, -4.3 and -4, and the ESP32-P4-WIFI6-Touch-LCD-10.1. |
- Install the plugin. Install Chromium on the Pi (
sudo apt install chromium), then install KIPCast from the Signal K App Store and enable it in Server → Plugin Config → KIPCast. - Install the firmware on a screen. Plug it into a computer and use the web installer in Chrome or Edge.
- Set up the screen. It opens a WiFi network called
KIPCast-and four characters. Join it on your phone and fill in the setup page: the boat's WiFi, and an id for the screen. The screen finds the Signal K server by itself. - Build its dashboards. Open Webapps → KIPCast in Signal K. See Recommended workflow for a new screen size.
Requirements: Signal K with KIP installed, Node.js 22.12 or later, and Chromium (sudo apt install chromium).
Install KIPCast from the App Store in the Signal K admin UI, or from npm: cd ~/.signalk && npm install signalk-kipcast. Then restart Signal K, go to Server → Plugin Config → KIPCast, enable it and save.
git clone https://github.com/g0kad/KIPCast.git ~/KIPCast
cd ~/KIPCast/signalk-kipcast && npm ci --omit=dev
cd ~/.signalk && npm install ~/KIPCast/signalk-kipcast
sudo systemctl restart signalkTo update later: cd ~/KIPCast && git pull, run npm ci --omit=dev in signalk-kipcast/, then restart Signal K.
In the Signal K admin UI, open Webapps → KIPCast. It lists every display the Pi knows about:
| Column | Shows |
|---|---|
| Display | The display id, as set on the screen's setup page. |
| Size | The resolution KIP is drawn at for that display. |
| Status | Whether the screen is connected, open in the viewer, or when it was last seen. |
Open takes you to the browser viewer for that display at its own size, which is where you set up its KIP. Forget removes a display from the list. Its KIP login and dashboards are kept, and a screen with that id adds itself back when it next connects.
Screens add themselves the first time they connect. To set up a display before its screen exists, use Add a display: enter the id you'll give the screen and pick its size (7" or 4.3" 800×480, 4" 480×480, 10.1" 1280×800, or custom).
The page and its changes need a Signal K login. The plugin status in Server → Plugin Config also lists the connected screens and their sizes, for example Connected: helm 480×480, shedtest 800×480.
- Reported by the screen. Each ESP32 sends its resolution when it connects. The Pi remembers it in
displays.jsonin the plugin's data folder. - Remembered. When the viewer opens a display id without its screen, it uses the remembered size, so dashboards are always laid out at the size the screen will show them.
- Default. An id the Pi has never seen uses the plugin's default width and height (800×480).
For an id the Pi has never seen, the viewer can also set a size in its URL, for example &w=480&h=480 after the ?id=.
If a screen connects while its id is open in the viewer at a different size, the page is reopened at the screen's size. The viewer reconnects by itself a couple of seconds later.
The ESP32 screens have no keyboard, so each display's KIP is set up in the browser viewer: open it from the Displays page, click the screen to give it keyboard focus, and type as normal. The first time, KIP shows its "Getting Started" guide and may ask you to log in to Signal K.
Each display id has its own browser profile, so its KIP login and settings are separate from other displays'. A new display id starts with a copy of the login and settings from the older shared profile (chrome-profile), if there is one. After that it's independent.
Where KIP keeps its dashboards depends on how it's logged in:
- Logged in to Signal K: KIP can save its configuration on the Signal K server, for that user. Every screen logged in as the same user loads the same dashboards.
- Not logged in: KIP keeps its configuration in that display's own browser profile.
So to give screens of different sizes their own dashboards, the simplest approach is one Signal K user per screen size or role, for example a helm user for the 4" screens and a saloon user for the 7" ones. Create the users in Security → Users in the Signal K admin UI, then log each display's KIP in as the right one. Screens that should show the same thing can share a user.
KIP's own profiles are another way to keep layouts apart within one user.
Editing KIP's layout on the touchscreen itself is fiddly, so build the dashboards on a computer and use the screen only to check the result:
- Create a Signal K user for the screen size. In Security → Users, add a user such as
kip480for the 4" screens orkip800for the 7" ones. - Build the dashboards in an ordinary browser. On a computer, open KIP (
http://<pi>:3000/@mxtommy/kip/), log in as that user, and add the dashboards and widgets and choose their Signal K paths. With a full-size window, mouse and keyboard this is much quicker than on the screen. A private window keeps this login separate from the one you normally use KIP with. - Log the display in as the same user. In Webapps → KIPCast, click KIP login for the display and enter that user's name and password. KIPCast checks them with Signal K, then the display's KIP logs in and the dashboards you just built load from the Signal K server. The screen doesn't need to be connected.
- Fine-tune while watching the screen. Click Open for the display. The viewer is drawn at the screen's exact size and everything you do in it appears on the screen straight away. Resize and move the widgets in the viewer until they read well on the screen itself.
Any other screen of the same size can then be logged in as the same user and gets the same dashboards.
A 480×480 screen has a little over half the pixels of an 800×480 one, and it's square. Some tips:
- Build separate dashboards for it rather than reusing the 7" ones. Do the final layout in the viewer opened from the Displays page, so you see exactly what the screen will show.
- Use fewer, larger widgets, for example a 2×2 grid of the numbers you read most at the helm.
- Keep touch targets large. A fingertip covers far more of a 4" screen than of a 7" one.
- Swiping between dashboards works the same on every size, so several simple dashboards beat one crowded one.
The viewer, on port 3050, uses the same protocol as the ESP32, and mouse drags are sent as touches. Click the screen to give it keyboard focus, and your typing and pastes go to KIP. Its display id box suggests the displays the Pi knows about.
Login. The viewer uses your Signal K login rather than a password of its own. Open it with Open on the Displays page: that asks Signal K for a one-time pass, which the viewer swaps for a cookie lasting 12 hours. Going to port 3050 any other way, or after the cookie runs out, shows a page saying to open the display from Signal K. The cookie covers every display id, so you can switch display in the viewer without going back.
This only protects the viewer if Signal K itself has security turned on. It can be switched off in the plugin settings, which leaves the viewer open to anyone who can reach port 3050.
The screens' own port, 3051, has no login: an ESP32 has no way to log in. Anything on the network that speaks the protocol can show and touch a display's KIP, so keep KIPCast on a network you trust.
| Setting | Default | Notes |
|---|---|---|
| Dashboard URL | http://localhost:3000/@mxtommy/kip/ |
Any web page works, not just KIP. |
| Default width / height | 800 × 480 | Only for a display id the Pi has never seen. ESP32 screens report their own size, which is remembered. See How a display's size is chosen. |
| JPEG quality | 70 | Frames are typically 30–50 KB at 800×480. |
| Max frames per second | 8 | Per display. Frames are only sent when the page changes, so an idle dashboard sends very few. |
| Display TCP port | 3051 | Port the ESP32 connects to. |
| Browser viewer port | 3050 | Port for the test viewer. |
| Input mode | touch |
Switch to mouse if a page ignores touch events. |
| Keep embedded web pages running | on | A page in KIP's Embed Webpage widget, such as AvNav, carries on where it was when you come back to its dashboard, instead of starting again. It keeps running in the background meanwhile. |
| Chromium path | auto | Checks /usr/bin/chromium, chromium-browser and google-chrome. If Chromium can't be found, the plugin still starts and says so in its status; displays can't connect until it's installed. |
| Browser viewer needs a Signal K login | on | See The browser viewer. |
A display's Chromium is shut down 5 minutes after its last screen disconnects, and starts again when a screen connects.
Memory: each connected display has its own Chromium, which used about 370 MB on a Pi 5 running KIP. Plan for that per screen alongside everything else the Pi runs. A display that has been off for 5 minutes uses nothing.
The web installer flashes the latest release from Chrome or Edge over USB, with nothing to install. The images are also attached to each GitHub release, to flash at offset 0x0 with esptool.
Each screen tells the plugin its firmware version when it connects, and each plugin release knows the firmware released with it. After updating the plugin from the App Store, Webapps → KIPCast shows each screen's firmware and flags any that are out of date, and the plugin's status line in Server → Plugin Config names them. To update a screen, plug it into a computer and run the web installer again. It keeps the screen's WiFi details and display id.
A screen shows its firmware version at the bottom of its status screens and on its setup page. Firmware from before 0.3.0 doesn't report a version, and is listed as "Before 0.3.0".
The firmware has no WiFi details built in. The first time it starts, the screen shows KIPCast setup and opens its own WiFi network, KIPCast- followed by the last four characters of its MAC address. Join it on a phone and the setup page opens; if it doesn't, browse to http://192.168.4.1. It asks for:
| Setting | Notes |
|---|---|
| WiFi network and password | The boat's WiFi. The page lists the networks the screen can see, strongest first: tap one to choose it, or type the name of a hidden network. |
| Display id | Letters, digits, - and _. Each id gets its own KIP on the Pi, and screens with the same id show the same thing. |
| Signal K server | Leave blank to find the Signal K server on the network (it announces itself over mDNS). Or enter the Pi's IP address or name. The screen remembers the address of the last server it connected to and tries that first, so it reconnects quickly and doesn't depend on Signal K's announcements, which can stop after the Pi's network drops. |
After saving, the screen restarts and connects. The settings are kept when the firmware is updated, unless you erase the flash.
To change the settings later, touch and hold the screen for two seconds while it shows Starting (for a couple of seconds after power-on) or any other status message. In setup, tapping the screen leaves without changes, and setup closes itself after 10 minutes.
Supported boards, each with its own PlatformIO environment:
| Board | Environment | Panel | Notes |
|---|---|---|---|
| Waveshare ESP32-S3-Touch-LCD-7 | kipcast-display (default) |
800×480 ST7262 | CH422G I/O expander |
| Waveshare ESP32-S3-Touch-LCD-4, Rev4.0 | kipcast-display-4in |
480×480 ST7701 | CH32V003 I/O expander. Earlier revisions use a TCA9554 and aren't supported yet. |
| Waveshare ESP32-P4-WIFI6-Touch-LCD-10.1 | kipcast-display-p4-10in |
800×1280 JD9365 MIPI-DSI, shown landscape at 1280×800 | ESP32-P4 (rev 3) with an ESP32-C6 for WiFi. Decodes JPEG in hardware. See the notes below. |
| Waveshare ESP32-S3-Touch-LCD-4.3, including the 4.3B | kipcast-display-43in |
800×480 ST7262 | Same hardware as the 7", so it uses the 7" config. Not yet tested on a real board. |
All have 16 MB flash, PSRAM and GT911 touch. The S3 boards have 8 MB PSRAM.
-
Install PlatformIO. The project uses the community pioarduino platform, because ESP32_Display_Panel v1.x needs Arduino core 3.x and the official platform is still on 2.x. PlatformIO downloads it automatically.
-
Optionally, build your details in so a freshly flashed screen skips the setup page:
cd kipcast-display/include cp secrets.example.h secrets.h # secrets.h is ignored by git
Edit
secrets.hand fill in your SSID, password andDISPLAY_ID(a different id for each screen), andKIPCAST_HOSTif the screen shouldn't find the Pi by itself. If you build for both boards,secrets.example.hshows how to give each its own id. Anything saved on the setup page takes priority;pio run -t eraseclears it. -
Build and flash over the ESP32's native USB. On the 7" and 4.3" that's the USB-C port marked USB; the 4" has only one USB-C port. The 10.1" uses its USB-C port marked UART, and must be built from PowerShell or cmd on Windows: its RISC-V toolchain won't install from Git Bash.
cd kipcast-display pio run -e kipcast-display -t upload # 7" pio run -e kipcast-display-4in -t upload # 4" pio run -e kipcast-display-43in -t upload # 4.3" pio run -e kipcast-display-p4-10in -t upload # 10.1" ESP32-P4 pio device monitor
Each build also writes
firmware.factory.bin, the single image that the web installer uses.Your own builds report their firmware version as
dev, which the plugin never flags as out of date. To build as a release version, setKIPCAST_VERSIONfirst, for exampleKIPCAST_VERSION=0.3.0 pio run. The release build sets it from the git tag, which must matchkipcast.firmwareinsignalk-kipcast/package.json.
Each board's panel config is in include/boards/<board>/esp_panel_board_custom_conf.h. The 7" config (also used by the 4.3") comes from Waveshare's 09_lvgl_v8_demo example, and the 4" config from Waveshare's esp32_s3_touch_lcd_4 board support package (both Apache-2.0). The build may warn that a config file is in an older format than the library expects. The newer settings it lacks don't apply to these boards, so the warning can be ignored.
7" and 4.3" quirks:
- If the USB port disappears (for example after running Waveshare's demo, which switches those pins to CAN), hold BOOT while plugging the cable in.
- One of the LCD data lines shares a pin with the ESP32's boot-mode pin, so after a reset or a flash the board can start in download mode with a blank screen. Unplug it and plug it back in.
- WiFi chip firmware. WiFi goes through an ESP32-C6 running Espressif's ESP-Hosted. Boards can arrive with C6 firmware older than ESP-Hosted 2.6, while KIPCast is built against 2.12.11. The log then shows
Response not received for [0x15e](Req_GetCoprocessorFwVersion)at every boot. To update the C6 over WiFi, set the screen up with KIPCast first, then runpio run -d kipcast-display/tools/c6-update -t upload -t monitor. It joins the WiFi KIPCast saved, downloads the matching C6 firmware from Espressif and installs it in about 30 seconds. Then install KIPCast again. - Rev 3 chips only. The build is for rev 3 ESP32-P4s (
esptool chip-idshows the revision). ESP32_Display_Panel picks a display clock that only older chips have, so the firmware sets the rev 3 one itself (boardConfig()inmain.cpp, withinclude/boards/p4-drivers). - COM port warning on Windows. If the board's CH343 serial port shows a warning in Device Manager (code 31), another program, such as a radio's virtual serial ports, may already use its COM number. Give the port an unused number under Properties → Port Settings → Advanced.
| Screen | Meaning |
|---|---|
| Green, KIPCast setup | The setup page is open |
| Blue, Starting or Joining WiFi | Joining WiFi |
| Amber, Looking for KIPCast | WiFi up, trying to reach KIPCast on the Pi |
| Dashboard | Connected |
If it can't join the WiFi or find the Pi, the screen says so and suggests what to check.
The serial monitor (115200 baud) prints each step, plus frame size and decode time every 50 frames.
This is the same for the ESP32 (TCP) and the browser viewer (WebSocket), so other displays can be added easily.
Display → Pi: text lines ending in \n:
| Line | Meaning |
|---|---|
H <id> <width> <height> [firmware] |
Hello. Must be the first line on TCP. The firmware version (from 0.3.0) lets the plugin flag out-of-date screens. The browser viewer passes the id as /ws?id= instead, and optionally a size as &w=&h=. |
A |
Ready for the next frame. The Pi sends nothing new until it gets this, so a slow display never builds up a backlog. |
T D <x> <y> / T M <x> <y> / T U <x> <y> |
Touch down / move / up, in display pixels. |
I <text> |
Type text into the focused field. <text> is URL-encoded (encodeURIComponent). |
K <key> [modifiers] |
Press a key: Enter, Tab, Backspace, Escape, Delete, Home, End, PageUp, PageDown, the arrow keys, or a letter/digit (for shortcuts such as Ctrl+A). Modifiers are a bitmask: Alt=1, Ctrl=2, Meta=4, Shift=8. |
P |
Keepalive. The ESP32 sends it after 10 s of silence. |
Pi → display:
- TCP: each frame is
KCF+ a 4-byte big-endian length + the JPEG bytes. - WebSocket: each frame is one binary message.
If a display doesn't send A within 5 s, the Pi sends the next frame anyway.
Display list (HTTP, JSON):
GET http://<pi>:3050/displays: read-only, used by the viewer. Needs the viewer's cookie.GET /plugins/signalk-kipcast/displayson Signal K: the same list plus the viewer port. Needs a Signal K login, as do the two below.POST /plugins/signalk-kipcast/displayswith{"id", "width", "height"}: add or resize a display.DELETE /plugins/signalk-kipcast/displays/<id>: forget a display.POST /plugins/signalk-kipcast/viewer-pass: a one-time pass for the viewer, as{"pass"}. Openhttp://<pi>:3050/?id=<id>&pass=<pass>within a minute to swap it for the cookie.passis null if the viewer login is switched off.
Run it outside Signal K:
cd signalk-kipcast
npm ci
KIPCAST_URL=http://openplotter.local:3000/@mxtommy/kip/ node standalone.jsEnvironment variables:
KIPCAST_URLKIPCAST_QUALITYKIPCAST_FPSKIPCAST_INPUT(touchormouse)KIPCAST_CHROMIUM
The ports are fixed at 3050 and 3051 in this mode, so stop the plugin first. The viewer has no login in this mode, as there's no Signal K to hand out passes.
MIT. The board configs in kipcast-display/include/boards/ are Apache-2.0 (Espressif Systems / Waveshare).
cd signalk-kipcast
npm testThey use Node's built-in test runner and a stand-in for Chromium, so they need neither a browser nor a network, and run on Windows or macOS as well as the Pi. The test that uses a real TCP socket is skipped when SIGNALK_REGISTRY_TEST is set, as it is when the Signal K App Store tests plugins.


