Arduino 2 different behaviors, depending on the pinMode.

Hi cattledog and thanks for your reply!

I am trying to improve existing solutions currently available in the internet for the DIY handbrakes, specifically for car simulators.
Existing solutions fall under 2 main categories - either digital or analog.

Digital handbrakes have a micro switch lever attached to the end of the handbrake movement range, so when the handbrake is fully pulled, the switch is pushed in and a signal is sent to the computer via the USB. http://img.directindustry.com/images_di/photo-g/121197-5916933.jpg

I am very new to these libraries and I have to wrap my head around the include xxx.h that can another include yyy.h inside itself (some sort of multi-level inheritance of libraries?) but at some point keyboard.h is mentioned when I check similar sketches. The idea behind this keyboard.h (I think) is that you simulate a keyboard attached to the PC, and that microswitch push simulates a key of your choice, let's say the "H" key. So when you open a notepad and pull the handbrake, it will start typing HHHHHHHHHH... indefinitely until you release the handbrake. Then you launch the game anf map the handbrake action to the H key. Now, you can pres the H on your actual keyboard or pull the handbrake on the "simulated" keyboard with one key.

There are similar stand-alone sketches that I will need to modify and add a condition that the "H" button is simulated not when the micro-switch is pushed in (I will not have it) but when the load on my load cell will reach 20kg+. This will be the first function of my sketch. The next one is for Analog reading.

Analog handbrakes are way better in terms of realism. They will transmit values thorughout the whole range of motion of the handbrake. The reason I want to incorporate both, digital and analog is because not all game publishers support analog or digital inputs for handbrake. If a game requires a simple on/off input, then the analog handbrake will not work. THerefore I have a switch that will change the way Arduino is presented to Windows (AXIS vs BUTON).

I will write about the Analog handbrakes in the next post in a minute.

Analog handbrakes exist in 2 flavors:

  • Attach the range of motion of the handbrake to a potentiometer. While it is better than a simple ON/OFF, it is not very realistic because the output is linear and directly proportional to the range of Handbrake motion. In reality it works differently - normally first 60% of the handbrake motion is spongy doesn't do anything to stop the car, until the brake pads touch the disk rotor. The effect must kick in in the remaining 40% of the motion range. Therefore the next solution is better:
  • Create a small hydraulic system with a master cylinder and a slave cylinder and fill it with real brake fluid. Also connect a transducer (pressure sensor) to the closed system and start reading the data. Now this kind of setup gives us a few advantages: If we pick right combination of springs, we can achieve a non-linear effect (no effect during the first 70% of the handbrake travel and obvious break effect in the last 30% for example, because the pressure will increase significantly in the last 30%, just like with the foot break system).
    http://www.derekspearedesigns.com/uploads/3/3/3/7/3337442/1797453_orig.jpg?413

As far as the sketches go, the Joystick.h library is used to present Arduino to Windows as an Axis device. Windows detects it as HID and even provides a basic calibration tool.

Now going back to my BUTTON, mode, I think I could use Joystick.h for both modes because joysticks also have buttons. So instead of switching from keyboard.h to joystick.h, I could probably use one for both purposes. However, I will need to map the button press to a "Button 1" rather than "H" key.

Also, I want to try the load cell technology as opposed to hydraulic. It can still measure the load and achieve similar results, plus no risk of break fluid leaks.

I hope all this makes sense.

Max

2trillion:
Hi.

I believe you might want to use an interrupt. Have your AXIS ISR called when it is triggered by a LOW input at 7 and your BUTTON ISR called when 7 is HIGH .

You would want to define your interrupt in the setup.

Hi, I heard about interrupts but I don't know how they work. I will need to research more if they can do what I need. Thanks!

J-M-L:
Can you explain ho the reset works?

If it does reset the arduino then in the setup read the state of the button but don't call a function, just set up a global modeAxis = (digitalRead(7) == LOW); Boolean variable and in the loop that's where you perform your test

if(modeAxis) 

{code for axis mode}
  else
  {code for button mode}

Hi, sure.

Please see the attachment. Can you please clarify why I can't do the IF in the setup but have to do it in the loop? I don't want to recheck the condition every iteration because it will not change until a reset.

Thanks!

maxturcan:
Please see the attachment. Can you please clarify why I can't do the IF in the setup but have to do it in the loop? I don't want to recheck the condition every iteration because it will not change until a reset.

I think I just found the answer to this specific question. Arduino can't hace 2 different loops that it wil run depending on the condition in the setup. Therefore I will have both mode functions inside the loop and a condition a the beginning of the loop. Is that correct?

