Interface And Application Programming

This week's assignment is to write an application that interfaces users with an input and/or output device (individual); to compare different tool options (in-group).

* All source files can be downloaded here.

* To keep the momentum of the previous weeks, I tried to write down what I understand about interfaces and communication protocols.

* I need to individually write an application that interfaces users with an input and/or output device. My plan for this week's progress was to write and test the applications with a commercial board. After the lockdown, I will test them with a board I make.

* This week's group assignment is to compare different development tool options.

Understanding interfaces and communication protocols

There was so much to learn this week. I thought this week's content would only revolve around software/application development, but it was actually a lot wider. Hence, in addition to work on the assignments, I would like to research further and correct what I had in mind about interfaces. Allow me to go through a very long note without any images attached, or go straight to the assignments below.

Networking terminology

Now I really understand why people need 4 years of university not to get confused when encountering networking terms like interfaces, protocols, buses, ports, connectors, etc. For example, there are so many things covered in just a single USB standard, and I couldn't afford to read all this huge amount of knowledge (my brain was starting to hurt). I could only look for information regarding the differences between these terms, and this Quora thread was a good starting point.

Interfaces vs. protocols

The term interface can have various meanings in different contexts, but all relate to a shared boundary across which two or more separate components of a computer system exchange information. The interfaces can be physical or virtual. A physical interface can be a connector for a cable modem and a virtual interface can be an API that allows the software to communicate with other software.

While an interface refers to the connecting point between 2 physical/virtual objects, a protocol defines rules to be complied with for exchanging information on that connecting point. Having said this, then interface is exactly where the elements communicate, transmit, and receive information. Whereas, a protocol is an agreed method or a guideline of steps for them to be able to understand each other. Devices and computing subsystems are interconnected through interfaces based on specific protocols. For us, the interfaces would be the ears and mouths, or hands in the case of communicating with the protocol of sign language.

Buses vs. ports

A bus is a specific kind of interface and it uses an implementation of some protocols to transfer data between components inside a computer. An example is the bus that connects a CPU to an internal memory within the same motherboard. The CPU knows what to say at what time to access memory and the memory knows how to reply with data when it is requested.

A port is commonly defined as a communication endpoint between the computer and external hardware. A port can refer to either a software port (for example, port 80 is a network port which is a logical number that allows HTTP traffic on a network), or a physical slot of the host computer that matches physical plugs or jacks (USB port, HDMI port, serial port).

Connectors vs. ports vs. interfaces vs. protocols

In the hardware context, the connector is the unique endpoint of a physical plug or jack. The interface is the meeting point of the connector and the port which is designed in the circuitry of the devices. It includes the design of both the plug and the port, the number and purpose of the wires, and the electrical signals that are passed across them.

Let's take an example of a 5-wire connector. Depending on which pin has which voltages/signaling, and what it is plugged into, the same connector can be used for all sorts of different interfaces - from MIDI ports/interfaces to PC/AT keyboard ports/interfaces. The protocol (that's which pins do what, at what voltages, and how that's interpreted by the computer/device at the end of the cable), however, varies. We cannot plug a MIDI cable to a PC/AT keyboard port and hear something. The two devices must have a protocol in common to speak to each other.

Interfaces in different contexts

As mentioned above, interface is where the separated components of a computer system communicate, transmit, and receive data. The information exchange can be between software, computer hardware, peripheral devices, humans, and combinations of these. For example, we have hardware (device) interfaces, software interfaces, or user interfaces.

Hardware/device interfaces

Hardware interfaces exist in many of the components, such as the various buses, ports, storage and I/O devices, etc. that allow physical devices to interact to a computer or other devices. For example, an Arduino Uno board being connected to a USB port is actually connected to the USB interface of the system, or a TV may connect to a Blu-ray player via an HDMI cable (HDMI interface). A hardware interface is described by the mechanical, electrical, and logical signals at the interface and the communication protocol for sequencing them (sometimes called signaling).

Wired hardware interfaces can be parallel with several electrical connections carrying parts of the data simultaneously, or serial where data are sent one bit at a time:

