Skip to content

15. Interface and application programming

Introduction

Goals:

For this week I’ve decided to complete the minimum requirements. My tasks will be:

  1. Use the 134KHz RFID sensor.
  2. Optionally use an FDX and HDX sensor to read cow RFID tags used in France and Mexico.
  3. Give a format to the data received in the mircro-controller code, form the sensor.
  4. Establish serial communication between micro-controller and computer.
  5. Read and parse the data received from micro-controller using Python.
  6. Make a short basic UI. Using QT.
  7. Make a distributable version of the software using a packaging library.
  8. Group assignment UI comparison

This is part of my final project RFID reader module.

  1. Micrcontroller reads RFID TAG and sends strings through SERIAL COM.
  2. Python RFID parses the data and detects error and manages it with exceptions.
  3. QT UI shows the data parsed.
  4. Python parser runs in a parallel Thread.

Micro-controller

I’ve used again my board from week 11.

// This code uses:

// Pin 31 for Serial communication at 9600 bauds
// 5 Volts for RFID Tag reader board

// Communication protocols: USB Serial abd "Serial1 interface" for RFID tag reader.

// This code reads the RFID tag and prints the result in SERIAL.

// Autor: Antonio de Jesus Anaya Hernandez
// Fab-Academy: 2021 Agrilab
// Country: France

/*#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,20,4);
*/
char rfid_tag [30];

void setup() {
  Serial1.begin(9600);
  Serial.begin(9600);
  /*
  Wire.begin();
  lcd.init();
  lcd.backlight();
  */
}

void loop() {
  int id = Serial1.read();

  if (id == 2){
    //beep();
    //rfid_tag[0] = id;
    for (int c = 0; c < 26; c++){
      int bit_ = Serial1.read();
      rfid_tag[c] = bit_;
   }
   Serial.println(rfid_tag);
   }

  /*
  lcd.setCursor(0,0);
  lcd.print(rfid_tag);
  */

  delay(200);

}

Decoding issues:

Exception error with Python:

Python

The Python programming language it’s my favorite programming language. So I’ve decided to skip a trial of multiple programming languages and just use the one I like the most.

About Python

Python is an interpreted, interactive, object-oriented programming language. It incorporates modules, exceptions, dynamic typing, very high level dynamic data types, and classes. It supports multiple programming paradigms beyond object-oriented programming, such as procedural and functional programming. Python combines remarkable power with very clear syntax. It has interfaces to many system calls and libraries, as well as to various window systems, and is extensible in C or C++. It is also usable as an extension language for applications that need a programmable interface. Finally, Python is portable: it runs on many Unix variants including Linux and macOS, and on Windows.

Fragment from Python’s documentation.

Configurations

I’m a Linux user and so my configuration follows this information.

  1. Linux Fedora 32
  2. Conda as Python environment version manager.
  3. Qt as User interface library.

For this the Configuration process may change a bit for Windows and Mac users.

Conda environment

Conda is an open source package management system and environment management system that runs on Windows, macOS and Linux. Conda quickly installs, runs and updates packages and their dependencies. Conda easily creates, saves, loads and switches between environments on your local computer. It was created for Python programs, but it can package and distribute software for any language.

Conda as a package manager helps you find and install packages. If you need a package that requires a different version of Python, you do not need to switch to a different environment manager, because conda is also an environment manager. With just a few commands, you can set up a totally separate environment to run that different version of Python, while continuing to run your usual version of Python in your normal environment

Taken from Anaconda’s documentation

Process overview:

We need to:

  1. Install Python Anaconda compiler,
  2. Create a new environment just for this weeks assignment,
  3. Install PyQt5 and requirements packages,
  4. Install PySerial library
  5. Code a program with Python using PyQT5 and PySerial Libraries.
  6. Make a distributable version of the program.

Creating a new environment:

conda create -n week15

Activate environment:

conda activate week15

Install pyserial:

pip install pyserial

First trial code:

import serial
baud_rate = 9600
port = '/dev/ttyUSB0'


# device = serial.Serial(port)
with serial.Serial(port, baud_rate, timeout=1) as serial_device:
    print(serial_device.name)
    data = serial_device.readline()

while(True):
    print(data)

Second trial code:

import serial
import time

print("This program reads the Serial port /dev/ttyACM0 at 9600 bauds and prints the output using the readline serial function on pyserial library. /dev/ttyACM0 its my board based on the ATSAMD11 microcontroller and it has connected in Serial2 the 134KHz RFID FDX tag reader board.")

baud_rate = 9600
port = '/dev/ttyACM0'
milking = True

def parse_rfid(rfid_code):
    head = rfid_code[:1]
    code = rfid_code[1:11]
    country = rfid_code[11:14]
    tail = rfid_code[14:]
    return (head, code, country, tail)

