Leostick Joystick? Yes, and here's how.

A shrunk down Leonardo-compatible board, thumb drive sized with native USB support. [Product info]
Neovin
Posts: 2
Joined: Tue Mar 18, 2014 7:07 pm

Furthering the quest for higher resolution

Post by Neovin » Tue Mar 18, 2014 7:32 pm

Hi sorry to bump this thread, am really struggling to use a full 10 bit range input for the axis and i was wondering if you could help. What i have working so far is


HID.cpp

Code: Select all


/* Copyright (c) 2011, Peter Barrett
**
** Permission to use, copy, modify, and/or distribute this software for
** any purpose with or without fee is hereby granted, provided that the
** above copyright notice and this permission notice appear in all copies.
**
** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
** SOFTWARE.
*/

#include "Platform.h"
#include "USBAPI.h"
#include "USBDesc.h"

#if defined(USBCON)
#ifdef HID_ENABLED

//#define RAWHID_ENABLED

//	Singletons for mouse and keyboard

Mouse_ Mouse;
Keyboard_ Keyboard;
// *** Add a joystick too
Joystick_ Joystick;

//================================================================================
//================================================================================

//	HID report descriptor

#define LSB(_x) ((_x) & 0xFF)
#define MSB(_x) ((_x) >> 8)

#define RAWHID_USAGE_PAGE	0xFFC0
#define RAWHID_USAGE		0x0C00
#define RAWHID_TX_SIZE 64
#define RAWHID_RX_SIZE 64

extern const u8 _hidReportDescriptor[] PROGMEM;
const u8 _hidReportDescriptor[] = {

	//	Mouse
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)	// 54
    0x09, 0x02,                    // USAGE (Mouse)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x85, 0x01,                    //     REPORT_ID (1)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x75, 0x05,                    //     REPORT_SIZE (5)
    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x09, 0x38,                    //     USAGE (Wheel)
    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x81, 0x06,                    //     INPUT (Data,Var,Rel)
    0xc0,                          //   END_COLLECTION
    0xc0,                          // END_COLLECTION

	//	Keyboard
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)	// 47
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x85, 0x02,                    //   REPORT_ID (2)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)

	0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)

	0x95, 0x08,                    //   REPORT_COUNT (8)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)

	0x95, 0x06,                    //   REPORT_COUNT (6)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x65,                    //   LOGICAL_MAXIMUM (101)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)

	0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0xc0,                          // END_COLLECTION

#if RAWHID_ENABLED
	//	RAW HID
	0x06, LSB(RAWHID_USAGE_PAGE), MSB(RAWHID_USAGE_PAGE),	// 30
	0x0A, LSB(RAWHID_USAGE), MSB(RAWHID_USAGE),

	0xA1, 0x01,				// Collection 0x01
    0x85, 0x03,             // REPORT_ID (3)
	0x75, 0x08,				// report size = 8 bits
	0x15, 0x00,				// logical minimum = 0
	0x26, 0xFF, 0x00,		// logical maximum = 255

	0x95, 64,				// report count TX
	0x09, 0x01,				// usage
	0x81, 0x02,				// Input (array)

	0x95, 64,				// report count RX
	0x09, 0x02,				// usage
	0x91, 0x02,				// Output (array)
	0xC0					// end collection
#endif

   // JOYSTICK HID
   0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
   0x09, 0x04,                    // USAGE (Joystick)
   0xA1, 0x01,                 // Collection (Application)
   0x85, 0x04,                    // REPORT_ID (4)

   0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
   0x09, 0x01,                    // USAGE (Pointer)
   0xA1, 0x00,                    // Collection (Physical)
   0x09, 0x30,                    // USAGE (x)
   0x09, 0x31,                    // USAGE (y)
   0x15, 0x00,                    // LOGICAL_MINIMUM (0)
   0x26, 0xFF, 0x00,              // LOGICAL_MAXIMUM (255)
   0x75, 0x08,                    // REPORT_SIZE (8)
   0x95, 0x02,                    // REPORT_COUNT (2)
    0x81, 0x02,                    // INPUT (Data,Var,Abs)
    0xC0,                          // END_COLLECTION

   0x05, 0x02,                    // USAGE_PAGE (Simulation controls)
   0x09, 0xBB,                    // USAGE (Throttle)
   0x15, 0x00,                    // LOGICAL_MINIMUM (0)
   0x26, 0xFF, 0x00,              // LOGICAL_MAXIMUM (255)
   0x75, 0x08,                    // REPORT_SIZE (8)
   0x95, 0x01,                    // REPORT_COUNT (1)
    0x81, 0x02,                    // INPUT (Data,Var,Abs)

