The two classes work perfectly independently, but not together

Hi, I am working on a code to run a sim racing gearbox (The SRT gearbox). I am writing classes to lighten the 1000 lines main .ino.
First class : SRTGearbox.h
It is the main class.. it basically only reads inputs from hall sensors.

Second class : SRTMouse.h
It reads the inputs from a joystick to emulate a mouse. It uses keyboard.h and mouse.h

**I made two codes to test each class. They work perfectly. **
But when I try to write one single code that uses both classes, the arduino disappears immediately after uploading the code. It happens with both Arduino Micro and Leonardo...

The main.ino creates a joystick object from the Joystick.h class.

The code is quite long so I put the whole project in attachment, I hope it's ok...

srt-gearbox-arduino-code-v2.6.zip (738.2 KB)

Not many members will chance downloading a zip file. Better for you if you make a minimal verifiable example that shows the problem and post it in accordance with the guidelines. Or post the whole code in code tags,

1 Like

Does either class contain blocking code?
Anything that waits for input, blocks execution until it's done.

Mouse.h and Keyboard.h are both quite capable of locking up the Arduino if used improperly. Read the documentation, as this is well-documented. Start by disabling, by commenting out code, EVERY function enabled in the init portion of the two libraries. Then enable them one-by-one. You'll quickly find which one is causing your problem.

Notes and Warnings

These core libraries allow the 32u4 and SAMD based boards (Leonardo, Esplora, Zero, Due and MKR Family) to appear as a native Mouse and/or Keyboard to a connected computer.

A word of caution on using the Mouse and Keyboard libraries: if the Mouse or Keyboard library is constantly running, it will be difficult to program your board. Functions such as Mouse.move() and Keyboard.print() will move your cursor or send keystrokes to a connected computer and should only be called when you are ready to handle them. It is recommended to use a control system to turn this functionality on, like a physical switch or only responding to specific input you can control. Refer to the Mouse and Keyboard examples for some ways to handle this.

When using the Mouse or Keyboard library, it may be best to test your output first using Serial.print(). This way, you can be sure you know what values are being reported.

1 Like

Yeah you're right, but I didn't know how to make it more simple :confused: I'll try to make it minimal and reupload it.

hm... there is no while(){}, so I don't see where it could block..

Yes probably. But i have those two features (gearbox and mouse) working perfectly together when all the functions were in a single file... Now that I have two classes, it doesn't work...

It could be due to how I written those classes... as it is the first time for me that I create classes...

delay( do nothing while holding everything else up milliseconds );

Every read/sense 'until' command blocks until finished. There are several in Arduino.

I emptied the main ino. It is almost a empty setup with empty loop, so I definitely think the problem comes from the classes (they are not used anymore in the main, and the result is still the same). Here is the SRTmouse.h :

#ifndef SRTmouse
#define SRTmouse
#include <Mouse.h>
class SRTMouse_
{
private:
  int XMousePin; 
  int YMousePin; 
  int SwitchMousePin;
  int analogcenter;
  int threshold ;
  bool onlyShowValue;      //use this to calibrate the joystick. Disables the mouse behaviour.
  bool invertX;           //invert X axis. Default is false.
  bool invertY;           //invert Y axis. Default is false.
  bool invertXY;          //invert X and Y axis. Default is false
  int thresholdUser;           
  int MouseSensitivity;
  bool keyPressed = false;
  unsigned long keyPressedTime =0;
  unsigned keyPressedDelay=100;
  unsigned delayNextPress = 300;
  bool MouseMode = true;
  unsigned long lastMove =0;
  
  void Arrow(unsigned long currentMillis);

protected:
bool mouseMode = true;

public:
  SRTMouse_(
  int XMousePinUser, 
  int YMousePinUser, 
  int SwitchMousePinUser,
  bool onlyShowValueUser,      //use this to calibrate the joystick. Disables the mouse behaviour.
  bool invertXUser,           //invert X axis. Default is false.
  bool invertYUser,           //invert Y axis. Default is false.
  bool invertXYUser,          //invert X and Y axis. Default is false
  int thresholdUser,           
  int MouseSensitivityUser);
  void set();
  void begin();
  bool getMouseMode();
  bool lastClickState = false;
  unsigned int startedToClick = 0;
  int configMouseDelay = 300;
  unsigned clickPressed = 15;
  unsigned long lastClick = 0;
  bool setAction(long currentmillis);
  void setMouse(bool sendData, unsigned long currentmillis);
    int xReading, yReading;
  int readAxis(int thisAxis
  );


};