# device = serial.Serial(port)
with serial.Serial(port, baud_rate, timeout=10) as serial_device:
    dev_name = serial_device.name
    print(dev_name)

    while(milking):
        time.sleep(0.5)
        data = serial_device.readline()
        if len(data) > 3:
            try:
                data_decoded = data.decode('utf-8')
                print("The RFID scanned tag code is: ", parse_rfid(data_decoded))
                data = None
            except UnicodeDecodeError:
                print("Scan again")
        else:
            print("No tags detected")

About Qt Graphic User Interfaces

In the past Computers used to have simple text based interfaces.

Over the years this changed to have visual more interactive interfaces.

PyQt5

*PyQt is one of the most popular Python bindings for the Qt cross-platform C++ framework. PyQt was developed by Riverbank Computing Limited. Qt itself is developed as part of the Qt Project. PyQt provides bindings for Qt 4 and Qt 5. PyQt is distributed under a choice of licences: GPL version 3 or a commercial license. *

From Riverbank Computing Limited website

Configuration

Install PyQt5 library:

pip install PyQt5

Test code:

Reference: https://pythonbasics.org/pyqt-hello-world/

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel

def window():
   app = QApplication(sys.argv)
   widget = QWidget()

   textLabel = QLabel(widget)
   textLabel.setText("Hello World!")
   textLabel.move(110,85)

   widget.setGeometry(50,50,320,200)
   widget.setWindowTitle("PyQt5 Example")
   widget.show()
   sys.exit(app.exec_())

if __name__ == '__main__':
   window()

Using it long side PySerial:

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel


class MainWindow():
    def __init__(self):
        self.app = QApplication(sys.argv)
        self.widget = QWidget()
        self.text1 = "Fab Academy 2021, week15"
        self.text2 = "0"

    def setLabel(self):
        self.label = QLabel(self.widget)
        self.label.setText(self.text2)
        self.label.move(110, 85)

    def setTitle(self):
        self.widget.setGeometry(50,50,320,200)
        self.widget.setWindowTitle(self.text1)

    def display(self):
        self.widget.show()
        sys.exit(self.app.exec_())


def __run():
    app = MainWindow()
    app.setLabel()
    app.setTitle()
    app.display()

def read_rfid():


__run()

Using Threading:

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
from python_serial_01 import read_rfid, output
import threading
import time


class MainWindow():
    def __init__(self):
        self.app = QApplication(sys.argv)
        self.widget = QWidget()
        self.text1 = "Fab Academy 2021, week15"
        self.text2 = "0"
        self.setLabel()
        self.setTitle()

    def setLabel(self):
        self.label = QLabel(self.widget)
        self.label.setText(self.text2)
        self.label.move(110, 85)

    def setTitle(self):
        self.widget.setGeometry(50,50,320,200)
        self.widget.setWindowTitle(self.text1)

    def display(self):
        self.widget.show()
        sys.exit(self.app.exec_())


a = MainWindow()


def update_label(text):
    while True:
        print("loop1")
        time.sleep(0.5)
        a.label.setText(text)


serial = threading.Thread(target=read_rfid)
updater = threading.Thread(target=update_label, args=(output,))
serial.start()
updater.start()
a.display()

UI

Views

Console output

Final code

View:

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
from PyQt5.QtGui import QPixmap
from python_serial_01 import read_rfid, try_serial
import threading
import time


class MainWindow():
    height = 400
    width = height * 1.618

    def __init__(self):
        self.app = QApplication(sys.argv)
        self.widget = QWidget()
        self.text1 = "Fab Academy 2021, week15"
        self.text2 = "0"*45
        self.setLabel()
        self.setTitle()
        self.setLogo()
        self.setConnection_icon()
        # self.setRFID_img()
        self.setMilk_img()

    def setLabel(self):
        self.label = QLabel(self.widget)
        self.label.setText(self.text2)
        self.label.resize(500, 50)
        self.label.move(200, 50)

    def setTitle(self):
        self.widget.setGeometry(50,50,self.width,self.height)
        self.widget.setWindowTitle(self.text1)

    def setConnection_icon(self):
        self.con_icon_holder = QLabel(self.widget)
        if try_serial():
            self.con_icon = QPixmap('ok.png')
        else:
            self.con_icon = QPixmap('not.png')
        self.con_icon_holder.setPixmap(self.con_icon)
        self.con_icon_holder.resize(self.con_icon.width(), self.con_icon.height())
        self.con_icon_holder.move(self.width -50, self.height-50 )

    def setRFID_img(self):
        self.rfid_icon_holder = QLabel(self.widget)
        self.rfid_icon = QPixmap('tag.png')
        self.rfid_icon_holder.setPixmap(self.rfid_icon)
        self.rfid_icon_holder.resize(self.rfid_icon.width(), self.rfid_icon.height())
        self.rfid_icon_holder.move(50, self.height -100)

    def setMilk_img(self):
        self.milk_icon_holder = QLabel(self.widget)
        self.milk_icon = QPixmap('milk.png')
        self.milk_icon_holder.setPixmap(self.milk_icon)
        self.milk_icon_holder.resize(self.milk_icon.width(), self.milk_icon.height())
        self.milk_icon_holder.move(50, (self.height-self.milk_icon.height())*0.5)

    def setLogo(self):
        self.logo = QPixmap('cow.png')
        self.logo_holder = QLabel(self.widget)
        self.logo_holder.setPixmap(self.logo)
        self.logo_holder.resize(self.logo.width(), self.logo.height())
        self.logo_holder.move(50, 50)


    def display(self):
        self.widget.show()
        sys.exit(self.app.exec_())


