DEV Community

Charlie
Charlie

Posted on • Originally published at charlieseay.com

From Paperweight to Miner with Bench

I bought a oneShot miner — a tiny ESP32-based Bitcoin mining device with a 2.8-inch display. It was supposed to plug in via USB, connect to WiFi, and start hashing. Instead, the display hung at 70% during boot, the web interface was nowhere to be found, and the documentation was thin enough to see through. It became a paperweight.

It sat on my desk for months. Occasionally I'd plug it in, stare at the frozen boot screen, unplug it, and go back to whatever I was actually doing.

What changed wasn't patience or a troubleshooting breakthrough. It was building a tool for a completely different reason that happened to solve this problem too.

The tool that changed the equation

Bench is an MCP server I built to give AI coding assistants direct visibility into USB hardware. MCP — Model Context Protocol — is the standard that lets tools like Claude Code call external capabilities. Bench exposes what's physically plugged into your machine: device names, vendor IDs, serial ports, storage volumes. The kind of information you'd normally get by running system_profiler or lsusb and squinting at the output.

I didn't build Bench for the miner. I built it because every time I plugged in an Arduino or an ESP32, my AI assistant had no idea it existed. I'd have to manually find the serial port, figure out the device type, and paste that information into the conversation. Bench eliminates that — the AI can query the hardware directly.

Once the tool existed, I had a thought: what about that dead miner sitting three inches from my keyboard?

Seeing the device for the first time

I asked Claude Code to list USB devices using Bench. For the first time, the miner had an identity:

Property Value
Chip CH340 USB-Serial Adapter
Vendor QinHeng Electronics (0x1A86)
Serial Port /dev/cu.usbserial-2120

A CH340 — a common USB-to-serial bridge chip, the kind you find on cheap ESP32 development boards. Now I had a serial port. If there's a serial connection, there's likely more to this device than a frozen LCD.

Finding the web server

The miner had connected to my WiFi before hanging. A network scan turned up a device at 192.168.0.132 serving HTTP on port 80. Hitting it in a browser revealed a full mining monitor dashboard — hashrate, temperature, pool configuration, wallet addresses.

The device wasn't dead. It was mining. The display was broken, but the mining software was running fine underneath. The firmware — NMMiner Monitor by NMTech — exposed a web interface with API endpoints:

Endpoint What it does
/swarm Live device status — hashrate, temperature, memory, uptime
/config Full device configuration — pools, wallets, WiFi, display settings
/broadcast-config Push new configuration to the device

What the manufacturer doesn't tell you

I pulled the config. Here's what came back (sanitized):

{
  "ssid": "<your-wifi-network>",
  "wifiPass": "<your-wifi-password-in-plaintext>",
  "PrimaryAddress": "18dK8EfyepKuS74fs27iuDJWoGUT4rPto1",
  "SecondaryAddress": "18dK8EfyepKuS74fs27iuDJWoGUT4rPto1"
}
Enter fullscreen mode Exit fullscreen mode

Your WiFi password is returned in plaintext from an unauthenticated HTTP endpoint. Anyone on your local network can read it by visiting a URL. No login. No token. No authentication of any kind.

That wallet address — 18dK8EfyepKuS74fs27iuDJWoGUT4rPto1 — isn't mine. It's the manufacturer's. Out of the box, this device mines Bitcoin for NMTech, not for you. Both the primary and secondary wallet addresses ship configured to the same manufacturer wallet. Unless you find the web interface (which requires knowing the device's IP, undocumented) and manually change the configuration, every hash your device computes enriches someone else's wallet.

The device works. It connects to a mining pool. It hashes. It just doesn't hash for you.

The full security picture

Since I was already in the weeds, I documented everything:

Finding Severity
WiFi credentials exposed in plaintext via /config Critical
Manufacturer's wallet hardcoded as default on both pools Critical
No authentication on any endpoint — dashboard, config, or broadcast High
No HTTPS — all data transmitted in cleartext High
Pool passwords visible in configuration Medium
System metrics (temperature, memory, RSSI, uptime) exposed to network Info