0x05, 0x02,                    // USAGE_PAGE (Simulation Controls)
    0x09, 0xba,                    // USAGE (Rudder)
    0x15, 0x00,                    // LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              // LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    // REPORT_SIZE (8)
    0x95, 0x01,                    // REPORT_COUNT (1)
    0x81, 0x02,                     // INPUT (Data,Var,Abs)


    0x05, 0x09,                    // USAGE_PAGE (Button)
    0x19, 0x01,                    // USAGE_MINIMUM (button 1)
    0x29, 0x08,                    // USAGE_MAXIMUM (button 8)
    0x15, 0x00,                    // LOGICAL_MINIMUM (0)
    0x25, 0x01,                    // LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    // REPORT_SIZE (1)
    0x95, 0x08,                    // REPORT_COUNT (8)
    0x55, 0x00,                    // UNIT_EXPONENT (0)
    0x65, 0x00,                    // UNIT (None)
    0x81, 0x02,                    // Input (Data,Var,Abs)
   0xC0              // end collection

};

extern const HIDDescriptor _hidInterface PROGMEM;
const HIDDescriptor _hidInterface =
{
	D_INTERFACE(HID_INTERFACE,1,3,0,0),
	D_HIDREPORT(sizeof(_hidReportDescriptor)),
	D_ENDPOINT(USB_ENDPOINT_IN (HID_ENDPOINT_INT),USB_ENDPOINT_TYPE_INTERRUPT,0x40,0x01)
};

//================================================================================
//================================================================================
//	Driver

u8 _hid_protocol = 1;
u8 _hid_idle = 1;

#define WEAK __attribute__ ((weak))

int WEAK HID_GetInterface(u8* interfaceNum)
{
	interfaceNum[0] += 1;	// uses 1
	return USB_SendControl(TRANSFER_PGM,&_hidInterface,sizeof(_hidInterface));
}

int WEAK HID_GetDescriptor(int i)
{
	return USB_SendControl(TRANSFER_PGM,_hidReportDescriptor,sizeof(_hidReportDescriptor));
}

void WEAK HID_SendReport(u8 id, const void* data, int len)
{
	USB_Send(HID_TX, &id, 1);
	USB_Send(HID_TX | TRANSFER_RELEASE,data,len);
}

bool WEAK HID_Setup(Setup& setup)
{
	u8 r = setup.bRequest;
	u8 requestType = setup.bmRequestType;
	if (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType)
	{
		if (HID_GET_REPORT == r)
		{
			//HID_GetReport();
			return true;
		}
		if (HID_GET_PROTOCOL == r)
		{
			//Send8(_hid_protocol);	// TODO
			return true;
		}
	}

	if (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType)
	{
		if (HID_SET_PROTOCOL == r)
		{
			_hid_protocol = setup.wValueL;
			return true;
		}

		if (HID_SET_IDLE == r)
		{
			_hid_idle = setup.wValueL;
			return true;
		}
	}
	return false;
}

//================================================================================
//================================================================================
// Joystick
//  Usage: Joystick.move(x, y, throttle, buttons)
//  x & y forward/left = 0, centre = 127, back/right = 255
//  throttle max = 0, min = 255
//  8 buttons packed into 1 byte

Joystick_::Joystick_()
{
}

void Joystick_::move(uint8_t x, uint8_t y, uint8_t Rudder, uint8_t throttle, uint8_t buttons)
{
    u8 j[5];
    j[0] = x;
    j[1] = y;
    j[2] = Rudder;
    j[3] = throttle;
    j[4] = buttons;
    //HID_SendReport(Report number, array of values in same order as HID descriptor, length)
    HID_SendReport(4, j, 5);
}

//================================================================================
//================================================================================
//	Mouse

Mouse_::Mouse_(void) : _buttons(0)
{
}

void Mouse_::begin(void)
{
}

void Mouse_::end(void)
{
}

void Mouse_::click(uint8_t b)
{
	_buttons = b;
	move(0,0,0);
	_buttons = 0;
	move(0,0,0);
}