OP just to clarify your terminology: your thread subject says "depending on the pinMode", but you don't ever change that. The pinMode will always be INPUT_PULLUP unless you invoke another pinMode() which although not impossible is not common.

What does change is the pin's value as read by digitalRead() and is HIGH by default due to the INPUT_PULLUP or LOW when pulled down by your switch.

Also I think JML meant this:

modeAxis = (digitalRead(7));

not:

modeAxis = (digitalRead(7) == LOW);

(Preceded by a

bool modeAxis;

above setup() of course.)

You need to do the test in loop() because it's in loop() that the work is being done, and your axis mode or button mode code is running continuously.

So read the switch once in setup() at startup or reset, setting the boolean accordingly, and then in loop() forever (ie until next reset) run the appropriate mode code.

optoaudio:
OP just to clarify your terminology: your thread subject says "depending on the pinMode", but you don't ever change that. The pinMode will always be INPUT_PULLUP unless you invoke another pinMode() which although not impossible is not common.

What does change is the pin's value as read by digitalRead() and is HIGH by default due to the INPUT_PULLUP or LOW when pulled down by your switch.

Also I think JML meant this:

modeAxis = (digitalRead(7));

not:

modeAxis = (digitalRead(7) == LOW);

(Preceded by a

bool modeAxis;

above setup() of course.)

You need to do the test in loop() because it's in loop() that the work is being done, and your axis mode or button mode code is running continuously.

So read the switch once in setup() at startup or reset, setting the boolean accordingly, and then in loop() forever (ie until next reset) run the appropriate mode code.

You are correct, I meant pin value, I've used different terminology. As for the "modeAxis = (digitalRead(7) == LOW); " I also thought ==LOW was unnecessary. I also understood why the IF mor the pin value must be in the loop. I falsely assumed that Arduino can have 2 independent loops that way be called depending on the condition in setup, but I was wrong. The IF and both mode functions must reside in the loop.

Right now I am testing the IF statement for the button mode, to output below 10 lbs or Above 10 lbs depending on the load applied. Seems to be working fine in the serial monitor. Now I need to find a way to replace the Serial.Print with the button simulation ON/OFF and make it readable by Windows.
Code below.

#include <HX711.h>

#define calibration_factor 23000 //This value is obtained using the SparkFun_HX711_Calibration sketch

#define DOUT  3
#define CLK  2

HX711 scale(DOUT, CLK);

void setup()
{
 Serial.begin(9600);
 pinMode(7, INPUT_PULLUP);

 scale.set_scale(calibration_factor); //This value is obtained by using the SparkFun_HX711_Calibration sketch
 scale.tare(); //Assuming there is no weight on the scale at start up, reset the scale to 0
}

void loop()
{
 if (scale.get_units() <= 10) {Serial.println("Below 10");} else {Serial.println("Above 10");}
}

Can you please clarify why I can't do the IF in the setup but have to do it in the loop? I don't want to recheck the condition every iteration because it will not change until a reset.

A bare Arduino program basically consists of three functions. from your old days, you might remember that a C program always starts with the main() function. main() first calls setup() once and next calls loop() in an endless loop.

So you already have an endless loop which frees you from having to write a loop (for, while, do while) in your setup().

You will need to include all required libraries regardless of the selected functionality; so in your code you will need to include both joystick.h and keyboard.h.

Also I think JML meant this:

I don't think J-M-L did :wink: digitalRead returns an integer, not a boolean. J-M-L's way is the correct way to convert from integer (LOW / not LOW) to a bool.

sterretje:
J-M-L's way is the correct way to convert from integer (LOW / not LOW) to a bool.

Oh ok, we live and learn. Thanks.

sterretje:
A bare Arduino program basically consists of three functions. from your old days, you might remember that a C program always starts with the main() function. main() first calls setup() once and next calls loop() in an endless loop.

So you already have an endless loop which frees you from having to write a loop (for, while, do while) in your setup().

You will need to include all required libraries regardless of the selected functionality; so in your code you will need to include both joystick.h and keyboard.h.

Yes, I do remember the main() function. Thanks for explaining it. I wanted to ask another related question if I may. All the .h libraries are accompanied by .cpp libraries. I don't want to clutter my code with unnecessary data ant try keep it clean. I will need a joystick.h, that requires the HID.h, so I get this "dependency chain". My .ino > joystick.h > hid.h. But then there is the joystick.cpp that is completely different code, also requiring joystick.h. I am not getting how the .cpp library fits in the big picture. Same for the HX711.h library for the load cell amp board. It comes with hx711.cpp. Do I need them?

