Files
MeshCore-Solo/solo_features/external_keyboard/index.html
T
2026-08-12 21:51:42 +00:00

1963 lines
40 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Documentation for the open source MeshCore firmware">
<link rel="canonical" href="https://meshcore-dev.github.io/meshcore/solo_features/external_keyboard/">
<link rel="prev" href="../../development/roadmap/">
<link rel="next" href="../clock_screen/clock_screen/">
<link rel="icon" href="../../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.7">
<title>External keyboard - MeshCore Docs</title>
<link rel="stylesheet" href="../../assets/stylesheets/main.ec1eaa64.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
<link rel="stylesheet" href="../../_stylesheets/extra.css">
<script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#external-keyboard-joystick" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header md-header--shadow" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../.." title="MeshCore Docs" class="md-header__button md-logo" aria-label="MeshCore Docs" data-md-component="logo">
<img src="../../_assets/meshcore.svg" alt="logo">
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
MeshCore Docs
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
External keyboard
</span>
</div>
</div>
</div>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://github.com/meshcore-dev/meshcore/" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
meshcore-dev/meshcore
</div>
</a>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../.." title="MeshCore Docs" class="md-nav__button md-logo" aria-label="MeshCore Docs" data-md-component="logo">
<img src="../../_assets/meshcore.svg" alt="logo">
</a>
MeshCore Docs
</label>
<div class="md-nav__source">
<a href="https://github.com/meshcore-dev/meshcore/" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
meshcore-dev/meshcore
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../.." class="md-nav__link">
<span class="md-ellipsis">
Introduction
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../cli_commands/" class="md-nav__link">
<span class="md-ellipsis">
CLI Commands
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../companion_protocol/" class="md-nav__link">
<span class="md-ellipsis">
Companion Protocol
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../docs/" class="md-nav__link">
<span class="md-ellipsis">
Local Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../faq/" class="md-nav__link">
<span class="md-ellipsis">
Frequently Asked Questions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../kiss_modem_protocol/" class="md-nav__link">
<span class="md-ellipsis">
MeshCore KISS Modem Protocol
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../nrf52_power_management/" class="md-nav__link">
<span class="md-ellipsis">
nRF52 Power Management
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../number_allocations/" class="md-nav__link">
<span class="md-ellipsis">
Number Allocations
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../packet_format/" class="md-nav__link">
<span class="md-ellipsis">
Packet Format
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../payloads/" class="md-nav__link">
<span class="md-ellipsis">
Payload Format
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../qr_codes/" class="md-nav__link">
<span class="md-ellipsis">
QR Codes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../stats_binary_frames/" class="md-nav__link">
<span class="md-ellipsis">
Stats Binary Frame Structures
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../terminal_chat_cli/" class="md-nav__link">
<span class="md-ellipsis">
Terminal Chat CLI
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_14" >
<label class="md-nav__link" for="__nav_14" id="__nav_14_label" tabindex="0">
<span class="md-ellipsis">
Design
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_14_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_14">
<span class="md-nav__icon md-icon"></span>
Design
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../design/nearby_redesign/" class="md-nav__link">
<span class="md-ellipsis">
Nearby Nodes — analiza i propozycja uporządkowania
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../design/solo_ui_framework/" class="md-nav__link">
<span class="md-ellipsis">
Solo UI framework — a guide for adding features
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../design/trail_redesign/" class="md-nav__link">
<span class="md-ellipsis">
Trail (Tools Trail) — analiza i propozycja uporządkowania
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_15" >
<label class="md-nav__link" for="__nav_15" id="__nav_15_label" tabindex="0">
<span class="md-ellipsis">
Development
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_15_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_15">
<span class="md-nav__icon md-icon"></span>
Development
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../development/roadmap/" class="md-nav__link">
<span class="md-ellipsis">
Feature roadmap
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_16" checked>
<label class="md-nav__link" for="__nav_16" id="__nav_16_label" tabindex="0">
<span class="md-ellipsis">
Solo features
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_16_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_16">
<span class="md-nav__icon md-icon"></span>
Solo features
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
External keyboard
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
External keyboard
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#external-keyboard-joystick" class="md-nav__link">
<span class="md-ellipsis">
External Keyboard &amp; Joystick
</span>
</a>
<nav class="md-nav" aria-label="External Keyboard &amp; Joystick">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#support-by-device" class="md-nav__link">
<span class="md-ellipsis">
Support by device
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#cardkb" class="md-nav__link">
<span class="md-ellipsis">
CardKB
</span>
</a>
<nav class="md-nav" aria-label="CardKB">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#typing" class="md-nav__link">
<span class="md-ellipsis">
Typing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#ext-kb-full-vs-compact" class="md-nav__link">
<span class="md-ellipsis">
Ext. KB — Full vs Compact
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#wired-joystick" class="md-nav__link">
<span class="md-ellipsis">
Wired joystick
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#wiring-heltec-v3-v4" class="md-nav__link">
<span class="md-ellipsis">
Wiring (Heltec V3 / V4)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#built-in-keyboards-cardputer-adv-t-echo-lite-keyshield" class="md-nav__link">
<span class="md-ellipsis">
Built-in keyboards (Cardputer ADV, T-Echo Lite + KeyShield)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_16_2" >
<label class="md-nav__link" for="__nav_16_2" id="__nav_16_2_label" tabindex="0">
<span class="md-ellipsis">
Clock screen
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_16_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_16_2">
<span class="md-nav__icon md-icon"></span>
Clock screen
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../clock_screen/clock_screen/" class="md-nav__link">
<span class="md-ellipsis">
Clock screen
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_16_3" >
<label class="md-nav__link" for="__nav_16_3" id="__nav_16_3_label" tabindex="0">
<span class="md-ellipsis">
Favourites dial
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_16_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_16_3">
<span class="md-nav__icon md-icon"></span>
Favourites dial
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../favourites_dial/favourites_dial/" class="md-nav__link">
<span class="md-ellipsis">
Favourites dial
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_16_4" >
<label class="md-nav__link" for="__nav_16_4" id="__nav_16_4_label" tabindex="0">
<span class="md-ellipsis">
Message screen
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_16_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_16_4">
<span class="md-nav__icon md-icon"></span>
Message screen
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../message_screen/message_screen/" class="md-nav__link">
<span class="md-ellipsis">
Message screen
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_16_5" >
<label class="md-nav__link" for="__nav_16_5" id="__nav_16_5_label" tabindex="0">
<span class="md-ellipsis">
Screen lock
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_16_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_16_5">
<span class="md-nav__icon md-icon"></span>
Screen lock
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../screen_lock/screen_lock/" class="md-nav__link">
<span class="md-ellipsis">
Screen lock
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_16_6" >
<label class="md-nav__link" for="__nav_16_6" id="__nav_16_6_label" tabindex="0">
<span class="md-ellipsis">
Settings screen
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_16_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_16_6">
<span class="md-nav__icon md-icon"></span>
Settings screen
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../settings_screen/settings_screen/" class="md-nav__link">
<span class="md-ellipsis">
Settings screen
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_16_7" >
<label class="md-nav__link" for="__nav_16_7" id="__nav_16_7_label" tabindex="0">
<span class="md-ellipsis">
Tools screen
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_16_7_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_16_7">
<span class="md-nav__icon md-icon"></span>
Tools screen
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../tools_screen/tools_screen/" class="md-nav__link">
<span class="md-ellipsis">
Tools screen
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#external-keyboard-joystick" class="md-nav__link">
<span class="md-ellipsis">
External Keyboard &amp; Joystick
</span>
</a>
<nav class="md-nav" aria-label="External Keyboard &amp; Joystick">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#support-by-device" class="md-nav__link">
<span class="md-ellipsis">
Support by device
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#cardkb" class="md-nav__link">
<span class="md-ellipsis">
CardKB
</span>
</a>
<nav class="md-nav" aria-label="CardKB">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#typing" class="md-nav__link">
<span class="md-ellipsis">
Typing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#ext-kb-full-vs-compact" class="md-nav__link">
<span class="md-ellipsis">
Ext. KB — Full vs Compact
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#wired-joystick" class="md-nav__link">
<span class="md-ellipsis">
Wired joystick
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#wiring-heltec-v3-v4" class="md-nav__link">
<span class="md-ellipsis">
Wiring (Heltec V3 / V4)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#built-in-keyboards-cardputer-adv-t-echo-lite-keyshield" class="md-nav__link">
<span class="md-ellipsis">
Built-in keyboards (Cardputer ADV, T-Echo Lite + KeyShield)
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<a href="https://github.com/meshcore-dev/meshcore/edit/main/docs/solo_features/external_keyboard.md" title="Edit this page" class="md-content__button md-icon" rel="edit">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 20H6V4h7v5h5v3.1l2-2V8l-6-6H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4zm10.2-7c.1 0 .3.1.4.2l1.3 1.3c.2.2.2.6 0 .8l-1 1-2.1-2.1 1-1c.1-.1.2-.2.4-.2m0 3.9L14.1 23H12v-2.1l6.1-6.1z"/></svg>
</a>
<h1>External keyboard</h1>
<h2 id="external-keyboard-joystick">External Keyboard &amp; Joystick</h2>
<p><a href="../../README.md">Go back</a></p>
<p>Two optional, auto-detected hardware add-ons — a build with them enabled runs
the same with nothing plugged in. Two of the newer boards also ship with
their own <strong>built-in</strong> keypad instead —
see <a href="#built-in-keyboards-cardputer-adv-t-echo-lite--keyshield">Built-in keyboards</a>.</p>
<ul>
<li><strong>CardKB</strong> — an M5Stack I2C QWERTY keyboard (address <code>0x5F</code>), for typing
messages, names and labels without walking the on-screen letter grid.</li>
<li><strong>Wired joystick</strong> — four direction contacts plus a Back button, replacing the
single-button navigation on boards that have no joystick of their own.</li>
</ul>
<h3 id="support-by-device">Support by device</h3>
<table>
<thead>
<tr>
<th>Device</th>
<th style="text-align: center;">CardKB</th>
<th style="text-align: center;">Wired joystick</th>
</tr>
</thead>
<tbody>
<tr>
<td>Seeed Wio Tracker L1 (OLED)</td>
<td style="text-align: center;">✅ Grove connector</td>
<td style="text-align: center;">onboard</td>
</tr>
<tr>
<td>Seeed Wio Tracker L1 (E-ink)</td>
<td style="text-align: center;">✅ Grove connector</td>
<td style="text-align: center;">onboard</td>
</tr>
<tr>
<td>GAT562 30S Mesh Kit</td>
<td style="text-align: center;"></td>
<td style="text-align: center;">onboard</td>
</tr>
<tr>
<td>Heltec V3 <em>(experimental)</em></td>
<td style="text-align: center;">✅ solder to free GPIOs</td>
<td style="text-align: center;">✅ solder to free GPIOs</td>
</tr>
<tr>
<td>Heltec V4 <em>(experimental)</em></td>
<td style="text-align: center;">✅ solder to free GPIOs</td>
<td style="text-align: center;">✅ solder to free GPIOs</td>
</tr>
<tr>
<td>M5Stack Cardputer ADV <em>(experimental)</em></td>
<td style="text-align: center;"></td>
<td style="text-align: center;">built-in keyboard instead, see below</td>
</tr>
<tr>
<td>LilyGO T-Echo Lite + KeyShield <em>(experimental)</em></td>
<td style="text-align: center;"></td>
<td style="text-align: center;">built-in keypad instead, see below</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="cardkb">CardKB</h2>
<p>Plug it into the second I2C bus (the Grove connector on the Wio Tracker L1;
see <a href="#wiring-heltec-v3--v4">Wiring</a> for the Heltec boards). The firmware probes
for it once at boot — nothing to enable in Settings.</p>
<p>The same bus is scanned for environment sensors, so a CardKB and a sensor can
share it.</p>
<h3 id="typing">Typing</h3>
<p>Printable characters insert straight at the cursor, bypassing the on-screen grid
completely. The alphabet and T9/ABC settings do not apply — a real keyboard sends
the right character already, so typing is always plain Latin ASCII regardless of
what Settings Keyboard is set to.</p>
<table>
<thead>
<tr>
<th>Key</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td>letters / digits / symbols / space</td>
<td>insert at the cursor</td>
</tr>
<tr>
<td>Backspace</td>
<td>delete the character before the cursor</td>
</tr>
<tr>
<td>Esc</td>
<td>cancel / back</td>
</tr>
<tr>
<td>Arrows</td>
<td>same as the joystick</td>
</tr>
<tr>
<td>Enter</td>
<td>same as the centre button</td>
</tr>
<tr>
<td><strong>Fn+Enter</strong></td>
<td><strong>submit the field</strong> — no need to find the DONE cell</td>
</tr>
<tr>
<td><strong>Fn+letter</strong></td>
<td>open the accent popup for that letter (e.g. Fn+A → á à ä ã…)</td>
</tr>
<tr>
<td><strong>Tab</strong></td>
<td>the Hold-Enter equivalent, everywhere — context menus, shift-lock, clear-all</td>
</tr>
<tr>
<td><strong>Fn+Esc</strong></td>
<td>lock / unlock the device (single press, works in both directions)</td>
</tr>
</tbody>
</table>
<p>Fn+Esc rather than the adjacent Fn+Backspace on purpose: Fn and Backspace sit
next to each other on CardKB's layout and would be far too easy to hit by
accident. See <a href="../screen_lock/screen_lock/">Screen Lock</a> for the physical
button equivalent.</p>
<h3 id="ext-kb-full-vs-compact">Ext. KB — Full vs Compact</h3>
<p><strong>Settings Keyboard Ext. KB</strong> picks how the on-screen keyboard behaves while
a CardKB is doing the typing.</p>
<table>
<thead>
<tr>
<th>Mode</th>
<th>Behaviour</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Full</strong> (default)</td>
<td>The letter grid stays on screen. Arrows and Enter drive the grid exactly as physical buttons do, so CardKB and the joystick can be used interchangeably.</td>
</tr>
<tr>
<td><strong>Compact</strong></td>
<td>The grid, the special-row icons and the status line are all hidden — only the text being typed and two shortcut hints remain. Arrows move the <strong>text cursor</strong> directly, and plain Enter submits the field (same as Fn+Enter).</td>
</tr>
</tbody>
</table>
<p><strong>Compact is designed to need no joystick at all</strong> — the right choice when
CardKB is the only input device, e.g. a Heltec V3/V4 with no joystick
soldered on.</p>
<p>Cursor mode and the accent / placeholder popups draw their own visible feedback,
so they behave identically in both modes.</p>
<hr />
<h2 id="wired-joystick">Wired joystick</h2>
<p>Four direction contacts plus a fifth "press" contact. Each contact simply
shorts its pin to ground — the firmware enables the internal pull-ups, so no
external resistors are needed.</p>
<ul>
<li>The stick's own press contact drives the centre / Enter press — your thumb
is already on the stick, so pressing it in is the natural "confirm" action.</li>
<li>The board's existing user button (PRG on the Heltec boards) becomes Back
instead. Back is <strong>not</strong> optional: the UI uses it unconditionally once the
joystick is enabled. Triple-clicking Back toggles the buzzer.</li>
<li><strong>Settings Display Joystick rotation</strong> rotates the direction mapping at
runtime (03), for a stick mounted sideways in a custom enclosure. It is
independent of display rotation.</li>
</ul>
<hr />
<h2 id="wiring-heltec-v3-v4">Wiring (Heltec V3 / V4)</h2>
<p>Neither board ships with a joystick or a keyboard header, so both are soldered to
free GPIOs. V3 and V4 are pin-compatible per Heltec's documentation and the solo
builds use the same assignment for both — <strong>confirmed working on real V4
hardware</strong>; still worth checking against your own V3 module before soldering.</p>
<table>
<thead>
<tr>
<th>Function</th>
<th>GPIO</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>CardKB SDA</td>
<td>3</td>
<td>second I2C bus (<code>Wire1</code>) — <em>not</em> the OLED's 17/18</td>
</tr>
<tr>
<td>CardKB SCL</td>
<td>4</td>
<td></td>
</tr>
<tr>
<td>Joystick UP</td>
<td>23</td>
<td></td>
</tr>
<tr>
<td>Joystick DOWN</td>
<td>6</td>
<td></td>
</tr>
<tr>
<td>Joystick LEFT</td>
<td>47</td>
<td></td>
</tr>
<tr>
<td>Joystick RIGHT</td>
<td>48</td>
<td></td>
</tr>
<tr>
<td>Joystick press — Enter</td>
<td>33</td>
<td>the stick's own fifth contact; required when the joystick is enabled</td>
</tr>
<tr>
<td>Back</td>
<td>0</td>
<td>the onboard PRG button — nothing to wire</td>
</tr>
</tbody>
</table>
<p>Everything above lives in the <code>[env:Heltec_v3_companion_solo_dual]</code> /
<code>[env:heltec_v4_companion_solo_dual]</code> blocks in
<a href="../../variants/heltec_v3/platformio.ini"><code>variants/heltec_v3/platformio.ini</code></a>
and <a href="../../variants/heltec_v4/platformio.ini"><code>variants/heltec_v4/platformio.ini</code></a>,
with comments explaining which pins are safe to reuse. To build a CardKB-only
device, comment out the joystick block and set Ext. KB to Compact.</p>
<hr />
<h2 id="built-in-keyboards-cardputer-adv-t-echo-lite-keyshield">Built-in keyboards (Cardputer ADV, T-Echo Lite + KeyShield)</h2>
<p><em>Experimental</em> — newly-added board support, not the CardKB/joystick add-ons
above. Both keypads are TCA8418-based and share one polling path, entirely
independent of the CardKB code — a board can have either, or neither.</p>
<ul>
<li><strong>M5Stack Cardputer ADV</strong> — built-in QWERTY, no CardKB or joystick needed.</li>
<li><strong>LilyGO T-Echo Lite + KeyShield</strong> — the KeyShield add-on gives the T-Echo
Lite a T9 keypad; without it the board has no usable input for the solo UI.</li>
</ul>
<p>Neither keypad follows CardKB's exact Fn-shortcut table (Fn+Enter,
Fn+letter accent popups, Tab, Fn+Esc lock) — see each board's own
<code>platformio.ini</code> / keyboard driver under <code>variants/</code> for its current keymap.</p>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"annotate": null, "base": "../..", "features": ["content.action.edit", "content.code.copy", "search.highlight", "search.suggest"], "search": "../../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
<script src="../../assets/javascripts/bundle.d7400e89.min.js"></script>
</body>
</html>