void Mouse_::move(signed char x, signed char y, signed char wheel)
{
	u8 m[4];
	m[0] = _buttons;
	m[1] = x;
	m[2] = y;
	m[3] = wheel;
	HID_SendReport(1,m,4);
}

void Mouse_::buttons(uint8_t b)
{
	if (b != _buttons)
	{
		_buttons = b;
		move(0,0,0);
	}
}

void Mouse_::press(uint8_t b)
{
	buttons(_buttons | b);
}

void Mouse_::release(uint8_t b)
{
	buttons(_buttons & ~b);
}

bool Mouse_::isPressed(uint8_t b)
{
	if ((b & _buttons) > 0)
		return true;
	return false;
}

//================================================================================
//================================================================================
//	Keyboard

Keyboard_::Keyboard_(void)
{
}

void Keyboard_::begin(void)
{
}

void Keyboard_::end(void)
{
}

void Keyboard_::sendReport(KeyReport* keys)
{
	HID_SendReport(2,keys,sizeof(KeyReport));
}

extern
const uint8_t _asciimap[128] PROGMEM;

#define SHIFT 0x80
const uint8_t _asciimap[128] =
{
	0x00,             // NUL
	0x00,             // SOH
	0x00,             // STX
	0x00,             // ETX
	0x00,             // EOT
	0x00,             // ENQ
	0x00,             // ACK
	0x00,             // BEL
	0x2a,			// BS	Backspace
	0x2b,			// TAB	Tab
	0x28,			// LF	Enter
	0x00,             // VT
	0x00,             // FF
	0x00,             // CR
	0x00,             // SO
	0x00,             // SI
	0x00,             // DEL
	0x00,             // DC1
	0x00,             // DC2
	0x00,             // DC3
	0x00,             // DC4
	0x00,             // NAK
	0x00,             // SYN
	0x00,             // ETB
	0x00,             // CAN
	0x00,             // EM
	0x00,             // SUB
	0x00,             // ESC
	0x00,             // FS
	0x00,             // GS
	0x00,             // RS
	0x00,             // US

	0x2c,		   //  ' '
	0x1e|SHIFT,	   // !
	0x34|SHIFT,	   // "
	0x20|SHIFT,    // #
	0x21|SHIFT,    // $
	0x22|SHIFT,    // %
	0x24|SHIFT,    // &
	0x34,          // '
	0x26|SHIFT,    // (
	0x27|SHIFT,    // )
	0x25|SHIFT,    // *
	0x2e|SHIFT,    // +
	0x36,          // ,
	0x2d,          // -
	0x37,          // .
	0x38,          // /
	0x27,          // 0
	0x1e,          // 1
	0x1f,          // 2
	0x20,          // 3
	0x21,          // 4
	0x22,          // 5
	0x23,          // 6
	0x24,          // 7
	0x25,          // 8
	0x26,          // 9
	0x33|SHIFT,      // :
	0x33,          // ;
	0x36|SHIFT,      // <
	0x2e,          // =
	0x37|SHIFT,      // >
	0x38|SHIFT,      // ?
	0x1f|SHIFT,      // @
	0x04|SHIFT,      // A
	0x05|SHIFT,      // B
	0x06|SHIFT,      // C
	0x07|SHIFT,      // D
	0x08|SHIFT,      // E
	0x09|SHIFT,      // F
	0x0a|SHIFT,      // G
	0x0b|SHIFT,      // H
	0x0c|SHIFT,      // I
	0x0d|SHIFT,      // J
	0x0e|SHIFT,      // K
	0x0f|SHIFT,      // L
	0x10|SHIFT,      // M
	0x11|SHIFT,      // N
	0x12|SHIFT,      // O
	0x13|SHIFT,      // P
	0x14|SHIFT,      // Q
	0x15|SHIFT,      // R
	0x16|SHIFT,      // S
	0x17|SHIFT,      // T
	0x18|SHIFT,      // U
	0x19|SHIFT,      // V
	0x1a|SHIFT,      // W
	0x1b|SHIFT,      // X
	0x1c|SHIFT,      // Y
	0x1d|SHIFT,      // Z
	0x2f,          // [
	0x31,          // bslash
	0x30,          // ]
	0x23|SHIFT,    // ^
	0x2d|SHIFT,    // _
	0x35,          // `
	0x04,          // a
	0x05,          // b
	0x06,          // c
	0x07,          // d
	0x08,          // e
	0x09,          // f
	0x0a,          // g
	0x0b,          // h
	0x0c,          // i
	0x0d,          // j
	0x0e,          // k
	0x0f,          // l
	0x10,          // m
	0x11,          // n
	0x12,          // o
	0x13,          // p
	0x14,          // q
	0x15,          // r
	0x16,          // s
	0x17,          // t
	0x18,          // u
	0x19,          // v
	0x1a,          // w
	0x1b,          // x
	0x1c,          // y
	0x1d,          // z
	0x2f|SHIFT,    //
	0x31|SHIFT,    // |
	0x30|SHIFT,    // }
	0x35|SHIFT,    // ~
	0				// DEL
};