In case of wireless communication, we have network interface card (NIC) and WNIC which are circuit boards that provide networking capabilities for a computer. They may enable a wired Ethernet connection or a wireless connection (such as Wifi or Bluetooth) via a PAN, a LAN or over a large-scale network through IP. These interface devices use an antenna to communicate via microwave radiation.

Software interfaces

A software interface may refer to a wide range of different types of interface at different levels: an operating system may interface with pieces of hardware; applications or programs running on the operating system may need to interact via data streams, filters, and pipelines; and in object-oriented programs, objects within an application may need to interact via methods.

Interfaces between software components can provide constants, data types, exception specifications, and method signatures. Sometimes, public/global variables are also defined as part of data interfaces. In some object-oriented languages like Java or C#, the term interface is used to define an abstract type that contains no data (fields) but defines behaviors (methods). An application programming interface (API) is a computing interface to a software component or a system, that defines how other components or systems can use it. A RESTful API is an API that uses HTTP requests to GET, PUT, POST, and DELETE data from the database of an application. These are the concepts that I'm familiar with.

User interfaces

A user interface is a point of interaction between a computer and humans. It includes any number of modalities of interaction where data is transferred between the user and the computer system. UI layers may interact with one or more human senses, including tactile UI (touch), visual UI (sight), auditory UI (sound), etc. This week's assignments mostly revolve around user interfaces.

Composite user interfaces (CUI) are UIs that interact with two or more senses. The most common CUI is a graphical user interface (GUI), which is composed of a tactile UI and a visual UI capable of displaying graphics. When sound is added to a GUI it becomes a multimedia user interface (MUI). There are 3 broad categories of CUI: standard (keyboards, mice, etc.), virtual (VR), and augmented (AR).

Developing applications as user interfaces

Individual assignment - Interface users with output devices
Ringtone collection - Processing + passive buzzer

I continued to build on top of a program written in the 11th week which I used to play the theme song of Super Mario game. First I added 3 more songs to the program: Harry Potter, Game of Thrones, and Tetris by looking for their melodies and tempos online. This is a cool repository with a collection of many of my favorite songs. However, the way the durations were calculated and how the music was composed vary. Hence, I had to rewrite a bit and it was such a hard job!

The next step was to write the Processing program. Processing is a flexible software sketchbook that allows its users to write code with Java-based syntax within the context of the visual arts. I used to code in Java so it was not a big problem. The Serial library of Processing was used to open a serial communication with the Arduino Uno board through the USB port, and then I could call write() functions to send the ID of the selected song to the Arduino program.

As shown in the above image, the code will be executed again and again within draw() function. I made a mistake by using the mousePressed == true condition instead of the mousePressed() function, that's why we could see in the console that the songs were repeated. Also, to control the data received from the Arduino side and avoid song repetitions, I sent a songId = 0 to stop the buzzer in between. In the end, here is how data was transferred from Processing to Arduino:

