Project Log : Arduino USB
Description
Project log for developing USB expansion shield for Arduino and associated code.
See also: Learning About Arduino and AVR-USB
. See also . : Featured as a chapter called Virtual USB Keyboard in the new book Practical Arduino by Jon Oxer complete with readable schematics and all.
Code
- arduinousb_release_004.tar.gz -- Fourth alpha release. Added generic USB device support. Added Python wrapper and demos. Same caveats as 002.
- arduinousb_release_003.tar.gz -- Third alpha release. Upgrade to version 2009-08-22 of V-USB driver. No new functionality, same caveats as 002.
- arduinousb_release_002.tar.gz -- Second alpha release. (Compatible with Arduino 0016 (not 0017!) and PCB design but not original protoboard design.)
- (old) arduinousb_release_001.tar.gz -- First alpha release. (Patched version of UsbKeyboard.h for Arduino 0012)
Notes
- ( 7 March 2008 )
- Have started construction of "mini" expansion shield on a piece of strip board.
- Connected USB "B" socket to board and wired ground and +5V (Vbus) from USB to Arduino to power it successfully.
- Helpfully found the required 2.2KOhm and 68 Ohm resistors needed in the latest box of electronics bits I purchased. (Of course they were in the last set of the resistors I looked through though.)
- Started drawing schematic in KiCad. Used USB "B" socket symbol from con-usb.lib from http://www.kicadlib.org/. Used Arduino pin layout from http://uchobby.com/.
- Also using Arduino Atmega168 pin mapping details. Note: INT0 == PD2 == IC Pin 4 == Arduino Digital Pin 2, INT1 == PD3 == IC Pin 5 == Arduino Digital Pin 3
- FIX: Schematic should probably have a diode to prevent powering Vbus accidentally.
- The source for usbdrv.h says, regarding the hardware:
USB lines D+ and D- MUST be wired to the same I/O port. We recommend that D+ triggers the interrupt (best achieved by using INT0 for D+), but it is also possible to trigger the interrupt from D-. If D- is used, interrupts are also triggered by SOF packets. D- requires a pullup of 1.5k to +3.5V (and the device must be powered at 3.5V) to identify as low-speed USB device. A pullup of 1M SHOULD be connected from D+ to +3.5V to prevent interference when no USB master is connected. We use D+ as interrupt source and not D- because it does not trigger on keep-alive and RESET states.
- For a 5V source it seems the pullup on D- needs to be 2.2K to 5v.
- Other circuits: usbasp, usbtinyisp, avrusb
- Socket pin outs: USB overview and Plug and Receptacle pinouts Update : August 2008 A possible pinout for the PCB part of the USB connector. Thanks Mr Spatial. :-)
- Soldered up zener diodes and pull up resistor.
- Plugged into linux host, produced this from tail -f /var/log/syslog :
Mar 7 04:52:38 localhost kernel: [685132.128973] usb 1-2: new low speed USB device using uhci_hcd and address 9 Mar 7 04:52:38 localhost kernel: [685132.252884] usb 1-2: device descriptor read/64, error -71 Mar 7 04:52:38 localhost kernel: [685132.480711] usb 1-2: device descriptor read/64, error -71 Mar 7 04:52:39 localhost kernel: [685132.696583] usb 1-2: new low speed USB device using uhci_hcd and address 10 Mar 7 04:52:39 localhost kernel: [685132.816506] usb 1-2: device descriptor read/64, error -71 Mar 7 04:52:39 localhost kernel: [685133.040335] usb 1-2: device descriptor read/64, error -71 Mar 7 04:52:39 localhost kernel: [685133.256203] usb 1-2: new low speed USB device using uhci_hcd and address 11 Mar 7 04:52:40 localhost kernel: [685133.663904] usb 1-2: device not accepting address 11, error -71 Mar 7 04:52:40 localhost kernel: [685133.775828] usb 1-2: new low speed USB device using uhci_hcd and address 12 Mar 7 04:52:40 localhost kernel: [685134.183550] usb 1-2: device not accepting address 12, error -71
- Above result "new low speed device" indicates pull up is in correct place. The error messages are because it's only the bare board plugged in. (Apparently strip board doesn't accept addresses.) :-)
- I'm using 3.6V, 0.5W Zener Diodes (1N5227) although I hear .25W is preferred, and apparently 1W don't work (according to a forum post).
- FIX: Change diodes in schematic to zeners.
- Modified PowerSwitch usbconfig.h file. Compiled with make on Ubuntu 7.10 machine.
- Before plugging in, remember to: upload firmware, change power jumper to "none", disconnect Arduino usb socket cable, attach shield, attach cable to expansion usb socket.
- Doesn't work. :-(
- Used this to upload (from OS X):
hardware/tools/avr/bin/avrdude -Chardware/tools/avr/etc/avrdude.conf -v -v -v -v -pm168 -cstk500v1 -P/dev/tty.usbserial-<id> -b19200 -D -Uflash:w:<path>main.hex:i
- Need to edit Makefile to set DEVICE to atmega168!
- Need to edit main.c
- Works!
- Using lsusb shows the device appearing/disappearing:
Bus 001 Device 040: ID 16c0:05dc
- Using sudo lsusb -v gives something that includes:
iManufacturer 1 www.obdev.at iProduct 2 PowerSwitch
- Edited EasyLogger enough to init okay.
- ( 8 March 2008 )
- I've now got the powerswitch usb echo demo working from an Arduino sketch as I'm flashing an LED every second. The Usbduino shield is in the house. :-)
- BTW an error message of the form "error: invalid conversion from `void*' to ..." is because C++ requires that casts from void * are explicit not implicit.
- BTW also, an error message of the form "error: expected initializer before int" or "error: expected initializer before int" may be because PROGMEM isn't recognised because you need to include the pgmspace.h file. Use gcc's -save-temps command to look in the .ii file to see if PROGMEM is replaced.
- ( 18 March 2008 )
- Trying to get IDE to compile library correctly from bare source—but it doesn't compile '.S' files at least...
- Try this in library directory first:
hardware/tools/avr/bin/avr-g++ -Wall -Os -I. -DUSB_CFG_CLOCK_KHZ=16000 -mmcu=atmega168 -c usbdrvasm.S
- Hmmm, so it seems the IDE doesn't do the compile the other things correctly either, so we need also:
hardware/tools/avr/bin/avr-g++ -Wall -Os -I. -DUSB_CFG_CLOCK_KHZ=16000 -mmcu=atmega168 -c usbdrv.c
- I guess that's why I compiled the other test by hand... :-)
- So the above commands will produce usbdrv.o and usbdrvasm.o files.
- Still not getting working keyboard data on OS X...
- On Ubuntu I can get some data out by doing: (depending on the usb device address)
sudo cat /dev/input/by-path/pci-0000\:00\:14.2-usb-0\:2\:1.0-event- | hexdump
- This produced when pressing the button: (index 1 // KEY_A // 4 ?)
0000000 86c7 47de 05cc 000b 0001 002a 0001 0000 0000010 86c7 47de 05d7 000b 0001 001e 0001 0000 0000020 86c7 47de 05d9 000b 0000 0000 0000 0000 0000030 86c7 47de da7f 000c 0001 002a 0000 0000 0000040 86c7 47de da89 000c 0001 001e 0000 0000 0000050 86c7 47de da8c 000c 0000 0000 0000 0000
- This produced when pressing the button: (index 2 // KEY_B // 5 ?)
0000000 88e5 47de a4c7 000b 0001 002a 0001 0000 0000010 88e5 47de a4d0 000b 0001 0030 0001 0000 0000020 88e5 47de a4d2 000b 0000 0000 0000 0000 0000030 88e5 47de 9ec5 000c 0001 002a 0000 0000 0000040 88e5 47de 9ece 000c 0001 0030 0000 0000 0000050 88e5 47de 9ed1 000c 0000 0000 0000 0000
- ( 21 March 2008 )
- ( 28 March 2008 )
- Discovered today that on Linux it's now working okay for HID, seemingly. (Although the first few key presses seem not to be recognised.)
- ( Site down for around a month )
- ( 9 July 2008 )
- During the downtime I discovered that the Arduino did function as an USB HID keyboard (with one button!) under 10.4 on a PowerBook G4 so it would seem maybe the 10.2 and/or the iBook didn't like it for some reason.
- I managed to send standard characters, modified keystrokes (e.g. Command-B), arrow keys and function keys—the latter of which I used to bring the Dashboard up on screen whenever I pushed the button attached to the Arduino.
- So, the short answer does seem to be that at least at some level AVRUSB and Arduino can be compatible—I'm not sure if there is a point at which one or other of them will break as I haven't tried anything very sophisticated.
- It would seem the "missing first keystroke" issue I noted is a known AVRUSB issue: First key is not sent (hid keyboard) Apparently you can send a dummy empty keystroke to work around it at least.
- After much delay, here is the full top and bottom views of my USB mini-shield (on strip- or vero-board) for Arduino, it should be enough for you to reconstruct it:
- Note that the traces are cut between the four pins on the USB connector and there are two traces that have drill bit induced breaks.
- Oh, ok, and it turns out I also had a schematic drawn up in KiCad, so I exported it to SVG for you (note that I haven't actually verified it's accurate): schematic for (AVRUSB) USB mini-shield for Arduino in SVG (Hmmm, Inkscape opened it okay, but Firefox 2 doesn't seem to like it.)
- ( 23 July 2008 )
- Have noticed AVRUSB firmware is now a download separate from the example projects. The code has also been reorganised. Not sure which version I should first release.
- ( 26 July 2008 )
- A couple of days ago I pulled the original PowerSwitch and HIDKeys source I had downloaded into SVN and re-applied the modifications I had made and documented compilation. I'm now working on tidying up the HID Keyboard sample with the aim of uploading "library" and sample sketch.
- ( 6 August 2008 )
- Last week I got a reasonable API/library design implemented.
- Added link to PCB pinout of USB socket to 7 March 2008 entry above.
- ( 12 August 2008 )
- Have released a first alpha release 001, by request. See Code section above.
- Realised I had planned to change UsbKeyboardDevice method named update to refresh but won't change it for this release.
- ( 19 August 2008 )
- Blog entries found after an email received with the UsbMouse code: BoarduinoUSB, UsbMouse library for Arduino. Thanks Michel! [ Update : UsbJoystick library for Arduino ]
- ( 13 September 2008 )
- It was produced more as an test of a KiCad Mac OS X nightly binary but here's an initial rough, untested and potentially inaccurate circuit for an Arduino-based USB "keyboard" device: arduino_usb_keyboard_circuit_001.pdf Note that the current code won't work on it due to moving some of the connections around.
- ( 19 September 2008 )
- Started Project Log USB Stealth Twiddler page.
- ( 27 January 2009 )
- Thanks to xSmurf the apparent cause of the instability has been identified. It appears the timer0 interrupt routine causes the USB side of things to barf. Interim work-around is to disable the timer in setup with:
// disable timer 0 overflow interrupt (used for millis) TIMSK0&=!(1<<TOIE0);
- Note that this "fix" will cause delay and millis to no longer function.
- With this workaround in place I can reliably repeat typing.
- In the interim I am using this for delays:
void delayMs(unsigned int ms) {
/*
*/
for (int i = 0; i < ms; i++) {
delayMicroseconds(1000);
}
}
- xSmurf suggested modifying the pre-scaler value for the timer as a possible solution.
( 18 February 2009 )
- Earlier in the month I managed to put together a PCB design and etch a PCB with it but haven't uploaded anything until now. The first version had a bunch of connections in reversed order (thanks to using generic connectors on the schematic) but the second revision seems to be functional. The second revision still needs a couple of modifications, mainly to provide support and isolation for the USB connector. The current board did work initially but now seems to have an issue which I need to verify—I think it's a construction issue rather than a design issue.
- @@ TODO : Need to upload the most recent code with the new pinout.
- Uploaded schematic pdf and kicad source files. Here's a screenshot of the board layout:

- Here's an etchable image as produced by http://circuitpeople.com/ but keep in mind the socket support holes aren't great and I still have a small problem with my etched board which I haven't 100% confirmed.
- @@ TODO : Add photographs.
( 23 February 2009 )
- Until I upload the correct code in an archive, here's a library patch that should work:
--- arduinousb_release_001/libraries/UsbKeyboard/usbconfig.h 2008-08-12 13:38:53.000000000 +1200
+++ arduino-0012/hardware/libraries/UsbKeyboard/usbconfig.h 2009-02-05 17:19:58.000000000 +1300
@@ -27,7 +27,8 @@
/* This is the port where the USB bus is connected. When you configure it to
* "B", the registers PORTB, PINB and DDRB will be used.
*/
-#define USB_CFG_DMINUS_BIT 3
+#define USB_CFG_DMINUS_BIT 4
+//#define USB_CFG_DMINUS_BIT 3
/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.
* This may be any bit in the port.
*/
@@ -39,13 +40,13 @@
/* ----------------------- Optional Hardware Config ------------------------ */
-/* #define USB_CFG_PULLUP_IOPORTNAME D */
+#define USB_CFG_PULLUP_IOPORTNAME D
/* If you connect the 1.5k pullup resistor from D- to a port pin instead of
* V+, you can connect and disconnect the device from firmware by calling
* the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h).
* This constant defines the port on which the pullup resistor is connected.
*/
-/* #define USB_CFG_PULLUP_BIT 4 */
+#define USB_CFG_PULLUP_BIT 5
/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined
* above) where the 1.5k pullup resistor is connected. See description
* above for details.
--- arduinousb_release_001/libraries/UsbKeyboard/UsbKeyboard.h 2008-08-12 13:38:53.000000000 +1200
+++ arduino-0012/hardware/libraries/UsbKeyboard/UsbKeyboard.h 2009-02-05 14:48:47.000000000 +1300
@@ -7,9 +7,18 @@
#define __UsbKeyboard_h__
#include <avr/pgmspace.h>
+#include <avr/interrupt.h>
+#include <string.h>
#include "usbdrv.h"
+// TODO: Work around Arduino 12 issues better.
+//#include <WConstants.h>
+//#undef int()
+
+typedef uint8_t byte;
+
+
#define BUFFER_SIZE 4 // Minimum of 2: 1 for modifiers + 1 for keystroke
@@ -122,6 +131,11 @@
PORTD = 0; // TODO: Only for USB pins?
DDRD |= ~USBMASK;
+ cli();
+ usbDeviceDisconnect();
+ usbDeviceConnect();
+
+
usbInit();
sei();
- And here is the demo patch:
--- arduinousb_release_001/examples/UsbKeyboardDemo1/UsbKeyboardDemo1.pde 2008-08-12 13:54:45.000000000 +1200
+++ arduinousb_release_001/examples/UsbKeyboardDemo1/UsbKeyboardDemo1.pde 2009-02-23 03:25:29.000000000 +1300
@@ -5,12 +5,25 @@
void setup() {
pinMode(BUTTON_PIN, INPUT);
digitalWrite(BUTTON_PIN, HIGH);
+
+ // disable timer 0 overflow interrupt (used for millis)
+ TIMSK0&=!(1<<TOIE0); // ++
+}
+
+void delayMs(unsigned int ms) {
+ /*
+ */
+ for (int i = 0; i < ms; i++) {
+ delayMicroseconds(1000);
+ }
}
void loop() {
UsbKeyboard.update();
+ digitalWrite(13, !digitalRead(13));
+
if (digitalRead(BUTTON_PIN) == 0) {
//UsbKeyboard.sendKeyStroke(KEY_B, MOD_GUI_LEFT);
@@ -32,7 +45,7 @@
UsbKeyboard.sendKeyStroke(KEY_ENTER);
- delay(200);
+ delayMs(200);
}
}
( 23 May 2009 )
- A while back a helpful correspondent sent me an annotated image of the proto-board shield:

( 24 May 2009 )
- Unfortunately I've discovered today that the code which seems to run reliably on Linux is still having problems on OS X. The result is only semi-reliable functioning... :-/ The errors result in:
May 24 01:31:54 ComputationDevice kernel[0]: USBF: 118274.167 [0x44d7400] The IOUSBFamily is having trouble enumerating a USB device that has been plugged in. It will keep retrying. (Port 2 of hub @ location: 0x1a000000) May 24 01:32:01 ComputationDevice kernel[0]: USBF: 118280.814 AppleUSBUHCI[0x421a000]::Found a transaction which hasn't moved in 5 seconds on bus 0x1a, timing out! (Addr: 0, EP: 0) May 24 01:32:05 ComputationDevice kernel[0]: USBF: 118285. 74 [0x44d7400] The IOUSBFamily was not able to enumerate a device.
- With the above PCB schematic (not the stripboard shield) this hex file should work (as well as it does): UsbKeyboardDemo1_20090524.hex
- Changing the demo to only send one character and reducing the delay from 200 to 20 doesn't cause the communication to stall, but then has repeated characters.
( 13 September 2009 )
- As part of the Learning About Jog Wheel project I have just discovered something. On a MacBook Pro (which I've mostly been testing on) if I plug a AVRUSB device into the left USB port it fails to enumerate but if I plug it into the right USB port it works right away. This would seem to match what appeared to be inconsistent behaviour I've observed while working on the project... Looks like I need to re-test the things I thought weren't working. :-/
( 16 October 2009 )
- Uploaded alpha release 002 of the code (see top of page). This incorporates all the patches to the original code mentioned above AFAICT. It is compatible with Arduino 0016 (not 0017) and the PCB design—either on a PCB or if you modify the protoboard design to work the same way.
- Oh, also, I have confirmed that the PCB design is working for me (once you ensure the socket legs don't short out things) so the problem I seemed to witness earlier was presumably due to the USB port I was testing it on. (Which is convenient because I broke a solder joint on the "fixed" one. :) )
( 17 October 2009 )
- Worked on upgrading the version of V-USB (formerly known as AVRUSB) used to vusb-20090822. I seem to have merged my changes okay and it appears to work as before. This should in theory incorporate some bug fixes although nothing noticable so far. Also should enable easier use of later examples. Still need to upload the changes.
( 19 October 2009 )
- Have begun to port the hid-data example from the latest VUSB code to work on the Arduino to function as a generic UsbDevice capable of receiving and sending data. Have managed to get the device recognised but communication appears to fail.
- Have for the hid-data example working on the Arduino—I moved the timer disable code out of the constructor and that seemed to fix things. Code not uploaded yet.
( 20 October 2009 )
- As partially documented at Learning About Python and USB I've now got a Python + libusb-1.03 + pyusb-1.x script reading and writing over USB to the ported hid-data Arduino UsbDevice with no driver required on OS X. Code not uploaded yet.
( 22 October 2009 )
- I've now got a generic "streaming" usb device implemented on the Arduino. Also a Python interface to it. A couple of demos including one for turning pins (and thus LEDs) on and off. The other implements a "decrypting" dongle. Code not uploaded yet. :)
- Example Python code:
from arduino.usbdevice import ArduinoUsbDevice theDevice = ArduinoUsbDevice(idVendor=0x16c0, idProduct=0x05df) theDevice.write(0x01) print theDevice.read()
- Example Arduino code:
#include <UsbStream.h>
void setup() {
UsbStream.begin();
UsbStream.write(0xff);
}
void loop() {
UsbStream.refresh();
if (UsbStream.available() > 0) {
int data = UsbStream.read();
}
}
- Want to look into putting a Processing wrapper together too.
- Oh, I just realised I didn't mention that I actually uploaded the release 003 the other day—that's the one that upgraded to the latest V-USB.
- Uploaded release 004 code (but forgot to update the release notes). Needs better documentation. Will be interested in hearing if it works okay for other people.
( 24 October 2009 )
- Modified streaming device to have dynamic device descriptor stored in RAM. This enables, for example, vendor and product IDs to be changed on the device rather than at compile time. Tried a couple of known pairs and the vendor was recognised and listed. Note: This approach doesn't allow changing something that changes the size of the descriptor. To do that requires a fully dynamic descriptor created/returned at runtime (which should be possible).
( 27 October 2009 )
- Awesome, I've just learned that assembler support in the Arduino IDE patch in Issue 110 has been applied to SVN. I just tested it and the code now compiles out of the box—I tested it with the library in the sketchbook directory. So, this should mean that once IDE version 0018 is released it's fully supported.
( 6 April 2010 )
- Link to a talk about a similar project: http://blip.tv/file/3384006/ and web page (via hackaday)
( 28 July 2010 )
- Thanks to some pushing from http://hypatia.ca/ I've finally sorted out the issues with Arduino 0018 and the ATmega328p. You want to add this around "usbPoll" and "usbInit" in "usbdrv.h":
#ifdef __cplusplus
extern "C"{
#endif
...
#ifdef __cplusplus
} // extern "C"
#endif
And then from http://forums.obdev.at/viewtopic.php?f=8&t=4022 add this to "usbconfig.h":
#define USB_INTR_VECTOR INT0_vect