#endif

And the SRTmouse.cpp :

#include "SRTmouse.h"
//#include <WProgram.h>
#include <Mouse.h>
#define TopSpeed  5
#include <Keyboard.h>
//v1.1

bool SRTMouse_::getMouseMode(){
  return mouseMode;
}

SRTMouse_::SRTMouse_(
  int XMousePinUser, 
  int YMousePinUser, 
  int SwitchMousePinUser,
  bool onlyShowValue,      //use this to calibrate the joystick. Disables the mouse behaviour.
  bool invertX,           //invert X axis. Default is false.
  bool invertY,           //invert Y axis. Default is false.
  bool invertXY,
  int thresholdUser,           
  int MouseSensitivityUser
  ){
  XMousePin=XMousePinUser;
  YMousePin=YMousePinUser;
  SwitchMousePin=SwitchMousePinUser;
  threshold = thresholdUser;
  MouseSensitivity =MouseSensitivityUser;
  startedToClick = 0;
  configMouseDelay = 400;
  }

void SRTMouse_::begin(){
  pinMode(XMousePin, INPUT);
  pinMode(YMousePin, INPUT);
  pinMode(SwitchMousePin, INPUT_PULLUP);
  Mouse.begin();
  Keyboard.begin();
}

bool SRTMouse_::setAction(long time){
  return true;
}

int SRTMouse_::readAxis(int thisAxis) {


  int reading = analogRead(thisAxis);

  if (abs(reading - SRTMouse_::analogcenter) < SRTMouse_::threshold) {
    reading = analogcenter;
  }

  reading = map(reading, 0, 1023, 0, SRTMouse_::MouseSensitivity);

  int distance = reading - SRTMouse_::MouseSensitivity / 2;
  if (distance == SRTMouse_::MouseSensitivity / 2) {
    distance = distance + TopSpeed;
  }
  if (distance == -SRTMouse_::MouseSensitivity / 2) {
    distance = distance - TopSpeed;
  }
  return distance;
}

void SRTMouse_::Arrow(unsigned long currentMillis){
  
  int Xreading = analogRead(XMousePin) - 512;
  int Yreading = analogRead(YMousePin) - 512;

  if (invertXY) {
    Xreading = analogRead(YMousePin) - 512;
    Yreading = analogRead(XMousePin) - 512;
  }


  if (invertY) {
    Yreading = map(Yreading, -512, 511, 511, -512);
  }

  if (abs(Xreading) < threshold) {
    Xreading = 0;
  }
  if (abs(Yreading) < threshold) {
    Yreading = 0;
  }

  Xreading = 0.5 * (Xreading - Yreading);
  Yreading = 0.5 * (Xreading + Yreading);

  if (currentMillis - keyPressedTime > keyPressedDelay) {

    Keyboard.releaseAll();
  }

  if (Xreading == Yreading) {
    return;
  }
  if (currentMillis - keyPressedTime > delayNextPress) {
    keyPressed = false;
  }

  if (!keyPressed && !onlyShowValue) {


    if (Xreading > 0) {
      if (Yreading > 0) {
        if (invertX) {
          Keyboard.press(KEY_LEFT_ARROW);
        }
        else {
          Keyboard.press(KEY_RIGHT_ARROW);
        }
        keyPressed = true;
        keyPressedTime = currentMillis;
        setAction(currentMillis);
      }

      else {
        if (!invertY) {
          Keyboard.press(KEY_UP_ARROW);
        }
        else {
          Keyboard.press(KEY_DOWN_ARROW);
        }
        keyPressed = true;
        keyPressedTime = currentMillis;
        setAction(currentMillis);
      }
    }
    else {
      if (Yreading > 0) {
        if (!invertY) {
          Keyboard.press(KEY_DOWN_ARROW);
        }
        else {
          Keyboard.press(KEY_UP_ARROW);
        }
        keyPressed = true;
        keyPressedTime = currentMillis;
        setAction(currentMillis);
      }

      else {
        if (invertX) {
          Keyboard.press(KEY_RIGHT_ARROW);
        }
        else {
          Keyboard.press(KEY_LEFT_ARROW);
        }
        keyPressed = true;
        keyPressedTime = currentMillis;
        SRTMouse_::setAction(currentMillis);
      }

    }
  }
}