uint8_t USBPutChar(uint8_t c);

// press() adds the specified key (printing, non-printing, or modifier)
// to the persistent key report and sends the report.  Because of the way
// USB HID works, the host acts like the key remains pressed until we
// call release(), releaseAll(), or otherwise clear the report and resend.
size_t Keyboard_::press(uint8_t k)
{
	uint8_t i;
	if (k >= 136) {			// it's a non-printing key (not a modifier)
		k = k - 136;
	} else if (k >= 128) {	// it's a modifier key
		_keyReport.modifiers |= (1<<(k-128));
		k = 0;
	} else {				// it's a printing key
		k = pgm_read_byte(_asciimap + k);
		if (!k) {
			setWriteError();
			return 0;
		}
		if (k & 0x80) {						// it's a capital letter or other character reached with shift
			_keyReport.modifiers |= 0x02;	// the left shift modifier
			k &= 0x7F;
		}
	}

	// Add k to the key report only if it's not already present
	// and if there is an empty slot.
	if (_keyReport.keys[0] != k && _keyReport.keys[1] != k &&
		_keyReport.keys[2] != k && _keyReport.keys[3] != k &&
		_keyReport.keys[4] != k && _keyReport.keys[5] != k) {

		for (i=0; i<6; i++) {
			if (_keyReport.keys[i] == 0x00) {
				_keyReport.keys[i] = k;
				break;
			}
		}
		if (i == 6) {
			setWriteError();
			return 0;
		}
	}
	sendReport(&_keyReport);
	return 1;
}

// release() takes the specified key out of the persistent key report and
// sends the report.  This tells the OS the key is no longer pressed and that
// it shouldn't be repeated any more.
size_t Keyboard_::release(uint8_t k)
{
	uint8_t i;
	if (k >= 136) {			// it's a non-printing key (not a modifier)
		k = k - 136;
	} else if (k >= 128) {	// it's a modifier key
		_keyReport.modifiers &= ~(1<<(k-128));
		k = 0;
	} else {				// it's a printing key
		k = pgm_read_byte(_asciimap + k);
		if (!k) {
			return 0;
		}
		if (k & 0x80) {							// it's a capital letter or other character reached with shift
			_keyReport.modifiers &= ~(0x02);	// the left shift modifier
			k &= 0x7F;
		}
	}

	// Test the key report to see if k is present.  Clear it if it exists.
	// Check all positions in case the key is present more than once (which it shouldn't be)
	for (i=0; i<6; i++) {
		if (0 != k && _keyReport.keys[i] == k) {
			_keyReport.keys[i] = 0x00;
		}
	}

	sendReport(&_keyReport);
	return 1;
}

void Keyboard_::releaseAll(void)
{
	_keyReport.keys[0] = 0;
	_keyReport.keys[1] = 0;
	_keyReport.keys[2] = 0;
	_keyReport.keys[3] = 0;
	_keyReport.keys[4] = 0;
	_keyReport.keys[5] = 0;
	_keyReport.modifiers = 0;
	sendReport(&_keyReport);
}

size_t Keyboard_::write(uint8_t c)
{
	uint8_t p = press(c);		// Keydown
	uint8_t r = release(c);		// Keyup
	return (p);					// just return the result of press() since release() almost always returns 1
}

#endif

#endif /* if defined(USBCON) */


USBAPI.h

Code: Select all


#ifndef __USBAPI__
#define __USBAPI__

#if defined(USBCON)

//================================================================================
//================================================================================
//	USB

class USBDevice_
{
public:
	USBDevice_();
	bool configured();

	void attach();
	void detach();	// Serial port goes down too...
	void poll();
};
extern USBDevice_ USBDevice;

//================================================================================
//================================================================================
//	Serial over CDC (Serial1 is the physical port)

