Minimus, Arduino IDE and USB
I have mentioned the Minimus AVR before on this blog. It is a very low-cost ATMEL micrcontroller board with a USB bootloader.
This post describes using the device as a HID (Human Interface Device) such as a keyboard or mouse. A challenge was set by a friend to take serial commands and, depending upon the command, output different strings to a computer via USB as if it was a keyboard plugged in.
This post gives some detail about issues and basic code for you to use.
Programming the Minimus
My blog post and work done by Spencer Owen gives info on getting the programming flow using the Arduino IDE, the TeensyDuino Arduino add-on and ATMEL flip to upload the .hex file.
The workflow is:
- Create code in the Arduino IDE.
- Upload using the Teensy 1.0 board. This will create the .hex file to upload to the Minimus.
- Put the Minimus into bootloader mode.
- Upload the .hex code to the Minimus using Atmel Flip.
- Reset the Minimus and it should run the code.
This workflow is OK for me but is a bit of a hassle. One day I’ll sort out some kind of makefile for this.
Note: This only works for Arduino IDE version 1.0.4. The Teensy is not yet supported on 1.0.5.
The challenge
The code needed to constantly monitor a serial port, wait for a certain set of characters (in this case ###~~~) and then write different lines as a keyboard, depending upon the number ofter the character set.
For example: if the received code was “###~~~3” then the keyboard should write “BUTTON 3 PRESSED”
Minimus as HID device
First I wanted to write as if the Minimus was a keyboard. The Teensy add-on gives some examples including a keyboard write example. This can be found by looking at the following example:
Here is the simple USB keyboard example.
/* Simple USB Keyboard Example Teensy becomes a USB keyboard and types characters You must select Keyboard from the "Tools > USB Type" menu This example code is in the public domain. */ int count = 0; void setup() { Serial.begin(9600); delay(1000); } void loop() { // Your computer will receive these characters from a USB keyboard. Keyboard.print("Hello World "); Keyboard.println(count); // You can also send to the Arduino Serial Monitor Serial.println(count); // increment the count count = count + 1; // typing too rapidly can overwhelm a PC delay(5000); }
It will write “Hello World ” to the keyboard every 5 seconds or so.
To upload it to the Minimus you must create the .hex file but telling the Arduino IDE that the output device will act as an HID device. This is done by setting Tools -> USB Type to “Keyboard + Mouse”, as shown here:
Ensure that you set this back to “Serial” before you use the serial monitor, or else you will not get any data and will think its not working (there is no error message).
This worked great and (slightly annoyingly) started writing “Hello World” anywhere where the cursor was active.
Problems encountered
I had already produced some code which worked on an Arduino to monitor the serial lines and perform actions depending upon the strings received. This had been working fine on the Arduino (ATMega328 IC). I changed this code to perform Keyboard.print() functions when different serial strings were sent. This code compiled OK with the work flow above to put it onto the Minimus. The problem was that it did not seem to perform correctly.
I was using the string functions including the .substring function to do the comparison on the serial data. This has worked fine for me before. The problem was that with one or two string searches then it worked fine, with three or more string searches then it did not work.
This was strange to me as the code compiled and uploaded correctly and no warnings were given.
When going through the problem with fellow Nottingham hackspace member Mouse, he suggested that the sting functions were very memory intensive and this could be the problem.
We re-wrote the code using a char array (rather than a sting) and used the strncmp function to compare a number of chars within the array. This is far less memory intensive.
It worked perfectly. So this is a bit of a warning to try and write memory efficient code for this device. I was using the MinimusAVR v1 with 16kB, rather than the newer version which have 32kB.
Final code
The final code worked well as can be seen in these screen shots:
This was the input data sent over the serial port.
The final code is available to download as a sketch here, or written here.
Please feel free to use and adapt for your own purposes.
/******************************************************** /****** Read Serial Data with the Minimus *************** /****** by Matt Little ********************************** /****** Date: 18/3/14 *********************************** /****** info@re-innovation.co.uk ************************ /****** www.re-innovation.co.uk ************************* /******************************************************** This will read serial commands via a software serial. It will update the colour of the LED board depending upon those commands. The minimus pin outputs are: Minimus: Arduino Equivalent: Connection to: PC2 20 PD0 0 PD1 1 PD2 2 Rx PD3 3 Tx PD4 4 PD5 5 PD6 6 PD7 7 PB0 8 Software Rx PB1 9 Software Tx GND PB2 10 PB3 11 PB4 12 PB5 13 PB6 14 PB7 15 PC7 16 PC6 17 RESET PC5 18 PC4 19 Vcc see www.re-innovation.co.uk for more details Modified: 18/3/14 Initial code written - Matt Little 22/3/14 Sorted to read software serial - Matt Little */ #include <stdlib.h> #include <string.h> #include <SoftwareSerial.h> #define BUFSIZ 7 //// Software Serial //SoftwareSerial mySerial(8, 9); // RX, TX (Pins PB0 and PB1 on minimus) // This line defines a "Uart" object to access the serial port HardwareSerial Uart = HardwareSerial(); boolean dataAvailable=LOW; // Flag to show data is available // ****** Serial Data Read*********** // Variables for the serial data read char str_buffer[BUFSIZ+1] = ""; // This is the holder for the string which we will display int counter=0; const int ledPin = 6; // Minimus has LED on pin 6 (PD6) int delayCounter = 0; void setup() { // initialize the digital pin as an output. pinMode(ledPin, OUTPUT); Serial.begin(9600); // USB, communication to PC or MacALIVE Keyboard.begin(); Uart.begin(9600); // Set up a serial output for data display and changing parameters Uart.println("Started Up"); } void loop() { getData(); // Check the serial port for data if(dataAvailable==HIGH) { // Do something here if data is available sortData(); // Go to the subroutine to sort the data dataAvailable=LOW; } delay(10); // Just slow everything down counter++; if(counter<=100) { digitalWrite(ledPin,HIGH); } else if(counter<200&&counter>100) { digitalWrite(ledPin,LOW); } else if(counter>=200) { counter=0; } else { counter=0; } } // **********************GET DATA SUBROUTINE***************************************** // This sub-routine picks up and serial string sent to the device and sorts out a power string if there is one // All values are global, hence nothing is sent/returned void getData() { // **********GET DATA**********************************ALIVE********* // We want to find the bit of interesting data in the serial data stream // All the data arrives as serial commands via the serial interface. // Data is in the format ###~~~X where X is a number from 0 to 9. if (Uart.available() > 0) { for(int i=0; i<BUFSIZ; i++) // Read in the BUFSIZ chars - this is the data { dataAvailable=HIGH; // Tell the rest of the program there is data available str_buffer[i] = Uart.read(); delay(10); } str_buffer[BUFSIZ] = 0; } else { dataAvailable=LOW; // Tell the rest of the program there is data available } } // **********************SORT DATA SUBROUTINE***************************************** // This sub-routine takes the read-in data string (12 char, starting with a) and does what is required with it // The str-buffer is global so we do not need to send it to the routine void sortData() { // Valid messages have a prefix: ###~~~ Uart.println("sortData"); if(strncmp(str_buffer, "###~~~", 6)==0) { Uart.println(str_buffer); switch(str_buffer[6]) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': Keyboard.write("BUTTON "); Keyboard.write(str_buffer[6]); Keyboard.write(" PRESSED"); break; default: Uart.println("Invalid button"); break; } } else { Uart.println("Invalid message"); } }
Hi,
Where can buy it?, the page minimususb is down….
Thanks
I think they have some here:
http://www.ck3.co.uk/minimus-32-avr-atmel-atmega32u2-usb-dev-board.html