Go Down

Topic: Have terminal bash script act as virtual "button" for Arduino? (Read 1 time) previous topic - next topic

realdannys

I've searched quite a lot for this and tried to piece bits of code together but with no luck so far.

All i'm looking to do is have a command I can run in terminal/bash script on the Mac trigger some code to run on the Arduino (Leonardo)

It doesn't matter what data the Mac sends, I only have one command to run so it doesn't need to look like separate buttons or anything.

So far i've tried

Code: [Select]
int incomingByte = 0;   // for incoming serial data
void setup() {
    Serial.begin(9600);
}

void loop() {

        // send data only when you receive data:
        if (Serial.available() > 0) {
                // read the incoming byte:
                incomingByte = Serial.read();
//run commands
  }
        }


and then from terminal/script both

Code: [Select]
echo "1" > /dev/tty.usbmodemHIDPC1 9600

and

Code: [Select]
screen > /dev/tty.usbmodemHIDPC1 9600

I see the Arduino light flash when I run those two commands in terminal which would seem to suggest that its receiving the data, but nothing runs.

I have stripped out my code that is due to be run by the way for simplicity. I also include keyboard.h and want the Leonardo send keyboard presses.

pert

I see the Arduino light flash when I run those two commands in terminal which would seem to suggest that its receiving the data, but nothing runs.
There's nothing to run in the code you posted.

I have stripped out my code that is due to be run by the way for simplicity. I also include keyboard.h and want the Leonardo send keyboard presses.
Don't add that extra complexity in yet. Start by getting an LED to turn on when the serial data is received.

realdannys

There's nothing to run in the code you posted.
Yes I know, that's why I said i've stripped my code out - the code i've got works perfectly - I just want it to be triggered my the terminal on the Mac.

GoForSmoke

I lined up the braces to make it easier to see at a glance. The baud rate and char instead of int are tips to try. Uno has 2K for heap and stack memory, if you develop good RAM habits from the start you can go far without a crash. BTW, don't use type String on Arduino.

Code: [Select]

char chr;   // for incoming serial data, type char is signed 8-bit, -128 to 127, ASCII is char codes. Save 1 byte over type int.

void setup()
{
    Serial.begin(115200);  // set serial monitor to this, it empties the outgoing buffer 12x faster than 9600.
}

void loop()
{
    // read data only when you receive data:
    if (Serial.available() > 0)
    {
        // read the next char in the Serial input buffer, might be the last/latest arrived
        chr = Serial.read();
        // run commands
        //     ummmmmmm, what commands?
        Serial.println( chr, HEX );
        // if ( chr == 'D' ) // do thing if key is D
    }
}


1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

realdannys

Thanks @GoForSmoke

I've used the below code but i've still got the same issue from terminal on the Mac.

If I use

Code: [Select]
echo "1" > /dev/tty.usbmodemHIDPC1 115200

Then I see the Leonardo LED flash once, but then nothing happens. Also it locks up terminal as if it's in a state of trying to send commands. Is there a terminal command I can add to the bash script that will effectively just ping the Arduino and exit so it's not locking the terminal session up?

Here is my Arduino code now.

Code: [Select]
#include <Keyboard.h>
char chr;   // for incoming serial data, type char is signed 8-bit, -128 to 127, ASCII is char codes. Save 1 byte over type int.
void setup() {
    Keyboard.begin();
    Serial.begin(115200);  // set serial monitor to this, it empties the outgoing buffer 12x faster than 9600.
}

void loop()
{
    // read data only when you receive data:
    if (Serial.available() > 0)
    {
        // read the next char in the Serial input buffer, might be the last/latest arrived
        chr = Serial.read();
        // run commands
        delay(3000);
  Keyboard.write('S');
  delay(100);
  Keyboard.write('Y');
  delay(100);
  Keyboard.write('S');
  delay(100);
  Keyboard.write('T');
  delay(100);
  Keyboard.write('E');
  delay(100);
  Keyboard.write('M');
  delay(100);
        Serial.println( chr, HEX );
        // if ( chr == 'D' ) // do thing if key is D
    }
}



I lined up the braces to make it easier to see at a glance. The baud rate and char instead of int are tips to try. Uno has 2K for heap and stack memory, if you develop good RAM habits from the start you can go far without a crash. BTW, don't use type String on Arduino.

Code: [Select]

char chr;   // for incoming serial data, type char is signed 8-bit, -128 to 127, ASCII is char codes. Save 1 byte over type int.

void setup()
{
    Serial.begin(115200);  // set serial monitor to this, it empties the outgoing buffer 12x faster than 9600.
}