class Serial_ : public Stream
{
private:
	ring_buffer *_cdc_rx_buffer;
public:
	void begin(uint16_t baud_count);
	void end(void);

	virtual int available(void);
	virtual void accept(void);
	virtual int peek(void);
	virtual int read(void);
	virtual void flush(void);
	virtual size_t write(uint8_t);
	using Print::write; // pull in write(str) and write(buf, size) from Print
	operator bool();
};
extern Serial_ Serial;

//================================================================================
//================================================================================
// Joystick
// Implemented in HID.cpp

class Joystick_
{
public:
   Joystick_();
   void move(uint8_t x, 
             uint8_t y,
             uint8_t Rudder, 
             uint8_t throttle, 
             uint8_t buttons);
};
extern Joystick_ Joystick;

//================================================================================
//================================================================================
//	Mouse

#define MOUSE_LEFT 1
#define MOUSE_RIGHT 2
#define MOUSE_MIDDLE 4
#define MOUSE_ALL (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)

class Mouse_
{
private:
	uint8_t _buttons;
	void buttons(uint8_t b);
public:
	Mouse_(void);
	void begin(void);
	void end(void);
	void click(uint8_t b = MOUSE_LEFT);
	void move(signed char x, signed char y, signed char wheel = 0);
	void press(uint8_t b = MOUSE_LEFT);		// press LEFT by default
	void release(uint8_t b = MOUSE_LEFT);	// release LEFT by default
	bool isPressed(uint8_t b = MOUSE_LEFT);	// check LEFT by default
};
extern Mouse_ Mouse;

//================================================================================
//================================================================================
//	Keyboard

#define KEY_LEFT_CTRL		0x80
#define KEY_LEFT_SHIFT		0x81
#define KEY_LEFT_ALT		0x82
#define KEY_LEFT_GUI		0x83
#define KEY_RIGHT_CTRL		0x84
#define KEY_RIGHT_SHIFT		0x85
#define KEY_RIGHT_ALT		0x86
#define KEY_RIGHT_GUI		0x87

#define KEY_UP_ARROW		0xDA
#define KEY_DOWN_ARROW		0xD9
#define KEY_LEFT_ARROW		0xD8
#define KEY_RIGHT_ARROW		0xD7
#define KEY_BACKSPACE		0xB2
#define KEY_TAB				0xB3
#define KEY_RETURN			0xB0
#define KEY_ESC				0xB1
#define KEY_INSERT			0xD1
#define KEY_DELETE			0xD4
#define KEY_PAGE_UP			0xD3
#define KEY_PAGE_DOWN		0xD6
#define KEY_HOME			0xD2
#define KEY_END				0xD5
#define KEY_CAPS_LOCK		0xC1
#define KEY_F1				0xC2
#define KEY_F2				0xC3
#define KEY_F3				0xC4
#define KEY_F4				0xC5
#define KEY_F5				0xC6
#define KEY_F6				0xC7
#define KEY_F7				0xC8
#define KEY_F8				0xC9
#define KEY_F9				0xCA
#define KEY_F10				0xCB
#define KEY_F11				0xCC
#define KEY_F12				0xCD

//	Low level key report: up to 6 keys and shift, ctrl etc at once
typedef struct
{
	uint8_t modifiers;
	uint8_t reserved;
	uint8_t keys[6];
} KeyReport;

class Keyboard_ : public Print
{
private:
	KeyReport _keyReport;
	void sendReport(KeyReport* keys);
public:
	Keyboard_(void);
	void begin(void);
	void end(void);
	virtual size_t write(uint8_t k);
	virtual size_t press(uint8_t k);
	virtual size_t release(uint8_t k);
	virtual void releaseAll(void);
};
extern Keyboard_ Keyboard;

//================================================================================
//================================================================================
//	Low level API

typedef struct
{
	uint8_t bmRequestType;
	uint8_t bRequest;
	uint8_t wValueL;
	uint8_t wValueH;
	uint16_t wIndex;
	uint16_t wLength;
} Setup;

//================================================================================
//================================================================================
//	HID 'Driver'

int		HID_GetInterface(uint8_t* interfaceNum);
int		HID_GetDescriptor(int i);
bool	HID_Setup(Setup& setup);
void	HID_SendReport(uint8_t id, const void* data, int len);

//================================================================================
//================================================================================
//	MSC 'Driver'

