Close

XForms' Form Designer

A project log for GUI Programming for a Console Hacker

After 30 years of procedural language programming (i.e. console programming), time to look at event driven programming.

agpcooperagp.cooper 12/29/2018 at 05:290 Comments

GUI Programming

I generally prefer to code directly but why not try the Form Designer (fdesign).

Where to Start? Perhaps page 93 of the documentation ("Part II - The Form Designer").

Okay after two days it is pretty straight forward and easy to use.

Here is my gCodeSender clone:


Fonts

That is changing the font type, size etc, does not seem to work. Te demo "fonts.c" does not work. It does not help that I Mint does not have the default font (i.e. helvetic-*-?, courier-*-?, times-*-?, charter-*-? and Symbol).

Even then, if I try to install my own, they register but same result (i.e. font).

After Another Two Days

The serial port opens and closes pretty reliably. An unplug while the port is open hangs the port but the next port is provided upon "Reflesh" (i.e. "/dev/ttyUSB0" is lost but "/dev/ttyUSB1" pops up). The Arduino serial controller has the same problem.

After lots of research I find the "hang" problem is not fixable. It is an issue with how the drivers work or coded.

I added locks to the serial ports just in case of two program instances inter-react.     

Still need to write the background serial port communications code. Looking to use the "idle callback procedure" for this. My stand alone serial test programs seem to be working properly.

Wrote some code to sort out the line ending options (it pretends to be a Grbl controller). Yes, I get everything:

The code looks like this:

void setup() {
  // start serial port at 9600 bps and wait for port to open
  Serial.begin(9600);
  while (!Serial);
  
  // send a byte to establish contact until receiver responds
  establishContact();
}

void loop() {
  static char inByte='\0';         // incoming serial byte
  static char lastByte='\0';       // last incoming serial byte

  if (Serial.available()>0) {
    // get incoming byte:
    lastByte=inByte;
    inByte=Serial.read();

    if ((lastByte=='\r')&&(inByte=='\n')) {
       // Block on second byte of \r\n series
       inByte='\0';
    } else if ((lastByte=='\n')&&(inByte=='\r')) {
       // Block on second byte of \n\r series
       inByte='\0';
    } else if ((inByte=='\n')||(inByte=='\r')||(inByte=='\0')) {
       // Print if /r, /n, /r/n, /n/r or /0
       Serial.println("ok");    
    }
  }
}

void establishContact() {
  while (Serial.available()<=0) {
    Serial.println("Grbl V");   // send the initialisation string
    delay(1000);
  }
}

Form Designer

The "Show" button calls "gCodeFilter" that pops up a display of the loaded file after filtering (i.e. Input Tolerance):

A Terminal Program