None of this is encrypted. None of it is authenticated. Anyone on your LAN can read your WiFi password, change the mining wallet, push new configuration to the device, or monitor what it's doing. The /broadcast-config endpoint accepts arbitrary configuration changes from any device on the network.

Fixing the firmware

The display issue turned out to be a known bug. The miner shipped with firmware v1.8.10, and the NMMiner GitHub issues were full of reports: boot hangs at 70-80%, display freezes, pool connectivity failures. Fixes landed across v1.8.24 through v1.8.27.

NMMiner provides a browser-based flash tool at flash.nmminer.com, but it requires Chrome's Web Serial API. I used esptool from the command line instead:

# Install esptool
brew install esptool

# Download the v1.8.27 firmware binaries
curl -sO "https://flash.nmminer.com/firmware/v1.8.27/esp32-2432s028r-ili9341/bootloader.bin"
curl -sO "https://flash.nmminer.com/firmware/v1.8.27/esp32-2432s028r-ili9341/partitions.bin"
curl -sO "https://flash.nmminer.com/firmware/v1.8.27/esp32-2432s028r-ili9341/boot_app0.bin"
curl -sO "https://flash.nmminer.com/firmware/v1.8.27/esp32-2432s028r-ili9341/firmware.bin"

# Flash the device
esptool --chip esp32 --port /dev/cu.usbserial-2120 --baud 460800 \
  write_flash \
  0x1000 bootloader.bin \
  0x8000 partitions.bin \
  0xe000 boot_app0.bin \
  0x10000 firmware.bin
Enter fullscreen mode Exit fullscreen mode

Notice the firmware path includes ili9341. That's the LCD driver — and I didn't get it right the first time.

NMMiner's documentation says their branded boards use the ST7789 display driver. I flashed the ST7789 variant first. Mining worked — 1.03 MH/s, shares accepted, pool connected — but the display was solid white. A glowing white rectangle.

The underlying board is a CYD — "Cheap Yellow Display" — an ESP32-2432S028R. These boards ship with either ST7789 or ILI9341 LCD controllers, and there's no reliable way to tell from the outside. NMMiner's docs say one thing; the hardware says another. When the documentation and the hardware disagree, the hardware wins. Cheap ESP32 boards are not known for their documentation accuracy.

I reflashed with the ILI9341 variant. Display came right up. WiFi credentials survived the flash — the device reconnected automatically, no AP setup required.

Configuring the device

With the firmware updated and display working, the last step was pushing correct configuration through the API:

  • Wallet: Changed both primary and secondary to my own address
  • Timezone: Changed from 8 (China Standard Time) to -5 (Central Daylight Time) — the device doesn't handle DST automatically, so you set the raw UTC offset
  • Display: Brightness to 100, auto-brightness enabled (the original display "failure" turned out to be brightness set to 0 in the config)
  • Pool: stratum+tcp://solobtc.nmminer.com:3333

All through curl to the /broadcast-config endpoint. No app. No special tooling. Just HTTP and JSON.

The result

The miner runs at 1.03 MH/s with 100% share acceptance. The display shows hashrate, pool status, and the correct time. Most importantly, it mines for the right wallet.

Will it ever mine a Bitcoin block solo? The odds are astronomical — an ESP32 doing SHA-256 at one megahash per second, competing against ASICs doing hundreds of terahashes. It's a lottery ticket that costs a few cents of electricity per month. But it's my lottery ticket now.

What Bench made possible

I didn't sit down to fix a miner. I sat down to build an MCP server that gives AI tools hardware awareness. The miner investigation was a side effect — a "let's see what happens if I point this at that dead device" moment that turned into a full security audit.

That's the value of giving your AI assistant access to the physical world. Not just identifying an Arduino's serial port (though that's useful) — lowering the friction between "I wonder what this thing is" and actually finding out. Bench gave Claude Code the ability to see the CH340 chip, find the serial port, and from there, the investigation unfolded naturally.

The miner went from paperweight to functioning device in one session. The security findings are a bonus — or a warning, depending on how you look at it. If you own one of these devices and haven't changed the wallet address, you're mining for the manufacturer. Check your config.

Bench is open source on GitHub.


Originally published at charlieseay.com

Top comments (0)