Tutorial - How to change firmware on 8u2

Hey,
I'm experimenting with this today.
I implemented your code. (Thanks!)
Now I have to figure out the encoding / decoding procedure.

My idea was this: send an "identification char" (a, b, c, d)
followed by the sensor data (0-255).

So in Arduino I do "Serial.println('a')" followed by "Serial.println(serialData)". That seems simple enough.

In the 82U I have this code.

bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
                                         uint8_t* const ReportID,
                                         const uint8_t ReportType,
                                         void* ReportData,
                                         uint16_t* const ReportSize)
{
USB_JoystickReport_Data_t* JoystickReport = (USB_JoystickReport_Data_t*)ReportData;

while (Serial_IsCharReceived())
      {
      temp=Serial_RxByte();
      if (temp == "a"){JoystickReport->X  =  Serial_RxByte();}
      if (temp == "b"){JoystickReport->Y  =  Serial_RxByte();}
      if (temp == "c"){JoystickReport->Z  =  Serial_RxByte();}
      if (temp == "d"){JoystickReport->Rz =  Serial_RxByte();}
      }

*ReportSize = sizeof(USB_JoystickReport_Data_t);
      return false; 
}

But that don't work. Bummer.
Could somebody point me to why?

Comment : I have had some difficulties with Flip : I often had the error 'Address is out of range'. I don't really now why?? Does someone have an idea?

I think this might actually have to do with the size of the generated HEX file. Whenever I wrote too much code, I got over 4KB and I got this error too.

I think CALLBACK_HID_Device_CreateHIDReport has to return quickly, you don't have enough time to wait for serial data inside CALLBACK_HID_Device_CreateHIDReport. Instead you should process serial data in main, and just copy it in the callback.

Also you are assuming that the two characters will come to the serial port together - they won't. There will be a delay between the identification character and the data. However because main does other tasks (the USBTask() calls) you can't just block it while waiting for serial characters. Instead you have to code it as a state machine.

You want to use Serial.print or Serial.write rather than println as you don't want the extra newline.

The information on descriptors can be found here:
http://www.usb.org/developers/hidpage/
Unfortunately it is all very complicated and detailed. That's just the way it is.

I tried to implement the state machine you suggested Stimmer.
However, I get the "adress out of range" error in Flip.
I assume this is because I ran out of program space.
My main loop now looks like this.

int main(void)
{
      SetupHardware();
      
      LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);
      sei();
      
      for (;;)
      {
            HID_Device_USBTask(&Joystick_HID_Interface);
            USB_USBTask();
            
            if (Serial_IsCharReceived())
                  {
                  sensorIdentification =Serial_RxByte();
                  dataReceived = true;
                  }
            
            if (Serial_IsCharReceived() && dataReceived)
                  {
                  if             (sensorIdentification == "a") {sensorA =  Serial_RxByte();}
                  else if (sensorIdentification == "b") {sensorB =  Serial_RxByte();}
                  else if (sensorIdentification == "c") {sensorC =  Serial_RxByte();}
                  else if (sensorIdentification == "d") {sensorD =  Serial_RxByte();}
                  else    {Serial_RxByte();}
                  
                  dataReceived = false;
                  }

      }
}

The "create hid report" like this:

bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
                                         uint8_t* const ReportID,
                                         const uint8_t ReportType,
                                         void* ReportData,
                                         uint16_t* const ReportSize)
{
USB_JoystickReport_Data_t* JoystickReport = (USB_JoystickReport_Data_t*)ReportData;

JoystickReport->X  =  sensorA;
JoystickReport->Y  =  sensorB;
JoystickReport->Z  =  sensorC;
JoystickReport->Rz =  sensorD;


*ReportSize = sizeof(USB_JoystickReport_Data_t);
      return false; 
}

As it stands right now, nonfunctional.
Even when I try to only transmit the data for sensor A with the X-axis, so I'm doing a few things wrong I think.

You're on the right lines. The first if should be

if (Serial_IsCharReceived() && !dataReceived)

Serial_IsCharReceived and Serial_RxByte are inlines, so you can try to save space by only calling them once. Other ways to save space include removing the LED code, and shortening the product strings in Descriptors.c.

The memory is full as 98%. To get free space you can disable all the functions that use LEDs.

I'have emulated a joystick with uno:

I send 5 byte on serial to 8u2.

You can create this data:
uint8_t tampon;
uint8_t c;
uint8_t tempi[8];

And modify the code:

