UnoJoy on R4 card

Hi,
I've got a problem i dont seam to get a good grasp on.

I've recently bought a R4 card and are trying to setup the UnoJoy in the card for a small project.
But the thing is, when i try to upload the UnoJoy sample to the card it gives me a error.
It's telling me that the a file is not included

In file included from C:\User\XXXXXXXXXXXXXXXXXXXXXXArduino\UnoJoyWin\UnoJoyArduinoSample\UnoJoyArduinoSample.ino:2:0:

C:\User\XXXXXXXXXXXXXXXXXXXXXXArduino\UnoJoyWin\UnoJoyArduinoSample\UnoJoy.h:50:14: fatal error: util/atomic.h:
No such file or directory
#include <util/atomic.h>
^~~~~~~~~~~~~~~
compilation terminated.

exit status 1

Compilation error: util/atomic.h: No such file or directory

So, my question is.
Does anyone know how to get UnoJoy to work on an R4 board or is it imposible?
Is there a way to get this missing file?

Copy that missing into the proper directory. Tried Google? There are answers there.

That is a header file related to avr processors. The new R4 has a different type of chip on it. So codes that work at the hardware level are having to be ported to the new hardware. The core libraries were done when the board came out. But user contributed libraries are sort of trickling in. You may be stuck waiting for someone to update that library to work with the R4.

Nevermind the last reply. I forgot how UnoJoy works.

UnoJoy works by reprogramming the USB com chip on the UNO to turn it into a USB host controller. The R4 has an ESP32 as the USB controller instead. So none of that code will work.

The good news is that it isn't necessary. The R4 can act as a USB host controller on its own like a Micro or Leonardo can. It doesn't need this hack that allowed an UNO to do it. You just need to find the appropriate library for controller emulation on the R4.

I think the library you want is Xinput but I don't know if there are any examples out for the R4 yet.

Thank you so much for the helpfull information :blush:

I thougt it might be something like that, but as a noob its nice to get a good and informative awnser!
Again thank u

To the best of my knowledge the Micro and Leonardo can't be programmed to be a host only a USB client. Also it is only the R4 WI-FI that contains an ESP32 not the other R4 UNO.

Thanks. I'm just getting into the R4.

And I think i was confused and client is what I meant. The OP needs a client to emulate a game controller.

I've been working myself on reading from an x-box controller. That's USB host and the R4 can do that too from what I hear.

Hi @xzorn. Please tell us which of these two boards you are using:

You might guess that the only difference between these boards is that the UNO R4 WiFi has a Wi-Fi connectivity capability. However, as already mentioned previously in the thread, there is another relevant difference between the two boards.

The primary RA4M1 microcontroller of the UNO R4 Minima is connected directly to your computer via the USB cable. So the joystick emulation is provided directly by that chip. On the UNO R4 WiFi, the ESP32 module serves as a USB "bridge" between the computer and the primary RA4M1 microcontroller in addition to providing the Wi-Fi connectivity. So even though your sketch still runs on the RA4M1, on the UNO R4 WiFi the ESP32 module is a factor when using that board in an application such as joystick emulation.

Hi,
Thank you for the reply.

I've bought the R4 WiFi.

Okey, so it would clearly be easier for me if I'd bought the R4 Minima for my project.
Sounds like i got a challenging project a head :ok_hand:

Even more so with one of the boards that have native USB capability and have accumulated a good deal of library support due to having been around for a long time. For example:

But the newer boards have a lot more resources and there are a lot more opportunities to make significant contributions to the Arduino project by sharing the unique solutions you find while working with these boards.

There was a previous discussion about the search for an HID joystick/gamepad emulation library compatible with the UNO R4 WiFi here:

Unfortunately that thread doesn't provide you with any solutions, but maybe will at least give some idea about what has already been tried.

A generic joystick class is not difficult to create. This one is patterned on the Mouse class defined in Mouse.h/Mouse.cpp. Note the Mouse class only depends on the HID class and does not contain board specific code. Each board defines its own board dependent HID class. The Mouse class works on Uno R4 WiFi so this board has a working HID class.

