cd /news/developer-tools/test-a-custom-prompt-against-an-alre… · home topics developer-tools article
[ARTICLE · art-83024] src=gist.github.com ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Test a custom prompt against an already-trained, already-flashed ESP32-S3 model

A developer released a shell script that lets users test custom prompts against an already-trained ESP32-S3 model without retraining or rewriting the 15MB model partition. The script tokenizes the prompt, patches the firmware sketch, and recompiles and flashes only the app, leaving the model untouched.

read3 min views7 publishedJul 31, 2026

| #!/usr/bin/env bash | | | # esp32-ai: test a custom prompt against an already-trained, already-flashed model. | | | # Does NOT retrain and does NOT rewrite the 15MB model partition. | | | # | | | # Usage: | | | # ./run_custom_prompt.sh "Once there was a robot" | | | set -euo pipefail | | | if [ "$#" -lt 1 ]; then | | | echo "Usage: $0 "Your custom prompt"" | | | exit 1 | | | fi | | | PROMPT="$1" | |

| REPO_DIR="esp32-ai" | |
| if [ -f "$REPO_DIR/pyproject.toml" ]; then | |

| cd "$REPO_DIR" | | | elif [ ! -f "pyproject.toml" ]; then | | | echo "Can't find the esp32-ai repo. Run this from the same folder as build_and_flash.sh, after it has cloned esp32-ai." | | | exit 1 | | | fi | | | if [ ! -f "data/bpe32768.json" ]; then | | | echo "data/bpe32768.json not found. Run build_and_flash.sh at least once first (it trains the tokenizer)." | | | exit 1 | | | fi | |

| # ---- Detect port ---- | |
| CANDIDATE_PORTS=(/dev/cu.usbmodem*) | |
| if [ ! -e "${CANDIDATE_PORTS[0]}" ]; then | |

| echo "No /dev/cu.usbmodem* device found. Is the board plugged in?" | | | exit 1 | |

| elif [ "${#CANDIDATE_PORTS[@]}" -eq 1 ]; then | |
| ESP32_PORT="${CANDIDATE_PORTS[0]}" | |

| else | |

| select choice in "${CANDIDATE_PORTS[@]}"; do | |
| [ -n "$choice" ] && ESP32_PORT="$choice" && break | |

| done | | | fi | |

| echo ">> Using port: $ESP32_PORT" | |
| # ---- Tokenize the custom prompt with the same tokenizer gen_assets.py uses ---- | |
| echo ">> Tokenizing prompt: \"$PROMPT\"" | |

| TOKENIZE_SCRIPT="$(mktemp /tmp/tokenize_prompt_XXXX.py)" | | | cat > "$TOKENIZE_SCRIPT" << 'PYEOF' | | | import sys | | | from tokenizers import Tokenizer | |

| tok = Tokenizer.from_file("data/bpe32768.json") | |
| prompt = sys.argv[1] | |
| ids = tok.encode(prompt).ids | |
| print(f'prompt "{prompt}" -> {len(ids)} ids: {ids}') | |
| print(f"round-trip: {tok.decode(ids)!r}") | |
| print("static const int PROMPT_IDS[] = {" + ", ".join(map(str, ids)) + "};") | |

| PYEOF | | | GEN_OUTPUT="$(uv run python "$TOKENIZE_SCRIPT" "$PROMPT")" | | | rm -f "$TOKENIZE_SCRIPT" | | | echo "$GEN_OUTPUT" | |

| PROMPT_IDS_LINE="$(echo "$GEN_OUTPUT" | grep -o 'static const int PROMPT_IDS\[\] = {[^}]*};')" | |
| if [ -z "$PROMPT_IDS_LINE" ]; then | |

| echo "!! Couldn't parse a PROMPT_IDS line out of the tokenizer output. Aborting." | | | exit 1 | | | fi | |

| echo ">> New prompt line: $PROMPT_IDS_LINE" | |
| # ---- Paste it into the sketch ---- | |

| SKETCH_FILE="firmware/esp32_llm/esp32_llm.ino" | |

| sed -i.bak -E "s/static const int PROMPT_IDS\[\] = \{[^}]*\};/${PROMPT_IDS_LINE//\//\\/}/" "$SKETCH_FILE" | |
| # ---- Rebuild and reflash just the app (model partition untouched) ---- | |
| FQBN='esp32:esp32:esp32s3:UploadSpeed=921600,USBMode=hwcdc,CDCOnBoot=default,UploadMode=default,CPUFreq=240,FlashMode=qio,FlashSize=16M,PartitionScheme=custom,PSRAM=opi,DebugLevel=info' | |

| echo ">> Recompiling firmware with new prompt..." | | | arduino-cli compile \ | |

| --fqbn "$FQBN" \ | |
| --build-property compiler.optimization_flags=-O3 \ | |
| --build-path /tmp/esp32-llm-uart \ | |

| firmware/esp32_llm | | | echo ">> Flashing app (model partition is untouched)..." | | | arduino-cli upload -p "$ESP32_PORT" \ | |

| --fqbn "$FQBN" \ | |
| --input-dir /tmp/esp32-llm-uart firmware/esp32_llm | |

| echo ">> Opening serial monitor. Press the board's RESET button now." | | | arduino-cli monitor -p "$ESP32_PORT" --config baudrate=115200 |

── more in #developer-tools 4 stories · sorted by recency
── more on @esp32-s3 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/test-a-custom-prompt…] indexed:0 read:3min 2026-07-31 ·