This is the program controlling movement in the Z axis (Z stage control), as well as responsible of implementing video and image capture (RaspiCAM control). An executable file
open_scope, is made from a main code file written in C and compiled with gcc from the following four files:
- open_scope.c
- build_Z.ui
- kimero_LAB.tiff
- microscope.tiff
The open_scope.cfile is the main C code. It uses the Gtk library for implementing GUI code and its constructed using Geany as development environment. We also use Glade as graphical development environment and therefore we use a secondary file; build_Z.ui, which contains code that GtkBuilder uses to create the GUI. This Glade-generated XML file contains all the graphical information needed by GtkBuilder to generate the Gtk code needed to link up all the GUI components with the rest of the code and libraries used. The kimero_LAB.tiff and microscope.jpg files correspond to our lab logo and an application image icon.
The OpenScope application, shown in the figure, runs on the Raspberry Pi and controls the Raspberry Pi camera (RaspiCAM). This application is also responsible of generating the messages to talk to the Arduino Z. These messages leave the Raspberry Pi from output PINS 24 and 29 and must arrive to the Arduino input pinsPINS 4 and PIN 5. These messages consist of two-bit words (0,0), (1,0), (0,1) communicating the movement wishes of the Raspberry Pi: stay-put, move up, move down. On the Raspberry Pi side, the code sets up PIN 24 and PIN 29 to be output pins and use them to communicate with the input pins on the Arduino Z side via a logic converter and using the gpio utility from the WiringPi library.
Another important aspect of the App’s GUI is that it allows to call rapistilfrom a Gtk button via the linux system call; here a shell is temporarelly forked while the pic is taken. For video streaming we use raspivid connected to the GUI via a Gtk switch butusing function g_spawn_async from the Glib library to do the forking.
The file open_scope.c contains the C code implemented the application engine. First we include the libraries, gtk, stdlib, signal, unistd, stdio, and string which we need,
#include <gtk/gtk.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <stdio.h> #include <string.h>
Now we define an important global variable we need to keep track of the process id (pid) of any raspivid session we spawn from our application when a user switches on the video Gtk switch on the GUI; we will need this pid to kill such process when the Gtk switch is switched off or the program exits.
// This is a global variable holding the PID of a child to be created when accessing // the camera with raspivid program used in the start_video call back GPid pid;
The first function we need to implement is set_gpio_modes which sets up the pin structure and the pin values the App needs to know by default at launch time.
void set_gpio_modes(void) { //We use 2 PINS to communicate Z axis changes to the Arduino // // PIN, wirPi n. 24 system("gpio mode 24 OUT"); system("gpio write 24 0"); // SET PIN @ LOW by Default // PIN, wirPi n. 29 system("gpio mode 29 OUT"); system("gpio write 29 0");// SET PIN @ LOW by Default // done with setting pins g_print("Finished setting up the GPIO PINs with gpio utility \n"); system("gpio readall"); }
We use set_gpio_modesto write the (0,0) message to a listening Arduino Z to communicate that we are stationary and do not wish to move in the Z direction. We also display the pin structure calling gpio readall via the system call. Next, we define the functions to deal with all the signals that could come from a user interacting with the Gtk Widgets. First, we define a standard Gtk function which handles user curiosity. Ashow_about function which is responsible of giving information to the interested user.
// HERE GOES THE ABOUT DIALOG BOX For info at a website static void show_about(GtkWidget *widget, gpointer data) { GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file("kimero_LAB.tiff",NULL); GtkWidget *dialog = gtk_about_dialog_new(); gtk_about_dialog_set_program_name (GTK_ABOUT_DIALOG(dialog), "Open Microscope Controller"); gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog),"version 1, January 2017"); gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog),"Open Source Code, The Keymer Lab"); gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog), "The Open Micro-Scope, an open source tool for DIY Biology. Thanks to Fondecyt 1150430."); gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog), "http://keymerlab.nl"); gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), pixbuf); g_object_unref(pixbuf), pixbuf =NULL; gtk_dialog_run(GTK_DIALOG (dialog)); gtk_widget_destroy(dialog); }//When the About button is clicked, //this function (an event handler) triggers the opening of a standard Gtk about dialog.
Another very important function is the function handling video capture. When a user, switches on the Gtkswitch on the video capture part of the GUI, it triggers Glib and Gtk to spawn a new process. This forked child process is needed to host the raspivid session responsible for the video streaming. The traditional system call approach (used with gpio and with raspistill) does not work here since the GUI cannot afford to freeze while waiting for the end of a video streaming that might never end. For this fanzy task we use the function g_spawn_async within our start_video handler.
// the call back needs this casting or it doesn't work properly static void start_video (GObject *switcher, GParamSpec *pspec, gpointer user_data) { if(gtk_switch_get_active(GTK_SWITCH (switcher))) { // here goes the code when the video swich is set ON g_print ("Video streaming started...\n"); // here is the argument vector to run as child process. gchar * child_argv[]= { "/usr/bin/raspivid", "-t","0", "-ex","off", "-p","0,20,400,400", "-v", "-awb","sun",NULL}; gint stdout; GError * error =NULL; // here we spawn the child process to be handle PID pid g_spawn_async (NULL, child_argv, NULL,G_SPAWN_DEFAULT,NULL, NULL, &pid, &error); g_print("We have spawn a child process who's pid is %i, and we did from process %i which was in turn made from parent %i (the running shell)\n",pid, getpid(),getppid()); }else { // here goes the code for when video switch is OFF g_print ("Video streaming stopped...\n"); // the line bellow doesnt close the child // but is useful IF ever porting to windows. g_spawn_close_pid (pid); // this is the kill way in UNIX kill(pid,9); } }
Most of the job is done by the call to function g_spawn_asyncfrom the GLib and by constructing the argument vector, child_argv,which is then used as launching command for the newly spawned child process. This is the key line of code within the function start_video. It is the construction of a string representing the unix argument vector of the child process. This is done by the following expression present in the code above,
gchar * child_argv[]= { "/usr/bin/raspivid", "-t","0", "-ex","off", "-p","0,20,400,400", "-v","-awb","sun", NULL };
This vector child_argv is passed onto g_spawn_async which is executed whenstart_videois called; which is when the Gtk switch is switched on. It is here where the argument vector is created so a child raspivid process which is spawned by OpenScope can have the standard parameters of the Linux API. When the user is done with video streaming she will switch the Gtk switch back to the off position; at this moment the same handler (start_video) will terminate the child process with ID matching to the PID value returned by g_spawn_async when it was called to create the child. The PID is a global variable we declared in the initial section of the code file.
Of course We need a function to clean up the mess in case we quit the main application without switching the video capture off. For this cases we write clean_up_b4_go to make sure all is clean before exit.
// not working properly...cannot recognize the switch... need to quit and kill raspivid if on static void clean_up_b4_go(GtkWidget *widget, gpointer data) { if(gtk_switch_get_state(GTK_SWITCH(data))) kill(pid,9); }
As with the case of video streaming, another important function microscope users are usually interested in is image capture. As you can imagine, we use a system call to call raspistill within the event handler responsible of handling the clicked signal coming out the “Capture” Gtk button. We implement this action in function take_picture, the handler responding to the click of the image capture Gtk button.
static void take_picture(GtkWidget *widget, gpointer data) { if(gtk_switch_get_state(GTK_SWITCH(data))) { gtk_switch_set_state (GTK_SWITCH(data),FALSE); system("raspistill -o open_scope_capture_picture.jpg -p 10,20,400,400 -v"); gtk_switch_set_state (GTK_SWITCH(data),TRUE); }else{ system("raspistill -o open_scope_capture_picture.jpg -p 10,20,400,400 -v"); } }
The only tricky part in the code above is that before shooting the picture, we need to check for the status of video streaming. This is because RaspiCAM would not let us take a pic if the camera is busy streaming video. The hack is to be polite and first ask, then act! So we first check the status of the Gtk switch and if it is on, we first turn it off, then take the pic, and finally we turn it back on; else we just go ahead and shoot the pic.
The final three functions to be defined correspond to handlers meant to respond to the clicking of each one of the three Gtk buttons controlling the Z movement: Up, Stop, Down. We first define a function called move_upto handle clicks of the Up Gtk button;triggering the Raspberry Pi to send from output pins 24 and 29 the (1,0) message to the Arduino.
static void move_up (GtkWidget *widget, gpointer data) { g_print ("Hello Open Scope: we are going up!\n"); system("gpio write 24 1"); system("gpio write 29 0"); }
For moving down we define move_down to write the message (0,1) to be send from output pins 24 and 29 to the Arduino Z when the Gtk button with the “Down” label is clicked.
static void move_down (GtkWidget *widget, gpointer data) { g_print ("Hello Open Scope: we are going down!\n"); system("gpio write 24 0"); system("gpio write 29 1"); }
For stopping actuation and stay put in a fix location we use a function called stop. This handler isresponsibleof sending the (0,0) message to the Arduino Z from output pins 24 and 29. When the Stop Gtk button is clicked, the stage stops moving.
static void stop (GtkWidget *widget, gpointer data) { g_print ("Hello Open Scope: we stop moving!\n"); system("gpio write 24 0"); system("gpio write 29 0"); }
Finally, in themain block we use GtkBuilder to extract all the GObjects we need to draw the GUI (window, buttons, separators, and labels, and a switch). These are defined in the XML file builder_Z.ui that we generate with Glade. We connect each Gtk button and Gtk switch to their respective handlers. Such connection is done using g_signal_connect. Here we also call the function set_gpio_modessetting up the gpio modes for the first time.
int main (int argc,char*argv[]) { GtkBuilder *builder; // Notice here we use builder (GLADE) to generate the interface GObject *window; GObject *button; GObject *switcher; gtk_init (&argc,&argv); /* Construct a GtkBuilder instance and load our UI description */ builder = gtk_builder_new (); gtk_builder_add_from_file (builder,"builder_Z.ui",NULL); /* Connect signal handlers to the constructed widgets. */ window = gtk_builder_get_object (builder,"window"); g_signal_connect (window,"destroy", G_CALLBACK (gtk_main_quit),NULL); button = gtk_builder_get_object (builder,"button_up"); g_signal_connect(button,"clicked", G_CALLBACK (move_up),NULL); button = gtk_builder_get_object (builder,"button_down"); g_signal_connect(button,"clicked", G_CALLBACK (move_down),NULL); button = gtk_builder_get_object(builder,"button_stop"); g_signal_connect(button,"clicked", G_CALLBACK(stop),NULL); button = gtk_builder_get_object(builder,"button_about"); G_signal_connect(button,"clicked",G_CALLBACK(show_about),GTK_WINDOW(window)); // Here we play with the switcher switcher = gtk_builder_get_object(builder,"switch_video"); gtk_switch_set_active (GTK_SWITCH (switcher),FALSE); g_signal_connect (GTK_SWITCH (switcher),"notify::active", G_CALLBACK (start_video), switcher); button = gtk_builder_get_object (builder,"button_take_photo"); q_signal_connect(button,"clicked",G_CALLBACK(take_picture),switcher); button = gtk_builder_get_object (builder,"quit"); g_signal_connect(button,"clicked",G_CALLBACK(clean_up_b4_go),switcher); g_signal_connect_swapped(button,"clicked",G_CALLBACK(gtk_main_quit),NULL); /*Here we configure the modes of the GPIO PINs and launch*/ set_gpio_modes(); gtk_main(); return 0; }