How to use a variable from the .cpp file in the main .ino sketch?

Hello all,

I'm trying to write an Arduino program to control a small go-cart with hoverboard motors and a usb steering wheel and pedals to control it.

I have connected the steering wheel to the arduino using a 'usb host module' and using the USB Host Library 2.0 I have a proof of principle working. On each event (button press or steering angle change), I get an update of all the angle and button states.

When I need to know now is how to implement this into my main code. The library example has 3 files. A .ino, a .cpp and a .h. Unlike I'm used to, I found that my steering wheel data was being read in the .cpp file. There is a variable called 'evt' there, that has all the states. And I can get specific values with 'evt->Z2' (for steering angle), or for example 'evt-<X' for some button states.

The question: How do I access this 'evt' variable from the main .ino sketch? I have a feeling I should not be doing my modifications in the .cpp file, but strictly in the .ino file. But I don't know how the access the data from there.

Thanks in advance! Here is the code:

The .ino file:

#include <usbhid.h>
#include <hiduniversal.h>
#include <usbhub.h>

// Satisfy IDE, which only needs to see the include statment in the ino.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#endif
#include <SPI.h>

#include "hidjoystickrptparser.h"

USB Usb;
USBHub Hub(&Usb);
HIDUniversal Hid(&Usb);
JoystickEvents JoyEvents;
JoystickReportParser Joy(&JoyEvents);


void setup() {
        Serial.begin(115200);
#if !defined(__MIPSEL__)
        while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
        Serial.println("Start");

        if (Usb.Init() == -1)
                Serial.println("OSC did not start.");

        delay(200);

        if (!Hid.SetReportParser(0, &Joy))
                ErrorMessage<uint8_t > (PSTR("SetReportParser"), 1);
}

void loop() {
        Usb.Task();
}

The .cpp file:

#include "hidjoystickrptparser.h"

JoystickReportParser::JoystickReportParser(JoystickEvents *evt) :
  joyEvents(evt),
  oldHat(0xDE),
  oldButtons(0) {
  for (uint8_t i = 0; i < RPT_GEMEPAD_LEN; i++)
    oldPad[i] = 0xD;
}

// Variabels.
int angletje = 0;
int Xval = 0;
int Yval = 0;
int DPADval = 0;
int Z2val = 0;
int Rzval = 0;

// Buttons //
// X-Channel
const int btn_X_base                 = 0;    // base value (no buttons pressed)
const int btn_X_ids[] = {1, 2, 4, 8, 16, 32, 64, 128};
bool btn_X_states[]          = {0, 0, 0, 0, 0, 0, 0, 0};

// Y-Channel
const int btn_Y_base            = 224;  // base value (no buttons pressed)
const int btn_Y_ids[]           = {225, 226, 228, 232, 240};
bool btn_Y_states[]          = {0, 0, 0, 0, 0};

// D-PAD Channel
const int btn_DPAD_base         = 255;  // base value
const int btn_DPAD_ids[]        = {0, 1, 2, 3, 4, 5, 6, 7};
bool btn_DPAD_states[]          = {0, 0, 0, 0, 0, 0, 0, 0};


void JoystickReportParser::Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {
  bool match = true;

  // Checking if there are changes in report since the method was last called
  for (uint8_t i = 0; i < RPT_GEMEPAD_LEN; i++)
    if (buf[i] != oldPad[i]) {
      match = false;
      break;
    }

  // Calling Game Pad event handler
  if (!match && joyEvents) {
    joyEvents->OnGamePadChanged((const GamePadEventData*)buf);

    for (uint8_t i = 0; i < RPT_GEMEPAD_LEN; i++) oldPad[i] = buf[i];
  }

  uint8_t hat = (buf[5] & 0xF);

  // Calling Hat Switch event handler
  if (hat != oldHat && joyEvents) {
    joyEvents->OnHatSwitch(hat);
    oldHat = hat;
  }

  uint16_t buttons = (0x0000 | buf[6]);
  buttons <<= 4;
  buttons |= (buf[5] >> 4);
  uint16_t changes = (buttons ^ oldButtons);

  // Calling Button Event Handler for every button changed
  if (changes) {
    for (uint8_t i = 0; i < 0x0C; i++) {
      uint16_t mask = (0x0001 << i);

      if (((mask & changes) > 0) && joyEvents) {
        if ((buttons & mask) > 0)
          joyEvents->OnButtonDn(i + 1);
        else
          joyEvents->OnButtonUp(i + 1);
      }
    }
    oldButtons = buttons;
  }
}

