How do you use the Joystick class to setup a HID joystick?

Status
Not open for further replies.

deelaleo

Well-known member
I am using the joystick library included in Teensy, since the one from Arduino does not work on the Teensy.

Although I can't find API usage and documentation, and I am stuck now.

When I connect the Teensy 3.1; Windows 10 see it as a 32 buttons joystick with HAT , Zaxis, Zrotation and 2 sliders.

How do I change the number of buttons and the axis? I just need 2 axis, but I need 40 buttons.

Also I get this behavior where any button pressed does throw off the axis (I did not connect the pots to the teensy); I did attach 2 images to show what happen after I press a button.

This is how I use the joystick class; but since arduino IDE does not do autocompletion, I can't even try to parse all the properties and methods and figure things out on my own.
Code:
int button1 = 2;
int button2 = 7;

void setup() {
  Joystick.useManualSend(true);

  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);

}

void loop() {
  // the 2 axis for the pots
  Joystick.sliderLeft(analogRead(7));
  Joystick.sliderRight(analogRead(8));

  if (digitalRead(button1 == HIGH) {
    Joystick.button(1,1);
    Joystick.send_now();
    delay(10);
  } else {
    Joystick.button(1,0);
    Joystick.send_now();
    delay(10);
  }

  if (digitalRead(button2 == HIGH) {
    Joystick.button(2,1);
    Joystick.send_now();
    delay(10);
  } else {
    Joystick.button(2,0);
    Joystick.send_now();
    delay(10);
  }
 
There have been a few threads lately on customizing the Joystick output,including: https://forum.pjrc.com/threads/48222-USB-Descriptor-packet-fun

You might try doing a search on the forum for keywords like joystick and see if that helps.

As shown in the mentioned thread, you need to edit the HID information, to describe your actual data, and then edit the Joystick code to generate the data in the format you described. In that thread Paul also mentioned that Windows puts other constraints on you like the size of packets must be multiples of 8 bits.

Good luck
 
I see; Thanks KurtE for the pointer.

So you don't just change settings using the IDE via the library, but you need to write a descriptor to identify the joystick from the ground up, and the code with the implementation.

I did come up with this; after searching on the forum and grabbing most of the code made in another post; although it is my first time so I am not even sure if that is correct.

I will try this later during the day; did I miss anything in the implementation or made any error that could risk to damage the computer or the Teensy? Thanks!

Added in usb_desc.h:
Code:
#elif defined(USB_CONTROLBOX)
	#define VENDOR_ID   0x16C0
	#define PRODUCT_ID  0x0489
	#define DEVICE_CLASS    0x03
	#define MANUFACTURER_NAME	{'T','e','e','n','s','y','d','u','i','n','o'}
	#define MANUFACTURER_NAME_LEN 11
	#define PRODUCT_NAME    {'C', 'o', 'n', 't', 'r', 'o', 'l', ' ', 'B', 'o', 'x'}
	#define PRODUCT_NAME_LEN  11
	#define EP0_SIZE              64
	#define NUM_ENDPOINTS        2
	#define NUM_USB_BUFFERS    30
	#define NUM_INTERFACE        1
	#define ARCADE_INTERFACE      0 // Joystick
	#define ARCADE_ENDPOINT    1
	#define ARCADE_SIZE        16
	#define ARCADE_INTERVAL    1
	#define ARCADE_DESC_OFFSET  (9)
	#define CONFIG_DESC_SIZE  (9 + 9+9+7)
	#define ENDPOINT1_CONFIG  ENDPOINT_TRANSIMIT_ONLY

In usb_desc.c:

Code:
#ifdef ARCADE_INTERFACE
		{ 0x2200, ARCADE_INTERFACE, arcade_report_desc, sizeof(arcade_report_desc) },
		{ 0x2100, ARCADE_INTERFACE, config_descriptor + ARCADE_DESC_OFFSET, 9 },
#endif

created usb_box.h and usb_box.c from a different thread, changing only the button value:

Code:
// usb_arcade.h
#ifndef _USB_ARCADE_H_
#define _USB_ARCADE_H_

#if defined(USB_ARCADE)

#include <inttypes.h>

// C language implementation
#ifdef __cplusplus
extern "C" {
#endif
	int usb_arcade_send(void);
	// we have packets that are 6 bytes long
	extern uint8_t usb_arcade_data[6];
#ifdef __cplusplus
}
#endif

// C++ interface
#ifdef __cplusplus
class usb_arcade_class
{
private:
	static uint8_t auto_send;

public:
	void button(uint8_t button, bool val) {
		if (--button >= 40) return;
		if (val) usb_arcade_data[button >= 8 ? 5 : 4] |= (1 << (button >= 8 ? (button - 8) : button));
		else usb_arcade_data[button >= 8 ? 5 : 4] &= ~(1 << (button >= 8 ? (button - 8) : button));
		if (auto_send) usb_arcade_send();
	}

	void axis(uint8_t axis, int8_t val) {
		if (axis >= 2) return;
		if (val > 0) usb_arcade_data[axis] = 127;
		else if (val < 0) usb_arcade_data[axis] = -127;
		else usb_arcade_data[axis] = 0;
		if (auto_send) usb_arcade_send();
	}

	void setAutoSend(bool send) {
		auto_send = send;
	}

	void send() {
		usb_arcade_send();
	}
};
extern usb_arcade_class Arcade;

#endif // __cplusplus
#endif // USB_ARCADE
#endif // _USB_ARCADE_H_

Code:
// usb_arcade.c
#include "usb_dev.h"
#include "usb_arcade.h"
#include "core_pins.h" // for yield()
#include "HardwareSerial.h"
#include <string.h> // for memcpy()

#ifdef USB_ARCADE // defined by usb_dev.h -> usb_desc.h

uint8_t usb_arcade_data[6];

// Maximum number of transmit packets to queue so we don't starve other endpoints for memory
#define TX_PACKET_LIMIT 3

static uint8_t transmit_previous_timeout = 0;

// When the PC isn't listening, how long do we wait before discarding data?
#define TX_TIMEOUT_MSEC 30

#if F_CPU == 96000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 596)
#elif F_CPU == 48000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 428)
#elif F_CPU == 24000000
#define TX_TIMEOUT (TX_TIMEOUT_MSEC * 262)
#endif

int usb_arcade_send(void)
{
	uint32_t wait_count = 0;
	usb_packet_t *tx_packet;

	while (1) {
		if (!usb_configuration) {
			return -1;
		}
		if (usb_tx_packet_count(ARCADE_ENDPOINT) < TX_PACKET_LIMIT) { tx_packet = usb_malloc();           if (tx_packet) break; }       if (++wait_count > TX_TIMEOUT || transmit_previous_timeout) {
			transmit_previous_timeout = 1;
			return -1;
		}
		yield();
	}

	transmit_previous_timeout = 0;
	memcpy(tx_packet->buf, usb_arcade_data, 6);
	tx_packet->len = 6;
	usb_tx(ARCADE_ENDPOINT, tx_packet);

	return 0;
}

#endif // USB_ARCADE

The following went in usb_inst.cpp and WProgram.h for the IDE editor

Code:
#ifdef USB_ARCADE
usb_arcade_class Arcade;
uint8_t usb_arcade_class::auto_send = 0;
#endif

Code:
/* Teensyduino Core Library
 * http://www.pjrc.com/teensy/
 * Copyright (c) 2017 PJRC.COM, LLC.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * 1. The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * 2. If the Software is incorporated into a build system that allows
 * selection among a list of target devices, then similar target
 * devices manufactured by PJRC.COM must be included in the list of
 * target devices and selectable in the same manner.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef WProgram_h
#define WProgram_h

#include <stdlib.h>
#include <string.h>
#include <math.h>

// some libraries and sketches depend on this
// AVR stuff, assuming Arduino.h or WProgram.h
// automatically includes it...
#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#include "avr_functions.h"
#include "wiring.h"
#include "HardwareSerial.h"

#define DMAMEM __attribute__ ((section(".dmabuffers"), used))
#define FASTRUN __attribute__ ((section(".fastrun"), noinline, noclone ))

#ifdef __cplusplus

#include "avr_emulation.h"
#include "usb_serial.h"
#include "usb_seremu.h"
#include "usb_keyboard.h"
#include "usb_mouse.h"
#include "usb_joystick.h"
#include "usb_arcade.h"
#include "usb_midi.h"
#include "usb_rawhid.h"
#include "usb_flightsim.h"
#include "usb_mtp.h"
#include "usb_audio.h"
#include "usb_touch.h"
#include "usb_undef.h" // do not allow usb_desc.h stuff to leak to user programs

#include "WCharacter.h"
#include "WString.h"
#include "elapsedMillis.h"
#include "IntervalTimer.h"

uint16_t makeWord(uint16_t w);
uint16_t makeWord(byte h, byte l);

#define word(...) makeWord(__VA_ARGS__)

unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);

void tone(uint8_t pin, uint16_t frequency, uint32_t duration = 0);
void noTone(uint8_t pin);

// WMath prototypes
int32_t random(void);
uint32_t random(uint32_t howbig);
int32_t random(int32_t howsmall, int32_t howbig);
void randomSeed(uint32_t newseed);
void srandom(unsigned int newseed);

#include "pins_arduino.h"

#endif // __cplusplus


// Fast memcpy
#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
#ifdef __cplusplus
extern "C" {
extern void *memcpy (void *dst, const void *src, size_t count);
}
#else
extern void *memcpy (void *dst, const void *src, size_t count);
#endif
#endif


#endif // WProgram_h
 
Also where do I get the existing settings for the Joystick class? As now I only know Joystick.button(); but nothing more. Thanks and merry Christmas!
 

Awesome, thanks for the link! I can see now that the variables for the axis are just X, Y and Z.
While I learn about making my own joystick device; I just set constant value for all the analog axis to be half of 1023; so they stay centered no matter what; while the hat is set to -1, which I believe is the center. I have only 1 axis enabled, which read the value just fine, so I am good to go for now. Thanks!
 
Status
Not open for further replies.
Back
Top