RuView Hardware Setup
ruvnet/RuView
Brings a RuView CSI sensing node online by building ESP32-S3 or ESP32-C6 firmware, flashing the board, provisioning WiFi and checking the serial output.
Build LVGL touchscreen applications that communicate with JanOS ESP32C5 firmware over UART.
$ npx skills add C5Lab/M5MonsterC5-CardputerADV --skill janos-uart-app -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install C5Lab/M5MonsterC5-CardputerADV janos-uart-app --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/C5Lab/M5MonsterC5-CardputerADV.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills/janos-uart-app .claude/skills/janos-uart-app && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "janos-uart-app" agent skill from https://github.com/C5Lab/M5MonsterC5-CardputerADV/tree/main/.github/skills/janos-uart-app into .claude/skills/janos-uart-app/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "janos-uart-app", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/C5Lab/M5MonsterC5-CardputerADV/tree/main/.github/skills/janos-uart-appType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add C5Lab/M5MonsterC5-CardputerADV --skill janos-uart-app -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install C5Lab/M5MonsterC5-CardputerADV janos-uart-app --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/C5Lab/M5MonsterC5-CardputerADV.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.github/skills/janos-uart-app .agents/skills/janos-uart-app && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "janos-uart-app" agent skill from https://github.com/C5Lab/M5MonsterC5-CardputerADV/tree/main/.github/skills/janos-uart-app into .agents/skills/janos-uart-app/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "janos-uart-app", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add C5Lab/M5MonsterC5-CardputerADV --skill janos-uart-app -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install C5Lab/M5MonsterC5-CardputerADV janos-uart-app --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/C5Lab/M5MonsterC5-CardputerADV.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.github/skills/janos-uart-app .cursor/skills/janos-uart-app && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "janos-uart-app" agent skill from https://github.com/C5Lab/M5MonsterC5-CardputerADV/tree/main/.github/skills/janos-uart-app into .cursor/skills/janos-uart-app/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "janos-uart-app", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/C5Lab/M5MonsterC5-CardputerADV.git --path .github/skills/janos-uart-app--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add C5Lab/M5MonsterC5-CardputerADV --skill janos-uart-app -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install C5Lab/M5MonsterC5-CardputerADV janos-uart-app --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/C5Lab/M5MonsterC5-CardputerADV.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.github/skills/janos-uart-app .gemini/skills/janos-uart-app && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "janos-uart-app" agent skill from https://github.com/C5Lab/M5MonsterC5-CardputerADV/tree/main/.github/skills/janos-uart-app into .gemini/skills/janos-uart-app/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "janos-uart-app", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install C5Lab/M5MonsterC5-CardputerADV janos-uart-appInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add C5Lab/M5MonsterC5-CardputerADV --skill janos-uart-app -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/C5Lab/M5MonsterC5-CardputerADV.git skills-src && mkdir -p .github/skills && cp -r skills-src/.github/skills/janos-uart-app .github/skills/janos-uart-app && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "janos-uart-app" agent skill from https://github.com/C5Lab/M5MonsterC5-CardputerADV/tree/main/.github/skills/janos-uart-app into .github/skills/janos-uart-app/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "janos-uart-app", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add C5Lab/M5MonsterC5-CardputerADV --skill janos-uart-app -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install C5Lab/M5MonsterC5-CardputerADV janos-uart-app --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/C5Lab/M5MonsterC5-CardputerADV.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.github/skills/janos-uart-app .opencode/skills/janos-uart-app && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "janos-uart-app" agent skill from https://github.com/C5Lab/M5MonsterC5-CardputerADV/tree/main/.github/skills/janos-uart-app into .opencode/skills/janos-uart-app/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "janos-uart-app", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
janos-uart-appBuild LVGL touchscreen applications that communicate with JanOS ESP32C5 firmware over UART.
Janos Uart App is an agent skill from C5Lab/M5MonsterC5-CardputerADV. Build LVGL touchscreen applications that communicate with JanOS ESP32C5 firmware over UART. Use when creating screens for Tab5, CoreS3, Cardputer, or any ESP-IDF device that sends UART commands like scannetworks, startdeauth, listhosts, showpass. Covers screen building, UART parsing, attack flows, and navigation patterns.
Its SKILL.md is about 4.7k tokens, which your agent loads only when the skill is triggered. The skill folder holds 1 other file (for example `commands-reference.md`).
It sits in Development. It works with ESP32. The licence is MIT.
8 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit ad750dd. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are c).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Janos Uart App loads about 4.7k tokens when it runs. Until then it costs about 86 tokens; SKILL.md has 674 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from C5Lab/M5MonsterC5-CardputerADV at commit ad750dd, republished under its MIT licence (© C5Lab). 674 words, ~4,668 tokens.
.claude/skills/janos-uart-app/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.Guide for building ESP-IDF + LVGL applications that control JanOS firmware on ESP32C5 via UART.
Reference implementation: /Users/janulrich/kod/dev/tabgit/M5MonsterC5-Tab5/main/main.c
For the complete command list, see commands-reference.md.
┌────────────────────────────────┐ UART (115200 8N1) ┌──────────────────────┐
│ UI Device (Tab5/CoreS3/etc) │ ──────────────────────────────> │ JanOS on ESP32C5 │
│ - LVGL touchscreen │ <────────────────────────────── │ - WiFi radio │
│ - Sends text commands │ Text responses │ - BLE radio │
│ - Parses text responses │ │ - SD card │
│ - FreeRTOS tasks for I/O │ │ - GPS (optional) │
└────────────────────────────────┘ └──────────────────────┘JanOS exposes a text CLI over UART. The UI app sends commands like scan_networks\r\n and parses the multi-line text responses to update the screen.
#define UART_NUM UART_NUM_1
#define UART_BAUD_RATE 115200
#define UART_BUF_SIZE 4096
static void uart_init(void) {
const uart_config_t uart_config = {
.baud_rate = UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
uart_driver_install(UART_NUM, UART_BUF_SIZE * 2, 0, 0, NULL, 0);
uart_param_config(UART_NUM, &uart_config);
uart_set_pin(UART_NUM, tx_pin, rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}Pin assignments (configurable, stored in NVS):
| Connector | TX | RX | Usage |
|---|---|---|---|
| Grove | 53 | 54 | UART1 (primary) |
| M5Bus | 37 | 38 | UART2 (Kraken secondary) |
For CoreS3: use the appropriate GPIO for your board. The Tab5 reference stores pin config in NVS and reads with get_uart1_pins().
Always append \r\n. Log every command sent.
static void uart_send_command(const char *cmd) {
uart_write_bytes(UART_NUM, cmd, strlen(cmd));
uart_write_bytes(UART_NUM, "\r\n", 2);
ESP_LOGI(TAG, "Sent: %s", cmd);
}All UART parsing follows the same pattern -- read bytes, accumulate into a line buffer character by character, parse on newline:
char rx_buffer[UART_BUF_SIZE];
char line_buffer[512];
int line_pos = 0;
while (!done && !timed_out) {
int len = uart_read_bytes(UART_NUM, rx_buffer, UART_BUF_SIZE - 1, pdMS_TO_TICKS(100));
if (len > 0) {
rx_buffer[len] = '\0';
for (int i = 0; i < len; i++) {
char c = rx_buffer[i];
if (c == '\n' || c == '\r') {
if (line_pos > 0) {
line_buffer[line_pos] = '\0';
// === PARSE THIS LINE ===
if (strstr(line_buffer, "COMPLETION_MARKER")) {
done = true;
break;
}
parse_data_line(line_buffer);
line_pos = 0;
}
} else if (line_pos < sizeof(line_buffer) - 1) {
line_buffer[line_pos++] = c;
}
}
}
}Every command response has a known end marker. Wait for it before proceeding:
| Command | Completion Marker |
|---|---|
scan_networks | "Scan results printed" |
wifi_connect | "SUCCESS" or "FAILED" or "TIMEOUT" |
start_nmap | "Scanned" + "open ports" (final summary line) |
list_hosts | "Discovered Hosts" (header line, data follows) |
list_sd | "HTML files found" (header, data follows), or timeout |
show_pass | Timeout (no explicit end marker) |
list_probes | Timeout (no explicit end marker) |
wpasec_upload | "Done:" |
start_pcap | "PCAP radio capture started" or "PCAP net capture started" (initial); on stop: "PCAP saved:" |
start_beacon_spam | "Beacon spam started. Use 'stop' to end." |
start_beacon_spam_ssids | "Beacon spam started. Use 'stop' to end." (same as start_beacon_spam) |
list_ssids | Timeout (no explicit end marker, list ends after last indexed line) |
add_ssid | "Added SSID:" |
remove_ssid | "SSID removed." |
version | "JanOS version: X.Y.Z" (single line, immediate) |
For commands without explicit end markers, use a timeout with empty-read detection (e.g., 3 consecutive empty reads of 500ms each).
Network scan CSV -- fields: index, SSID, (empty), BSSID, channel, security, RSSI, band:
// Line: "1","AX3","","C4:2B:44:12:29:20","112","WPA2","-59","5GHz"
static bool parse_network_line(const char *line, wifi_network_t *net) {
if (line[0] != '"') return false;
char temp[256];
strncpy(temp, line, sizeof(temp) - 1);
char *fields[8] = {NULL};
int field_idx = 0;
char *p = temp;
while (*p && field_idx < 8) {
if (*p == '"') {
p++;
fields[field_idx] = p;
while (*p && *p != '"') p++;
if (*p == '"') { *p = '\0'; p++; }
field_idx++;
if (*p == ',') p++;
} else { p++; }
}
if (field_idx < 8) return false;
net->index = atoi(fields[0]);
strncpy(net->ssid, fields[1], sizeof(net->ssid) - 1);
strncpy(net->bssid, fields[3], sizeof(net->bssid) - 1);
net->channel = atoi(fields[4]);
strncpy(net->security, fields[5], sizeof(net->security) - 1);
net->rssi = atoi(fields[6]);
strncpy(net->band, fields[7], sizeof(net->band) - 1);
return true;
}Host list -- parse " IP -> MAC" lines:
if (strstr(line, "Our IP:") != NULL) {
// Extract IP after "Our IP: " up to ","
} else if (strstr(line, "->") != NULL) {
char *p = line;
while (*p == ' ') p++;
// read IP until space, skip " -> ", read MAC
}Evil twin passwords -- "SSID", "password":
if (line[0] == '"') {
char *ssid_start = line + 1;
char *ssid_end = strchr(ssid_start, '"');
if (ssid_end && *(ssid_end+1) == ',' && *(ssid_end+3) == '"') {
*ssid_end = '\0';
char *pass_start = ssid_end + 4;
char *pass_end = strchr(pass_start, '"');
if (pass_end) { *pass_end = '\0'; /* ssid_start and pass_start are your values */ }
}
}HTML file list -- "N filename.html":
if (strstr(line_buffer, "HTML files found") != NULL) {
header_found = true;
} else if (header_found && line_pos > 2) {
int file_num;
char filename[64];
if (sscanf(line_buffer, "%d %63s", &file_num, filename) == 2) {
// store filename at index file_num
}
}Probe list -- "N SSID":
char *p = line;
while (*p == ' ') p++;
if (isdigit((unsigned char)*p)) {
int idx = 0;
while (isdigit((unsigned char)*p)) { idx = idx * 10 + (*p - '0'); p++; }
while (*p == ' ') p++;
if (*p != '\0') {
char ssid[33];
strncpy(ssid, p, sizeof(ssid) - 1);
// trim trailing whitespace
}
}Deauth detector -- "[DEAUTH] CH: N | AP: name (BSSID) | RSSI: N":
static bool parse_deauth_line(const char *line, deauth_entry_t *entry) {
if (!strstr(line, "[DEAUTH]")) return false;
const char *ch = strstr(line, "CH:");
entry->channel = atoi(ch + 3);
const char *ap = strstr(line, "AP:"); ap += 3; while (*ap == ' ') ap++;
const char *paren = strchr(ap, '(');
size_t ap_len = paren - ap;
while (ap_len > 0 && ap[ap_len-1] == ' ') ap_len--;
memcpy(entry->ap_name, ap, ap_len);
const char *paren_end = strchr(paren + 1, ')');
memcpy(entry->bssid, paren + 1, paren_end - paren - 1);
entry->rssi = atoi(strstr(line, "RSSI:") + 5);
return true;
}Handshake success:
if (strstr(line, "HANDSHAKE IS COMPLETE AND VALID")) { /* handshake validated */ }
if (strstr(line, "PCAP saved:")) { /* extract filename from path */ }
if (strstr(line, "handshake saved for SSID:")) { /* extract SSID after "SSID: " */ }Evil twin password capture:
if (strstr(line, "connected to SSID=")) { /* extract SSID between ' quotes */ }
if (strstr(line, "password=")) { /* extract password after = */ }
if (strstr(line, "Password verified!")) { /* attack succeeded */ }wifi_connect result (password is optional -- omit for open networks):
if (strstr(rx_buffer, "SUCCESS")) { connected = true; }
if (strstr(rx_buffer, "FAILED") || strstr(rx_buffer, "TIMEOUT")) { failed = true; }
// Extract DHCP IP from: "DHCP IP: 192.168.0.5, Netmask: 255.255.255.0, GW: 192.168.0.1"
const char *dhcp = strstr(rx_buffer, "DHCP IP:");
if (dhcp) { /* parse IP, Netmask, GW */ }start_nmap progress and results:
// Progress line: " Scanning 192.168.0.4 ports 21-143 [1/100] ..."
if (strstr(line, "Scanning") && strstr(line, "ports") && strchr(line, '[')) {
char ip[16]; int port_from, port_to, current, total;
sscanf(line, " Scanning %15s ports %d-%d [%d/%d]",
ip, &port_from, &port_to, ¤t, &total);
int pct = (current * 100) / total;
// update progress bar
}
// New host: "Host: 192.168.0.4 (00:C0:CA:B4:E6:3F)"
if (strncmp(trimmed, "Host:", 5) == 0) {
char ip[16], mac[18];
if (sscanf(trimmed, "Host: %15s (%17[^)])", ip, mac) == 2) {
// new host with MAC
} else if (sscanf(trimmed, "Host: %15s (MAC unknown)", ip) == 1) {
// new host without MAC
}
}
// Open port: " 135/tcp open MSRPC"
int port; char service[32];
if (sscanf(trimmed, "%d/tcp open %31s", &port, service) == 2 ||
sscanf(trimmed, "%d/tcp open %31s", &port, service) == 2) {
// found open port
}
// Completion: "Scanned 4 hosts, found 4 open ports"
if (strstr(line, "Scanned") && strstr(line, "open ports")) {
int hosts, ports;
sscanf(line, "Scanned %d hosts, found %d open ports", &hosts, &ports);
// scan complete
}show_*_page()
├── Create LVGL objects (container, labels, buttons, table)
├── Register event callbacks
├── Start background UART monitor task (if needed)
└── Set as current visible page
[User interacts / UART data arrives]
cleanup (on back/close):
├── Set monitoring_active = false
├── Send "stop" via UART
├── Wait for task to finish (furi_thread_join or vTaskDelete)
├── Delete LVGL objects (lv_obj_del)
├── NULL all pointers
└── Free allocated memorystatic lv_obj_t *create_tile(lv_obj_t *parent, const char *icon_text,
const char *label_text, lv_color_t bg_color,
lv_event_cb_t cb, const char *user_data) {
lv_obj_t *tile = lv_btn_create(parent);
lv_obj_set_size(tile, TILE_WIDTH, TILE_HEIGHT);
lv_obj_set_style_bg_color(tile, bg_color, LV_STATE_DEFAULT);
lv_obj_set_style_bg_color(tile, lv_color_lighten(bg_color, 50), LV_STATE_PRESSED);
lv_obj_set_style_radius(tile, 12, 0);
lv_obj_set_style_shadow_width(tile, 0, 0);
lv_obj_set_flex_flow(tile, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(tile, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_t *icon = lv_label_create(tile);
lv_label_set_text(icon, icon_text);
lv_obj_set_style_text_font(icon, &lv_font_montserrat_28, 0);
lv_obj_set_style_text_color(icon, lv_color_white(), 0);
lv_obj_t *label = lv_label_create(tile);
lv_label_set_text(label, label_text);
lv_obj_set_style_text_color(label, lv_color_white(), 0);
if (cb) lv_obj_add_event_cb(tile, cb, LV_EVENT_CLICKED, (void *)user_data);
return tile;
}Scale TILE_WIDTH and TILE_HEIGHT based on display resolution. Tab5 uses 230x140 for a 1280x720 display. For CoreS3 (320x240), use ~100x80 or similar.
static void main_tile_event_cb(lv_event_t *e) {
const char *name = (const char *)lv_event_get_user_data(e);
if (strcmp(name, "WiFi Scan & Attack") == 0) show_scan_page();
else if (strcmp(name, "Global WiFi Attacks") == 0) show_global_attacks_page();
else if (strcmp(name, "Bluetooth") == 0) show_bluetooth_menu_page();
// ...
}// Semi-transparent overlay covering the whole screen
overlay = lv_obj_create(lv_scr_act());
lv_obj_remove_style_all(overlay);
lv_obj_set_size(overlay, lv_pct(100), lv_pct(100));
lv_obj_set_style_bg_color(overlay, lv_color_hex(0x000000), 0);
lv_obj_set_style_bg_opa(overlay, LV_OPA_50, 0);
lv_obj_clear_flag(overlay, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_flag(overlay, LV_OBJ_FLAG_CLICKABLE); // block clicks to background
// Centered popup
popup = lv_obj_create(overlay);
lv_obj_set_size(popup, POPUP_W, POPUP_H);
lv_obj_center(popup);
// ... add content to popup ...Close popup: lv_obj_del(overlay) deletes both overlay and popup (child).
LVGL is NOT thread-safe. Any LVGL call from a background UART task must be wrapped:
if (bsp_display_lock(0)) {
lv_label_set_text(status_label, "Connected!");
bsp_display_unlock();
}If bsp_display_lock is not available, use a mutex or LVGL's built-in lv_lock()/lv_unlock() (LVGL 9.x).
heap_caps_malloc(size, MALLOC_CAP_SPIRAM)) for large data structures (network lists, probe arrays, host tables).uart_send("scan_networks")
→ wait for "Scan results printed"
→ parse CSV lines into network array
→ show network list with checkboxes
→ user selects networks and taps attack button
uart_send("select_networks 1 3 5") // 1-based indices
uart_send("start_deauth") // or start_evil_twin, start_handshake, etc.
→ start monitor task to parse UART output
→ update screen with attack status
// On back/stop:
uart_send("stop")scan_networks → select_networks → list_sd → user picks HTML
→ select_html <index> → start_evil_twin
→ monitor for: "Client connected", "Password received", "Password verified!"
→ stop// Check if password known via show_pass evil, or ask user
// For open networks, omit the password:
wifi_connect <SSID> // open network
wifi_connect <SSID> <password> // WPA/WPA2 network
→ wait for "SUCCESS" / "FAILED" / "TIMEOUT"
list_hosts
→ wait for "Discovered Hosts", parse IP->MAC lines
// User taps a host:
arp_ban <MAC> [IP]
→ show "ARP Poisoning Active"
// On back:
stopwifi_connect <SSID> [password]
→ wait for "SUCCESS" / "FAILED" / "TIMEOUT"
start_nmap [quick|medium|heavy] [IP]
→ Phase 1 (host discovery): parse "Total: N hosts discovered"
→ Phase 2 (port scanning):
for each "Host: <IP> (<MAC>)" line → add host to list
for each "Scanning <IP> ports X-Y [current/total]" → update progress bar
for each "<port>/tcp open <service>" → add open port to current host
"(no open ports)" → mark current host as no-ports
→ Completion: "Scanned N hosts, found M open ports"
// Can be stopped anytime with:
stop → "(scan stopped by user)"list_sd → user picks HTML → select_html <index>
start_portal <SSID> // or start_karma <probe_index>
→ monitor for client connections and form submissions
→ stopstart_wardrive // or start_wardrive_promisc
→ wait for "GPS fix obtained" (show "Acquiring GPS Fix..." until then)
→ parse CSV network lines as they arrive
→ show "Logged N networks to ..."
→ handle "GPS fix lost!" / "GPS fix recovered:"
→ stoplist_ssids → show indexed SSID list
→ user can add_ssid <name> to append
→ user can remove_ssid <index> to delete
start_beacon_spam_ssids
→ wait for "Beacon spam started. Use 'stop' to end."
→ stopOr with inline SSIDs:
start_beacon_spam "SSID1" "SSID2" "SSID3"
→ wait for "Beacon spam started. Use 'stop' to end."
→ stop// Radio mode (no prerequisites):
start_pcap radio
→ wait for "PCAP radio capture started -> ..."
→ stop → "PCAP saved: ... (N frames, M drops)"
// Net mode (requires WiFi connection):
wifi_connect <SSID> [password]
→ wait for "SUCCESS"
start_pcap net
→ wait for "PCAP net capture started -> ..."
→ stop → "PCAP saved: ... (N frames, M drops)"scan_bt → parse device list → show scrollable list
→ user taps device → scan_bt <MAC> → continuous RSSI updates
→ stopWhen porting from Tab5 (1280x720) to CoreS3 (320x240):
lv_font_montserrat_14 for body, lv_font_montserrat_18 for titles (instead of 20/28).lv_table or lv_list with small row heights.show_<feature>_page() functionbsp_display_lock()/unlock() from background tasksmonitoring = false, send "stop", delete LVGL objects, free memory© C5Lab, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file in .github/skills/janos-uart-app of C5Lab/M5MonsterC5-CardputerADV.
Open the folder on GitHubat commit ad750dd
Janos Uart App next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Janos Uart App this skillC5Lab/M5MonsterC5-CardputerADV | 232 | — | ~4.7k | Automated safety check: Pass | MIT | |
| RuView Hardware Setupruvnet/RuView | 97k | — | ~1.8k | Automated safety check: Notes | MIT | |
| Esp32 Firmware Engineeralxv2016/folloup-sticky | 116 | 1 repos | ~3.8k | Automated safety check: Pass | GPL-3.0 | |
| RuView mmWave Radar Setupruvnet/RuView | 97k | — | ~907 | Automated safety check: Notes | MIT | |
| Embedded DebugFastLED/FastLED | 7.5k | — | ~1.4k | Automated safety check: Pass | MIT | |
| Auto EmbeddedDunCanYounG-1/MICU-auto-embedded | 253 | — | ~1.6k | Automated safety check: Pass | CC-BY-NC-4.0 |
ruvnet/RuView
Brings a RuView CSI sensing node online by building ESP32-S3 or ESP32-C6 firmware, flashing the board, provisioning WiFi and checking the serial output.
alxv2016/folloup-sticky
ESP32 firmware engineering for ESP-IDF projects. An agent skill from alxv2016/folloup-sticky.
ruvnet/RuView
Sets up and runs 60 GHz and 24 GHz mmWave radar sensing on ESP32 boards in RuView, alone or fused with WiFi CSI.
FastLED/FastLED
Firmware crash analysis, stack trace decoder, and register dump interpreter for ESP32/ARM/AVR platforms.
DunCanYounG-1/MICU-auto-embedded
全平台嵌入式 AI 开发框架(对标 Trellis):把 RIPER-5 五阶段协议 + 四文件记忆 + 分层架构门禁 + Scout/Builder/Verifier 多 Agent + 24 个工具调用技能(build/flash/debug/serial/can/modbus/visa/static/memory/rtos/scons),做成『装进工程、项目级 hook…
BlueAndi/Pixelix
A skill your agent uses when writing, reviewing, or refactoring C/C++ firmware code in this repository (src/, lib/, test/) — creating or editing .h/.hpp/.cpp files, applying MISRA-oriented and…
Works with
Categories
Build LVGL touchscreen applications that communicate with JanOS ESP32C5 firmware over UART. Janos Uart App is an agent skill from C5Lab/M5MonsterC5-CardputerADV. Build LVGL touchscreen applications that communicate with JanOS ESP32C5 firmware over UART.
Janos Uart App fits situations like: creating screens for Tab5; any ESP-IDF device that sends UART commands like scannetworks.
Run `npx skills add C5Lab/M5MonsterC5-CardputerADV --skill janos-uart-app -a claude-code`. Or copy the skill folder (.github/skills/janos-uart-app in C5Lab/M5MonsterC5-CardputerADV) into .claude/skills/janos-uart-app in your project. Claude Code loads it when a task matches its description.
Run `npx skills add C5Lab/M5MonsterC5-CardputerADV --skill janos-uart-app -a codex`. Or copy the skill folder (.github/skills/janos-uart-app in C5Lab/M5MonsterC5-CardputerADV) into .agents/skills/janos-uart-app in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add C5Lab/M5MonsterC5-CardputerADV --skill janos-uart-app -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/janos-uart-app, .gemini/skills/janos-uart-app, .github/skills/janos-uart-app and .opencode/skills/janos-uart-app in your project.
SKILL.md names no scripts, command-line tools or credentials: Janos Uart App is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Janos Uart App is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.7k tokens (SKILL.md is roughly 19k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Janos Uart App: RuView Hardware Setup (ruvnet/RuView, 97k stars), Esp32 Firmware Engineer (alxv2016/folloup-sticky, 116 stars), RuView mmWave Radar Setup (ruvnet/RuView, 97k stars) and Embedded Debug (FastLED/FastLED, 7.5k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
C5Lab (a GitHub organization) maintains it in C5Lab/M5MonsterC5-CardputerADV, which has 232 GitHub stars. The repository was last updated on September 28, 2026.
Source: C5Lab/M5MonsterC5-CardputerADV on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.