Fab Academy 2020
12. Interface and Application Programming

Assignment :

Link to Group Assignment

Week 12 learning journey :

The objective of this week is to create an application or Graphics User Interface (GUI) that interfce with our input/output device as well as presenting the data to the user in a visual or graphical way. The application can also be seen as an extension to your board, in term of processing, as you can distribute certain portion of your workload from your board to the PC/notebook or other computing device like the smaller 'Raspberry Pi' that would be much more powerful and having more memory.

In my 'Input Device' week, I was using an ultrasonic distance sensor, so this week I am using back my ultrasonic Sensor and ATTiny44 board.

I am using Python to create a stand-alone application as well as trying out JavaScipt to create a simple web-based application. For Python programming, I am using Eclipse and for Javascript, I am using Visual Studio Code.

Content for this webpage includes :
    Setup my HC-SR04 ultrasonic sensor and board
    A. Progamming in Python using Eclipse
    B. Progamming in JavaScript using Visual Studio Code


Programming language :

Python :

Python is a powerful, portable open source programming language and it fully support object-oriented programming as well as structured programming. It is a popular programming language and is easy to learn for beginners.

I am using Python 3.8.2 (latest available as of 12 April 2020). Take note that there is a bit of difference between Python3 and Python2 syntax. Eg. Python2 syntax for print is print "...", while Python3 syntax for print is print("..."). Python3 syntax now look more like "object-oriented scripting". In addition one impovement is that Python3 syntax has become more user friendly and easy to read and understand. Neils's code was written in Python2 and I am coding in Python3.

Python has a wide range of resources online, ranging from free online learning tutorials on Youtube to online searching for answers/advices on how to code or troubleshoot an issue.

JavaScript :

JavaScript is a cross-platform, object-oriented scripting programming langauage for web Development used to make webpages interactive (eg. click on a button, swipe, tap, create popup menus, complex animations, etc.).

JavaScript is mainly used for front-end web development, that function on the client web browser instead of on a back-end web server. There are advanced server side versions of JavaScript known as Node.js as well as other advance JavaScript technology for the back-end or Web Server. Most of the modern websites (like Facebook, etc) uses some form of JavaScript and is hard to find any website that doesn't have JavaScript scripting in it.

JavaScript is not java and totally different from java. Beside just making websites interactive, JavaScript now has the capability to do things that other scripting languages like Python can do.

Google Chrome as well as other web browsers has JavaScript engine built-in which can execute JavaScript code.

"Node.js" which is an open source server environment and enable you to run run JavaScript in a local enviroment without a web browser. Node.js is a Runtime Environment (running .js code with node.exe) and comes with its own JavaScript Libraries which are very fast in code execution. Node.js files uses the extension ".js".

The back end server (or any computer which is running node.js) creates the interface between the client browser and the hardware I/O. The hardware I/O in this case is the serialport; to listen and write to the serialport.
Over at the client end, user will be viewing the html webpage using any web browser. the webpage establises a stable and persistent connection to the back end (node.js) server.

A server-client model allows multiple users to be able to connect to the server.


Programming tools (text editor and IDE) :

Here I am starting with Python and looking for a good Python IDE. IDE does more than writting codes, they can run the program and help you debug any issue.

List of some of the text editors with support of Python as well as other programming language like html, javascript, etc. :
    - Atom
    - Brackets
    - Sublime Text
List of some of the IDEs :
    - Python IDLE
    - Visual Studio Code
    - Eclipse

Python's IDLE is a very simple text editor that is pre-installed with Python :


Atom, another text editor for writing code but offers more features and plug-ins (same as Brackets, when you create a new file, you need to select Python at the bottom right corner of the screen) :