Here you go the long but simple programs I wrote:

    
    /* 
     * convert notes to frequency 
     */
    #define NOTE_B0  31
    #define NOTE_C1  33
    #define NOTE_CS1 35
    #define NOTE_D1  37
    #define NOTE_DS1 39
    #define NOTE_E1  41
    #define NOTE_F1  44
    #define NOTE_FS1 46
    #define NOTE_G1  49
    #define NOTE_GS1 52
    #define NOTE_A1  55
    #define NOTE_AS1 58
    #define NOTE_B1  62
    #define NOTE_C2  65
    #define NOTE_CS2 69
    #define NOTE_D2  73
    #define NOTE_DS2 78
    #define NOTE_E2  82
    #define NOTE_F2  87
    #define NOTE_FS2 93
    #define NOTE_G2  98
    #define NOTE_GS2 104
    #define NOTE_A2  110
    #define NOTE_AS2 117
    #define NOTE_B2  123
    #define NOTE_C3  131
    #define NOTE_CS3 139
    #define NOTE_D3  147
    #define NOTE_DS3 156
    #define NOTE_E3  165
    #define NOTE_F3  175
    #define NOTE_FS3 185
    #define NOTE_G3  196
    #define NOTE_GS3 208
    #define NOTE_A3  220
    #define NOTE_AS3 233
    #define NOTE_B3  247
    #define NOTE_C4  262
    #define NOTE_CS4 277
    #define NOTE_D4  294
    #define NOTE_DS4 311
    #define NOTE_E4  330
    #define NOTE_F4  349
    #define NOTE_FS4 370
    #define NOTE_G4  392
    #define NOTE_GS4 415
    #define NOTE_A4  440
    #define NOTE_AS4 466
    #define NOTE_B4  494
    #define NOTE_C5  523
    #define NOTE_CS5 554
    #define NOTE_D5  587
    #define NOTE_DS5 622
    #define NOTE_E5  659
    #define NOTE_F5  698
    #define NOTE_FS5 740
    #define NOTE_G5  784
    #define NOTE_GS5 831
    #define NOTE_A5  880
    #define NOTE_AS5 932
    #define NOTE_B5  988
    #define NOTE_C6  1047
    #define NOTE_CS6 1109
    #define NOTE_D6  1175
    #define NOTE_DS6 1245
    #define NOTE_E6  1319
    #define NOTE_F6  1397
    #define NOTE_FS6 1480
    #define NOTE_G6  1568
    #define NOTE_GS6 1661
    #define NOTE_A6  1760
    #define NOTE_AS6 1865
    #define NOTE_B6  1976
    #define NOTE_C7  2093
    #define NOTE_CS7 2217
    #define NOTE_D7  2349
    #define NOTE_DS7 2489
    #define NOTE_E7  2637
    #define NOTE_F7  2794
    #define NOTE_FS7 2960
    #define NOTE_G7  3136
    #define NOTE_GS7 3322
    #define NOTE_A7  3520
    #define NOTE_AS7 3729
    #define NOTE_B7  3951
    #define NOTE_C8  4186
    #define NOTE_CS8 4435
    #define NOTE_D8  4699
    #define NOTE_DS8 4978
    /* 
     * pin variables
     */
    #define buzzer A0
    /* 
     * data received from Processing app 
     */
    char songId;
    /* 
     * melody and tempo of Super Mario
     */
    int mario[] = {
        NOTE_E7, NOTE_E7, 0, NOTE_E7, 0, NOTE_C7, NOTE_E7, 0, NOTE_G7, 0, 0, 0, NOTE_G6, 0, 0, 0,
        NOTE_C7, 0, 0, NOTE_G6, 0, 0, NOTE_E6, 0, 0, NOTE_A6, 0, NOTE_B6, 0, NOTE_AS6, NOTE_A6, 0,
        NOTE_G6, NOTE_E7, NOTE_G7, NOTE_A7, 0, NOTE_F7, NOTE_G7, 0, NOTE_E7, 0, NOTE_C7, NOTE_D7, NOTE_B6, 0, 0
    };
    int mario_tempo[] = {
        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
        6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
    };
    /* 
     * melody and tempo of Tetris
     */
    int tetris[] = {
        NOTE_E5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, 
        NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_D5, NOTE_F5, NOTE_A5, NOTE_G5, NOTE_F5,
        NOTE_E5, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, 0
    };
    int tetris_tempo[] = {
        4, 8, 8, 4, 8, 8, 4, 8, 8, 4, 8, 8, -4, 8, 4, 4, 4, 4, 3, 
        -4, 8, 4, 8, 8, -4, 8, 4, 8, 8, 4, 8, 8, 4, 4, 4, 4, 4, 4
    };
    /* 
     * melody and tempo of Game of Thrones
     */
    int got[] = {
        NOTE_G4, NOTE_C4, NOTE_DS4, NOTE_F4, NOTE_G4, NOTE_C4, NOTE_E4, NOTE_F4, 
        NOTE_G4, NOTE_C4, NOTE_DS4, NOTE_F4, NOTE_D4, NOTE_G3, NOTE_AS3, NOTE_C4, NOTE_D4, 
        NOTE_G3, NOTE_AS3, NOTE_C4, NOTE_D4, NOTE_F4, NOTE_AS3, NOTE_DS4, NOTE_D4, NOTE_F4, 
        NOTE_AS3, NOTE_DS4, NOTE_D4, NOTE_C4
    };
    int got_tempo[] = {
        2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 
        2, 1, 1, 2, 2, 1, 1, 4,
        4, 4, 1, 1, 4, 4, 1, 1, 4
    };
    /* 
     * melody and tempo of Harry Potter
     */
    int harrypotter[] = {
        NOTE_D4, NOTE_G4, NOTE_AS4, NOTE_A4, NOTE_G4, NOTE_D5, NOTE_C5, NOTE_A4, NOTE_G4, NOTE_AS4, NOTE_A4,
        NOTE_F4, NOTE_GS4, NOTE_D4, NOTE_D4, NOTE_G4, NOTE_AS4, NOTE_A4, NOTE_G4, NOTE_D5, 
        NOTE_F5, NOTE_E5, NOTE_DS5, NOTE_B4, NOTE_DS5, NOTE_D5, NOTE_CS5, NOTE_CS4, NOTE_B4, NOTE_G4
    };
    int harrypotter_tempo[] = {
        4, -4, 8, 4, 2, 4, -2, -2, -4, 8, 4, 2, 4, -2, 4,
        -4, 8, 4, 2, 4, 2, 4, 2, 4,
        -4, 8, 4, 2, 4, -2
    };
    /* 
     * begin serial communication at the same baud rate with the Processing program
     */
    void setup(void)
    {
        pinMode(buzzer, OUTPUT);
        Serial.begin(115200);
    }
    /* 
     * receive songId from Processing app
     * and pass it as an argument to sing() function
     */
    void loop()
    { 
        if (Serial.available()) 
        { 
            songId = Serial.read(); 
        }
        sing(songId);
        // avoid repeated songs
        songId = '0';
        delay(2000);
    }
    /* 
     * use tone() to play songs with the buzzer: targeted pin, frequency, duration
     * songId is the parameter
     */
    void sing(char id) 
    {
        if (id == '1') 
        {
            int duration = 0;
            for (int i = 0; i < (sizeof(mario) / sizeof(int)); i++) {           
                // to calculate the note duration, take one second divided by the note type
                // e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc. 
                duration = 1000/mario_tempo[i];
                tone(buzzer, mario[i], duration); 
                // to distinguish the notes, set a minimum time between them
                delay(duration * 1.30); 
                // stop the tone playing
                noTone(buzzer);
            }
            delay(10);
        }
        else if (id == '2')
        {        
            int duration = 0;
            for (int i = 0; i < (sizeof(tetris) / sizeof(int)); i++) {  
                duration = 1800 / abs(tetris_tempo[i]);
                if (tetris_tempo[i] < 0) {
                    // notes are represented with positive durations are regular note, just proceed
                    // dotted notes are represented with negative durations
                    // increases the duration in half for dotted notes
                    duration *= 1.5; 
                }
                tone(buzzer, tetris[i], duration); 
                delay(duration * 1.10);
                noTone(buzzer);
            }
            delay(10);
        }
        else if (id == '3')
        {        
            int duration = 0;
            for (int i = 0; i < (sizeof(got) / sizeof(int)); i++){           
                duration = got_tempo[i] * 250;
                tone(buzzer, got[i], duration); 
                delay(duration); 
                noTone(buzzer);
            }
            delay(10);
        }
        else if (id == '4')
        {        
            int duration = 0;
            for (int i = 0; i < (sizeof(harrypotter) / sizeof(int)); i++){  
                duration = 1600 / abs(harrypotter_tempo[i]);
                if (harrypotter_tempo[i] < 0) {
                    duration *= 1.3; 
                }
                tone(buzzer, harrypotter[i], duration); 
                delay(duration * 1.10);
                noTone(buzzer);
            }
            delay(10);
        }
    }
    
    
    
    /* 
     * declare an object of Serial class 
     * for sending and receiving data over serial communication protocol
     * in this case I called it "Uno" 
     * because my USB port was connected to an Arduino Uno board 
     */ 
    import processing.serial.*;
    Serial Uno;
    /* 
     * declare the icons used 
     */ 
    PImage icon1;
    PImage icon2;
    PImage icon3;
    PImage icon4;
    /* 
     * load icons and open the serial connection 
     */ 
    void setup() {
        size(700, 700);
        background(230);
        /* 
         * load images in the "data" folder 
         */
        icon1 = loadImage("mario.png");
        icon2 = loadImage("tetris.png");
        icon3 = loadImage("harry-potter.png");
        icon4 = loadImage("got.png");
        /* 
         * add arguments for the serial communication: parent, port name, baud rate 
         * use the same baud rate with the Arduino program
         */ 
        Uno = new Serial(this, "/dev/cu.usbmodem14301", 115200);
    }
    /* 
     * similar to loop() in Arduino 
     */
    void draw() {
        image(icon1, 120, 120);
        image(icon2, 450, 120);
        image(icon3, 120, 400);
        image(icon4, 450, 400);
    }
    /* 
     * function called whenever the mouse is clicked
     */
    void mousePressed() {
        Uno.write(check());
        Uno.clear();
    }
    /* 
     * send the id of the song selected to the Arduino program
     */
    char check() {
        if (120 < mouseX && mouseX < 270 && 120 < mouseY && mouseY < 270) {
            println("Super Mario Bros");
            return '1';
        }
        else if (450 < mouseX && mouseX < 600 && 120 < mouseY && mouseY < 270) {
            println("Tetris");
            return '2';
        }
        else if (120 < mouseX && mouseX < 270 && 400 < mouseY && mouseY < 550) {
            println("Harry Potter");
            return '4';
        }
        else if (450 < mouseX && mouseX < 600 && 400 < mouseY && mouseY < 550) {
            println("Game of Thrones");
            return '3';
        }
        else {
            return '0';
        }
    }
    
    

