Skip to content

Latest commit

 

History

History
229 lines (193 loc) · 15.4 KB

0001-2019-11-28.md

File metadata and controls

229 lines (193 loc) · 15.4 KB

28 Nov 2019

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:

Digispark rev3

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:

  1. I already had Arduino IDE 1.8.5 installed from my STM32 experiments. I haven't upgraded it.
  2. 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.
  3. Launched the Arduino IDE and went to File => Preferences and added the "Additional Boards Manager URL": http://digistump.com/package_digistump_index.json
  4. Went to Tools => Board => Boards Manager, selected Type: Contributed, and searched for digistump.
  5. "Digistump AVR Boards" is the first result that comes up. Clicking on this reveals the "Install" button.
  6. 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).
  7. 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:

  1. Start with the Digispark unplugged.
  2. Went to File => Examples => Digispark_Examples => Start.
  3. Clicked the "Verify" (tick) button to make sure it could compile OK.
  4. 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.
    
  5. Plugged in the Digispark into a USB port.
  6. 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:

  1. WIN+R to bring up Windows' "Run" dialog.
  2. Types in notepad and hits ENTER.
  3. Types Hello, World! This is a test into Notepad.
  4. Goes into a loop where, each second, it will:
    1. Toggle Caps Lock.
    2. Hit TAB.
    3. Hit F5 (which is just a Notepad command to insert the current date and time).
    4. Types Hello again. Shoot is: followed by the number of an incrementing counter.
    5. 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).
  • 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 is DigiKeyboard.update() but also DigiKeyboard.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 the DigiUSB 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 is 46 (0x46), Mute is 7F, and there is no distinct key for Sleep?)

  • 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