Visual Studio Code :
Visual Studio Code is a lightweight but powerful source code editor which comes with built-in support for JavaScript, Node.js and has a rich ecosystem of extensions for other languages (such as C++, C#, Java, Python, PHP, Go).
Adding the Python extension is easy (File -> Preference -> Exttensions) :


Create a new file and you can select Python or other programming language mode


The Console screen below the Editor panel displays the output of the Python code. You can easily run your code with the graphics and the graphics is immediately displayed to you



Use of Eclipse to write my Python code :
I finally settled down with using Eclipse 2020-03 (https://www.eclipse.org). Eclipse is an integrated development environment (IDE) and it has been popular for developing java application, but it also also support other programming languages like C/C++, Python, JavaScript, etc. Eclipse will be installed in your C:\Users\{Username} directory. To remove it, just simply delete these eclipse folders.

Installing Eclipse for Windows and PyDev for Eclipse :
1. Install Java JDK.
2. Install Eclipse 2020-03 from https://www.eclipse.org/. Installation location directories for Eclipse is "%username%/eclipse\%eclipse module selected%\eclipse"
3. I followed the installation guide on http://www.pydev.org/manual_101_install.html to install PyDev (Python IDE plugin) for Eclipse by going to Help-> Install New Software.


4. On the next screen, type http://www.pydev.org/updates and press [ENTER], then select the PyDev


5. Configure the Python Interpreter to designate the location for your Python.exe software. Pointing to C:\Program Files (x86)\Python38-32\python.exe


6. File -> New -> Project -> PyDev -> PyDev Project to start coding
7. Click the "run" icon on the taksbar and select "Python run". Only need to select once per file.


8. The output is being display in the Console (or 'output' screen') window below the Editor window.


9. When you hover you move over certain commonly Python command like "print" or even a function name, Eclipse will display certain useful information (if available) for beginner. Visual Studio Code also has this nice feature. Python's IDLE does not have this feature.


Eclipse is a much needed upgrade and impovement for me in writing code. Previously I was using a simple text editor to write my python code.



Getting started : Setup my HC-SR04 ultrasonic sensor and board :

I am using an ultrasonic sensor along with my ATTiny44A board that I had made in 'input device' week.

I setup up my ultrasonic sensor as shown in the photo. The measurement data is being sent over the serial port (baudrate speed : 9600) to my notebook.
Photo

My Audrino code for the ATtiny44 board is as follow :

/*
  Ultrasonic_sensor.ino
  by Lucas Lim
  created on 22/4/2020
  Purpose: To activate Ultrasonic sensor and send data to computer

  Ultrasonic sensor Pins:
     VCC: +5V DC
     Trig : Trigger (INPUT) - Arduino Pin9 (PB1)
     Echo: Echo (OUTPUT)    - Arduino Pin10 (PB0)
     GND: GND

  Original code created by Rui Santos, https://randomnerdtutorials.com
 */

#include <SoftwareSerial.h>
SoftwareSerial mySerial(1, 0); // RX, TX


const int trigPin = 9;    // Trigger pin
const int echoPin = 10;   // Echo pin
long duration, cm;
int c = 1;

void setup() {
  //Initialising Serial Port Communication
  mySerial.begin (9600);

  //Define inputs and outputs
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}

void loop() {
  // read from serial for stop character
  if (mySerial.available()>0){
    // read from serial
    c = mySerial.read();
  }
  while (c != 0){
    // The sensor is triggered by a HIGH pulse of 10 or more microseconds.
    // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
    digitalWrite(trigPin, LOW);
    delayMicroseconds(2);
    // set the trip pin high high for 10 smicroseconds
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);

    // Read the signal from the sensor: a HIGH pulse whose
    // duration is the time (in microseconds) from the sending
    // of the ping to the reception of its echo off of an object.
    duration = pulseIn(echoPin, HIGH);

    // Convert the time into a distance
    cm = duration *0.034/2;

    mySerial.print(cm);
    mySerial.println();

    delay(100); //delay 1 tenth of a sec
  }
}
   

A. Progamming in Python using Eclipse

1. Install Python from Python website.

2. 'pip' is a Python package manager which is installed along with latest version of Python 3 and 'pip' is use to install, remove and manage external Python modules/libraries. Pip.exe is located in %python directory%\Scripts and runs from the command line and not from Python Shell.
Check version of pip by typing :

python -m pip --version


3. Update pip to latest version by typing :

python -m pip install -U pip


4. Install "PySerial" allows Python to access the serial port to send and receive data.

pip install pyserial


The installed modules (using pip) can be found in the Eclipse's libraries directory -> under site packages on the 'Workspace' panel on the left.


5. I ran a simple Python code to test whether I can receive data from the target board with the ultrasonic sensor.


6. I referred to Neil's Python2 code on the ultrasonic sensor in the 'Input devices' week on class site as a good starting point.

7. Command line option :
I used the built-in module known as "argparse" to take in user input from the command line. User has the option to specify which serial comm port to be use, otherwise the default comm port will be "COM6".

import argparse

parser = argparse.ArgumentParser(description='Ultrasonic Sensor GUI')
parser.add_argument("--c", default="COM6", type=str, help="enter com port")

args = parser.parse_args()
port = args.c
print(port)

8. Running the program :
To run the python program, user can type in the following statement at the command prompt.

python ultrasonic_sensor_gui.py
or
python ultrasonic_sensor_gui.py --c {serial comm port number} eg. python ultrasonic_sensor_gui.py --c COM6


9. User interaction buttons :
I used the module 'tkinter' which is a powerful graphic library for python and added in a few buttons, draw the rectangle, line, text, etc. and arrange my graphic objects in a grid foramt.

I created three buttons for the user to interact with the input device :
   [Start] - start viewing the measurement data, reading data from board
   [Stop] - pause reading, stop the ultrasonic sensor, incorporating serial.write() to board
   [Exit] - stop the ultrasonic sensor and exit the program

It is a good practice to be able to stop the ultrasonic sensor as you are developming your application.

10. GUI design :
'tkinter' is built into the Python standard library which does not require additional installation and configuration. Which is also cross-platform, so the same code works on Windows, macOS, and Linux. There is a lot of online resouces and tutorial on learning to use 'tkinter'.

Some of the key functions are as follow :

from tkinter import * # this import tkinter lib

root = Tk() # this create my root (or master) window object

xxx = LabelFrame(parent window, option, ... ) # create a frame container with a text label text and a border.

canvas = Canvas(parent window, option, ... ) # the canvas is a rectangular area intended for drawing pictures or other complex layouts. You can place graphics, text, etc on a Canvas.

canvas.create_line(x0, y0, x1, y1, ..., options) # create a line item
canvas.create_rectangle(x0, y0, x1, y1, ..., options) # create a rectangle item
canvas.create_text(x0, y0, ..., options) # create a text item

xxx = Label(parent window, option, ... ) # this widget implements a display box where you can place text

xxx.pack() # after you created an tkinter graphic item, you can use pack() which is one of the three geometry managers and place the object into the parent(or root) window
xxx.grid(grid options... ) this is another type of geometry manager that organizes widgets in a table-like structure in the parent window.

root.mainloop() # the final line to start tkinter app


11. Developing my application using Eclipse.




12. My Python code for the GUI interface is as follow :

#! Python3
#
# ultrasonic_sensor_gui.py
# by Lucas Lim
# created on 24/4/2020
#
# Purpose: A GUI for Ultrasonic sensor to be display on the computer
#
# The work is provided for academic purpose for Fab Academy 2020.
# Users accept all as is, no warranty is provided, no call/email
# from users will get a response. Users accept all liability.
#
#
from tkinter import *
from time import sleep
import serial
import argparse
import array as arr

# var for distance calculation
cm = 0
a = arr.array('i', [0, 0, 0, 0, 0])   # create an int array for aver_reading

# var for ploting bar chart
x0 = 50
y0 = 120

# option to take in command line argument for serial port
parser = argparse.ArgumentParser(description='Ultrasonic Sensor GUI')
parser.add_argument("--c", default="COM6", type=str, help="enter com port")

args = parser.parse_args()
port = args.c
print(port)

#
# open serial port
#
try:
    ser = serial.Serial(port, 9600, timeout=8)
except serial.serialutil.SerialException:
    print ("COM port specified is not connected!")
    quit()


def move():
    n = 0
    aver_cm = 0

    print("Start")   # for debugging purpose
    # check whether serial port is open, as user might already run the program in another window
    # also check whether user have pressed "Start" multiple times
    if ser.is_open:
        ser.close()
        ser.open()
    else:
        ser.open()
        ser.write(1)      # tell board to switch on ultrasonic sensor

    while ser.is_open:
        try:
            cm = int(ser.readline().decode('utf-8'))
        except Exception as e:
            print(e)

        # limit distance to 200cm for easy plotting of bar chart
        if cm >= 200:
            cm = 200

        # average 5 readings
        a[n] = cm
        if n == 4:
            aver_cm = int(sum(a) / 5)
            n = 0
        else:
            n += 1

        # bar chart horizonal scale is 500px : 200 cm, therefore 1cm = 2.5px
        # multiply measure data by 2.5 to display onto the bar chart hori scale
        x = aver_cm * 2.5

        # update bar chart
        canvas.itemconfigure("text",text=str(aver_cm) + " cm")
        canvas.coords('rect1', x0, y0-80, x0+x, y0-30)
        canvas.update()


def stop():
    # check if user have pressed "Stop" multiple times
    if not ser.is_open:
        return
    ser.write(0)      # tell board to switch off ultrasonic sensor
    ser.close()
    print("Stop!")    # for debugging purpose
    print(ser.is_open)


def exit():
    if ser.is_open:
        ser.write(0)
        ser.close()
    else:
        ser.close()
    root.quit()
    print("Exit!")    # for debugging purpose


#
# set up GUI
#
root = Tk()
root.title('Ultrasonic Sensor Program')   # window title
root.geometry('700x520')                  # overall window size

myLabel = Label(root, text="\nUltrasonic Sensor Interface\n", font='Arial 16 italic bold')
myLabel.pack()

# Canvas for ploting bar chart is 600 x 200
canvas = Canvas(root, width=600, height=200, background="white")
canvas.create_line(x0, 25, x0, y0)
canvas.create_line(x0, y0, 550, y0)

# measurement data in text below bar chart
canvas.create_text(x0+250, y0+55, text=str(cm) + " cm", font=("Helvetica", 20), tags="text", fill="blue")

# measurement data in blue color bar
canvas.create_rectangle(x0, y0-80, x0+10, y0-30, tags="rect1", fill="blue")

c1 = 0
c2 = 0

# drawing the horizontal scale of the bar chart
while c1 <= 500:
    canvas.create_line(x0 + c1, y0, x0 + c1, y0+10)
    canvas.create_text(x0 + c1, y0+17, text=str(c2), font=("Helvetica", 9), fill="grey")
    c1 +=25
    c2 += 10
canvas.pack()


# set up a frame for text message and buttons
frame = LabelFrame(root, text="Instruction :", font='Arial 12', padx = 30, pady =15) # this padding is inside the frame
frame.pack(padx=5, pady=5) # this padding refer to the outside of the frame to the border

# Creating a Label widget inside of "frame"
myText = """
Hello,
Welcome to my ultrasonic sensor program

Press Start to start measuring distance
Press Stop to pause
Press Exit to close program
"""
myLabel = Label(frame, text=myText, font='Arial 12 italic', wraplength=600, anchor=NW, justify=LEFT)
myLabel.grid(row=0, column=0, rowspan=5)

# set a empty row/space between text and button
emptySpace1 = Label(frame, text="   ")
emptySpace1.grid(row=0, column=1, rowspan=5)
emptySpace2 = Label(frame, text="   ")
emptySpace2.grid(row=0, column=2)


button_start = Button(frame, text="Start", command=move, padx=100, pady=5)
button_start.grid(row=1, column=2)
button_stop = Button(frame, text="Stop", command=stop, padx=100, pady=5)
button_stop.grid(row=2, column=2)
button_exit = Button(frame, text="Exit", command=exit, padx=102, pady=5, fg="red")
button_exit.grid(row=3, column=2)

root.mainloop()
  


Video on Python GUI

Duration : 44 secs
No Audio



B. Progamming in JavaScript using Visual Studio Code

Programming Tool setup :
I be testing out and using Visual Studio Code to create a HTML (.html) webpage and a JavaScript (.js) resource file for my main html page. User can just double-click on the ultrasonic_sensor.html file to be openned by any web browser like Google Chrome, Microsoft Edge, etc.

1. Install Visual Studio Code and install the extension "Live Server" which would allow me to run a web server and preview the changes I made immedaiely after I make a save on my html file.



2. Install node.js. After install, you can run "node -v" at the Command Prompt and it will show you the version that you had installed. Mine is ver 12.16.2. Node.js' Package Manager (NPM) is also installed, this is same as python's 'pip', which allow you to install node.js modules.
3. Create a simple .js file and run it. The output is displayed in the console screen below.
Note that when you typed "console." The Intellisense extension introduces autocomplete feature and show you the methods available to you.


4. Install the serialport as well as the ws (websocket) npm package.

npm install serialport
npm install ws


5. Create a new folder called "node_modules" in the same directory where your node .js file is located. Copy the modules that you need from the master "C:\program files\nodejs\node_modules" into the newly created "node_modules" (eg. serialport npm module) so that the module can be called by your .js file.

6. Refer to serialport 8.x.x readline online documentation to learn how to receive data from the serialport.
Note :
The default Parsers are Transform streams that process incoming data. To use the parsers, you must create them and then pipe the Serialport to the parser. Be careful to only write to the SerialPort object and not the parser (eg. use port.write())
The "Readline parser" used, provide a delimiter (defaults to \n). It will tell parser-readline to finds the delimiter you configure, how to split the incoming stream and transform the incoming stream from serialport to string and the default encoding is "utf8".


7. Setting up Websocket :
The node.js server established the communication link with the hardware I/O via the serialport.

The client browser displays the html web page and which the user can interact, like clicking to start viewing the measurement data, pause and exit the web page.

The html web page communicate with the node.js server at the back end, this is possible with the implementation of 'Websocket'. Websocket establises a stable and persistent connection which allow the server and client to communication with each other in real time.

I refer to ws (websocket) online documentation to learn how to send and receive data via websocket.

wss = new WebSocket.Server -> create a new server instance on node.js and a HTTP server is automatically created, started, and using the port that you specify.

websocket.send(data)
Send data through the connection

websocket.onopen {Function}
An event listener to be called when the connection is established. The listener receives an OpenEvent named "open".

websocket.onerror {Function}
An event listener to be called when an error occurs. The listener receives an ErrorEvent named "error".

websocket.onmessage {Function}
An event listener to be called when a message is received from the server. The listener receives a MessageEvent named "message".

etc...

8. Drawing the bar chart using SVG (Scalable Vector Graphics) in drawing vector based graphic element like line, rectangular, etc. You can refer to SVG tutorial online.

9. Running the node.js codes and html client webpage:
In Visual Studio Code, you can run node.js code directly and creates your own node.js server. Alternatively, you can also run the node.js file using node.exe through the command line.
Having the "live preview" extension you had installed which become a live web server for you to host your html file which the client will see.



Server Code (Node.js) :

// Node.js (JavaScript) code
//
// ultrasonic_server.js
// by Lucas Lim
// created on 24/4/2020
//
// Purpose: JavaScript to capture measurement data from
// Ultrasonic sensor to be pass on to client browser (GUI)
//
// The work is provided for academic purpose for Fab Academy 2020.
// Users accept all as is, no warranty is provided, no call/email
// from users will get a response. Users accept all liability.
//
"use strict"  // run code in strict mode

// variables for serial port communication
var serial_port = 'COM6'
var baud = 9600
// variables for websocket communication
var server_port = '1234'
var client_address = '::ffff:127.0.0.1'  //IP6 address
var value = 0


//
// open serial port
//
const SerialPort = require('serialport')
const Readline = require('serialport/node_modules/@serialport/parser-readline')
const port = new SerialPort(serial_port, { baudRate: baud })
const parser = port.pipe(new Readline({ delimiter: '\r\n' }))

//
// check serial port status
//
port.on("open", () => {
    console.log('serial port open')
});

//
// read data from serial port
//
parser.on('data', data =>{
    console.log(data)
    value = parseInt(data)  //convert data to int
});

//
// Setting up websocket
//
console.log("listening for connections from "+client_address+" on "+server_port)
var WebSocket = require('ws')
var wss = new WebSocket.Server({port:server_port})

// turn on websocket and listen to the connection
wss.on('connection', function(ws) {
   if (ws._socket.remoteAddress != client_address) {
      console.log("error: client address " + ws._socket.remoteAddress + " doesn't match")   // for debugging purpose
      return
    }
   console.log("connected to "+ client_address + " on port " + server_port)  // for debugging purpose

   ws.on('message', function(data) {
    var check = parseInt(data)
    if (check == 1){
        console.log("msg received from client is 1")   // for debugging purpose
        if (port.isOpen == false) {
            port.open(function (err) {
                console.log('open port');
            });

            port.on("open", () => {
                console.log('serial port open')
            });

            //
            // read data from serial port
            //
            parser.on('data', data =>{
                console.log(data)
                value = parseInt(data)  //convert data to int
            });
        }

    } else if (check == 0){
        console.log("msg received from client is 0)    // for debugging purpose
        port.close(function (err) {
            console.log('port closed');
        });
    }

      // send measurement data to client
      ws.send(value)
    })
})

Client code (html) :

<!DOCTYPE html>
<html lang="en">
<!--
//
// ultasonic_client.html
// by Lucas Lim
// created on 24/4/2020
//
// Purpose: A web-based GUI for Ultrasonic sensor to receive
// and display measurement reading
//
// The work is provided for academic purpose for Fab Academy 2020.
// Users accept all as is, no warranty is provided, no call/email
// from users will get a response. Users accept all liability.
//
-->
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Ultrasonic Sensor">
    <meta name="author" content="Lucas Lim">

    <title>
        Ultrasonic Sensor Interface
    </title>

    <!-- beautifying the click buttons -->
    <style>
        .button {
          background-color: #4CAF50; /* Green */
          border: none;
          color: white;
          text-align: center;
          text-decoration: none;
          display: inline-block;
          font-size: 16px;
          margin: 4px 2px;
          cursor: pointer;
          padding: 10px 24px;
        }

        .button:hover {
            background-color: "black";
        }
    </style>

</head>

<body style="font-family: Arial, Helvetica, sans-serif;">
    <br>

    <!--
    //
    // set up SVG bar chart
    //
    -->
    <svg width="600" height="150">
        <line x1="30" y1="0" x2="30" y2="100" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="30" y1="100" x2="530" y2="100" style="stroke:rgb(3, 3, 3);stroke-width:2" />

        <line x1="30" y1="100" x2="30" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="55" y1="100" x2="55" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="80" y1="100" x2="80" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="105" y1="100" x2="105" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="130" y1="100" x2="130" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="155" y1="100" x2="155" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="180" y1="100" x2="180" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="205" y1="100" x2="205" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="230" y1="100" x2="230" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="255" y1="100" x2="255" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="280" y1="100" x2="280" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="305" y1="100" x2="305" y2="110" style="stroke:rgb(3, 3, 3);
        stroke-width:2" />
        <line x1="330" y1="100" x2="330" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="355" y1="100" x2="355" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="380" y1="100" x2="380" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="405" y1="100" x2="405" y2="110" style="stroke:rgb(3, 3, 3);
        stroke-width:2" />
        <line x1="430" y1="100" x2="430" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="455" y1="100" x2="455" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="480" y1="100" x2="480" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="505" y1="100" x2="505" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />
        <line x1="530" y1="100" x2="530" y2="110" style="stroke:rgb(3, 3, 3);stroke-width:2" />

        <text x="27" y="120" fill="dark grey" font-size="10">0</text>
        <text x="50" y="120" fill="dark grey" font-size="10">10</text>
        <text x="75" y="120" fill="dark grey" font-size="10">20</text>
        <text x="100" y="120" fill="dark grey" font-size="10">30</text>
        <text x="125" y="120" fill="dark grey" font-size="10"">40</text>
        <text x="150" y="120" fill="dark grey" font-size="10">50</text>
        <text x="175" y="120" fill="dark grey" font-size="10">60</text>
        <text x="200" y="120" fill="dark grey" font-size="10">70</text>
        <text x="225" y="120" fill="dark grey" font-size="10">80</text>
        <text x="250" y="120" fill="dark grey" font-size="10">90</text>
        <text x="272" y="120" fill="dark grey" font-size="10">100</text>
        <text x="299" y="120" fill="dark grey" font-size="10">110</text>
        <text x="324" y="120" fill="dark grey" font-size="10">120</text>
        <text x="349" y="120" fill="dark grey" font-size="10">130</text>
        <text x="374" y="120" fill="dark grey" font-size="10">140</text>
        <text x="399" y="120" fill="dark grey" font-size="10">150</text>
        <text x="423" y="120" fill="dark grey" font-size="10">160</text>
        <text x="448" y="120" fill="dark grey" font-size="10">170</text>
        <text x="473" y="120" fill="dark grey" font-size="10"">180</text>
        <text x="498" y="120" fill="dark grey" font-size="10">190</text>
        <text x="523" y="120" fill="dark grey" font-size="10">200</text>

        <text x="250" y="150" fill="blue" font-size="28" id="text">100</text>
        <text x="300" y="150" fill="blue" font-size="28"">cm</text>

        <rect x="30" y="25" width="0" height="40" id="rect" fill="blue"/>
        </svg>

    <br>
    <br>


    <script type="application/javascript">

        function ws_connection(msg) {
            document.getElementById("message").innerHTML = "msg is " + msg; //  for debugging purpose
            var server = "127.0.0.1:1234";
            var socket = new WebSocket("ws://"+server);  // open a websocket

            if (msg==0) {
                socket.onopen = function (event) {
                    console.log("Stopping/pausing") //  for debugging purpose
                    document.getElementById("message").innerHTML = "Send msg 0 to server"  //  for debugging purpose
                    socket.send(msg)
                }
            }


            if (msg==1) {
                document.getElementById("message").innerHTML = "Receiving data " + msg; //  for debugging purpose
                var rect = document.getElementById("rect")
                var text = document.getElementById("text")

                socket.onopen = function (event) {
                    console.log("connected to "+server) //  for debugging purpose
                    socket.send(msg)
                    socket.send("request")

                }

                socket.onerror = function (event) {
                    console.log("can't connect to "+server)
                }

                socket.onmessage = function (event) {
                    value = event.data;

                    // adjusting value to fit within bar chart
                    if (value > 200) {
                        value = 200
                    }

                    // adjusting the SVG bar width
                    rect.setAttribute("width", value * 2.5)
                    text.firstChild.nodeValue = value
                    socket.send("request")

                }
            }
        }  // end of ws_connection -->

        function but_Exit() {
            window.close();
        }  // end of but_Exit()


    </script>



    <div style="max-width:550px; border: 0.5px groove; padding: 15px; background-color: #dee7ee;" >
        <h2>Ultrasonic Sensor Interface</h2>
        Hello,<br>
        Welcome to my ultrasonic sensor program<br>
        <br><br>
        Press Start to start measuring distance<br>
        Press Stop to pause<br>
        Press Exit to close the webpage<br>
        <br>
        <button class="button" type="button" onclick="ws_connection(1)">Start</button>
           
        <button class="button" type="button" onclick="ws_connection(0)">Stop</button>
           
        <button class="button" type="button" onclick="but_Exit()">Exit</button>
    </div>

    <br><br>
</body>

<footer>
    Fab Lab Singapore Polytechnic
    <br>
        <!-- for debugging purpose -->
        <p id="message"></p>
</footer>

</html>


Video on JavaScript GUI

Duration : 51 secs
No Audio



Reflection :

Application development :
The main challenge for this week was learning python as well as Javascript programming, in addition, we have to interface with the board and create a graphical or visual aid for the user, a simple data virtualisation to represent your data in a bar chart or graph...

Python vs Javascript :
After coding the same stuff in both python and javascript with node.js, I found that both are similiar in nature, with slight differences in the syntax and libraries, Python3 can be both object orientated programming as well as structure programming, but Javascript is not Object-orientated.

A server-client model open up the possiblity for multiple users to interact with your device(s), like node.js, but the programming require more effort like looking into coding integrating html/ccs/javascript/plug-ins, security, stability, monitoring connection status, etc. Web development open up more possibilities, eg. your html web page can incorporate CCS style sheet/coding, embedding a video, adding other web componenets or other stuff, etc. It might be possible to install node.js on a small device like a Raspberry Pi 4 running linux.

Python3 is much user friendly and easy to learn. The javascript code is smiliar to C/C++ which is a bit hard to learn from a beginner point of view. Each of them primarily use in different scenario and serving different purposes, python more toward application development while javascript web-based development. From a layman point of view, I believe python application would be more stable, it is a stand-alone application and it doesn't need to setup a node.js server or a web server.

Issues faced with javascript/node.js :
1. The ws module is quite basic and limited.
2. I had problem trying to use "websocket.close()" on the node.js server as well as client. As I intended for the current user just to close the ws communication channel, but it seem not possible as server is still transmitting data to the client. I looked at another ws module call "socket.io" but it would take me too much time to expriement with it...

Eclipse vs Visual Studio Code :
Both allows me to run my code off off my Editor windows. But I found that Visual Studio Code is much user friendly, easy to use, and can switch to other programming languages easily, like from html to javascript to python. However, it does not have a taskbar below the menu bar, or ribbon style task bar, it be nice to include one and a customisable one to add or remove icons.

If you just focus on python programming or just java or other, it be nice to use Eclipse, as it have a lot of features. Eclipse has a it's own Web Tools Platform (WTP) for web development, but I installed, the 'workspace' get a bit confused with I switch back-and-fo from python to html/javascript... This is maybe I am still new to Eclipse.

Future development to Phyton GUI application :
1. When user click on Start button, I should make it 'DISABLED' with it's text greyed out, so that user cannot click on the button again.
2. To build it into an stand-alone executable program (like .exe).
3. Maybe logging all the readings into a file and store on the harddisk
4. Maybe to look out for other ploting tool/packages like matplotlib, etc.


Files :

My files :
Arduino file Ultrasonic_sensor_for_GUI.ino
Python file ultrasonic_sensor_gui.py
ultrasonic_server.js
ultasonic_client.html


References :

Python programming :
Python 3.8.2 documentation
Youtube video on "Learn Python - Full Course for Beginners [Tutorial]"
Python Module Index
Welcome to pySerial’s documentation

GUI on Python :
Youtube video on "Tkinter Course - Create Graphic User Interfaces in Python Tutorial"
https://www.tutorialspoint.com/python/python_gui_programming.htm

JavaScript programming :
JavaScript Guide
What exactly is Node.js?
serialport 8.x.x online documentation
ws (websocket) GitHub repo and documentation