a = MainWindow()


def update_label():
    while True:
        print("loop1")
        time.sleep(0.5)
        data = read_rfid()
        a.label.setText(data)


updater = threading.Thread(target=update_label)
updater.start()
a.display()

Rfid library:

# print("This program reads the Serial port /dev/ttyACM0 at 9600 bauds and prints the output using the readline serial function on pyserial library. /dev/ttyACM0 its my board based on the ATSAMD11 microcontroller and it has connected in Serial2 the 134KHz RFID FDX tag reader board."

import serial
import time
import logging


baud_rate = 9600
port = '/dev/ttyACM0'
output = "Default"


def hex_to_dec(data):
    reversed = data[::-1]
    decimal = int(reversed, 16)
    return str(decimal)


def parse_rfid(rfid_code):
    head = rfid_code[:1]
    code = rfid_code[1:11]
    country = rfid_code[11:14]
    tail = rfid_code[14:]
    return "{code}, {country}".format(code=hex_to_dec(code), country=hex_to_dec(country))


def try_serial():
    try:
        with serial.Serial(port, baud_rate, timeout=10) as serial_device:
            dev_name = serial_device.name
            print(dev_name)
            serial_device.close()
            return True
    except:
        print("No conection")
        return False


def read_rfid():
    try:
        with serial.Serial(port, baud_rate, timeout=10) as serial_device:
            dev_name = serial_device.name
            print(dev_name)
            data = serial_device.readline()
            if len(data) > 3:
                try:
                    data_decoded = data.decode('utf-8')
                    output = "The RFID scanned tag code is: %s" % parse_rfid(data_decoded)

                except UnicodeDecodeError:
                    output = "Scan again"
            else:
                output = "No tags detected"
            print(output)
            serial_device.close()
            return output
    except:
        return "Not connected"

MCurses

NCurses it’s a text based interface design standard, and Mcurses its a version for Micro-controllers.

Ncurses for microcontrollers

git clone https://github.com/ChrisMicro/mcurses
/*---------------------------------------------------------------------------------------------------------------------------------------------------
   mcurses box demo

   Copyright (c) 2011-2015 Frank Meyer - frank(at)fli4l.de

   Revision History:
   V1.0 2015 xx xx Frank Meyer, original version
   V1.1 2017 01 14 ChrisMicro, converted to Arduino example

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   Modification for Fab Academy week09:

   IO pins:
   PA02 <- Digital pull-up BUTTON input
   PA05 -> Digital LED output

   Autor: Antonio de Jesus Anaya Hernandez
   Year: 2021
   Org: Fab Academy
   Lab: AgriLab
   Country: France


  ---------------------------------------------------------------------------------------------------------------------------------------------------
*/
#include "mcurses.h"

#define         myitoa(x,buf)                   itoa ((x), buf, 10)

int buttonState = 0;

char PROGMEM logoFab[] = "\r\            ######## ########\n\
\r\        ########          #######\n\
\r\      ###################     ######\n\
\r\     ##      ##############       ###\n\
\r\   ((         #############         %%%\n\
\r\  (((            /#####             %%%\n\
\r\ ((((                               %%%%\n\
\r\ ((((   (((((((           %%%%%%%%%%%%%%\n\
\r\ ((((   (((((((((       &%%%%%%%%%%%%%%%\n\
\r\  (((   ((((((((((     &%%%%%%%%%   %%%*\n\
\r\   ((    (((((((((     %%%%%%%%%    %%%\n\
\r\    (((   (((((((       (%%%%%    %%%\n\
\r\      ((((((((((              %%%%%%\n\
\r\        ((((((((          %%%%%%%\n\
\r\            (((((((( %%%%%%%%\n\
";

void Arduino_putchar(uint8_t c)
{
  Serial.write(c);
}

void setup()
{
  Serial.begin(115200);

  setFunction_putchar(Arduino_putchar); // tell the library which output channel shall be used

  initscr();                  // initialize mcurses

  pinMode(5, OUTPUT);
  pinMode(2, INPUT);
}

void loop()
{
  buttonState = digitalRead(2);

  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState == LOW) {
    // turn LED on:
    digitalWrite(5, HIGH);
    mvaddstr_P (18, 0, PSTR("fabacademy.org"));
    mvaddstr_P (20, 0, PSTR("This is an example of MCURSES a NCURSES library for microcontrollers"));
  } else {
    // turn LED off:
    digitalWrite(5, LOW);

  }
  char    buf[10];
  uint8_t idx;
  mvaddstr_P (0, 0, PSTR(logoFab));
  delay(500);
  clear ();

}

Files

Icons from flaticon:


Last update: May 18, 2021