I found this Serial Terminal program (source: https://en.wikibooks.org/wiki/Serial_Programming/termios):

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h> // needed for memset

int main(int argc,char **argv) {
  struct termios tio;
  struct termios stdio;
  int tty_fd;
  char c='D';

  printf("Please start with %s /dev/ttyUSB0 (for example)\n",argv[0]);

  memset(&stdio,0,sizeof(stdio));
  stdio.c_iflag=0;
  stdio.c_oflag=0;
  stdio.c_cflag=0;
  stdio.c_lflag=0;
  stdio.c_cc[VMIN]=1;
  stdio.c_cc[VTIME]=0;
  tcsetattr(STDOUT_FILENO,TCSANOW,&stdio);
  tcsetattr(STDOUT_FILENO,TCSAFLUSH,&stdio);
  fcntl(STDIN_FILENO,F_SETFL,O_NONBLOCK);  // make the reads non-blocking

  memset(&tio,0,sizeof(tio));
  tio.c_iflag=0;
  tio.c_oflag=0;
  tio.c_cflag=CS8|CREAD|CLOCAL; // 8n1, see termios.h for more information
  tio.c_lflag=0;
  tio.c_cc[VMIN]=1;
  tio.c_cc[VTIME]=5;

  tty_fd=open(argv[1],O_RDWR|O_NONBLOCK);  // O_NONBLOCK might override VMIN and VTIME, so read() may return immediately.
  cfsetospeed(&tio,B9600);                 // 9600 baud
  cfsetispeed(&tio,B9600);                 // 9600 baud
  tcsetattr(tty_fd,TCSANOW,&tio);

  while (c!='q') {
    if (read(tty_fd,&c,1)>0) write(STDOUT_FILENO,&c,1); // if new data is available on the serial port, print it out
    if (read(STDIN_FILENO,&c,1)>0) write(tty_fd,&c,1);  // if new data is available on the console, send it to the serial port
  }

  close(tty_fd);

  return(0;
}

Here is my version:

// Serial Port Code
#include <stdio.h>
#include <fcntl.h>                 // File Control Definitions
#include <termios.h>               // POSIX Terminal Control Definitions
#include <unistd.h>                // UNIX Standard Definitions
#include <sys/file.h>              // flock()
// The Serial Port Structure
struct termios SerialPortSettings; // Serial Port Stucture
int fd=-1;                         // Serial Port File Descripter
char portstr[13]="/dev/ttyUSB0";   // Serial Port Name
bool PortFlag=false;
bool OpenPortFlag=false;
int BaudSpeed=9600;
int BaudCode=B9600;                // Baud Constant (9600).
.
.

void Open_Port_cb(FL_OBJECT *ob,long data FL_UNUSED_ARG) {
  OpenPortFlag=fl_get_button(ob);
  fprintf(stderr,"Open Port Flag %d\n",OpenPortFlag);

  if (OpenPortFlag) {
    // Get selected port name
    strcpy(portstr,fl_get_choice_text(fd_Control->Set_Port));

    // Set the Port
    fd=open(portstr,O_RDWR|O_NOCTTY|O_SYNC);
    if (fd!=-1) {
      // Lock access so that another process can not also use the port
      if (flock(fd,LOCK_EX|LOCK_NB)==0) {
        // Get the current attributes of the serial port - Should fail if no hardware
        if (tcgetattr(fd,&SerialPortSettings)==0) {             
          tcgetattr(fd,&SerialPortSettings);                     // Get the current attributes of the serial port

          cfsetispeed(&SerialPortSettings,BaudCode);             // Set Read Speed as 9600
          cfsetospeed(&SerialPortSettings,BaudCode);             // Set Write Speed as 9600

          SerialPortSettings.c_cflag&=~PARENB;                   // Disables the Parity Enable bit (PARENB), So No Parity
          SerialPortSettings.c_cflag&=~CSTOPB;                   // CSTOPB = 2 Stop bits, here it is cleared so 1 Stop bit
          SerialPortSettings.c_cflag&=~CSIZE;                    // Clears the mask for setting the data size
          SerialPortSettings.c_cflag|=CS8;                       // Set the data bits = 8

          SerialPortSettings.c_cflag&=~CRTSCTS;                  // No Hardware flow Control
          SerialPortSettings.c_cflag|=CREAD|CLOCAL;              // Enable receiver, ignore Modem Control lines

          SerialPortSettings.c_iflag&=~(IXON|IXOFF|IXANY);       // Disable XON/XOFF flow control both i/p and o/p
          SerialPortSettings.c_iflag&=~(ICANON|ECHO|ECHOE|ISIG); // Non Cannonical mode

          SerialPortSettings.c_oflag&=~OPOST;                    // No Output Processing

          // Setting Time outs (set for: do not wait - return immediately)
          SerialPortSettings.c_cc[VMIN]=0;                       // Block until at least n characters read
          SerialPortSettings.c_cc[VTIME]=0;                      // Block until at least n * 100ms increments

          if ((tcsetattr(fd,TCSANOW,&SerialPortSettings))==0) {  // Set the attributes to the termios structure
            PortFlag=true;             
            fprintf(stderr,"\nSerial Port %s Baud %d Mode %s\n",portstr,BaudSpeed,"8N1");
          } else {
            PortFlag=false;
          }                                 
        } else {
          PortFlag=false;             
        }
      } else {
        PortFlag=false;             
      }
    } else {
       PortFlag=false;             
    }
    if (PortFlag) {
       // Discards old data in the rx buffer
       tcflush(fd,TCIFLUSH);
    } else {
       if (fd!=-1) {
         // Close the Port (if open)
         close(fd);
         // Free the port so that others can use it
         flock(fd,LOCK_UN);
         fd=-1;
       }
       fl_clear_choice(fd_Control->Set_Port);
       fl_addto_choice(fd_Control->Set_Port,"Refresh!");
       fl_set_button(fd_Control->Open_Port,false);             
       fprintf(stderr,"\nSerial Port not found!\n");
    }
  } else {
     if (fd!=-1) {
       // Close the Port (if open)
       close(fd);
       // Free the port so that others can use it
       flock(fd,LOCK_UN);
       fd=-1;
     }
  }
}

Both work but there are some differences in the set up that needs to be checked, "O_NONBLOCK" for example.

Still more code to write.

Font Issue Solved

Finally, I found an XForms-Tookkit log that said to install "xfont-100dpi".

I found the font in "Software Manager", "installed it", "restarted" and now it works.

Also you can use "xlsfonts" to enumerate the "installed fonts".

Still I have not been able to install my own fonts.

"xfont-75dpi" also works but is better suited to small screens.

Serial Success

The code is talking to the "Fake Grbl" (i.e. the Arduino) nicely now:

Here Fake Grbl sends the initialisation code "Grbl V" (the ">> " is added by gCodeSender).

I responded with "Hello" and then Grbl responds with "ok".

Closing the port close the link. Reopening the port, re-initialises the Arduino.

All working good.

File Processing

What remains is to load and process the gCode file.

AlanX

Discussions