RC CAR From PC: Turn pins low on key release - Switch Statement

Hi everyone,

I'm new to arduino and im trying to control an RC car from my Macbook using WASD.
My UNO R4 Wifi digital pins are wired into a 4 relay board which controls the RC transmitter by bridging some connections on the RC PCB.

I'm using "Screen" on my osx terminal so I don't have to hit enter after every keypress to serial.
screen /dev/tty.usbmodemDC5475CBEFC02 9600

So far everything is working well. My only issue is that when I release one of the keys (W/A/S/D), it stays in that state. Eg; W is to accerlerate forwards, when I release that key, it stays accelerating.
Same thing with the other pins/relays.

I'm using a switch statement to do this. Right now, I have a case where if you press the spacebar, it sets the pins low. This works fine but I'd much rather have the pins go low when you release the corresponding key.

I've tried adding pinsLow() under each case but that just sets them low indefinitely.
I've also set the default state to be low but I believe it's stuck in the last "case".

I've had a look at other options, like PyGame, Processing, Johnny-five and they all seem like overkill. Surely there is an easy way that I can achieve this just within the Arduino IDE?

My coding knowledge isn't very great and I've been mostly reading other threads to figure things out so please forgive any spaghetti/wrong/redundent code.

Edit: I've also just realised I won't be able to turn and accelerate at the same time, but that might be a topic for another thread if I can't figure it out lol.

//#include <Keyboard.h>
//char ctrlKey = KEY_LEFT_GUI;
#define FORWARDS 2
#define BACKWARDS 3
#define LEFT 4
#define RIGHT 5

int pins[4] = { FORWARDS, BACKWARDS, LEFT, RIGHT };
int incomingByte = 0;

void setup() {
  //Keyboard.begin();
  // initialise pin outputs
  init(pins);
  // loop through pins and set output to low
  pinsLow();
};

void loop() {

  if (Serial.available() > 0) {
    // read the incoming byte
    incomingByte = Serial.read();
    Serial.println(incomingByte);
    // if incomingByte === case, then set pin output
    pinsLow();
    switch (incomingByte) {
      case 'w':
        Serial.println("Forward");
        pinOff(pins[1]);  // Turn off reverse relay
        pinOn(pins[0], "forwards");
        break;
      case 's':
        Serial.println("Backwards");
        pinOff(pins[0]);  // Turn off forwards relay
        pinOn(pins[1], "backwards");
        break;
      case 'a':
        Serial.println("Left");
        pinOff(pins[3]);  // Turn off turning right
        pinOn(pins[2], "left");
        break;
      case 'd':
        Serial.println("Right");
        pinOff(pins[2]);  // Turn off turning left
        pinOn(pins[3], "right");
        break;
      case 32:
        Serial.println("Turn off all motors");
        pinsLow();
        break;
      default:
        Serial.println("Standby");
        pinsLow();
    };
  };
};

// turn pin on
void pinOn(int pin, String direction) {
  digitalWrite(pin, HIGH);
  //Serial.println(String("Pin: ") + pin + String(" Direction: ") + direction + String(" Active: true"));
};

// turn pin off
void pinOff(int pin) {
  digitalWrite(pin, LOW);
  //Serial.println(String("Pin: ") + pin + String(" Active: false"));
}

// turn all off
void pinsLow() {
  for (int pin = 0; pin < 4; pin++) {
    pinMode(pins[pin], OUTPUT);
    digitalWrite(pins[pin], LOW);
  };
};

// init pins
void init(int pins[]) {
  Serial.begin(9600);
  Serial.println("Arduino initialised:");
  for (int pin = 0; pin < 4; pin++) {
    printPin(digitalRead(pins[pin]), pins[pin]);
  };
};

// print pin
void printPin(bool output, int pin) {
  //Serial.println(String("Pin: ") + pin + String(" Active: ") + boolean(output));
}

Thanks in advance and sorry if this is in the wrong section.

#define FORWARDS 2
#define BACKWARDS 3
#define LEFT 4
#define RIGHT 5

const byte pins[4] = { FORWARDS, BACKWARDS, LEFT, RIGHT };

void setup() {
  Serial.begin(9600);
  Serial.println("Arduino initialised:");
  for (int i = 0; i < 4; i++) {
    pinMode(pins[i], OUTPUT);
  }
}

void loop() {

  if (Serial.available() > 0) {
    byte incomingByte  = Serial.read();   // read the incoming byte
    Serial.println(incomingByte);
  
    switch (incomingByte) {
      case 'w':
        Serial.println("Forward");
        digitalWrite(BACKWARDS, LOW );  // Turn off reverse relay
        digitalWrite(FORWARDS, 1);
        break;
      case 's':
        Serial.println("Backwards");
        digitalWrite(FORWARDS, 0); // Turn off forwards relay
        digitalWrite(BACKWARDS, 1);
        break;
      case 'a':
        Serial.println("Left");
        digitalWrite(RIGHT, 0); // Turn off turning right
        digitalWrite(LEFT, 1);
        break;
      case 'd':
        Serial.println("Right");
        digitalWrite(LEFT, 0); // Turn off turning left
        digitalWrite(RIGHT, 1);
        break;
      case 32:
        Serial.println("Turn off all motors");
        for (int i = 0; i < 4; i++)     digitalWrite(pins[i], LOW);
        break;
    }
  }
}