for (;;)
      {
            HID_Device_USBTask(&Joystick_HID_Interface);
            USB_USBTask();

            if (c==8)
                  c=0;
            if (Serial_IsCharReceived())
        {
                  tampon=Serial_RxByte();
                  if (tampon==0xFF)
                        c=0;
                  tempi[c]=tampon;
                  c+=1;
            }

And add that in the call back function

      JoystickReport->X=tempi[1];
      JoystickReport->Y=tempi[2];
      JoystickReport->Z=tempi[3];      
      JoystickReport->Rz=tempi[4];
      JoystickReport->Button1 =tempi[5];
      JoystickReport->Button2 =tempi[6];
      JoystickReport->Button3 =tempi[7];

The arduino has to send only bytes.
You have to use print function, not println that is sending others bytes after your data.
You can use a logic analyser to follow the data that are exchanged.

In this example, Uno has to send 0xFF as first byte (tempi[0]). However you can't send 0xFF as a data value or it will reset the c value.

My code now looks like this:

for (;;)
      {
            HID_Device_USBTask(&Joystick_HID_Interface);
            USB_USBTask();
            
            if (Serial_IsCharReceived() && !dataReceived)
                  {
                  sensorIdentification =Serial_RxByte();
                  dataReceived = true;
                  }
            
            if (Serial_IsCharReceived() && dataReceived)
                  {
                  uint8_t data = Serial_RxByte();
                  
                  if             (sensorIdentification == "a") {sensorA =  data;}
                  else if (sensorIdentification == "b") {sensorB =  data;}
                  else if (sensorIdentification == "c") {sensorC =  data;}
                  else if (sensorIdentification == "d") {sensorD =  data;}
                  
                  dataReceived = false;
                  }

      }

In the callback

USB_JoystickReport_Data_t* JoystickReport = (USB_JoystickReport_Data_t*)ReportData;

JoystickReport->X  =  sensorA;
JoystickReport->Y  =  sensorB;
JoystickReport->Z  =  sensorC;
JoystickReport->Rz =  sensorD;


*ReportSize = sizeof(USB_JoystickReport_Data_t);
      return false; 
}

But the values don't update in any way.
When I run make, I get some warnings though.

warning: comparison with string literal results in unspecified behavior
warning: comparison between pointer and integer

Could this be the source of my problems?

I'm trying to implement your code ant.
How do you send bytes out of the Arduino (atmega).

Serial.print(data, BYTE)

or just

Serial.print(data)

data ranging from 0-254?

sensorIdentification == "a" is wrong, it should be sensorIdentification == 'a' (single quotes instead of double) and the same for b,c and d.

I'm implementing (at least trying to) ant's code.
Now I'm finally getting some movement on the axes in windows again.
So at least something is getting through.
But nothing useful. I suspect it has something to do with the 0xFF start byte.

In uno's code I define something like that:

byte start_b;
byte b1; 
....

start_b=0xFF;
b1= ...
...

Serial.print(start_b);
Serial.print(b1);
....

Alright!
It worked.
My problem so far was that I sent out my "sensorData" as int-s instead of bytes. So now it Works.

But now, In windows, I get another problem.
The values that I put into JoystickReport->X range from 0-254.
It seems the values wrap around.
I'm testing with a potentiometer, and the values roll over from 0-63 in the windows testing screen quite a few times.

I'm not quite sure where to pinpoint this problem.

Maybe it's because you data has to be between -100 and 100.

If your range is 0-255 you should change the descriptor:

Replace:

      0x15, 0x9c,          /*     Logical Minimum (-100)                         */
      0x25, 0x64,          /*     Logical Maximum (100)                          */

by:

      0x15, 0x00,          /*     Logical Minimum (0)                         */
      0x26, 0xff, 0x00,    /*     Logical Maximum (255)                          */

I thought of this too.
Didn't make a difference.
Why did you add the extra 0x00 after 0xff by the way?
My Code, as it stands now.

Hid Descriptor

