HID Control of a Web Page

Pages
Contributors: Nate
Favorited Favorite 15

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
  }
}

Writing a packet to HID bus

See the WR packet?

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).

Teensy with LED and Trimpot

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.

Teensy with BJT controlled motor

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:

Wire the transistor as follows:

Basic motor control circuit

If you need a refresher on schematic symbols, visit How to read a schematic.

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.