The following joystick class works on Uno R4 WiFi when plugged into a Windows 11 PC. The joystick has 32 buttons, 11 axes, and 2 8-way direction pads. The limits match the limits of the Windows generic joystick device driver. The class depends only on HID.h and does not contain board specific code so it may work on other boards.

JoystickWin.h

/*
  JoystickWin.h
*/

#ifndef JOYSTICKWIN_H_
#define JOYSTICKWIN_H_

#include "HID.h"

#if !defined(_USING_HID)

#warning "Using legacy HID core (non pluggable)"

#else

#define AXIS_MIN  (0)
#define AXIS_MID  (AXIS_MAX / 2)
#define AXIS_MAX  (0xFFFFUL)
#define BUTTONS_MAX (32)

//================================================================================
//================================================================================
//  JoystickWin

// This overlays the data format specified by the HID report descriptor in
// JoystickWin.cpp
typedef struct __attribute__((__packed__)) {
  uint32_t buttons;
  uint8_t  hat1:4;
  uint8_t  hat2:4;
  uint16_t x;
  uint16_t y;
  uint16_t z;
  uint16_t rx;
  uint16_t ry;
  uint16_t rz;
  uint16_t rudder;
  uint16_t throttle;
  uint16_t accelerator;
  uint16_t brake;
  uint16_t steering;
} JoystickWin_Report_t;

class JoystickWin_ {
private:
  JoystickWin_Report_t _hid_report;
public:
  JoystickWin_(void);
  void begin(void);
  void end(void);
  void press(int b);
  void release(int b);
  void releaseAll();
  bool isPressed(int b);
  void hat1(int d);
  void hat2(int d);
  void x(uint16_t a) { _hid_report.x = a; }
  void y(uint16_t a) { _hid_report.y = a; }
  void z(uint16_t a) { _hid_report.z = a; }
  void rx(uint16_t a) { _hid_report.rx = a; }
  void ry(uint16_t a) { _hid_report.ry = a; }
  void rz(uint16_t a) { _hid_report.rz = a; }
  void rudder(uint16_t a) { _hid_report.rudder = a; }
  void throttle(uint16_t a) { _hid_report.throttle = a; }
  void accelerator(uint16_t a) { _hid_report.accelerator = a; }
  void brake(uint16_t a) { _hid_report.brake = a; }
  void steering(uint16_t a) { _hid_report.steering = a; }
  void sendReport();
};
extern JoystickWin_ JoystickWin;

#endif
#endif  // JOYSTICKWIN_H_

JoystickWin.cpp

/*
  JoystickWin.cpp
*/

#include "JoystickWin.h"

#if defined(_USING_HID)