int		MSC_GetInterface(uint8_t* interfaceNum);
int		MSC_GetDescriptor(int i);
bool	MSC_Setup(Setup& setup);
bool	MSC_Data(uint8_t rx,uint8_t tx);

//================================================================================
//================================================================================
//	CSC 'Driver'

int		CDC_GetInterface(uint8_t* interfaceNum);
int		CDC_GetDescriptor(int i);
bool	CDC_Setup(Setup& setup);

//================================================================================
//================================================================================

#define TRANSFER_PGM		0x80
#define TRANSFER_RELEASE	0x40
#define TRANSFER_ZERO		0x20

int USB_SendControl(uint8_t flags, const void* d, int len);
int USB_RecvControl(void* d, int len);

uint8_t	USB_Available(uint8_t ep);
int USB_Send(uint8_t ep, const void* data, int len);	// blocking
int USB_Recv(uint8_t ep, void* data, int len);		// non-blocking
int USB_Recv(uint8_t ep);							// non-blocking
void USB_Flush(uint8_t ep);

#endif

#endif /* if defined(USBCON) */
Now if we were to concentrate on just the "throttle" axis i would make these changes
/////////////////////////////////////////////////////////////////////
0x05, 0x02, // USAGE_PAGE (Simulation Controls)
0x09, 0xbb, // USAGE (Throttle)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x03, // LOGICAL_MAXIMUM (1023)
0x75, 0x10, // REPORT_SIZE (16) // putting this as 16 bit for
0x95, 0x01, // REPORT_COUNT (1) // future ADC upgrade

0x81, 0x02 // INPUT (Data,Var,Abs)
/////////////////////////////////////////////////////////////////////
Joystick_::Joystick_()
{
}

void Joystick_::move(uint8_t x, uint8_t y, uint8_t Rudder, uint16_t throttle, uint8_t buttons)
{
u8 j[5];
j[0] = x;
j[1] = y;
j[2] = Rudder;
j[3] = throttle;
j[4] = buttons;
//HID_SendReport(Report number, array of values in same order as HID descriptor, length)
HID_SendReport(4, j, 5);
}
/////////////////////////////////////////////////////////////////////
class Joystick_
{
public:
Joystick_();
void move(uint8_t x,
uint8_t y,
uint8_t Rudder,
uint16_t throttle,
uint8_t buttons);
};
extern Joystick_ Joystick;
/////////////////////////////////////////////////////////////////

This does not work, it will upload but get no input registering, I'm really pulling my hair out.
Thanks, Gavin

Neovin
Posts: 2
Joined: Tue Mar 18, 2014 7:07 pm

Re: Leostick Joystick? Yes, and here's how.

Post by Neovin » Tue Mar 25, 2014 2:08 am

Please help.

angusgr
Freetronics Staff
Freetronics Staff
Posts: 853
Joined: Tue Apr 09, 2013 11:19 pm
Location: Melbourne, Australia
Contact:

Re: Leostick Joystick? Yes, and here's how.

Post by angusgr » Tue Mar 25, 2014 10:29 pm

Hi Gavin,