void loop()
{
    // read data only when you receive data:
    if (Serial.available() > 0)
    {
        // read the next char in the Serial input buffer, might be the last/latest arrived
        chr = Serial.read();
        // run commands
        //     ummmmmmm, what commands?
        Serial.println( chr, HEX );
        // if ( chr == 'D' ) // do thing if key is D
    }
}




realdannys

Ah i've cracked triggering it!

It needs to be

Code: [Select]

echo "1" > /dev/cu.usbmodemHIDPC1 115200


That works and exits in terminal and triggers the command.

Now the only problem as it's in loop is that it keeps writing system over and over. Is there a way I can make it just write the command once and stop? I'm wanting to put this in a script where I have a lot of keyboard commands in just the void setup part which run once, but it seems I need to use loop for listening as it didn't work when I put this code into setup. It's just I only want it to run the code once, not loop forever once the byte is received.

Current code is

Code: [Select]
#include <Keyboard.h>
char chr;   // for incoming serial data, type char is signed 8-bit, -128 to 127, ASCII is char codes. Save 1 byte over type int.
void setup() {
    Keyboard.begin();
    Serial.begin(115200);  // set serial monitor to this, it empties the outgoing buffer 12x faster than 9600.
}

void loop()
{
    // read data only when you receive data:
    if (Serial.available() > 0)
    {
        // read the next char in the Serial input buffer, might be the last/latest arrived
        if (chr = Serial.read() > 0)
        {// run commands
        delay(3000);
  Keyboard.write('S');
  delay(100);
  Keyboard.write('Y');
  delay(100);
  Keyboard.write('S');
  delay(100);
  Keyboard.write('T');
  delay(100);
  Keyboard.write('E');
  delay(100);
  Keyboard.write('M');
  delay(100);
        Serial.println( chr, HEX );
        // if ( chr == 'D' ) // do thing if key is D
        }
    }
}


I lined up the braces to make it easier to see at a glance. The baud rate and char instead of int are tips to try. Uno has 2K for heap and stack memory, if you develop good RAM habits from the start you can go far without a crash. BTW, don't use type String on Arduino.

Code: [Select]

char chr;   // for incoming serial data, type char is signed 8-bit, -128 to 127, ASCII is char codes. Save 1 byte over type int.

void setup()
{
    Serial.begin(115200);  // set serial monitor to this, it empties the outgoing buffer 12x faster than 9600.
}

void loop()
{
    // read data only when you receive data:
    if (Serial.available() > 0)
    {
        // read the next char in the Serial input buffer, might be the last/latest arrived
        chr = Serial.read();
        // run commands
        //     ummmmmmm, what commands?
        Serial.println( chr, HEX );
        // if ( chr == 'D' ) // do thing if key is D
    }
}




GoForSmoke

 after it sends the message, read all available chars and it won't send the message until you trigger it again.

while( Serial.available())  Serial.read(); // empties the serial input buffer

Maybe take more time reading the code you have to start seeing how it works and what advantages you can take.

You could code the whole thing in setup() if you really only want 1 time action. Setup is where you put what only runs once.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

pert

Also posted at:
https://arduino.stackexchange.com/q/60362
If you're going to do that then please be considerate enough to add links to the other places you cross posted. This will let us avoid wasting time due to duplicate effort and also help others who have the same questions and find your post to discover all the relevant information. When you post links please always use the chain links icon on the toolbar to make them clickable.

realdannys

after it sends the message, read all available chars and it won't send the message until you trigger it again.

while( Serial.available())  Serial.read(); // empties the serial input buffer

Maybe take more time reading the code you have to start seeing how it works and what advantages you can take.

You could code the whole thing in setup() if you really only want 1 time action. Setup is where you put what only runs once.
For me it didn't work at all if I put it in setup, and in loop as soon as I triggered it it kept repeating over and over rather than waiting for another trigger.

At the moment I did

Code: [Select]
#define EVER (;;)

and at the end of the loop

Code: [Select]
  Keyboard.write('l');
  delay(100);
        Serial.println( chr, HEX );
        // if ( chr == 'D' ) // do thing if key is D
 for EVER;


Probably not the cleanest way but it does make it run once and allows me to run single run code in setup.

realdannys

#9
Mar 06, 2019, 10:15 pm Last Edit: Mar 06, 2019, 10:20 pm by realdannys Reason: Adding code
Back after two months. At one point I thought i'd cracked this but now can't for the life of me get it to work properly in my script.