It's time for music!

Individual assignment - Interface users with input devices
Flappy Bird - Pygame + pySerial + HR-SC04 ultrasonic sensor

After being introduced to Pygame by my classmate David Prieto, I found it really cool and the way it works is kinda easy to understand. Since I have never programmed anything properly in Python, I decided to give it a try. Pygame is a cross-platform set of Python modules designed for writing video games. It includes computer graphics and sound libraries designed to be used with the Python programming language. To install Pygame, I ran $ pip install pygame in Terminal and then wrote a Flappy Bird clone. In case you didn't know, Flappy Bird was written by a Vietnamese developer. Also, I had developed a Flappy Bird clone using Android Studio before, so the task was simply to TRANSLATE Java to Python.

I started with writing the classic program of making the bird jump by pressing the space bar. In order to get used to the new Python language, I referred to some online projects, for example here and here. I tried to rewrite the program in a simpler way, and it worked perfectly, so I could get rid of one debug layer. The code will be explained below.

The next step was to use pySerial library to access the data received from the USB port. First I used the exact Arduino program I wrote in the 9th week to begin the serial communication and printed the distance values so that the Python program could read it. Next, I imported the pySerial library in my Python code and opened a serial communication with the same port and baud rate as in the Arduino program. With this, I could read the data printed, converted them to numbers, and used them to adjust how high the bird could jump. I then faced a problem that the bird was not pulled down as law of gravity but kept flying higher and higher. The measured data was also really unstable:

After asking David to take a look at my code, he suggested that maybe that was a problem of trying to read the sensor's data twice before calling the jump() function: once when I pressed the space bar, and once when the data is available. I simply deleted the extra Bird.read() line, and it worked (not like a charm because I was so bad of playing this game, and the accuracy of the sensor was not really high)!

To stabilize the readings and make it smoother, I referred to the filtering technique introduced by Oscar here. Basically, the program reads repeatedly from the sensor, calculates a running average and prints it to serial monitor. Below is the final working code with detailed comments:

    
    /* 
     * pin variables
     */
    int trig = 32;
    int echo = 35;
    /* 
     * variables for filtering readings
     */
    int readings[5]; // array of readings 
    int currentIndex = 0; // index of the current reading
    int total = 0; 
    int average = 0; 
    /* 
     * begin serial and initialize all readings to 0
     */
    void setup() {
        pinMode(trig, OUTPUT); 
        pinMode(echo, INPUT);
        Serial.begin(115200);
        for (int i = 0; i < 5; i++) {
            readings[i] = 0; 
        }
    }
    /* 
     * filter and print values
     */
    void loop() {
        // subtract the last reading
        total = total - readings[currentIndex];
        // read distance from the sensor
        readings[currentIndex] = readDistance();
        // calculate total value
        total = total + readings[currentIndex];
        if (currentIndex >= 5) {
            currentIndex = 0;
        }
        // calculate average value
        average = total / 5;
        Serial.println(average);
        delay(1);
    }
    /* 
     * read distance values
     */
    int readDistance() {
        digitalWrite(trig, LOW);
        delayMicroseconds(5);
        digitalWrite(trig, HIGH);
        delayMicroseconds(20);
        digitalWrite(trig, LOW);     
        duration = pulseIn(echo, HIGH);
        distance = (duration * 0.034)/2;
        return distance;
    }
    
    
    
    /* 
     * libraries incl. pySerial and Pygame
     */ 
    import os
    import sys
    import serial
    import pygame, math
    from random import randint
    from time import sleep
    /* 
     * initialize Pygame 
     */ 
    pygame.init()
    /* 
     * useful constant variables 
     * since Pygame doesn't have a collision detection feature
     */ 
    WIDTH = 1000
    HEIGHT = 580
    BIRD_WIDTH = 50
    BIRD_HEIGHT = 36
    PIPE_WIDTH = 75
    PIPE_LENGTH = 450
    PIPE_GAP = 10
    /* 
     * loading graphics 
     */ 
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    pygame.display.set_caption("Flappy Bird!")
    bird = pygame.image.load('bird.png') 
    background = pygame.image.load('bg.png')
    pipeBottom = pygame.image.load('pipe.png')
    pipeTop = pygame.transform.rotate(pipeBottom, 180)
    font = pygame.font.SysFont('Comic Sans MS', 30)
    /* 
     * open serial connection with the Arduino Uno board
     * if there are more than 10 values in queue, reset input buffer
     */ 
    uno = serial.Serial("/dev/cu.usbmodem14301", 115200)
    uno.setDTR()
    uno.flush()
    if (uno.in_waiting > 10):
        uno.reset_input_buffer()
    /* 
     * setup the game: game states, scores, counters, etc.
     */ 
    def setupGame() :
        global gameRun
        gameRun = True
        global gameOver 
        gameOver = False
        global gameStart 
        gameStart = False
        global gameRunCount
        gameRunCount = 0
        global score
        score = -2
        global pipes
        pipes = []
        /* 
         * create a new Bird object with an initial position (250,250)
         */ 
        Bird.birdX = 250
        Bird.birdY = 250
        Bird.vel = 0
    /* 
     * Bird class 
     */ 
    class Bird :
        birdX = 250
        birdY = 250
        distanceVal = 0
        vel = 0  
        /* 
         * read data from serial communication and convert to integers
         */ 
        def read() :
            strDistance = uno.readline().strip().decode('utf-8') 
            // handle the case of missing data  
            if not strDistance:
                Bird.distanceVal = 0
            else:
                Bird.distanceVal = int(strDistance) 
            // trigger a custom USEREVENT when data is available         
            if Bird.distanceVal != 0 :
                jump = pygame.event.Event(pygame.USEREVENT)
                pygame.event.post(jump)
        /* 
         * filter the value and adjust the velocity of the bird
         */ 
        def jump() :   
            if 3 < Bird.distanceVal < 30:
                Bird.vel = -Bird.distanceVal 
            else:
                Bird.vel = 0 
        /* 
         * update the bird position
         */ 
        def update() :
            if gameStart :   
                if Bird.birdY < (HEIGHT - BIRD_HEIGHT) : 
                    Bird.birdY += Bird.vel
                    Bird.vel += 1
                else :
                    Bird.birdY = (HEIGHT - BIRD_HEIGHT)
                    die()
                if Bird.birdY < 0 :
                    Bird.birdY = 0
                    Bird.vel -= 0
            else :
                Bird.birdY = 250 + math.sin(gameRunCount/10) * 15
    /* 
     * Pipe class 
     */ 
    class Pipe() :
        def __init__(self, direction, x, length) :
            self.direction = direction
            self.pipeX = x
            self.pipeLength = length
        /* 
         * update the pipes' positions as the bird moves
         */ 
        def update(self) :
            if self.direction == "BOTTOM" :
                screen.blit(pipeBottom, (self.pipeX , HEIGHT - self.pipeLength)) 
            else :
                screen.blit(pipeTop, (self.pipeX , self.pipeLength - PIPE_LENGTH)) 
            if not gameOver :
                self.pipeX -= PIPE_GAP
        /* 
         * detect collision by comparing (x,y) of the bird and (x,y) of the pipes
         */ 
        def checkCollide(self) :
            if self.direction == "TOP" :
                if Bird.birdX + BIRD_WIDTH >= self.pipeX and self.pipeX + PIPE_WIDTH >= Bird.birdX : 
                    if Bird.birdY <= self.pipeLength : 
                        die()
            else :
                if Bird.birdX + BIRD_WIDTH >= self.pipeX and self.pipeX + PIPE_WIDTH >= Bird.birdX : 
                    if Bird.birdY + BIRD_HEIGHT >= HEIGHT - self.pipeLength : 
                        die()
    /* 
     * function to generate a random pair of pipes
     * when the bird passes a pair of pipes, score++
     */ 
    def generatePipes() :
        random = randint(100, 200)
        pipes.append(Pipe("TOP", 900, random))
        pipes.append(Pipe("BOTTOM", 900, HEIGHT - (random + BIRD_HEIGHT * 4)))
        global score
        score += 1
    /* 
     * game over function
     */ 
    def die() :
        global gameOver
        gameOver = True
        gameRun = False
    /* 
     * call the setupGame() function to prepare the game
     */ 
    setupGame()
    /* 
     * execute codes while the game state is runned
     */ 
    while gameRun:
        pygame.time.delay(5)
        /* 
         * listen to events: ESC key, space board, and the custom USEREVENT 
         */ 
        for event in pygame.event.get() :
            if event.type == pygame.KEYDOWN :
                if event.key == pygame.K_ESCAPE :
                    gameRun = False
                elif event.key == pygame.K_SPACE :
                    if not gameStart :
                        gameStart = True
                    if not gameOver :
                        Bird.read()
                        Bird.jump()    
            elif event.type == pygame.QUIT :
                gameRun = False
            elif event.type == pygame.USEREVENT :
                if not gameOver :
                    Bird.jump()
        /* 
         * display the background, generate pipes, update the bird position 
         */            
        screen.blit(background, (0,0))
        if gameRunCount % 45 == 0 and gameStart :
            generatePipes()
        for p in pipes :
            p.update()
            p.checkCollide()
        Bird.update()
        screen.blit(bird, (Bird.birdX, Bird.birdY))
        /* 
         * update distances for debugging
         */
        distance = font.render(str(Bird.distanceVal), False, (0, 0, 0))
        screen.blit(distance, (20, 45))
        /* 
         * update scores
         */ 
        scoreboard = font.render(str(score), False, (255, 255, 255))
        if score > -1 :
            screen.blit(scoreboard, (20, 20))
        pygame.display.update()
        if not gameOver:
            gameRunCount += 1
    /* 
     * quit Pygame
     */        
    pygame.quit()
    
    

