I spent the last 3 nights trying out a Digispark, which is a minimal Arduino clone in a USB-PCB-plug form factor with a built-in USB bootloader:
NOTE: The board pictured is the same as mine and is apparently a cheap Chinese clone; there is no official rev3 version.
An article I saw was a little critical of it, basically saying its dev method differs too much from normal Arduino stuff and it is poorly supported by Digistump, without recent OS support. There was an update to that article, though, which discussed better results.
I've never actually used an Arduino before and hardly used the Arduino IDE, but I didn't have too much trouble. I followed their setup Wiki on Windows 10:
- I already had Arduino IDE 1.8.5 installed from my STM32 experiments. I haven't upgraded it.
- Downloaded the Digistump Drivers, extracted them, and ran
DPInst64.exe
(for my 64-bit platform). They seemed to go in fine. This is apparently necessary for Arduino 1.6.6+ and maybe also for Windows 8/10. - Launched the Arduino IDE and went to
File
=>Preferences
and added the "Additional Boards Manager URL":http://digistump.com/package_digistump_index.json
- Went to
Tools
=>Board
=>Boards Manager
, selectedType: Contributed
, and searched fordigistump
. - "Digistump AVR Boards" is the first result that comes up. Clicking on this reveals the "Install" button.
- It doesn't take long after clicking "Install", but it might come up with an error about drivers. Fortunately, I think I've found this can be ignored (because of step 2 above).
- Went to
Tools
=>Board
=>Digispark (Default - 16.5mhz)
NOTE: I also first tried this on my MS Surface 1 tablet running Windows 8.1. It didn't seem to work (namely, it couldn't run the toolchain executables, especially reporting that micronucleus
"is not recognized as an internal or external command, operable program or batch file"). I think I've worked out this was because my %PATHEXT%
system environment variable was damaged; easily fixed, but I haven't since tried running the Arduino IDE on that tablet.
With the environment now ready, I tried a test program:
- Start with the Digispark unplugged.
- Went to
File
=>Examples
=>Digispark_Examples
=>Start
. - Clicked the "Verify" (tick) button to make sure it could compile OK.
- Clicked the "Upload" (right-arrow) button. After compiling it displays:
Running Digispark Uploader... Plug in device now... (will timeout in 60 seconds) > Please plug in the device ... > Press CTRL+C to terminate the program.
- Plugged in the Digispark into a USB port.
- Pretty soon after, the uploader responded:
> Device is found! connecting: 16% complete connecting: 22% complete connecting: 28% complete connecting: 33% complete > Device has firmware version 1.6 > Available space for user applications: 6012 bytes > Suggested sleep time between sending pages: 8ms > Whole page count: 94 page size: 64 > Erase function sleep duration: 752ms parsing: 50% complete > Erasing the memory ... erasing: 55% complete erasing: 60% complete erasing: 65% complete > Starting to upload ... writing: 70% complete writing: 75% complete writing: 80% complete > Starting the user app ... running: 100% complete >> Micronucleus done. Thank you!
Shortly after this, the LED started blinking at 0.5Hz.
I borrowed a simple PWM example, noting that the LED is on Pin 1:
#define LED_PIN 1
#define DELAY 5
// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(LED_PIN, OUTPUT);
}
// the loop routine runs over and over again forever:
void loop() {
for (int i=0; i<=255; ++i) {
analogWrite(LED_PIN, i);
delay(DELAY);
}
for (int i=255; i>=0; --i) {
analogWrite(LED_PIN, i);
delay(DELAY);
}
}
I then tried a virtual USB Keyboard example, via File
=> Examples
=> DigisparkKeyboard
=> Keyboard
:
#include "DigiKeyboard.h"
void setup() {
// don't need to set anything up to use DigiKeyboard
}
void loop() {
// this is generally not necessary but with some older systems it seems to
// prevent missing the first character after a delay:
DigiKeyboard.sendKeyStroke(0);
// Type out this string letter by letter on the computer (assumes US-style
// keyboard)
DigiKeyboard.println("Hello Digispark!");
// It's better to use DigiKeyboard.delay() over the regular Arduino delay()
// if doing keyboard stuff because it keeps talking to the computer to make
// sure the computer knows the keyboard is alive and connected
DigiKeyboard.delay(5000);
}
This worked as expected, and so I then adapted it to do a few more things:
#include "DigiKeyboard.h"
#define KB DigiKeyboard
#define KEY_ESC 0x29
#define KEY_TAB 0x2b
#define KEY_CAPSLOCK 0x39
#define KEY_PRTSC 0x46 // Print Screen key.
void setup() {
// don't need to set anything up to use DigiKeyboard
}
void loop() {
// this is generally not necessary but with some older systems it seems to
// prevent missing the first character after a delay:
KB.sendKeyStroke(0);
KB.sendKeyStroke(KEY_R, MOD_GUI_LEFT);
KB.delay(600);
KB.print(F("notepad"));
KB.sendKeyStroke(KEY_ENTER);
KB.delay(600);
KB.println(F("Hello, World! This is a test"));
KB.delay(500);
int shoot = 0;
while (1) {
KB.delay(1000);
KB.sendKeyStroke(KEY_CAPSLOCK);
KB.sendKeyStroke(KEY_TAB);
KB.sendKeyStroke(KEY_F5);
KB.print(F(" Hello again. Shoot is: "));
KB.println(shoot);
switch (shoot++) {
case 0: KB.sendKeyStroke(KEY_PRTSC, MOD_GUI_LEFT); break;
case 1: KB.sendKeyStroke(KEY_PRTSC, MOD_ALT_LEFT); break;
default: shoot--;
}
}
}
This does the following, with brief pauses (1 second or less) at key spots to ensure the system responds quickly enough:
- WIN+R to bring up Windows' "Run" dialog.
- Types in
notepad
and hits ENTER. - Types
Hello, World! This is a test
into Notepad. - Goes into a loop where, each second, it will:
- Toggle Caps Lock.
- Hit TAB.
- Hit F5 (which is just a Notepad command to insert the current date and time).
- Types
Hello again. Shoot is:
followed by the number of an incrementing counter. - Either (based on the incrementing counter, which stops at 2), will do WIN+Print Screen or ALT+Print Screen, or do nothing.
Here are some other things I think I've learned about this device and V-USB:
- The ATtiny85 has 8kB flash ROM and 512 bytes of RAM.
- At least 2kB of the flash ROM are used by the bootloader.
- At least some libraries used with the Digispark differ from the normal Arduino libraries, probably due to its restricted capacity/capabilities. There are some specific documented differences between normal Arduino and Digispark with more detail here.
- There is a handy quick reference about the Digispark which I think has enough other example stuff that it should be combined with other tutorials/examples.
- I2C can't be used with rev0 boards unless the LED is disconnected.
- Arduino's
F()
function can be used to wrap strings and access them from flash ROM rather than having them consume RAM, but from what I'm reading this can only be used with certain function prototypes that understand the difference and hence are designed to read each byte from flash ROM directly, rather than just treating them like a normal pointer. - The USB HID Keyboard protocol supposedly only supports up to 6 simulatenous keys (besides, I think, 8 modifier keys like left CTRL, right CTRL, etc).
- There is some discussion about this here.
- There is also the old
HID1_11.pdf
USB HID Device Class Definition, Appendix C with more information on this. - The Wikipedia USB HID page might also provide a good high-level summary.
- The Digispark's
DigiKeyboard
library can be used for basic USB keyboard emulation but is lacking some features, as far as I can tell:- Can't set or receive keyboard LED status information.
- Can't specifically control which keys are up and down at any given time, and instead works by sending a single key (plus optional modifier keys) down-and-up sequence. In other words, only 1 of the 6 available "key slots" is supported.
DigiKeyboard
needs to be given time to perform USB updates in order to stay connected to the host. This must happen at least every 50ms. For this purpose, there isDigiKeyboard.update()
but alsoDigiKeyboard.delay()
to implement a delay loop that does this refresh automatically.- The code for
DigiKeyboard
is found here so maybe we can figure out how to extend it to support some of its missing features, or otherwise replace it. Understanding V-USB might help us understand this better. - This is apparently a way to set a random seed with Arduino:
randomSeed(analogRead(0));
- There are some interesting Digispark-specific features/tricks, including:
- Detecting if clock calibration occurred (possibly to detect whether USB is connected or not?)
- Underclocking the CPU to save power.
- Jumping to the bootloader.
- Removing the bootloader's 5-second startup delay.
- 3 (usable) hardware PWM pins, or only 2 if using USB simultaneously too, plus additional software PWM.
- Arduino's
analogWrite()
typically means doing PWM (8-bit resolution) on a digital pin. - The Digistump website shows lots of "shields" that would good give ideas for other projects that can be done in this minimal form factor.
- The
Echo
DigiUSB example shows some basic part of initialising theDigiUSB
library and doing basic USB read/write. I'm not sure what driver requirements there are: Someone seems to think it's a HID device but I'm not sure if it's true. - There is also DigiBlink which appears to change the USB device name, too (handled just inside
DigiUSB.h
?) - There is the
digiusb
Ruby Gem for communicating specifically with DigiUSB devices (code and example here).
Other notes:
-
The PJRC/Teensy website has info on Arduino USB projects and gives more insight into how USB keyboards work including the "6 key slots" thing (see "The Micro Manager Way" heading). It also includes this important statement re scan codes in USB:
If you are familiar with the older PS/2 keyboard "scan codes", please be aware that USB keyboard codes work differently (and use different numerical values). The old scan codes sent different messages for key down and key up events, where each message represented a single key's change. With USB, each message represents the complete state of the keyboard. Up to any 6 keys may be pressed at once, plus any combination of the 8 modifier keys.
-
The V-USB repo has some examples of host-side code which might also be useful/interesting for experimenting with USB control. Not sure how this would play with device drivers trying to take over.
-
V-USB has heaps of example projects including hardware.
-
There is also an example that shows basic HID reading/writing, which means data transfer can be done without any special drivers needed on the host...?
-
USBtiny is apparently similar to V-USB, and well-explained, but more-limited.
-
There is some stuff here about common Ardunio USB HID key scan code convention stuff.
-
The page, Scan Codes Demystified has heaps of information on various keyboard scan codes, and includes columns specifically for USB key definitions (e.g. it shows
PrtSc
is46
(0x46),Mute
is7F
, and there is no distinct key forSleep
?) -
More details here on V-USB and HID keyboards. Looks pretty good, and includes info on receiving LED state changes.
-
Another practical example of custom USB devices with V-USB, here.
Other thoughts:
- Later I might also try the Digispark Pro. I wouldn't pay US$10 for it, but instead I will try a cheap Chinese knockoff again.
- Is using this a good way to try out various Arduino libraries and get to know what all the fuss is about? Or is it too different/limited compared to normal Arduino models/clones?
- Some project ideas and other things to try learning:
- CDC (Virtual COM port via USB)
- USB Mouse
- RGB LED (using PWM)
- I2C stuff, including IO Expander
- Simple SPI stuff, inc. reading buttons/keypads or controlling LEDs/7-seg displays using serial shift registers
- Interfacing to other simple devices
- Making an actual interface adapter for USB keyboard, joystick, or whatever -- NES controller should be easy, since it uses a serial shift register.
- Learning more V-USB stuff
- Shifting clock speed
- Replacing bootloader