void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt) {
  // Save raw values to variables.
  Xval = evt->X;
  Yval = evt->Y;
  DPADval = evt->Z1;
  Z2val = evt->Z2;
  Rzval = evt->Rz;

  // Check steering angle.
  angletje = map(evt->Z2, 0, 255, -90, 90);
  Serial.println("ANGLE:" + (String)angletje);
}

void JoystickEvents::OnHatSwitch(uint8_t hat) {
  Serial.print("Hat Switch: ");
  Serial.print(hat);
  Serial.println("");
}

void JoystickEvents::OnButtonUp(uint8_t but_id) {
  Serial.print("Up: ");
  Serial.println(but_id, DEC);
}

void JoystickEvents::OnButtonDn(uint8_t but_id) {
  Serial.print("Dn: ");
  Serial.println(but_id, DEC);
}

The .h file:

#if !defined(__HIDJOYSTICKRPTPARSER_H__)
#define __HIDJOYSTICKRPTPARSER_H__

#include <usbhid.h>

struct GamePadEventData {
        uint8_t X, Y, Z1, Z2, Rz;
};

class JoystickEvents {
public:
        virtual void OnGamePadChanged(const GamePadEventData *evt);
        virtual void OnHatSwitch(uint8_t hat);
        virtual void OnButtonUp(uint8_t but_id);
        virtual void OnButtonDn(uint8_t but_id);
};

#define RPT_GEMEPAD_LEN		5

class JoystickReportParser : public HIDReportParser {
        JoystickEvents *joyEvents;

        uint8_t oldPad[RPT_GEMEPAD_LEN];
        uint8_t oldHat;
        uint16_t oldButtons;

public:
        JoystickReportParser(JoystickEvents *evt);

        virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
};

#endif // __HIDJOYSTICKRPTPARSER_H__

it looks like evt is an argument that you need to pass to those functions.

it is type GamePadEventData defined in the .h

you can create a GamePadEventData variable in your .ino file and pass a pointer to it (e.g. & evtData) in the functions declared in the .h file

not sure i follow the code in the .cpp

Thanks for the tip. Could you maybe expand a bit on how to do this?

So in the .ino, I create a new variable. What type (int, byte, char, etc)? Or should it be "GamePadEventData" type, like this?:

GamePadEventData mydata = ???;

And how do "pass a pointer" in the .h file?

???

Thanks!

P.S. I also don't understand the .cpp. But all the relevant magic seems to be in the OnGamepadChanged part. That is also the only part I made some changes to. The rest is the standard library example 'USBHID Joystick'.

shouldn't you call one of the functions in the .cpp file?

that function takes a ptr (i.e. evt) which is operates on. code in the .ino file can look at the components of the GamePadEventData, X, Y, Z1, Z2, Rz

see how Usb is passed as a ptr to Hub(&Usb) and JoyEvents is passed as a pointer in Joy(&JoyEvents)

not sure i understand what the code in the .cpp file is doing. looks like callback functions

I tried the following in the .ino, based on your hints:

GamePadEventData gped;

void loop() 
{
Serial.println(gped.Z2);
}

Its does compile, but the value is always '0'.

what's the error?

I don't have time to download the library and crawl through its code. But, it looks like all the action happens via the Task() function of the USB class. I'm guessing that it manages the USB interface and invokes your custom Parse() callback (that you previously registered using the SetReportParser() function) as required.

You must write your Parse() function as a member function of a custom class that inherits from the 'HIDReportParser' class. You can see this in the example's hidjoystickrptparser.h file.

This is not Beginner-Level C++ coding.

@gcjr, There is no error. But the steering angle value I'm trying to read is always 0 this way. While the evt->Z2 value in the.cpp file does give the correct values as I turn the wheel. I guess I didn't do it correctly.

@gfvalvo
Thanks. But I'm afraid that goes a bit over my head.

Probably a very bad idea, but:
Could I perhaps connect 2 digital io pins together with a series resistor, use them as softwareserial. And from the .cpp file, send the values over serial as they come in. And in the .ino file, read the serial data coming in?
It feels extremely crude and there HAS to be a better way. But for now, it's the only way I think I could realize with my skill level.

sorry, misread the comment

presumably a ptr to the local variable is passed to a function that updates the values in the variables

as @gfvalvo suggests, this may be too confusing for a newbie

Bit late. Sorry.
I guess I'll just use an extra Arduino in my project. One will only run the usb steering wheel and pass through the values via serial to another Arduino or STM that will do the rest.

I still find it weird though, that such a trivial thing can be so hard. There must be tons of people using an old USB joystick to control their arduino projects. What could be the reason that the library author(s) would not include an example of how to access the values from the main ino sketch?

It's not that hard. But as I mentioned, it requires C++ coding above the Beginner level.

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