Sampling keyboard input at a certain rate in C++ - c++

What I want to do is sampling the keyboard input at a certain rate (e.g. 10-20 Hz). I use a while loop that read from stdin (I use read because I want to read asynchronously, e.i. I don't want to press enter every time) and then I have a pause before a new cycle starts to keep the sampling frequency stable.
The user press left/right arrow to give a command (to a robot). If nothing is pressed, the output is 0.
The problem is that, during the pause, the stdin buffer is written (I suppose), and so the read will return an old input. The final result is that the output is delayed. So if I press left the output immediately change to 1, but when I release it takes some seconds to return to 0. I want to remove this delay.
My aim is to sample just the more recent key pressed, in order to synchronize user input and output command without delays. Is there a way? Thank you in advance.
This is the method I'm using:
void key_reader::keyLoop()
{
char c;
bool dirty = false;
int read_flag;
// get the console in raw mode
tcgetattr(kfd, &cooked);
memcpy(&raw, &cooked, sizeof(struct termios));
raw.c_lflag &= ~(ICANON | ECHO);
// Setting a new line, then end of file
raw.c_cc[VEOL] = 1;
raw.c_cc[VEOF] = 2;
tcsetattr(kfd, TCSANOW, &raw);
//FD_ZERO(&set); /* clear the set */
//FD_SET(kfd, &set); /* add our file descriptor to the set */
//timeout.tv_sec = 0;
//timeout.tv_usec = 10000;
if (fcntl(kfd, F_SETFL, O_NONBLOCK) == -1)
{
perror("fcntl:"); // an error accured
exit(-1);
}
puts("Reading from keyboard");
puts("---------------------------");
puts("Use arrow keys to move the turtle.");
ros::Rate r(10);
while (ros::ok())
{
read_flag = read(kfd, &c, 1);
switch (read_flag)
{
case -1:
// case -1: is empty and errono
// set EAGAIN
if (errno == EAGAIN)
{
//no input yet
direction = 0;
break;
}
else
{
perror("read:");
exit(2);
}
// case 0 means all bytes are read and EOF(end of conv.)
case 0:
//no input yet
direction = 0;
break;
default:
ROS_DEBUG("value: 0x%02X\n", c);
switch (c)
{
case KEYCODE_L:
ROS_DEBUG("LEFT");
direction = 1;
dirty = true;
break;
case KEYCODE_R:
ROS_DEBUG("RIGHT");
direction = -1;
dirty = true;
break;
}
}
continuos_input::input_command cmd;
cmd.type = "Keyboard";
cmd.command = direction;
cmd.stamp = ros::Time::now();
key_pub.publish(cmd);
r.sleep();
}
}

I feel that the issue is with your subscriber rather than publisher. I can see that you have used rate to limit the publishing rate to 10Hz. Do confirm the publishing rate using Topic Monitor in rqt. Also setting a lower queue size for the publisher might help. Can't give a more definitive answer without referring to your subscriber node.

Related

Catch input from another shell

I currently have a C/C++ program which uses a barcode scanner as a keyboard, catches the input and does something with it. Here's the relevant parts of code:
int get_InStream() {
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
void nonblock(int state) {
struct termios ttystate;
tcgetattr(STDIN_FILENO, &ttystate);
if (state == 1) {
// ~ICANON: turn off canonical mode
// ~ECHO: not display character
//~ ttystate.c_lflag &= ~ECHO; // (ICANON & ECHO);
tcgetattr( 0, &ttystate); /* read curr. setting */
original_mode = ttystate; /* remember these */
ttystate.c_lflag &= ~ICANON; /* no buffering */
//~ ttystate.c_lflag &= ~ECHO; /* no echo either */
tcsetattr( 0 , TCSANOW, &ttystate); /* install settings */
//minimum of number input read.
ttystate.c_cc[VMIN] = 1;
}
else if (state == 0) {
//~ // turn on canonical mode
//~ ttystate.c_lflag |= ECHO;
tcsetattr(0, TCSANOW, &original_mode); /* undo -icanon, -echo */
}
// set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}
bool keyState(int key) { // Uses ASCII table
bool pressed = false;
int i = get_InStream(); // Allows to read from terminal
if (i != 0) {
char c = fgetc(stdin);
if (c == (char) key) {
pressed = true;
} else {
pressed = false;
char string_key = c;
pthread_mutex_lock(&id_mutex);
// Append character to content buffer
strcat(content, string_key);
pthread_mutex_unlock(&id_mutex);
}
}
return pressed;
}
void* get_inputContent(void* arg) {
pthread_detach(pthread_self());
nonblock(1);
while (1) {
// if this returns True, content contains data
if (keyState(0x0A)) { // 0x0A in ASCII table corresponds to New Line
pthread_mutex_lock(&id_mutex);
printf("Read this %d characters through barcode scanner: %s\n", strlen(content), content); //DEBUG
// doSomething()
strcpy(content, "\0"); // empty out content
pthread_mutex_unlock(&id_mutex);
}
}
nonblock(0);
pthread_exit(NULL);
}
Right now this works well as a separate thread from the main program, but if I open another terminal while the main program is running and I leave the focus on the new one, the barcode input is not caught by the thread.
So I'd like, either in C/C++ or in Bash, to let's say share the input accross various terminals, so that my thread can use it. Is there any way to do this?
I've searched for various options:
another descriptor to use in select()
using export in Bash
writing to a shared file
but I'm not so sure for any of those. Any suggestions?
EDIT: the program is being run on Raspberry Pi OS, previously Raspbian if I'm not mistaken
This is a XY problem situation right here. Your problem 'X' is
How can I access the keyboard device as which the barcode scanner presents itself to the system regardless of the current state of the system?
But you think, that by solving the problem 'Y'
How can I keygrab input directed to a different terminal?
Problem Y is hard, because it has security implications. Problem X is easy, but its solution depends on the operating system being used.
You mention bash and POSIX style file descriptor. So I'm guessing, that you're on some flavor of Linux. And with that, the problem is easy to solve! Each and every input device presents itself as a evdev device under /dev/input/event<X>. Your barcode scanner will show up there, as well.
You could either implement the evdev protocol yourself. Or just use libinput to do the heavy lifting.
And it being present as an independent input device allows you to do a few things:
Use udev to control which user accounts get access to it.
Use udev to actually detach it from the terminals, so that the barcode scanner can not be used to input (possibly harmful) commands.

Handling non-input keystrokes?

I'm not sure exactly how to phrase what I'm trying to ask; in C++, using the stdio.h header instead of iostream, how would I make it so that if the escape key is pressed at any point, the program is terminated? Is there something I could add once at the top of the program, or would I have to add it to every loop/conditional individually? Below is my code (the sleep() function is just for a visual loading/calculating effect):
#include <stdio.h>
#include <math.h>
#include <windows.h>
void repeat();
void quadratic()
{
double a, b, c;
double ans[2];
printf("-Arrange your equation in the form aX^2+bX+c \n-Enter the value of a: ");
scanf("%lf", &a);
printf("-Enter the value of b: ");
scanf("%lf", &b);
printf("-Enter the value of c: ");
scanf("%lf", &c);
double radical=((b*b)-(4*a*c));
double root=sqrt(radical);
double negB=(-1)*b;
double denominator=2*a;
if(denominator==0)
{
printf("Calculating");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf("\nError: Denominator must be non-zero.\n \n \n");
}
else if(radical==0)
{
ans[0]=negB/denominator;
printf("Both roots are equal: both values are X=%lf\n \n \n", ans[0]);
}
else if(radical<0)
{
printf("Calculating");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
double r,i;
radical*=-1;
r=negB/(2*a);
i=sqrt(radical)/(2*a);
printf("\nBoth roots are imaginary numbers.\n");
printf("Non-real answer(s): X=%lf+%lfi X=%lf-%lfi\n \n \n",r,i,r,i);
}
else
{
ans[0]=(negB+root)/denominator;
ans[1]=(negB-root)/denominator;
printf("Calculating");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf("\nX=%lf, X=%lf\n \n", ans[0], ans[1]);
}
repeat();
}
void repeat()
{
quadratic();
}
int main(void)
{
quadratic();
return 0;
}
The terminal used in stdio is likely to be line buffered
(cooked). If it is, checking for the escape key through scanf will
not work.
SEE THESE URLS:
Capture characters from standard input without waiting for enter to be pressed
scanf not reading input
CURSES or NCURSES will detect the escape key (ASCII character 27), depending
on the terminal type.
This code can be used in WINDOWS to check for the ESCAPE
key.
#include <conio.h>
#include <ctype.h>
int ch;
_cputs( "Type 'Y' when finished typing keys: " );
do
{
ch = _getch();
ch = toupper( ch );
if (ch != 27) {
_cputs( "CHARACTER: " );
_putch( ch );
_putch( '\r' ); // Carriage return
_putch( '\n' ); // Line feed
}
} while( ch != 27 );
Is there something I could add once at the top of the program, or would I have to add it to every loop/conditional individually?
I think you can add something once, and use it to catch key stroke events easily throughout your program.
Following is a code snippet showing a function I have used to handle key stroke events in a console application. It uses GetAsyncKeyState(). Included is a section that shows how to capture a CTRL key, and how you can do something once you see it. (the snippet shown shows how I capture a <ctrl><shift><h> key sequence to display a help menu for using this particular routine.
Note: In the description, delay_x(float delay) is simply a custom, non-blocking sleep, or delay function that includes a call to the following snippet. It is called from within the main program loop while(1){...} . Exiting the program is provided in one of the keystroke combinations: <CTRL><SHIFT><K>
Code snippet:
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
//
// SetAppState() is called continuously from within delay_x()
// to capture keystroke combinations as they occur asynchronously
// with this application, Keystroke combinations are listed below
//
// Note: GetAsyncKeyState() can maintian information regarding the
// state of a key instantaineously by use the MSB,
// and recently by using the LSB.
//
// For this application
// only instantaineous information will be kept, minimizing
// conflicts with other keyboard shortcut definitions
// defined by other applications that may be running
// simultaineously.
//
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
void SetAppState(void)
{
short state=0;
short state1=0;
state = GetAsyncKeyState(VK_CONTROL);
if (0x80000000 & state) //check instantaineous state of key
{
state = GetAsyncKeyState(VK_SHIFT);
if (0x80000000 & state) //check instantaineous state of key
{
state = GetAsyncKeyState('h');
state1 = GetAsyncKeyState('H');
if ((0x80000000 & state) ||
(0x80000000 & state1))
{ sprintf(gTempBuf, "Usage - keystrokes to access and control the PaAutoStartSlot application:\n\n"
"<CTRL><SHIFT> H (H)elp - \n"
"<CTRL><SHIFT> V o(V)erride - \n"
"<CTRL><SHIFT> S (S)tatus - \n"
"<CTRL><SHIFT> K (K)ill - \n"
"<CTRL><SHIFT> N (N)o - \n"
"<CTRL><SHIFT> I (I)Inside - \n"
"<CTRL><SHIFT> O (O)Outside- \n"
"\nSee log file at this location for runtime errors: \n\n%s", LOGFILE);
MessagePopup("Usage Menu",gTempBuf);
}
///// ... more code ...
End of snippet
Edit - Answer to questions in comments how to call GetAsyncKeyState()
There are many ways you could call GetAsyncKeyState() at the same time other stuff is going on. Threads are a good way. You can also do it all in line using a while()/switch(){} combination. Here is a very simple example of how you could do this (in pseudo code)
int gRunning = 1;
int state = 1;
int main(void)
{
//create variables, initialize stuff
while(gRunning)//this is your main program loop
{
delay_x(1.0);//check for keystrokes
switch(state) {
case 1:
//do some stuff here
//and experiment with values passed to delay_x(n)
delay_x(10000);//check for keystrokes
state++;
break;
case 2:
//do some different stuff here
delay_x(10000);//check for keystrokes
state++;
break;
... Add as many cases as you need for your program.
case n://last case, set execution flow to top
//do some more different stuff here
delay_x(10000);//check for keystrokes
state = 1;//loop back to top
break;
}
}
return 0;
}
void delay_x (float delay)
{
static clock_t time1;
static clock_t time2; clock();
time1 = clock();
time2 = clock();
while ((time2 - time1) < delay)
{
time2 = clock();
SetAppState(); //see partial definition in my original answer above.
}
}
Note: Using this method, you can have as many, or as few, cases as you need, the important thing is to keep a steady flow of calls to GetAsyncKeyState(). This does that via the call to delay_x().
Note2: Here is the segment (added to above definition) that will cause your program to exit:
state = GetAsyncKeyState('k');
state1 = GetAsyncKeyState('K');
if ((0x80000000 & state) ||
(0x80000000 & state1))
{
printf("Kill Program");
gRunning = FALSE;
}

Why is serial port skipping data when sending data?

I have written some C++ code to talk to my arduino via serial. It just tries to make oscillations on two servo motors using sine and cosine, but it is skipping data. I'm not sure why this is happening. I am using the termios.h for the serial stuff. The output from C++ is something like "V180H90" i.e. Vertical 180, Horizontal 90. I was using fstream and usleep() to send data before and it was working, but I'd like to use a better method than delaying by some arbitrary number.
Thanks for any help or guidance.
My arduino code
#include <Servo.h>
typedef enum { NONE, GOT_V, GOT_H } states;
states state = NONE;
Servo pan;
Servo tilt;
int laser = 11;
unsigned int currentValue;
int v_pan = 0;
int v_tilt = 0;
void setup()
{
pan.attach(10);
tilt.attach(9);
Serial.begin(9600);
state = NONE;
}
void processVertical(const unsigned int value)
{
Serial.print("Vertical = ");
Serial.println(value);
int result = 1300 + (value - 90) * 2;
//Serial.println(result);
tilt.writeMicroseconds(result);
}
void processHorizontal(const unsigned int value)
{
Serial.print("Horizontal = ");
Serial.println(value);
int result = 1500 + (value - 180) * 1;
//Serial.println(result);
pan.writeMicroseconds(result);
}
void handlePreviousState()
{
switch(state)
{
case GOT_V:
processVertical(currentValue);
break;
case GOT_H:
processHorizontal(currentValue);
break;
}
currentValue = 0;
}
void processIncomingByte (const byte c)
{
if (isdigit(c))
{
currentValue *=10;
currentValue += c - '0';
}
else
{
handlePreviousState();
switch (c)
{
case 'V':
state = GOT_V;
break;
case 'H':
state = GOT_H;
break;
default:
state = NONE;
break;
}
}
}
void loop()
{
if(Serial.available() > 0)
{
processIncomingByte(Serial.read());
}
digitalWrite(laser, HIGH);
}
//check out writeMicroseconds
My C++ code
// Program for sending data to serial
#include <iostream>
#include <sstream>
#include <string>
#include <termios.h>
#include <fcntl.h>
#include <math.h>
using namespace std;
//open serial port
int openPort(string path)
{
int fd; //file descriptor for port
fd = open(path.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
cerr << "Cannot open port" << endl;
else
fcntl(fd, F_SETFL, 0);
return (fd);
}
//set options for an open serial port
void setOptions(int fd)
{
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
//No parity 8N1
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
//No flow control
options.c_cflag &= ~CRTSCTS;
//Turn off s/w flow control
options.c_iflag &= ~(IXON | IXOFF | IXANY);
//Turn on read and ignore ctrl lines
options.c_cflag |= (CLOCAL | CREAD);
if( tcsetattr(fd, TCSANOW, &options) < 0) {
cerr << "Could not set attributes" << endl;
}
}
//write to serial port
void writePort(int fd, string data)
{
int n = write(fd, data.c_str(), 9);
if (n < 0)
cerr << "Cannot write to port" << endl;
}
int main() {
string path = "/dev/tty.usbmodemfd131";
//string path = "/dev/tty.usbmodemfa141";
int fd = openPort(path);
setOptions(fd);
stringstream ss;
string output;
unsigned short vertical = 0;
unsigned short horizontal = 0;
unsigned short freq = 10;
for(int i = 0; i < 360; i++) {
vertical = ((cos(i * freq * ((M_PI)/180))) + 1) * 90;
horizontal = ((sin(i * freq * ((M_PI)/180))) + 1) * 90;
ss << "V" << vertical << "H" << horizontal << endl;
output = ss.str();
ss.str("");
writePort(fd, output);
// cout << output; //DEBUG
}
close(fd);
return 0;
}
The "processIncomingByte" loop inside the device may have suffered a speed problem as you are processing the previous state (handlePreviousState) immediately after you receive a new mode.
The problem may be caused by doing a Serial.print in the corresponding function while the value-data bytes are still incoming continuously from the PC. Serial print is a relatively slow process in micro-controller logic.
I am not familiar with Arduino hardware, but some lower end micro-controller board is performing software serial interface using bitbanging method, so when you transmit, the receiving is completely stopped. To verify this you can remark the Serial.print to see whether it helps.
Anyway, doing lengthy processing in the middle of incoming data stream is alway problematic, unless you have a hardware serial interface in the device with lots of FIFO buffers.
A proper way to this problem is to receive the whole message inside a buffer first and then process it only when a end-of-message marker is received. For example, insert your message inside the [] pair like [V180H90]. Reset the buffer upon the "[" and process the buffer after you receive the "]". When you are collecting bytes into the buffer, make sure you also check for buffer overflow.
If you just shove data down the port's throat, it'll do its best not to set on fire, but the excess data isn't going to be sent. After all, the port operates at a finite speed and is a pretty limited and dump device.
So, before sending a character to the port you need to check the status of the port to see if it's actually ready to accept another character of data for transmission. Some serial ports can even generate interrupts when they can take more data to help you avoid wasteful status polling.
Also, sometimes two serial ports on the two devices can be connected with an extra pair of non-data signals (RTS and CTS) to indicate whether the receiving side is ready to receive more data. If you have those connected and your device is using them to indicate its readiness, your program should take the state of the device's CTS into account as well.
Clearly your device reads/process data slower than you send it via serial port. I see few possible solutions here:
1) Implement flow control and send data via serial port in blocking mode. You still have to wait after sending, but only as much as it is needed for your device to read and process data.
2) Implement two way communication so your device sends confirmation message (i.e. any single ASCII symbol) to indicate that it is ready to accept data.
3) Divide your code into two parallel parts i.e. : main loop (or an ISR) only reads data from serial port and stores it in a ring buffer, another loop polls the ring buffer and takes/process data from it as soon as there is some data available. This is the most difficult solution of the three as you need two separate threads (or a thread and an ISR) and protect ring buffer from concurrent access, but also the most powerful and flexible.
You are writing data out too quickly to the serial device and the device itself is spitting out data faster than you can read it back in on the other side of the device.
The correct way to cope with this is to throttle the speed of writes to the serial device to avoid flooding it with data.

Non-blocking keyboard read - C/C++

I got this following function with me working now. But what I need to improve is that it would read input from the keyboard (on the terminal) EVEN THOUGH IT IS NOT BEING PRESSED. I need to know when it is NOT pressed (idle) so that the switch case block will fall into the default section. At this point, the read() function waits until there's an input from the user. Can anyone give a suggestion just based on modifying this following code?
NOTE: I'm a Java programmer, and still learning C/C++ so it might be hard to get in my head a little bit. Thanks guys..
EDIT: I found this link, and seems to have something related to what i'm looking for at the line of fcntl(STDIN_FILENO,F_SETFL,flags | O_NONBLOCK); . But since I barely know anything in C, I completely have no Idea what it's saying, yet.
http://www.codeguru.com/forum/showthread.php?t=367082
int kfd = 0;
struct termios cooked, raw;
char c;
bool dirty = false;
//get the console in raw mode
tcgetattr(kfd, &cooked);
memcpy(&raw, &cooked, sizeof(struct termios));
raw.c_lflag &=~ (ICANON | ECHO);
// Setting a new line, then end of file
raw.c_cc[VEOL] = 1;
raw.c_cc[VEOF] = 2;
tcsetattr(kfd, TCSANOW, &raw);
puts("Reading from keyboard");
puts("=====================");
puts("Use arrow keys to navigate");
while(true){
//get the next event from the keyboard
if(read(kfd, &c, 1) < 0)
{
perror("read():");
exit(-1);
}
linear_ = angular_ = 0;
ROS_DEBUG("value: 0x%02X\n", c);
switch(c)
{
case KEYCODE_L:
ROS_DEBUG("LEFT");
angular_ = -1.0;
dirty = true;
break;
case KEYCODE_R:
ROS_DEBUG("RIGHT");
angular_ = 1.0;
dirty = true;
break;
case KEYCODE_U:
ROS_DEBUG("UP");
linear_ = 1.0;
dirty = true;
break;
case KEYCODE_D:
ROS_DEBUG("DOWN");
linear_ = -1.0;
dirty = true;
break;
default:
ROS_DEBUG("RELEASE");
linear_ = 0;
angular_ = 0;
dirty = true;
break;
}
The OP seems to have answered their question:
I think I solved my problem. Please, anyone, verify and let me know if this is the right way to do it, or is it the complete way to do it (am I missing any other addition step e.g. resetting it back again -if that even makes sense) .
So what i found is to add this 3 lines before entering the while loop:
flags = fcntl(0, F_GETFL, 0); /* get current file status flags */
flags |= O_NONBLOCK; /* turn off blocking flag */
fcntl(0, F_SETFL, flags); /* set up non-blocking read */

How to identify shift/ctrl keys were pressed in the linux world (without using GL and X11/XKBlib.h) in C++?

I used to do in windows:
SHORT key1 = ::GetKeyState(VK_LSHIFT)& 0xF0;
SHORT key2 = ::GetKeyState(VK_RSHIFT)& 0xF0;
SHORT key3 = ::GetKeyState(VK_LCONTROL)& 0xF0;
SHORT key4 = ::GetKeyState(VK_RCONTROL)& 0xF0;
bShift = (key1 != 0) || (key2 != 0);
bCtrl = (key3 != 0) || (key4 != 0);
How to detect if the shift/ctrl keys were pressed in the linux world?
Without using GL(it can be pressed not in the graphic window..) or X11..
Is there something general to retrieve it directly form the OS?
Thanks,
Vladimir.
I would do this using Linux Input API. Take a look at Youarefunny's answer here to see how you can check current key state (pressed or released).
Such check may take noticeable amount of time especially if you need to call it very often. So once you determine the initial state you may monitor for changes by reading input events from device file, like this (I skipped error-checking for brevity):
#include <stdio.h>
#include <linux/input.h>
int main (int argc, char *argv[])
{
struct input_event ev;
FILE *kbd = fopen("/dev/input/by-id/usb-Dell_Dell_QuietKey_Keyboard-event-kbd", "r");
while (fread(&ev, sizeof(ev), 1, kbd) == 1)
{
if (ev.type == EV_KEY && (ev.code == KEY_LEFTSHIFT || ev.code == KEY_RIGHTSHIFT))
{
switch (ev.value)
{
case 0: printf("Shift released\n"); break;
case 1: printf("Shift pressed\n"); break;
case 2: printf("Shift repeated\n"); break;
default: break;
}
}
// similarly for KEY_LEFTCTRL, KEY_RIGHTCTRL, etc.
}
fclose(kbd);
return 0;
}
Note that reading /dev/input/* files will probably require root privileges (unless you run chmod before) since default access mode is 640 and the files belong to root.