Renewable Energy Innovation

  • Increase font size
  • Default font size
  • Decrease font size
Email Print

No image

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:

  1. Create code in the Arduino IDE.
  2. Upload using the Teensy 1.0 board. This will create the .hex file to upload to the Minimus.
  3. Put the Minimus into bootloader mode.
  4. Upload the .hex code to the Minimus using Atmel Flip.
  5. 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:

alt

 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:

alt

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

alt

The final code worked well as can be seen in these screen shots:

alt

This was the input data sent over the serial port.

alt

 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 ***********************************
/****** This email address is being protected from spambots. You need JavaScript enabled to view it.
  ************************
/****** 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"); } }