I'm not a USB HID expert, but I can offer some advice. Otherwise we'll need to wait for the original author to check the forums again (please don't "bump" your post again, I'm sure they'll see it the next time they come by.)

However there is one thing. The updated move() code you posted still only sends an 8-bit throttle value back to the computer, even though the descriptor says that value will be 16 bits wide. Change it to something like this:

Code: Select all

void Joystick_::move(uint8_t x, uint8_t y, uint8_t Rudder, uint16_t throttle, uint8_t buttons)
{
u8 j[6];
j[0] = x;
j[1] = y;
j[2] = Rudder;
j[3] = throttle >> 8;
j[4] = (u8)throttle;
j[5] = buttons;
//HID_SendReport(Report number, array of values in same order as HID descriptor, length)
HID_SendReport(4, j, 6);
}
This puts the upper 8-bits into element 3 of the array, and the lower 8-bits into element 4, and increases the length of the report from 5 bytes (old length) to 6. If you get nonsense values then you might need to swap those around, but I think that byte order is right for USB HID.

If this doesn't work then drake250 will hopefully come back past soon.

- Angus

Adrifran39
Posts: 2
Joined: Sun Apr 13, 2014 8:14 pm

Re: Leostick Joystick? Yes, and here's how.

Post by Adrifran39 » Sun Apr 13, 2014 8:39 pm

I've just stumbled across this forum in trying to make sense of the Arduino Leonardo I recently bought with the intention of using it to build a game controller for use with a flight simulator. I am new to microcontrollers but a little familiar with the Uno and au fait with electronics generally but find understanding the procedures I need, a little more than mind-numbing.

Firstly, does the fact that I want to use a Leonardo preclude me from using what Drake250 has developed for the LeoStick? If not, how would you download the files you provide onto the Leonardo? Remember, I am a real beginner in this and so if you can recommend a manual or tutorial that will help me understand basics with the Leonardo or the Atmega 32U4 I would be in your debt!
:oops:

Adrifran39
Posts: 2
Joined: Sun Apr 13, 2014 8:14 pm

Re: Leostick Joystick? Yes, and here's how.

Post by Adrifran39 » Tue Apr 15, 2014 8:34 pm

Hoping for a response sometime, I've been casting around and thought perhaps this link might be of interest: http://www.joysticks.ru/download/files/ ... ual_v1.pdf Originating about ten years ago, the design used an ATmega8 then an ATmega16. The Leostick and Leonardo use an ATmega32 but I think they are all from the same stable aren't they? Does this mean there is some kind of innate compatibility? Could the design be used with the ATmega16? Unfortunately the originators of the design seem to have evaporated!
:cry:

drake250
Posts: 19
Joined: Mon May 28, 2012 1:17 pm
Location: Adelaide

Re: Leostick Joystick? Yes, and here's how.

Post by drake250 » Tue Jul 01, 2014 12:38 pm

Hey Adifran, Neovin, sorry I haven't been on the forum for a while.

I don't see why you can't do what I have done with a Leonardo - they should be fairly similar if not identical in the HID stuff, but I haven't got one to test with so just guessing.

The code angus posted should work but I can't remember if the least significant bits come first or last, if you're still stuck I can drag out the laptop I was developing on and see what I can find - I have had 10 bit inputs working, so I should be able to find some example code for you. Just remember that there aren't any unused bits in the data unless you define them in the descriptor, each bit of input data in the array comes straight after the last (think of it as an array of bits), and the whole thing needs to be a multiple of 8 bits long - you can define null data in the descriptor but I always just added enough buttons to round out the number of bits. Hope that makes sense!

Drewol
Posts: 1
Joined: Mon Jan 26, 2015 6:58 pm

Re: Leostick Joystick? Yes, and here's how.

Post by Drewol » Mon Jan 26, 2015 7:03 pm

Hello, I have want to have a program tell my arduino to turn on or off some LEDs (might actually want to send a brightness value 0-255) but i cant quite figure out how i add that to the USBAPI.h and HID.cpp

Ashley James
Posts: 9
Joined: Sun Mar 08, 2015 1:00 pm

Re: Leostick Joystick? Yes, and here's how.

Post by Ashley James » Mon Apr 27, 2015 9:14 am

Drewol wrote:Hello, I have want to have a program tell my arduino to turn on or off some LEDs (might actually want to send a brightness value 0-255) but i cant quite figure out how i add that to the USBAPI.h and HID.cpp
If I understand you correctly, you want to control some LED's using a Joystick? If so you don't need to add those arguments to the USB header files, just to the Arduino sketch. I'm a bit busy atm, however if you can post what kind of result you want I can see about writing some code. As a start, you can do something like below, just a simple LOW/HIGH for Y = UP/DOWN & X = LEFT/RIGHT. I have it configured to a Joystick from a Nintendo GameCube controller where the values range from X/Y: 0 to 700 (350 is centered). I also have a sketch where you set up 8 LEDs and the LEDs light in the direction you are pressing (you can light multiple LEDs for diagonals unlike the simple one) and the LEDs will brighten the further the stick is pushed. I believe the later might be what you are asking?

Code: Select all

int RedLED = 8;
int BlueLED = 9;
int GreenLED = 10;
int WhiteLED = 11;

const int XPin = 0;
const int YPin = 1;
int X = -1;
int Y = -1;

void readJoystick() {
  X = analogRead(XPin);
  Y = analogRead(YPin);
}

void setup() {
  pinMode(RedLED, OUTPUT);
  pinMode(BlueLED, OUTPUT);
  pinMode(GreenLED, OUTPUT);
  pinMode(WhiteLED, OUTPUT);
}

void loop() {
  readJoystick();
  
  if (analogRead(X) < 100) {
    digitalWrite(RedLED, HIGH);
    digitalWrite(BlueLED, LOW);
    digitalWrite(GreenLED, LOW);
    digitalWrite(WhiteLED, LOW);
  }
  else if (analogRead(X) > 600) {
    digitalWrite(RedLED, LOW);
    digitalWrite(BlueLED, HIGH);
    digitalWrite(GreenLED, LOW);
    digitalWrite(WhiteLED, LOW);
  }
  else if (analogRead(Y) < 100) {
    digitalWrite(RedLED, LOW);
    digitalWrite(BlueLED, LOW);
    digitalWrite(GreenLED, HIGH);
    digitalWrite(WhiteLED, LOW);
  }
  else if (analogRead(Y) > 600) {
    digitalWrite(RedLED, LOW);
    digitalWrite(BlueLED, LOW);
    digitalWrite(GreenLED, LOW);
    digitalWrite(WhiteLED, HIGH);
  }
}
This article needs an update for IDE 1.6.x and above as the directories have changed:

The HID.cpp & USBAPI.h are now located in:

Code: Select all

%/Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino
The new HID descriptor that shows in 'Game Controllers' is 'USB IO Board'.
New Dir.jpg
Arduino v1.6.x Directory
I was making some circuits to convert some console controllers to USB, including Sega and Nintendo ones, which is rather easy however I'm trying with Arduino to use them also over USB. I believe I made a simple HID patch for the NES controller, if I got the Clock/Strobe/Data correct, after that I'll start with the other Nintendo ones then the Sega. If I get any success I'll post the patch, if not I'll probably post the patch anyway for troubleshooting whilst I try to work it out.

Ashley James
Posts: 9
Joined: Sun Mar 08, 2015 1:00 pm

Re: Leostick Joystick? Yes, and here's how.

Post by Ashley James » Tue Apr 28, 2015 3:26 am

I have finished the NES GamePad and it works correctly, the HID reports 4 Axes + 4 Buttons under Linux and Windows. Update: I also done a SNES GamePad, the HID reports 4 Axes + 8 Buttons under Linux and Windows.

I should note that this was written using Arduino IDE 1.6.3, LeoStick bootloader V2 and board profile v3.3, so it's up to date and is backwards compatible with other Arduino IDEs.
NES_SNES Controllers.png
NES + SNES Controller Windows
Update: I have remade the HID + Arduino Sketch which is much nicer and now supports all buttons pressed simultaneously. I have also labeled functions inside the sketch and defined the inputs from binary to decimal reversing the serial input to read B00000001 instead of B11111110. The resulting sketch is about 1/3 of the size it was.
NES_HID.zip
NES HID USB Controller
(19.44 KiB) Downloaded 181 times
Update: I have additionally converted the HID + Arduino Sketch to support the SNES GamePad also, after much hassle trying to support the 16-bit (2 byte) data for the buttons.
SNES_HID.zip
SNES HID USB Controller
(19.86 KiB) Downloaded 158 times
Edit: In line #73, where it states:

Code: Select all

JoySt.Buttons = JoySt.Buttons & (32768 & 16384 & 8192 & 4096 & 128 & 64 & 32 & 16);
I shifted the bits to the wrong space in the byte; it should actually be:

Code: Select all

JoySt.Buttons = JoySt.Buttons & (2048 & 1024 & 512 & 256 & 8 & 4 & 2 & 1);
The function is meant to release button presses after each loop (otherwise they stay on), oddly enough it seems to work without it. I'll update it after I finished doing a Nintendo GameCube and Nintendo 64 HID + Arduino Sketch.
Last edited by Ashley James on Wed Apr 29, 2015 2:07 pm, edited 3 times in total.

Ashley James
Posts: 9
Joined: Sun Mar 08, 2015 1:00 pm

Re: Leostick Joystick? Yes, and here's how.

Post by Ashley James » Wed Apr 29, 2015 8:46 am

Just for giggles, I also done a Serial.print sketch for both the NES & SNES GamePads, to demonstrate how to use them without HID. These don't really belong here as they will work for all Arduino boards, however they may be useful if you need to work out whether the controller is working before you modify the HID headers.
NES_to_SerialPrint.zip
NES Serial Print
(1012 Bytes) Downloaded 135 times
SNES_to_SerialPrint.zip
SNES Serial Print
(1.07 KiB) Downloaded 115 times

Post Reply