GUI dialog tool and serial IO (Solved)

For discussions about programming, programming questions/advice, and projects that don't really have anything to do with Puppy.
Post Reply
Message
Author
User avatar
Uten
Posts: 129
Joined: Tue 29 Jan 2008, 11:00

GUI dialog tool and serial IO (Solved)

#1 Post by Uten »

Hello,

For my arduino projects I have started to wonder if it possible to use some kind of dialog program, like GtkDialog, and serial communication to make a frontend on a PC.

I want as few dependencies as possible.

From the top of my head I think I can make a dialog and send commands down the serial ( /dev/ttyUSB0 ) line. But I don't know how to read values back from the serial line asynchronously.

This code will do in a terminal

Code: Select all

#!/bin/bash
# interactive serial communication over usb port
# I tried it against arduino uno program
#
# SOURCE: https://unix.stackexchange.com/questions/22545/how-to-connect-to-a-serial-port-as-simple-as-using-ssh/311680#311680
#
if [[ $# -lt 1 ]]; then
    echo "Usage:"
    echo "  femtocom <serial-port> [ <speed> [ <stty-options> ... ] ]"
    echo "  Example: $0 /dev/ttyS0 9600"
    echo "  Press Ctrl+Q to quit"
fi

# Exit when any command fails
set -e

# Save settings of current terminal to restore later
original_settings="$(stty -g)"

# Kill background process and restore terminal when this shell exits
trap 'set +e; kill "$bgPid"; stty "$original_settings"' EXIT

# Remove serial port from parameter list, so only stty settings remain
port="$1"; shift

# Set up serial port, append all remaining parameters from command line
stty -F "$port" raw -echo "$@"

# Set current terminal to pass through everything except Ctrl+Q
# * "quit undef susp undef" will disable Ctrl+\ and Ctrl+Z handling
# * "isig intr ^Q" will make Ctrl+Q send SIGINT to this script
# NOTE: This line did not work for me.
#stty raw -echo isig intr ^Q quit undef susp undef

# Let cat read the serial port to the screen in the background
# Capture PID of background process so it is possible to terminate it
cat "$port" & bgPid=$!

# Redirect all keyboard input to serial port
#cat >"$port"
while read cmd
do
   echo "$cmd" 
done > "$port"
So I would like to capture stuff from the line

Code: Select all

cat "$port" & bgPid=$!
Parse it and update my gui with the values.
This part

Code: Select all

while read cmd
do
   echo "$cmd" 
done > "$port"
would be replaced with GUI elements like buttons etc.

Anyone know of examples or solutions I might use?
Last edited by Uten on Mon 05 Feb 2018, 11:07, edited 1 time in total.

User avatar
technosaurus
Posts: 4853
Joined: Mon 19 May 2008, 01:24
Location: Blue Springs, MO
Contact:

#2 Post by technosaurus »

There are plenty of examples in the gtkdialog tips thread as well as the gtkdialog source, but the best way is to look at the script of a program that does something similar to what you want.
Check out my [url=https://github.com/technosaurus]github repositories[/url]. I may eventually get around to updating my [url=http://bashismal.blogspot.com]blogspot[/url].

jafadmin
Posts: 1249
Joined: Thu 19 Mar 2009, 15:10

#3 Post by jafadmin »

It is worth noting that picocom or minicom are included with most puppies.
https://linux.die.net/man/8/picocom

It can also send and receive files over the serial port

User avatar
Uten
Posts: 129
Joined: Tue 29 Jan 2008, 11:00

#4 Post by Uten »

@jafadmin: semms like minicom and picocom is left out in fatdog. A terminal cli solution is to use

Code: Select all

platformio device monitor
witch launch a miniterm.py session with some custom settings.

Anyway this is what I have come up with. It works for personal quick fix use. But I guess a pyGTK and miniterm.py is a reasonable trade-off if you need something more serious.

The most important tricks are probably the stty configuration and the timer trick in gtkdialog to get "asynchronous" updates. You may use multiple timers.

My bash script:

Code: Select all

#! /bin/bash

#
# Demo program making a GUI interface against a arduino uno micro 
# controller. 
#
# RESOURCE: https://playground.arduino.cc/Interfacing/LinuxTTY
#
# NOTICE: This "works" but is not a stable solution. The arduino board
#         resets when the dialog is connected. This is done as part of
#         the stty call (or first read in the backround loop).
#
#         Sometimes the ttyUSB0 connection will "hang". When it do I 
#         I reset it with this commend:
#
#                modprobe -r ch341 && sleep 3 && modprobe ch341
#
#         You will have to replace the module ch341 with the one in use 
#         on your system. 
#
#

# 
# The serial device to use
export port="/dev/ttyUSB0"
#
# Save original config from device
stty_orig="$(stty -g)"
#
# a file in /dev/shm/ is in memmory
export file="/dev/shm/foo"
#
# The stty configuration has to be right to get reliable communication.
# Most samples on the net use only stty /dev/tty[] 9600 which is to 
# little
stty -F "$port" cs8 9600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr \
		-isig -icanon -iexten -echo -echoe -echok -echoctl \
		-echoke noflsh -ixon -crtscts 


echo "initiated" > "$file"
# Background loop / process to read from arduino board


#
# Make a sub shell to read from the serial port
#
# The simple solution, but it also can generate huge files. maybe use it 
# for logging?
#cat "$port" > $file & 
#
# A read loop that works for this sample
export	prev=""
while read -r line; do
	if [[ "$line" != "" ]] && [[ "$prev" != "$line" ]]; then
		# TODO: Part of this line disappears? We should get a line
		# cmdSend: [<command>]
		# (40) line:=<command> <--> prev:=<previous command>
		echo "($LINENO) line:=$line <--> prev:=$prev"
		echo "$line" >> "$file"
		prev="$line"
	else
		sleep .1
	fi	
done < "$port"  &

#
# Save pid of sub shell so we can clean up later
bgPid=$!
if [ "$bgPid" == "0" ]; then
	echo "ERROR: Reading serial device failed"
	exit 1
fi

#
# Trap exit and cleanup background process
trap 'set +e; stty "$stty_orig";rm -f "$file"; kill "$bgPid"' EXIT

#
# helper function. Must be exported to be called by gtkdialog
function cmdSend() {
	echo "$1" > "$port"
	echo "cmdSend: [$1]"
}; export -f cmdSend

cmdSend "version"

function getLastLine() {
	#sed -n '$p' "$file"	
	if [ -f "$file" ]; then
		#cat "$file"
		tail --lines=3 "$file"
	fi
}; export -f getLastLine


#
# This is the code used by gtkdialog. notice the timer (last part)
# to update the information label. This is how we get values
# from the arduino device.
export MAIN_DIALOG='
<vbox>
    <text>
      <label>--- Crane demo ---</label>
    </text>
	<button>
		  <label>demo</label>
		  <action>cmdSend "demo"</action>
	</button>
	<button>
		<label>up</label>
		<action>cmdSend "up"</action>
	</button>
<hbox>
	<button>
		<label>left</label>
		<action> cmdSend "left"</action>
	</button> 
	<button>
		<label>stop</label>
		<action> cmdSend "stop"</action>
	</button>
	<button>
		<label>right</label>
		<action> cmdSend "right"</action>
	</button>
</hbox>
	<button>
		<label>down</label>
		<action> cmdSend "down"</action>
	</button> 
  <text>
  <variable>MSG</variable>
  <input> getLastLine </input>
 </text>
 <timer milliseconds="true" interval="250" visible="false">
  <action type="refresh">MSG</action>
 </timer>	   
</vbox>
'

#
# gtkdialog
gtkdialog --program=MAIN_DIALOG

#cat "$file"
exit 0
My scetch (I use platformio to compile/upload to arduino board)

Code: Select all

/*
Into Robotics
*/
#include <SerialCommand.h> 

bool bRunning=false;
int arduinoLed=13; 
int x1 = 2; /* x axis motor relay 1 */
int x2 = 3; /* x axis motor relay 2 */
int y1 = 4; /* y axis motor relay 1 */
int y2 = 5; /* y axis motor relay 2 */

SerialCommand sCmd;

unsigned long uTime=0;

void setup()
{
	// Setup output ports used by relay board
	all_output( x1, x2, y1, y2);
	/* The 4 Relay Module SBX board has default high=off */
	off( x1, x2);
	off( y1, y2);

	// Turn off default internal led
	pinMode(arduinoLed, OUTPUT);
	digitalWrite(arduinoLed,LOW);

	// Setup serial communication, could be faster
	Serial.begin(9600); 

	// Setup serial command interface
	sCmd.addCommand("right", SCMD_right );
	sCmd.addCommand("left", SCMD_left );
	sCmd.addCommand("up", SCMD_up );
	sCmd.addCommand("down", SCMD_down );
	sCmd.addCommand("stop", SCMD_stop);
	sCmd.addCommand("demo", demo);
	sCmd.addCommand("help", help);
	sCmd.addCommand("version", version);
	sCmd.setDefaultHandler(SCMD_error);
	Serial.flush();
}
void demo() {
	Serial.println("Demo start");
	left(x1, x2);
	delay(2000);
	off(x1, x2);
	delay(2000);
	right(x1, x2);
	delay(2000);
	off(x1, x2);
	delay(2000);
	Serial.println("Demo end");
}
void version() {
	Serial.println("Version: 0.1");
}
void help() {
	Serial.println("Available commands");
	Serial.println("right - Turn crane right");
	Serial.println("left  - Turn crane left");
	Serial.println("up    - Hoist hook");
	Serial.println("down  - Lower hook");
	Serial.println("stop  - Stop all actions");
	Serial.println("demo  - Run a demo");
	Serial.println("help  - Print this help");
	Serial.println("version - Print version information");
}
void all_output(int r1, int r2, int r3, int r4) {
	pinMode(r1, OUTPUT);
	pinMode(r2, OUTPUT);
	pinMode(r3, OUTPUT);
	pinMode(r4, OUTPUT);
}
void off(int r1, int r2) {
	digitalWrite(r1, HIGH);
	digitalWrite(r2, HIGH);
	bRunning=true;
} 
void left(int r1, int r2) {
	digitalWrite(r1, LOW);
	digitalWrite(r2, HIGH);
	bRunning=true;
}
void right(int r1, int r2){
	digitalWrite(r1, HIGH);
	digitalWrite(r2, LOW);
	bRunning=true;
}

void SCMD_left() {
	left(x1, x2);
	uTime=millis();
	Serial.println("left");
} 
void SCMD_right() {
	right(x1,x2);
	uTime=millis();
	Serial.println("right");
}
void SCMD_up() {
	right(y1,y2);
	uTime=millis();
	Serial.println("up");
}
void SCMD_down() {
	left(y1, y2);
	uTime=millis();
	Serial.println("down");
}
void SCMD_stop() {
	off(x1, x2);
	off(y1, y2);
	bRunning=false;
	uTime=millis()-uTime;
	Serial.println("stop");
	
}
void SCMD_error(const char *command) {
	SCMD_stop();
	Serial.print("Unrecognized command,[");
	Serial.print(command);
	Serial.println("]");

}
void loop() {
	if (Serial.available() >0){
		sCmd.readSerial();
		Serial.flush();
	} else {
		delay(250);
	}
}
Happy coding
uten

jamesbond
Posts: 3433
Joined: Mon 26 Feb 2007, 05:02
Location: The Blue Marble

#5 Post by jamesbond »

@jafadmin: semms like minicom and picocom is left out in fatdog. A terminal cli solution is to use
Fatdog comes with microcom (part of busybox). But microcom is useful for interactive sessions.
For programmatic read/write, "echo" and "cat" and "read" is better. You need to stop the board from resetting, though, do "stty -F /dev/ttyUSB0 -hupcl" - see https://arduino.stackexchange.com/a/16779. Of put 10uF cap between reset line and the ground.
Fatdog64 forum links: [url=http://murga-linux.com/puppy/viewtopic.php?t=117546]Latest version[/url] | [url=https://cutt.ly/ke8sn5H]Contributed packages[/url] | [url=https://cutt.ly/se8scrb]ISO builder[/url]

User avatar
mister_electronico
Posts: 969
Joined: Sun 20 Jan 2008, 20:20
Location: Asturias_ España
Contact:

#6 Post by mister_electronico »

very interesting , can that maybe you are interested in this.

http://www.murga-linux.com/puppy/viewto ... 473#877473

video

https://www.youtube.com/watch?v=qrVFsOH82i4

see you

User avatar
Uten
Posts: 129
Joined: Tue 29 Jan 2008, 11:00

#7 Post by Uten »

Thanks for the input guys. Sorry for the late replies. I'm not very active online these days.

Your right @Jamesbond microcom is part of fatdog. I must have typed it wrong when I tested.

After I got the stty init right I have not had much trouble with communication. I have just used it on board (Chines knock of uno board from aliexpress). But I will keep the reset call in my notes for later use.

looks like a nice project you have there @mister_electronico.

Happy coding
Uten

Post Reply