I re-uploaded the code to the ESP32 board designed during the 9th week. It's totally impossible to take a picture and win a high score, but in general, the result are the same. Just try to move my hand poco a poco!

Group assignment - Comparing development tool options

To make this part more like a "group" assignment, I included here the development tools that my classmates went through in addition to what I did. All of them tried many workflows and made amazing programs, but I limited the list to workflows that are different from mine.

Student Application Input device Output device Development tools Language Development board Device interface What I find interesting
Myself Ringtone collection - Passive buzzer Processing Java Arduino Uno Processing's Serial Well-documented, lots of tutorials, is inexhaustible for bigger projects with lots of libraries
Myself Flappy Bird Ultrasonic sensor - Pygame Python My Final Project's ESP32 board pySerial Many modules specialized for graphic games
Benjamin Scott Wavemaker Inertial Measurement Unit (IMU) - WebSocket + Progressive Web App + P5.js JavaScript Barduino WebSocket's broadcast message Full-duplex asynchronous messaging
Antoine Jaunard Experimental music instrument Buttons, light & sound sensors - openFrameworks C++ Circuit Playground Express Serial No specific IDE, clean and simple projects containing both Arduino and oF programs

Conclusion

There're indeed many more tools I'd love to try for this week's assignment, for example Unity or Blynk. However, at this point, I decided that application programming is not really my focus while participating the Fab Academy. Hence, I'm quite OK with what I did this week.