HID Control of a Web Page
Sending HID Packets
We've shown how to read sensors and display the readings. Now, let's control a motor from a webpage!
Download and compile the HID_TXRX_Example
code onto the Teensy.
language:c
/*
Example HID to Webcontrol - Basic transmit and receive over HID
By: Nathan Seidle (SparkFun Electronics)
Date: January 6th, 2014
This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).
This example shows how to control output pins based on the incoming HID packets.
*/
//Declare hardware pins
byte statLED = 6; //Teeny 2.0++ has status LED on pin 6
byte trimpot = A7; //For the trimpot. This is labeled F7 on the Teensy2.0++
byte port1Pin = 16; //PWM output for motor. Labeled C6 on Teensy 2.0++
byte port2Pin = 15; //PWM output for LED. Labaled C5 on Teensy 2.0++
//Global variables
int trimpotValue; //Contains analog to digital value of the trimpot
long readingTime; //Controls how often we send our trimpot value to the computer
unsigned int counter = 0; //Used to show packet count sent to computer
byte outgoingBuffer[16]; //Holds the 16 bytes sent to computer
//These variables contain the commands from the computer
int port1Value;
int port2Value;
//These keep track of what has changed. We don't want to update analogWrites at 33Hz
int oldPort1Value;
int oldPort2Value;
//These defines help break the incoming 16 byte frame into pieces
#define PORT1 0
#define PORT2 2
void setup()
{
//Setup input/sensor ports
pinMode(trimpot, INPUT_PULLUP);
//Setup output ports
pinMode(port1Pin, OUTPUT);
pinMode(port2Pin, OUTPUT);
analogWrite(port1Pin, 0);
analogWrite(port2Pin, 15);
//Blink to show we're alive
pinMode(statLED, OUTPUT);
for(int i = 0 ; i < 5 ; i++)
{
digitalWrite(statLED, HIGH);
delay(25);
digitalWrite(statLED, LOW);
delay(25);
}
digitalWrite(statLED, LOW); //Turn off LED
//Fill the buffer with zeros
for(int x = 0 ; x < 16 ; x++)
outgoingBuffer[x] = 0;
readingTime = millis();
}
void loop()
{
byte incomingBuffer[16];
//Check to see if we have received a frame from the computer
int response = RawHID.recv(incomingBuffer, 0); // 0ms timeout = do not wait
if (response > 0)
{
//Toggle the status LED when we receive a frame from the computer
if(digitalRead(statLED) == LOW)
digitalWrite(statLED, HIGH);
else
digitalWrite(statLED, LOW);
//Decode the incoming buffer
port1Value = incomingBuffer[PORT1] << 8 | incomingBuffer[PORT1 + 1]; //Combine MSB/LSB into int
port2Value = incomingBuffer[PORT2] << 8 | incomingBuffer[PORT2 + 1];
//Push values to output pins
updateOutputs();
}
//Send sensor readings to computer every 30ms, about 33Hz
if (millis() - readingTime > 30) {
readingTime += 30;
//Read inputs
trimpotValue = averageAnalogRead(trimpot);
//Fill the head with the analog value
outgoingBuffer[0] = trimpotValue >> 8;
outgoingBuffer[1] = trimpotValue & 0xFF;
//Fill the tail end with a counter
outgoingBuffer[14] = counter >> 8; //MSB
outgoingBuffer[15] = counter & 0xFF; //LSB
counter++;
//Send the read frame to the computer
response = RawHID.send(outgoingBuffer, 100);
}
}
//Takes a series of readings on a given pin
//Returns the average
int averageAnalogRead(int pinToRead)
{
byte numberOfReadings = 8;
unsigned int runningValue = 0;
for(int x = 0 ; x < numberOfReadings ; x++)
runningValue += analogRead(pinToRead);
runningValue /= numberOfReadings;
return(runningValue);
}
//If the new port values are different from before then upate the output pins
void updateOutputs(void)
{
//Port 1 - PWM control
port1Value = map(port1Value, 0, 100, 0, 255); //Slider is 0 to 100. PWM goes from 0 to 255
if(port1Value != oldPort1Value) //Only send new values to pin if the value is indeed new
{
oldPort1Value = port1Value; //Remeber this new value
if(port1Value > 3) //Have a minimum threshold
analogWrite(port1Pin, port1Value);
else
analogWrite(port1Pin, 0); //Turn off this pin if value is too close to zero
}
//Port 2 - PWM control
port2Value = map(port2Value, 0, 100, 0, 255); //PWM goes from 0 to 255
if(port2Value != oldPort2Value) //Only send new values to pin if the value is indeed new
{
oldPort2Value = port2Value; //Remeber this new value
if(port2Value > 3) //Have a minimum threshold
analogWrite(port2Pin, port2Value);
else
analogWrite(port2Pin, 0); //Turn off this pin if value is too close to zero
}
}
Let's double check that the proper code is running. Open SimpleHID, and select the Teensyduino from the list. Once you see data scrolling past, type 32
in the fourth byte, and hit Write. The LED on the Teensy should toggle. The status LED will toggle every time a HID frame is received from the computer. And as a bonus, by writing 0x32 into the fourth byte, you just set pin C5 to a 50% duty cycle (0x32 in HEX is 50 in decimal).
To prove it, add an LED to pin C5. The long leg of the LED is the anode and connects to pin C5. The cathode (short leg) connects to GND. Next, write a few different values (for example 05, 32, FE, and 00) into the fourth byte, and send them from SimpleHID. You should see the brightness of the LED change. Open the html page and slide the bottom slider up and down. You should see the brightness of the LED change, as you move the slider.
The final example requires that we use a 2N3904 transistor to control the motor. The Teensy can't drive the motor directly, but it can control the on/off valve of the 2N3904.
Grab the following parts:
- 2N3904 (or a similar NPN transistor that can handle 100mA or more)
- 330 ohm resistor (any resistor value from 100 to 5k should work)
- DC motor
Wire the transistor as follows:
Once the motor has been wired to C6, use the top slider on the example HTML page to control the speed of the motor. You can also control the speed of the motor by using SimpleHID to write values (00, 05, FE, etc) to the 2nd byte.
For extra credit, think about how you would modify the code so that both the webpage and the trimpot could control the motor speed. Think you've got it? Give it a try!
For super credit, how would you make the slider control the motor speed as well as direction? Hint: You'll probably need an h-bridge.