Author: Marc Evers
This guide will allow you to speed up and/or delegate Enterprise Enrollment of ChromeOS and ChromeOS flex devices* (e.g. for a POC) as well as automating a local install of ChromeOS flex (to convert a fleet of existing Windows or Mac OS devices).
If you are purchasing 100s of new devices, using Zero-Touch Enrollment is highly recommended.
Prerequisites:
- Google Admin account credentials with enrollment privileges. Demo Chrome can be used for testing and demonstration purposes.
- A ChromeOS or Windows device with a USB-A port
- One or more Digispark boards – currently only clones are available
The search term is “ATTiny85 USB”. There are micro-USB (female) and USB-A (male PCB extension) variants. I chose the latter as it works without adapter cables in most devices. - USB cables, extensions and/or hubs as appropriate
- command line, scripting or programming knowledge
* Assumes functioning WiFi, timings may have to be adjusted for lower-spec flex devices.
Alternatively, you can use an ethernet connection or USB-ethernet adapter and skip the WiFi part by disabling that option.
Setting up the Arduino IDE
There is an excellent Chrome plug-in and a Progressive Web App available for programming and learning Arduino on ChromeOS. However, our (admittedly a little exotic) Digispark clone is not supported yet, so we have to install the “classic” IDE under Linux.
We’re tapping into the vast Arduino hardware and software ecosystem to program the board. The script or program is called a “sketch”. You will have to edit your details in it and upload it via the IDE.
Note: You can skip steps 1 & 2 and alter 4 if you are installing the IDE under Windows.
1) Enable Linux Development Environment
Go to Settings\Advanced\Developers in ChromeOS and enable the Linux Development Environment.
See Google Support for details.
2) Install the IDE
Open the new Terminal app and type/paste
sudo apt install arduino
Then press Enter.
Reply “y” to confirm installation and download. Once these complete you will see the Arduino IDE under “Linux apps”:
3) Install additional Boards Manager Packages
There are 100s of Arduino-compatible boards. To integrate the Digispark clone we need to install two packages by enabling the repositories inside the IDE.
Open the IDE and go to File\Preferences.
Paste
http://digistump.com/package_digistump_index.json, http://drazzy.com/package_drazzy.com_index.json
under Additional Boards Manager URLs. Note URLs are separated by a comma and a space.
Now go to Tools\Board:”Arduino Uno”(default)\Boards Manager
Search for and install Digistump AVR Boards (the original creator of the board, contains keyboard library)
And ATTinyCore (updated driver/bootloader for the microcontroller family, enables programming the clones as they have newer firmware installed)
Packages. Now close the IDE.
4) Enabling USB and Updating the Micronucleus Driver/Bootloader
As the Digispark is programmed via the USB port which it itself emulates in software(!) we have to perform some minor manual configuration. Thanks to Startingelectronics and the Arduino Stack Exchange community, which contain further details.
Open the Terminal if it’s not open still and paste the following commands, followed by Enter to confirm:
sudo nano /etc/udev/rules.d/49-micronucleus.rules
This opens the Nano text editor, paste
SUBSYSTEMS==”usb”, ATTRS{idVendor}==”16d0″, ATTRS{idProduct}==”0753″, MODE:=”0666″
KERNEL==”ttyACM*”, ATTRS{idVendor}==”16d0″, ATTRS{idProduct}==”0753″, MODE:=”0666″, ENV{ID_MM_DEVICE_IGNORE}=”1″
then press CTRL+O and CTRL+X to save and exit. Confirm any writes with “y”.
Paste and confirm
sudo udevadm control –reload-rules
to apply the above.
Note: the next three commands may rely on differing paths if higher version numbers are installed. If you encounter errors, start typing and extend the commands by pressing <TAB> twice at each step to see the options.
cd .arduino15/packages/digistump/tools/micronucleus/2.0a4
sudo mv -iv micronucleus micronucleus.backup
(renamed ‘micronucleus’ -> ‘micronucleus.backup’ should be returned)
sudo ln -s ~/.arduino15/packages/ATTinyCore/tools/micronucleus/2.5-azd1b/micronucleus
Uploading sketches
1) Testing the Board and IDE Config
Open the IDE again. The Boards selection now has two more sub-headings:
Select Digispark (Default – 16.5mhz) from Digistump AVR Boards. Note that the Programmer setting switches to “Micronucleus”, if not please revisit step 4.
Now you are ready to try programming your board!
Delete all text and paste the Digistump Blink sketch (basic board test, will blink LED) into the IDE.
void setup() {
pinMode(0, OUTPUT); // LED on Model B
pinMode(1, OUTPUT); // LED on Model A
}
void loop() {
digitalWrite(0, HIGH); // Turn the LED on
digitalWrite(1, HIGH);
delay(1000); // Wait for a second
digitalWrite(0, LOW); // Turn the LED off
digitalWrite(1, LOW);
delay(1000); // Wait for a second
}
You should save the sketch, e.g. as Digistump_blink.
To check your sketch click on the tick icon (Verify).
Have your board ready to plug in for the next step!
If there are no errors click on the arrow next to it (Upload). After compiling you have 60 seconds to plug the board in and connect it to Linux:
If successful, you should see something similar to the following:
The board’s LEDs will start blinking immediately after as long as it’s powered.
2) Modifying and uploading the Enrollment sketch
Repeat the steps from 1) with the following sketch. You will have to edit it beforehand by adding your credentials and selecting which inputs you want to trigger. Also note differences in keyboard layout and other notes in the commented sections.
The sketch below has been tested on a Chromebook with a UK keyboard and ChromeOS v108, v109 & v110.
/* modified from https://docs.google.com/document/d/1mtaGMOef8TFPNa5IiOVHR7HtHhwVfTP4w3QbS9j1vE0/edit?pli=1 */
/* Edit the following section with your details */
//WiFi
String ssid = “Wifi SSID”;
String ssid_password = “Password”;
//Chrome admin or enrollment account details, note
String email = “user.name\”demochrome.com”; /* use email@domain.org for US keyboard, replace @ with \” for UK or } for FR
/* we can install another library for a UK keyboard or just “fix” this one character as ” and @ are swapped between US and UK and we need to add \ to use the quote as a simple character */
String email_password = “[password]”;
//Enable (1) or disable (0) code sections = keyboard inputs, make sure the line ends with;
//ChromeVox popup, disable if you intend to be quick or disable it manually each time
int chromevox = 1;
//Wireless Setup Screen, disable if LAN is used
int wireless = 1;
//Enterprise Enrollment, you likely want this enabled
int enrollment = 1;
//Asset Identifier, disable if you want to give each asset a name manually
int asset = 1;
/* the next section contains other definitions */
#include <DigiKeyboard.h>
#define KEY_TAB 43
#define KEY_DOWN_ARROW 0x51
int keyWait = 275;
/* setup runs once */
void setup() {
// turn LEDs off
digitalWrite(0, LOW);
digitalWrite(1, LOW);
/* emulated keystrokes start here */
//ChromeVox popup
if(chromevox > 0){
DigiKeyboard.sendKeyStroke(0);
wait(5);
pressKey(KEY_TAB, 1);
pressKey(KEY_ENTER, 1);
}
//Welcome Screen (defaults to “Get started”)
wait(1);
pressKey(KEY_ENTER, 1);
//Wireless Setup Screen
if(wireless > 0){
pressKey(KEY_TAB, 3);
pressKey(KEY_ENTER, 1);
pressKey(KEY_TAB, 4);
pressKey(KEY_ENTER, 1);
pressKey(KEY_TAB, 3);
pressKey(KEY_ENTER, 1);
wait(2);
DigiKeyboard.print(ssid);
pressKey(KEY_TAB, 1);
pressKey(KEY_ENTER, 1);
pressKey(KEY_DOWN_ARROW, 2);
pressKey(KEY_ENTER, 1);
pressKey(KEY_TAB, 1);
DigiKeyboard.print(ssid_password);
pressKey(KEY_ENTER, 1);
wait(10); //Wait to connect to wireless
}
//Next
pressKey(KEY_TAB, 3);
pressKey(KEY_ENTER, 1);
wait(5);
//Enterprise Enrollment
if(enrollment>0) {
pressKey(KEY_TAB, 2);
pressKey(KEY_ENTER, 1);
wait(5);
pressKey(KEY_TAB, 8); // as email or phone is not effectively highlighted – can likely be skipped/removed in newer versions
DigiKeyboard.print(email);
pressKey(KEY_ENTER, 1);
wait(4);
DigiKeyboard.print(email_password);
pressKey(KEY_ENTER, 1);
}
// Asset Identifier can be added manually, if not, this will Skip & select Done
if(asset > 0) {
pressKey(KEY_TAB, 2);
pressKey(KEY_ENTER, 1);
wait(4);
pressKey(KEY_ENTER, 1);
}
//turn both LEDs on
digitalWrite(0, HIGH);
digitalWrite(1, HIGH);
}
/* loop is the main function as the microcontroller would run continuously */
void loop() {
}
//function definitions
uint8_t pressKey(uint8_t key, int times) {
for (int i = 1; i <= times; i++) {
DigiKeyboard.delay(50);
DigiKeyboard.sendKeyStroke(key);
DigiKeyboard.delay(keyWait);
}
}
int wait(int seconds) {
for (int i = 0; i < seconds; i++) {
DigiKeyboard.delay(1000);
}
}