You said I would need both the joystick.h and the keyboard.h. Can you please elaborate why?
From my research those who implemented a digital (button mode) handbrake, used the keyboard.h to simulate a key press. And those who wanted analog handbrake used the joystick.h since it had the axis range feature. I am trying to marry both modes, and yes, I would probably need both libraries but... the joystick.h makes Arduino detectable as a standard gamepad with thumb joysticks and buttons. Can't I reuse this library to map the digital mode to one of the gamepad's buttons rather than keyboard's keys?

As for the J-M-L's post, I am confused now and will need to wrap my head around it.
^^^ I think I got it. modeAxis = either true or false, depending whether the pin reads LOW or not.

The wire-frame of my sketch. No functions defined yet, but the switch has been tested and proven to work based on the output to the serial monitor. Mode1 - detects 5 lbs threshold and Mode2 detects 10 lbs threshold.

Now I need to figure out what libraries I need and how to map things. I am thinking to use raw data from the sensor and map to joystick axis, rather than converting it to human readable lbs and then mapping to the axis, the way it is set now with the scale.get_units function from HX711.h.

#include <HX711.h>
#define calibration_factor 23000 //This value is obtained using the SparkFun_HX711_Calibration sketch
#define DOUT  3
#define CLK  2

HX711 scale(DOUT, CLK);

bool modeAxis;

void setup()
{
  Serial.begin(9600);
  pinMode(7, INPUT_PULLUP);
  modeAxis = (digitalRead(7) == LOW);

  scale.set_scale(calibration_factor); //This value is obtained by using the SparkFun_HX711_Calibration sketch
  scale.tare(); //Assuming there is no weight on the scale at start up, reset the scale to 0
}

void loop()
{
if(modeAxis)
{if (scale.get_units() <= 5) {Serial.println("Below 5");} else {Serial.println("Above 5");}}
else
{if (scale.get_units() <= 10) {Serial.println("Below 10");} else {Serial.println("Above 10");}}
}

Considering you say in your Original Post that you are a professional Server/Network Admin I don't think I have come across a more confused description of a problem.

It jumps from reading a digital pin in setup() to joysticks to hydraulic hand-brakes with some HID thrown in to spice things up.

Maybe you could take a few minutes to write a concise description of the thing you are trying to implement.

It is a simple matter to read a digital pin in setup() and save the value for later use. Then it would be easy to have two functions either of which can run depending on the value that was detected. Something like

byte modeVal;

void setup() {
  modeVal = digitalRead(modePin);

}

void loop() {
  if (modeVal == 0) {
     functionForMode0();
  }
  else {
    functionForMode1();
 }
}

and put the code that should happen for the different modes into the two different functions.

...R
Planning and Implementing a Program

Indeed I really meant modeAxis = (digitalRead(7) == LOW); :slight_smile:

First because if modeAxis is a Boolean you want to assign a Boolean value to it and digitalRead() is not a Boolean. In my opinion This is a good practice to code like this, this is a way to check your logic as you go. If you see yourself assigning something of a type into something of a different type that should ring a bell and you should ask yourself - what will the compiler do and is that coherent. Sure enough in this case it could work as the rules of the C/C++ language will convert it for you (so the extra code I added will anyway be added by the compiler for you) but here it would fail because i wanted a Boolean representing that the code needed to be in axis mode so it would need to be true when the switch is LOW. Just reading the switch would show false in that case (LOW is 0 which will be promoted into false) and thus the Boolean would be semantically incorrect (but you could solve that by calling it modeButton instead :slight_smile: )

For the rest yes you want the loop() to spin if you want to keep the structure of a typical arduino program. If you really want to save the few nano seconds of the test at each loop() indeed you could be calling your own infinite loops as functions at the end of the setup() but if someone wanted to expand on your code and use serialEvent() for example then that would not work

It's also a good practice to give meaningful names to pins so that you can understand while reading the code

pinMode(7, INPUT_PULLUP);
modeAxis = (digitalRead(7) == LOW);

is less readable than

const byte modeButtonPin = 7;
....
pinMode(modeButtonPin, INPUT_PULLUP);
modeAxis = (digitalRead(modeButtonPin) == LOW); // parenthesis not needed but for sake of clarity to junior programers

And the second version does not take more memory (compiler will substitue the const litteral directly into the function call)