USB_Descriptor_HIDReport_Datatype_t PROGMEM JoystickReport[] =
{
      0x05, 0x01,          /* Usage Page (Generic Desktop)                       */
      0x09, 0x04,          /* Usage (Joystick)                                   */
      0xa1, 0x01,          /* Collection (Application)                           */
      0x09, 0x01,          /*   Usage (Pointer)                                  */
      0xa1, 0x00,          /*   Collection (Physical)                            */
      0x05, 0x01,          /*     Usage Page (Generic Desktop)                   */
      0x09, 0x30,          /*     Usage (X)                                      */
      0x09, 0x31,          /*     Usage (Y)                                      */
      0x09, 0x32,          /*     Usage (Z)                                      */
      0x09, 0x35,          /*     Usage (Rz)                                     */
      0x15, 0x00,          /*     Logical Minimum (0)                         */
      0x26, 0xff, 0x00,    /*     Logical Maximum (255)                          */
      0x75, 0x08,          /*     Report Size (8)                                */
      0x95, 0x04,          /*     Report Count (4)                               */ 
      0x81, 0x82,          /*     Input (Data, Variable, Absolute, Volatile)     */
      0xc0,                /*   End Collection                                   */
      0x05, 0x09,          /*   Usage Page (Button)                              */
      0x09, 0x02,          /*   Usage (Button 2)                                 */
      0x09, 0x01,          /*   Usage (Button 1)                                 */
      0x15, 0x00,          /*   Logical Minimum (0)                              */
      0x25, 0x01,          /*   Logical Maximum (1)                              */
      0x75, 0x01,          /*   Report Size (1)                                  */
      0x95, 0x02,          /*   Report Count (2)                                 */
      0x81, 0x02,          /*   Input (Data, Variable, Absolute)                 */
      0x75, 0x06,          /*   Report Size (6)                                  */
      0x95, 0x01,          /*   Report Count (1)                                 */
      0x81, 0x01,          /*   Input (Constant)                                 */
      0xc0                 /* End Collection                                     */
};
typedef struct
            {
                  int8_t  X; /**< Current absolute joystick X position, as a signed 8-bit integer */
                  int8_t  Y; /**< Current absolute joystick Y position, as a signed 8-bit integer */
                  int8_t  Z; /**< Current absolute joystick Z position, as a signed 8-bit integer */
                  int8_t  Rz; /**< Current absolute joystick Rz position, as a signed 8-bit integer */
                  uint8_t Button; /**< Bit mask of the currently pressed joystick buttons */
            } USB_JoystickReport_Data_t;

Data Holders

uint8_t dataIn;
uint8_t data[5];
uint8_t counter;

Main loop

int main(void)
{
      SetupHardware();
      
      //LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);
      
      sei();
      
      for (;;)
      {
            HID_Device_USBTask(&Joystick_HID_Interface);
            USB_USBTask();
            
            if (counter==5){counter=0;}
            if (Serial_IsCharReceived())
        {
                  dataIn = Serial_RxByte();
                  if (dataIn == 0xFF){counter=0;}
                  data[counter]=dataIn;
                  counter+=1;
            }


      }
}
USB_JoystickReport_Data_t* JoystickReport = (USB_JoystickReport_Data_t*)ReportData;

JoystickReport->X  =  data[1];
JoystickReport->Y  =  data[2];
JoystickReport->Z  =  data[3];
JoystickReport->Rz =  data[4];

JoystickReport->Button = 0;

*ReportSize = sizeof(USB_JoystickReport_Data_t);
      return false;

And Arduino code:

int sensorBottom[] = {0, 0, 0, 0};
int sensorTop[] = {1024, 1024, 1024, 1024};

byte dataStart = 0xFF;
byte sensorValue;

void setup() 
  {  
  // initialize serial communications at 9600 bps:
  Serial.begin(9600); 
  }

void loop() {
  // Send start byte to 8u2
  Serial.print(dataStart); 
  
  for (int i = 0; i < 4; i++)
    {
    sensorValue = analogRead(i);            

    sensorValue = map(sensorValue, sensorBottom[i], sensorTop[i], 0, 255);
    sensorValue = constrain(sensorValue, 0, 255);
    
    // print the results to the serial monitor:         
    Serial.print(sensorValue);
    }
  delay(40);    
}

Problem: the values "wrap" around.
In the Windows 7 calibrate dialog I see the values ranging from 0 to 63 and then wrap around, a total of 4 time. (This makes sense in a way, it equals 256. I'm still not sure why though...)
Also, no matter what range (I tried 64 steps instead of 265), the values still "roll around" 4 times in a row.

The problem seems to be with

sensorValue = analogRead(i);

sensorValue is a byte so its range is 0-255

then you map:

sensorValue = map(sensorValue, 0, 1024, 0, 255);

so sensorValue=255*255/1024=63

I hope this can help you

sensorValue = map(sensorValue, 0, 1024, 0, 255);

Please don't do this.
sensorValue = analogRead(i)/4;
produces the same result, in much less time.

Hey Guys,
You were right.
It was indeed the analogRead() value being pushed in a byte.
Thanks!

thanks for this tutorial. gives me some hope to be able to build my custom mouse one day :slight_smile:

ant.b - this worked great. I have a few things to figure out, but with a little modification, my uno thinks and looks like a joystick. Thank you so much for posting your tut! :slight_smile:

Clarification for reply #10. According to the datasheet, the chip looks at the HWB pin state as reset is released (on the rising edge of /Reset). So you should take the wire off the reset pin before taking the wire off the HWB pin. If you take both wires off at once, it's a matter of luck whether the HWB pin is still grounded when you release the reset pin.

So to save frustration, I recommend following the datasheet, and remove the Reset wire before you remove the HWB wire.