void SRTMouse_::setMouse(bool sendData, unsigned long currentMillis){


     if (!invertXY) {
    xReading = readAxis(SRTMouse_::XMousePin);
    yReading = readAxis(SRTMouse_::YMousePin);
  }
  else {
    xReading = readAxis(SRTMouse_::YMousePin);
    yReading = readAxis(SRTMouse_::XMousePin);
  }

  if (invertX) {
    xReading = map(xReading, -MouseSensitivity, MouseSensitivity, MouseSensitivity, -MouseSensitivity);
  }
  if (invertY) {
    yReading = map(yReading, -MouseSensitivity, MouseSensitivity, MouseSensitivity, -MouseSensitivity);
  }
  if (getMouseMode() && !onlyShowValue) {
    if (currentMillis - lastMove > 20) { //20 is the refreshRate
      Mouse.move(xReading, yReading, 0);
      lastMove = currentMillis;
    }
  }
  else {
    SRTMouse_::Arrow(currentMillis);
  }
    
    if (sendData) {
    if (SRTMouse_::getMouseMode()) {
      Serial.print("Mouse Mode. ");
    }
    else {
      Serial.print("Arrow Mode. ");
    }
    Serial.print("Mouse Position : ");
    Serial.print(analogRead(XMousePin));
    Serial.print(", ");
    Serial.print(analogRead(YMousePin));
    if (!digitalRead(SwitchMousePin)) {
      

    }
    Serial.println(".");
    Serial.print(startedToClick);
  }

  if (!digitalRead(SwitchMousePin) && lastClickState == false) { //si on est enfoncé alors qu'avant on ne l'était pas, on note le temps de départ
    startedToClick = currentMillis;
    lastClickState = true;
  }

  if (digitalRead(SwitchMousePin) && lastClickState == true) { //si on détecte un relâchement
    if ( currentMillis - startedToClick > configMouseDelay) { // si on était enfoncé depuis longtemps :
      mouseMode = !mouseMode;
      Keyboard.releaseAll();
    }
    else {if(!onlyShowValue){                                            // si on n'était pas enfoncé depuis longtemps :
      if (mouseMode) {
        Mouse.press(); 
      }
      else {
        Keyboard.press(KEY_RETURN);
      }}
      lastClick = currentMillis;
    }
    setAction(currentMillis);
    lastClickState = false;
  }
  if (currentMillis - lastClick > clickPressed) {
    if (mouseMode) {
      Mouse.release();
    }
    else {
      Keyboard.release(KEY_RETURN);
    }
  }
   }

Part of the code that you upload to boards with native USB handles the USB; writing outside the bounds of arrays might overwrite variables used by the USB code in which case you invoke undefined behaviour (e.g. not able to upload, board disappears, ...)

I've downloaded your zip. With the above comment in mind, this worries me :wink:

void setButtonState() {
  for (int i = 0; i < sizeof(currentButtonState) + 1; i++) {

Why the ' + 1 ' ?

I suggest that you do a thorough boundary check for all arrays.

Yes you're right, the +1 isn't needed...
Otherwise it wasn't working even before I changed that line... But I'll investigate in that way too.

Ok I think I know from where it comes from. I removed all the libraries I want to use later, and now the Arduino doesn't disappear. It's still quite strange because those libraries weren't included in the code...
Do you know which one messed up everything ? I just kept the Joystick and HID ones.

In fact, it may be due to a W11 update... I didn't have this issue since the last updates...

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