J-M-L:
Indeed I really meant modeAxis = (digitalRead(7) == LOW); :slight_smile:

Could you take a moment please to explain the order of precedence and what's getting assigned to what in there?

modeAxis = either true or false, depending whether the pin reads LOW or not.

--> exactly

optoaudio:
Could you take a moment please to explain the order of precedence and what's getting assigned to what in there?

well the compiler understands it's an assignment of a value into a memory location by the name of modeAxis so it will transcode this into machine language commands to say "store at the address pointed by the variable modeAxis the value resulting from the evaluation of the right side of the = sign.

In order to do this the compiler needs to evaluate that expression (digitalRead(7) == LOW)
the compilers understands this is the result of an equality test (operation ==) and in order to evaluate the "equality of to expressions", it needs to calculate each expression

so (1) it evaluates digitalRead(7). this is a function call, so it calls the function, lets it run its course, and store te resulting value somewhere in a register when possible or in memory (on the stack)

then it knows it does not need to evaluate the right side of the == test as this one is a literal value 0

So (2) then it compares the register value with 0 and generates true (1) if they are the same or false (0) if they are not. This is the result of the evaluation of the right side,

and as modeAxis is a global variable (not the result of a computation) the compiler knows exactly the memory address pointed by the variable modeAxis and thus the result of the right side evaluation is what ends up stored there which is the (3) third step

makes sense?

maxturcan:
I wanted to ask another related question if I may. All the .h libraries are accompanied by .cpp libraries. I don't want to clutter my code with unnecessary data ant try keep it clean. I will need a joystick.h, that requires the HID.h, so I get this "dependency chain". My .ino > joystick.h > hid.h. But then there is the joystick.cpp that is completely different code, also requiring joystick.h. I am not getting how the .cpp library fits in the big picture.

You said I would need both the joystick.h and the keyboard.h. Can you please elaborate why.

Libraries usually consist of one or more h files and one or more cpp files.

The h files contain constants, variables and function prototypes; you include the h files so the compiler knows about the existence of those when the file that contains those includes is compiled.

The c/cpp files of the library are also compiled although in the arduino environment this is a little less obvious. Those files contain the actual implementation of the functions.

In the last step the linker links all compiled files into one program. The linker is quite clever and will throw away all compiled code that is not used. So if your library contains three functions but you only use one of them, the other two will not be in the final program.

If you want your single program to act as a keyboard or a joystick depending on switch position, you need the libraries that provide these functionalities.

J-M-L:
In order to do this the compiler needs to evaluate that expression (digitalRead(7) == LOW)
the compilers understands this is the result of an equality tests (operation ==) and oder to evaluate the equality of to expressions, it needs to calculate each expression

Your explanation is all very academic, but at the end of the day digitalRead() returns a one or a zero. The Arduino implementation happens to create the definition of HIGH and LOW:

#define HIGH 0x1
#define LOW  0x0

all you are accomplishing is:

modeAxis = (digitalRead(7) == 0);

so if you use the expression

bool result = digitalRead(somePin)

or to invert it:

bool result = !digitalRead(somePin)

the Rvalue, 0 or 1, is simply cast to a bool, which is guaranteed to result false or true respectively.

my guess is that if the compiler isn't smart enough to optimize the comparison to a compile time constant (i.e. (digitalRead(7) == LOW) that you are merely adding instructions for a net sum gain of zilch.

I'm curios to see how it actually looks...

J-M-L:
makes sense?

Indeed, thanks, makes perfect sense, but I also just read BulldogLowell's reply which makes me wonder if it's all necessary.

Read again #16 where I explain why wrote it that way...

Robin2:
Considering you say in your Original Post that you are a professional Server/Network Admin I don't think I have come across a more confused description of a problem.

It jumps from reading a digital pin in setup() to joysticks to hydraulic hand-brakes with some HID thrown in to spice things up.

Thanks Robin2 for your reply. Yes, I am a professional Network/Server administrator and I have zero experience with hardware programming. It's very hard for me to formulate a question about something that I barely know. My apologies for messing up terminology and using primitive description of the problem. I am still learning how everything connects (literally and not).

As for my original post, it has the question, detailed description of my hardware and connections and the operation modes I need as well as libraries that others have used for this purpose. And not a word about handbrakes.

The first member who replied wanted more details and I was probably too excited to share the info and went into too much detail about the project. As far as the question goes, everything is in the OP, that I am making sure to update if other valuable info seems to be missing.

Let me know if you think the OP is confusing, or how to make it look more professional and I will be happy to change it.