static const uint8_t _hidReportDescriptor[] PROGMEM = {
  0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
  0x09, 0x04,        // Usage (Joystick)
  0xA1, 0x01,        // Collection (Application)
  0x85, 0x04,        //   Report ID (4)
  0x05, 0x09,        //   Usage Page (Button)
  0x19, 0x01,        //   Usage Minimum (0x01)
  0x29, 0x20,        //   Usage Maximum (0x20)
  0x15, 0x00,        //   Logical Minimum (0)
  0x25, 0x01,        //   Logical Maximum (1)
  0x75, 0x01,        //   Report Size (1)
  0x95, 0x20,        //   Report Count (32)
  0x55, 0x00,        //   Unit Exponent (0)
  0x65, 0x00,        //   Unit (None)
  0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
  0x05, 0x01,        //   Usage Page (Generic Desktop Ctrls)
  0x09, 0x39,        //   Usage (Hat switch)
  0x15, 0x00,        //   Logical Minimum (0)
  0x25, 0x07,        //   Logical Maximum (7)
  0x35, 0x00,        //   Physical Minimum (0)
  0x46, 0x3B, 0x01,  //   Physical Maximum (315)
  0x65, 0x14,        //   Unit (System: English Rotation, Length: Centimeter)
  0x75, 0x04,        //   Report Size (4)
  0x95, 0x01,        //   Report Count (1)
  0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
  0x09, 0x39,        //   Usage (Hat switch)
  0x15, 0x00,        //   Logical Minimum (0)
  0x25, 0x07,        //   Logical Maximum (7)
  0x35, 0x00,        //   Physical Minimum (0)
  0x46, 0x3B, 0x01,  //   Physical Maximum (315)
  0x65, 0x14,        //   Unit (System: English Rotation, Length: Centimeter)
  0x75, 0x04,        //   Report Size (4)
  0x95, 0x01,        //   Report Count (1)
  0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
  0x09, 0x01,        //   Usage (Pointer)
  0x15, 0x00,        //   Logical Minimum (0)
  0x27, 0xFF, 0xFF, 0x00, 0x00,  //   Logical Maximum (65534)
  0x75, 0x10,        //   Report Size (16)
  0x95, 0x06,        //   Report Count (6)
  0xA1, 0x00,        //   Collection (Physical)
  0x09, 0x30,        //     Usage (X)
  0x09, 0x31,        //     Usage (Y)
  0x09, 0x32,        //     Usage (Z)
  0x09, 0x33,        //     Usage (Rx)
  0x09, 0x34,        //     Usage (Ry)
  0x09, 0x35,        //     Usage (Rz)
  0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
  0xC0,              //   End Collection
  0x05, 0x02,        //   Usage Page (Sim Ctrls)
  0x15, 0x00,        //   Logical Minimum (0)
  0x27, 0xFF, 0xFF, 0x00, 0x00,  //   Logical Maximum (65534)
  0x75, 0x10,        //   Report Size (16)
  0x95, 0x05,        //   Report Count (5)
  0xA1, 0x00,        //   Collection (Physical)
  0x09, 0xBA,        //     Usage (Rudder)
  0x09, 0xBB,        //     Usage (Throttle)
  0x09, 0xC4,        //     Usage (Accelerator)
  0x09, 0xC5,        //     Usage (Brake)
  0x09, 0xC8,        //     Usage (Steering)
  0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
  0xC0,              //   End Collection
  0xC0,              // End Collection
};

//================================================================================
//================================================================================
// JoystickWin

JoystickWin_::JoystickWin_(void) {
  static HIDSubDescriptor node(_hidReportDescriptor, sizeof(_hidReportDescriptor));
  HID().AppendDescriptor(&node);
}

void JoystickWin_::begin(void) {
  _hid_report.buttons = 0;
  _hid_report.hat1 = 8;
  _hid_report.hat2 = 8;
  _hid_report.x = AXIS_MID;
  _hid_report.y = AXIS_MID;
  _hid_report.z = AXIS_MID;
  _hid_report.rx = AXIS_MID;
  _hid_report.ry = AXIS_MID;
  _hid_report.rz = AXIS_MID;
  _hid_report.rudder = AXIS_MID;
  _hid_report.throttle = AXIS_MID;
  _hid_report.accelerator = AXIS_MID;
  _hid_report.brake = AXIS_MID;
  _hid_report.steering = AXIS_MID;
  sendReport();
}

void JoystickWin_::end(void) {
  begin();
}

void JoystickWin_::sendReport() {
  HID().SendReport(4, (void *)&_hid_report, sizeof(_hid_report));
}

void JoystickWin_::press(int b) {
  constrain(b, 0, BUTTONS_MAX - 1);
  _hid_report.buttons |= (uint32_t)(1UL << b);
}

void JoystickWin_::release(int b) {
  constrain(b, 0, BUTTONS_MAX - 1);
  _hid_report.buttons &= ~(uint32_t)(1UL << b);
}

void JoystickWin_::releaseAll() {
  _hid_report.buttons = 0;
}

bool JoystickWin_::isPressed(int b) {
  constrain(b, 0, BUTTONS_MAX - 1);
  return ((_hid_report.buttons & (uint32_t)(1UL << b)) != 0) ? true : false;
}

void JoystickWin_::hat1(int d) {
  constrain(d, 0, 15);
  _hid_report.hat1 = d;
}

void JoystickWin_::hat2(int d) {
  constrain(d, 0, 15);
  _hid_report.hat2 = d;
}

JoystickWin_ JoystickWin;

#endif
1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.