I'm basically looking for the Leonardo to enter all the keyboard presses and then wait for the computer to tell it's done and carry on with the rest of the script.

After putting the above code within a button press it no longer works anymore.

A limited version of my code is as follows

Code: [Select]
#include <HID-Project.h>
#include <HID-Settings.h>
char chr;   // for incoming serial data, type char is signed 8-bit, -128 to 127, ASCII is char codes. Save 1 byte over type int.
int buttonPin;
int buttonPin2;

void setup() {
  Serial.begin(115200);
  BootKeyboard.begin();
  Keyboard.begin();
  Mouse.begin();
  pinMode(5, OUTPUT);
  buttonPin = 4; //whatever pin your button is plugged into
  buttonPin2 = 6; //whatever pin your button is plugged into
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);
}

void loop() {
unsigned int AnalogValue;
AnalogValue = analogRead(A0);

  //check button pressed, if so enter program condition (inside if statement)
  if(digitalRead(buttonPin) == LOW) //functions based off of button pulling input pin LOW
  {
    Keyboard.print("Button Pressed");
    delay(1000);
    //Various keyboard button presses and commands removed
    Keyboard.press("O");
    delay(1000);
    //At this point I want the Leonardo to sit and wait for the computer to tell it's ready for the next part by sending a signal over serial.
    Keyboard.print("Continuing Code");
    delay(1000);
    while (analogRead(A0) > 180){ // waits for light sensor to get a reading below 180 then carries on with code
      }
     delay (50);
    Bootkeyboard.press("R");


So as you can see, I have a light sensor now too and that sits and waits for the light to reach a certain level before continuing the code, I need to do this now in the script with the computer which will in terminal send some serial data from a bash script by sending using the following line

Code: [Select]
echo "1" > /dev/cu.usbmodemCHIDGG1

GoForSmoke

You used this in the one that worked

echo "1" > /dev/cu.usbmodemHIDPC1 115200

and the next in the one that doesn't

echo "1" > /dev/cu.usbmodemCHIDGG1

One of these things is not like the other.

PS :  you can do straight terminal IO without using HID.

- don't have Serial Monitor open
- get the board com port address from IDE->Tools->Port
- in Linux I use cu to connect the terminal to the board
    $ cu -l /dev/ttyACM0 -s 115200
- to get cu use
    $ sudo apt-get install cu
- in Windoze I've used PuTTY or Hyperterminal.

Always disconnect the terminal before compiling with the IDE, reconnect after the sketch loads.
Terminals get sketch serial output and send chars to board serial input as they are pressed. No need for HID.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

realdannys


The answer I was looking for was

Code: [Select]
while(Serial.available() == 0){}

GoForSmoke

You can do this without HID, it would take less effort.

You think of code in terms of blocking to force steps into order. It is what you can do with what you know about code. What it does is ensure that only one thing happens at once while everything else waits.

There is another way that does not block, handles things as they happen. Each step begins by checking if it should run

if ( serial.available > 0 ) { ....
}

if ( buttonState == PRESSED ) { ....
}

if ( digitalRead( sensorPin[ 3 ] ) == LOW ) { ....
}
 
switch ( processState ) { ....
//  where each state has a case to run
}

and none of them blocks then each one gets a chance to run no matter which other is not triggered yet.. unless one must trigger to set the trigger for another either on condition or as part of a sequence.

There are a few techniques to start out with explained in the blogs addressed below. They amount to a non-blocking toolkit good enough to get started writing effective real world code.

http://gammon.com.au/blink
http://gammon.com.au/serial
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

realdannys

You can do this without HID, it would take less effort.
I use HID because that's how the Leonardo is being used as a keyboard running macro scripts on the computer waiting for the computer to tell it when it's finished - so it's connected via USB - this way is simple.

At the moment blocking doesn't matter - the script is full of delays where it needs to be 5 seconds or so before the next key press - at some point if I find I need a pause button a few times then i'll try and get ti re-written so that it'll work with one, but at the minute it works perfectly for me.

GoForSmoke

I use HID because that's how the Leonardo is being used as a keyboard running macro scripts on the computer waiting for the computer to tell it when it's finished - so it's connected via USB
And you have trouble getting the terminal output?

"Back after two months. At one point I thought i'd cracked this but now can't for the life of me get it to work properly in my script."

Without the HID you can send and read the terminal using Serial. I do it with an Uno and Linux terminal. What Arduino prints goes into console as if I'd typed it and runs. What the console outputs I can read on Arduino but the PC must connect the terminal to the Arduino com port at the correct baud rate.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

Go Up