An Arduino HID tool that automated Chromebook enrollment for Blue Hills Regional Technical — used to provision over 300 devices in two days, cutting per-device enrollment time by ~70%.
- Overview
- Results
- How It Works
- What's Different from Upstream Centipede
- Hardware
- Setup
- Usage
- Reliability
- What I Learned
- Acknowledgements
- License
Chromebook enrollment is slow and repetitive: each device takes an IT staff member 5–10 minutes of walking through the same OOBE screens, Wi-Fi setup, the Ctrl+Alt+E enrollment shortcut, credential entry, waiting for the configuration download. For one device it's fine; for a school district provisioning hundreds, it becomes hours of manual keyboard work.
This project, deployed at Blue Hills Regional Technical, automated the flow using an Arduino Pro Micro as a USB HID keyboard. Plug the Arduino into a freshly-powerwashed Chromebook, and the board takes over, connects to Wi-Fi, triggers enrollment, types credentials, waits for configuration to download, and signals completion via the on-board LED.
| Metric | Value |
|---|---|
| Devices enrolled | 300+ |
| Time window | ~2 days |
| Per-device time, manual | 5–10 minutes |
| Per-device time, automated | ~2–3 minutes |
| Time reduction | ~70% |
| Failure rate | ~10 / 300 (~3%) |
| Estimated IT staff time saved | ~25 hours |
- The Arduino enumerates as a USB HID keyboard.
- On boot it waits a few seconds for the ChromeOS OOBE to fully load.
- It walks the welcome → Wi-Fi → enrollment → credentials flow by sending the same key sequences (Tab, Down-Arrow, Enter, character input) a human operator would press.
- After triggering enrollment with
Ctrl+Alt+Eand typing credentials, it waits ~60 seconds for configuration to finish downloading, then signals success via the on-board LED.
A debug pin (D2 → GND) skips the flow for safe testing.
This sketch is a trimmed fork of Centipede (CDW Amplified for Education, MIT License) — the de facto open-source Chromebook enrollment automator.
The version handed to me did not work for our ChromeOS 137 fleet, the UI navigation sequences (tab counts, down-arrow counts, timing windows) were tuned for older OOBE flows. I rebuilt them by:
- Reading through the upstream Centipede code to understand the architecture and intent of each function.
- Stepping through the ChromeOS 137 OOBE manually, counting the keystrokes required between each screen and timing how long each transition took.
- Patching the broken sequences and re-testing on a powerwashed device, iterating until the flow ran reliably.
Once it was stable in production, I trimmed the codebase to just what we needed:
| Removed | Reason |
|---|---|
| Version-compat branches (pre-137) | We deploy only ChromeOS 137 |
| Advanced EAP (PEAP, LEAP, EAP-TLS, EAP-TTLS) | Our SSID is WPA2/PSK |
| Certificate enrollment | Not part of our flow |
| Powerwash function | We powerwash before plugging in |
| Retry logic | Re-plug recovers cleanly |
| Sign-in, remove-enrollment-wifi | Out of scope for first-boot enrollment |
| ToS walker, ChromeVox escape, showVersion | Not needed for our flow |
Result: same proven flow, ~70% smaller codebase, easier to read and maintain.
| Component | Notes |
|---|---|
| Microcontroller | Arduino Pro Micro (or Leonardo / any ATmega32U4 board with native HID support) |
| Cable | USB-A to micro-USB (data, not charge-only) |
| Optional | Jumper wire for the debug pin (D2 → GND) |
A regular Arduino Uno or Nano will not work, they lack native HID and cannot emulate a keyboard without additional firmware.
- Clone the repository:
git clone https://github.com/davidribeiro-dev/chromebook-enroll.git- Copy
config.h.exampletoconfig.hand fill in your real values:
#define WIFI_SSID "YourSchoolWiFi"
#define WIFI_PASSWORD "YourWiFiPassword"
#define USER_EMAIL "you@school.org"
#define USER_PASSWORD "YourEnrollmentPassword"config.h is gitignored — credentials never reach the repo.
3. Open chromebook-enroll.ino in the Arduino IDE.
4. Select your board (Pro Micro / Leonardo) and the matching port.
5. Upload.
- Powerwash the target Chromebook and wait for the OOBE welcome screen.
- Plug the flashed Arduino into the Chromebook.
- The board takes over. The on-board LED blinks during the flow and turns solid on success.
- ~2–3 minutes later the device is enrolled and ready.
For safe testing without running the flow, jumper pin D2 → GND before plugging in.
Across the production run (300+ devices over two days):
- ~97% completed with no intervention.
- ~3% (~10 devices) needed manual recovery, generally caused by an unexpected post-update screen or a brief disconnection during the flow.
- Recovery procedure: power-cycle back to the first OOBE screen, re-plug the Arduino, re-run. All affected devices completed on a second attempt.
- HID timing is fragile. Different Chromebooks complete the same OOBE step in different amounts of time. The right pause length is whichever one works for the slowest device in your fleet.
- Read before rewriting. Centipede was years of accumulated tribal knowledge about ChromeOS OOBE quirks. Reading through it carefully was faster than starting from a blank file — even though most of what I read I ended up cutting.
- Secrets management starts on day one. Hardcoded credentials in source are how district passwords end up on the public internet. Externalizing them to a gitignored
config.hwas the first thing I did when forking.
- Centipede by CDW Amplified for Education (MIT License) — the upstream project this fork is derived from.
This project is dual-credited:
- Original Centipede code © 2023 CDW Amplified for Education
- Trimmed fork, debugging, and additions © 2026 David Ribeiro
Both released under the MIT License — see LICENSE for details.