3) Modifying and uploading the Flex Install sketch
Repeat the steps from 1) with the Flex Install sketch below. This one has only two parameters, essentially a startup delay and test mode. The former depends on the type of device you’re converting and the speed of the USB drive.
If you want to convert a number of similar devices it’s worth timing the startup until the ChromeVox popup once.
The red LED will be on when the delay timer starts running(~7 seconds after power on), off while the keystrokes are passed to the OS and blinking once done.
Also consider if you want to plug the Digispark board in separately or together with the USB drive you’re booting from (e.g. in a USB hub) and add some time for that. Note that more advanced hubs with e.g. HDMI and Ethernet interact with the OS and require you to plug in the Digispark board after the ChromeOS splash screen!
Again, check the notes in the commented sections of the sketch for more details.
The sketch below has been tested on a Dell XPS 13 9380 (F12 for boot menu) with a Kensington USB-C hub.
//Enable (1) or disable (0) code sections = keyboard inputs, make sure the line ends with;
//startup delay in seconds – note this is from the time the device is plugged in and powered. 420s gives you a very safe 7 minutes – 2 to plug in and select boot device and 5 for the OS boot from a slow drive
int startup = 420;
//Installation vs Test mode – go through with the final “Are you sure” popup
int reallyinstall = 0;
/* you can switch further modules off and on here to adjust for potential future GUI changes */
//ChromeVox popup, disable if you intend to be quick or disable it manually each time
int chromevox = 1;
/* the next section contains other definitions */
#include <DigiKeyboard.h>
#define KEY_TAB 43
#define KEY_DOWN_ARROW 0x51
int keyWait = 275;
/* setup runs once */
void setup() {
// turn LEDs on
digitalWrite(0, HIGH);
digitalWrite(1, HIGH);
//Startup delay
wait(startup);
// turn LEDs off
digitalWrite(0, LOW);
digitalWrite(1, LOW);
/* emulated keystrokes start here */
//ChromeVox popup
if(chromevox > 0){
DigiKeyboard.sendKeyStroke(0);
wait(5);
pressKey(KEY_TAB, 1);
pressKey(KEY_ENTER, 1);
}
//Welcome Screen (defaults to “Get started”)
DigiKeyboard.sendKeyStroke(0);
wait(1);
pressKey(KEY_ENTER, 1);
wait(5);
//Install Screen
pressKey(KEY_TAB, 4); //Install
pressKey(KEY_ENTER, 1); //tick
pressKey(KEY_TAB, 1); //Next
pressKey(KEY_ENTER, 1);
wait(5);
//Install ChromeOS Flex
pressKey(KEY_ENTER, 1);
wait(5);
// Confirmation popup
if(reallyinstall > 0) {
pressKey(KEY_TAB, 1);
pressKey(KEY_ENTER, 1);
}
}
/* loop is the main fuction as the microcontroller would run continuously */
/* this sketch will blink the LED (on either variant) as the screen might be off and more time might pass until you return to the device */
void loop() {
digitalWrite(0, HIGH); // Turn the LED on
digitalWrite(1, HIGH);
delay(1000); // Wait for a second
digitalWrite(0, LOW); // Turn the LED off
digitalWrite(1, LOW);
delay(1000); // Wait for a second
digitalWrite(0, HIGH); // Turn the LED on
digitalWrite(1, HIGH);
delay(250); // Wait for a quarter second
digitalWrite(0, LOW); // Turn the LED off
digitalWrite(1, LOW);
delay(250); // Wait for a quarter second
}
//function definitions
uint8_t pressKey(uint8_t key, int times) {
for (int i = 1; i <= times; i++) {
DigiKeyboard.delay(50);
DigiKeyboard.sendKeyStroke(key);
DigiKeyboard.delay(keyWait);
}
}
int wait(int seconds) {
for (int i = 0; i < seconds; i++) {
DigiKeyboard.delay(1000);
}
}
References::
https://docs.google.com/document/d/1mtaGMOef8TFPNa5IiOVHR7HtHhwVfTP4w3QbS9j1vE0