Hey,

Thanks for your response!

When I initially ran this code I received some errors:
"lvalue required as left operand of assignment"
I fixed this by changing line 19 to:

byte incomingByte = Serial.read();  // read the incoming byte

I also changed case 'a' and 'd' to include digital write instead of pinOff/On.

While this verifies and compiles, I still have the same issue.
When using Screen in terminal, the W/A/S/D keys do not switch off the pins on keyrelease.
This is my desired outcome - On key release when using Screen, the pins are set low.
Your code is defintely more concise than my original (Thanks for that), but I'm still looking for a way to have pins set to low on key release.

Please let me know if I'm missing something and thanks for your help!

Here is a pic of the wiring, just for completeness.
Everything works ok - just not the relays turning off after keyrelease/keyup :joy:

of course not, you send character over serial. this sketch examine the letter and switch in appropriate mode. and set all pins to low only if receive the space character

the key value can be captured when read and processed the next time thru loop() when Serial.available() is zero. once the captured key is processed, the captured key value is set to zero, indicating not delayed processing is needed

Das your "Screen" software even send anything when the key is relelased? I suppose not.

1 Like

You're correct, but that is not what I'm asking.
On key release I want the relays/pins to switch to low/off.
I'm asking how can I achieve that.

How do you propose I detect key release?

well, if you describe how you generate pressing...

Use something different to Screen. You need something that sends events on key press and key release.

Terminal programs do not typically do that.

You could… have a key press mean go forward, then forward motion stop after a time without more characters arriving.

Then continuous forward motion would mean holding down a key and getting the key repeat feature to keep telling the car to go forward.

This is not such a bad thing, a normal remote control for a little car requires that you hold a stick or a button during the entire forward motion time.

If you really need key release, you could use an Arduino capable of reading a keyboard to gather keystrokes, then form them into press and release events to send along to your code.

a7

didn't i describe how in post #6?

Sorry, maybe I misunderstand.
You can see how I'm "generating" my keypress(es) from my original post.
It's within the switch statement.

Haven't had a chance to try to implement your suggestion yet.
Hopefully I can figure something out from this :crossed_fingers:

i think this demonstrates the concept

const byte PinLed  = 10;
const byte PinBut  = A2;

byte butState;
int  flag;

#define Off HIGH
#define On  LOW

void
loop () {

    byte but = digitalRead (PinBut);
    if (butState != but)  {
        butState = but;
        delay (20);         // debounce

        if (HIGH == but)    // released
            flag = 1;
    }

    if (flag)  {
        digitalWrite (PinLed, ! digitalRead (PinLed));
        flag = 0;
    }
}

void setup ()
{
    Serial.begin (9600);

    pinMode      (PinLed, OUTPUT);
    digitalWrite (PinLed, Off);

    pinMode (PinBut, INPUT_PULLUP);
    butState = digitalRead (PinBut);
}

sorry, i see no keys, no presses, no releases, no attached keyboard, keypad or IR remote control decoder.
if i have understand right, you connect arduino thru UART-USB with computer or thru bluetooth to console app on mobile phone. there you touch/type something and one byte will be sent to UART. so, we see the arduino have no idea what happen on other side. if you want let arduino know more you should send more information.
so, i ask again, how you generate key presses?

I'm using "Screen" on my osx terminal

Screen in OSX amounts to @xerxeshm using a terminal emulation program, with a setting that allows keystrokes to go immediately, not wait until a return key or some send button is clicked.

So there is nothing being generated for anyone to consume or act upon when a key is released.

a7

2 Likes

With the "screen" software on your Macbook you simply can't as @alto777 already pointed out.

There may be a solution if the keyboard has a autorepeat feature. You switch off your relay pins on a time based manner if no new key strokes arrive at the serial line. The turnoff time must be selected a little bit longer than the max time between two autorepeat strokes. You must restart the time with every key that arrives. So if you release the key no auto-repeat keystrokes are generated anymore and your relay pins are switched off when the time expires.

Just thought I'd post an update:

I gave up on my initial approach and had my partner help me write a Node.js application that hosts a local webpage which displays the FPV camera and uses event listeners for keypresses to send to the arduino.

You can find the software stack and more info in the readme.

You can find the repo here:

Will probably require rewriting some code for your application but hey, it works.

Hopefully this helps someone that had the same idea as me.
Cheers

1 Like

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