Traffic Light Intersection Simulator

Introduction

The Traffic Light Intersection Simulator records user input through a touch screen of traffic flow at a four-way intersection for play back. Additionally it is capable of storing and reading to a FAT 32 formatted microSD card. The simulator is also capable of clearing the memory, removing the last input and play back of what is currently saved.

High Level Design

The idea originated from a project one of our parents made (Carlos Contreras Aponte) during his masters to assist with an assignment requiring the data logging of traffic flow at an intersection. This version is a more modern approach utilizing a resistive touch screen for user input as well as a microSD card for storage. The TFT screen and microSD card are connected as individual SPI channels, while the resistive touch film on the screen is connected through two digital and two analog pins. Although the TFT screen and resistive touch film share the same breakout board and come together, these need to be calibrated through software for a touch value to match its equivalent location on the screen. It is important to note that the program will not finish boot unless there is a microSD card.

The logical structure for the program consists of two threads. One reads the resistive touch film input while the other interprets the input. If the program determines the input to be a traffic movement gesture, this gesture will be saved in an array. In the case the input is not a traffic movement gesture it determines if it was a press of a “button” for either Clear, Undo, Play, Save to microSD or Load from microSD card. Each gesture is accompanied with a message of to confirm what gesture was input. While the simulator is playing back the traffic flow from the gesture array, the simulator becomes locked and will not read input from the resistive touch film.

Hardware design

The simulator we built uses an Adafruit 2.8″ TFT LCD with Resistive Touchescreen Breakout Board (ILI9341) and an Adafruit MicroSD Card Breaout Board+. The TFT screen it uses the PIC32’s SPI channel 1, while the microSD card reader uses SPI channel 2. The TFT’s CS, D/C and RST and the microSD card reader’s CS and CD pins were connected as regular GPIO pins. The SD card library we use include for a Write Enable pin which the microSD card reader breakout board does not include. The resistive touch film on the TFT screen uses four pins: Y+, Y-, X+ and X-. To read where it is being touched on X axis, the X+ and X- must be set as digital outputs, with X+ being a logical 1 and the X- a logical 0. Then you read through an analog input from either Y+ or Y-, which we used Y-. To read on Y axis you simply repeat the process as reading on X axis, but invert the axis. We used X- as the analog input pin. We also include 330Ω resistors between the microcontroller and touchscreen to protect them from overcurrent. The value of the resistors used was decided to be same one as used for same reason on previous projects for the course.

Program design

Our touch screen library and SD card implementation were built off of libraries previously written by Syed Tahmid Mahbub. The SD card library are originally from Microchip, but unable to obtain them from Microchip’s webside directly we found a project by Syed Tahmid Mahbub which used them. This library was already modified by Syed Tahmid Mahbub to be compatible with protothreads as well as a modification for lack of CD and WE from the microSD card, as well as the pins and connections for his project. We modified to use CD from microSD card, as well as for our specific connections which include TFT. The main function initializes the TFT and SD card, then draws an intersection with 5 buttons: PLAY, UNDO, CLEAR, STORE (to SD card), and LOAD (to SD card). The function then begins two threads: a touch thread and a gesture thread.

The touch thread detects the locations where the user touches the TFT, and the thread runs forever. A state machine DRAG_STATE is used to switch the state of the TFT between WAITING, TOUCHING, and DONE. While a touch point is not registered, a while loop within the touch thread waits for an input. The while loop breaks when a touch point is found and, if the state is WAITING, the most recent point found is determined to be the initial point of the gesture. The state of the state machine is also changed to TOUCHING. While the user is still touching the screen, the while loop mentioned above continuously gets the touch point. Once the TFT no longer detects a user touching the screen for 5 iterations of the while loop, the final point is determined to be the last valid point captured. The state is changed to DONE.

The gesture thread interprets the touch data from the touch thread, and it runs forever. The gesture thread yields until it detects that the state of the state machine DRAG_STATE is DONE. A function getGesture is called with the initial and final points from the touch thread. The getGesture function checks if the initial and final points are within the intersection. If they are, then the function returns a value from 0-15, which signifies one of the 16 combinations of entry and exit from the intersection. Otherwise, the function returns GESTURE_ILLEGAL with a value of 16. GESTURE_ILLEGAL is reserved for the various buttons on the screen.

If the gesture was not illegal, then the user is informed which gesture was drawn and the gesture is added to an int array called gestures. If the gesture was illegal, then the gesture thread determines if a button was pushed, in the same way that getGesture recognizes a gesture. The UNDO button will remove the last gesture from the gestures array and print on the TFT either the gesture that was deleted or that no gestures were found. The CLEAR button will remove all gestures from the gestures array and print on the TFT either the array was emptied or that no gestures were found. These two functions are simply implemented by changing the index of the next empty gesture, gInd, to 0.

The PLAY button will play all gestures stored in the gestures array up to, but not including, the gesture at index gInd. The program will print on the TFT either the gesture that was most recently played or that no gestures were found. To play the gestures, a function gesture_draw() is called on each gesture in the gestures array. As a sidenote, originally gesture_draw was going to be its own separate thread instead of a function, which explains our choice of global variables. Since each gesture follows a different path, gesture_draw does a case-by-case analysis of all gestures. The movement is shown by drawing new circles which move in the direction of the gesture and erasing old circles once there are 100 circles on screen.

The STORE button will write all gestures stored in the gestures array into a text file, inter.txt, on the SD card. The first four digits indicate the number of gestures stored in the gestures array, padded with zeros. Every subsequent pair of digits describes a gesture, from 00 to 15, denoting the 16 possible movements in the 4-way intersection. The user can now clear the gestures array without fearing for lost data.

The LOAD button will load all gestures stored on the SD card into the gestures array. The first four digits indicate the number of gestures stored in the gestures array, which is set equal to the position of the pointer of the next empty index in the gestures array. Every subsequent pair of digits describes a gesture, and the program loops over those digits and adds them to the gestures array. The user can now playback the newly loaded gestures, if desired.

Results of the design

The program would respond in less than 250 ms to user input. Playback took 500 ms per gesture, which is slow enough that it is easy to see the data, yet fast enough to keep the user’s attention. Sometimes a touch would not be registered or the wrong gesture would be recorded, but the UNDO button and the constant prints to screen would help ensure that the user is inputting the correct values. There is no potential for interference with any external signals or noise. The design is inexpensive, intuitive, and efficient, with potential for use in academia and in industry.

Conclusions

We expected the TFT to be able to take in touch data and for the SD card to store the touch data. We also expected efficient playback of our gestures input. The results were consistent with our expectations. Next time, we would try to make the text files written to the SD card easier to read, as it is currently a single sequence of digits. Other avenues of improvement include assigning timestamps for vehicle entry and exit, adding traffic lights and optimizing throughput, storing multiple files in the SD card and adding a UI for file creation on the TFT, and parsing real traffic data for replay on the TFT. The libraries used in this code were developed by Syed Tahmid Mahbub, and they are publicly available on his blog. No legal, safety, or ethical concerns apply.

code

/*
            * File:        TFT, touchscreen, SD card
            * Author:      Carlos Contreras, Kevin Chaudhari
            * Using touch screen library by Syed Tahmid Mahbub
            * http://tahmidmc.blogspot.com/2015/08/pic32-tic-tac-toe-demonstration-of.html
            * For use with Sean Carroll's Small Board
            * http://people.ece.cornell.edu/land/courses/ece4760/PIC32/target_board.html
            * Target PIC:  PIC32MX250F128B
            * SD Card implementation based off: SYED TAHMID MAHBUB ? PIC32 based audio player with microSD card and 12-bit audio out
            * http://tahmidmc.blogspot.com/2015/01/audio-player-using-pic32-microsd-card.html
            */
           
           /* SPI pins configuration for SD Card:
            * Using SPI2       (defined in HardwareProfile.h)
            *  SDO:    RA1    (TRIS defined in HardwareProfile.h and PPS defined in SD-SPI.c)
            *          physical pin 3
            *  SDI:    RA2    (TRIS defined in HardwareProfile.h and PPS defined in SD-SPI.c)
            *          physical pin 9
            *  SCK:    RB14    (TRIS defined in HardwareProfie.h)
            *          physical pin 25
            *  CS:     RB3     (defined in HardwareProfile.h)
            *          physical pin 7
            *  CD:     RA3     (defined in HardwareProfile.h)
            *          physical pin 10
            *  WE:     RB4     (defined in HardwareProfile.h)
            *          physical pin 17
            *          SD card breaktout doesn't have WE pin - overriden in code
            * 
            * 
            * SPI Pins configuration for TFT
            * Using SPI 1
            * 
            *  SDO:    RB11    (TRIS defined in tft_master_fp.h and PPS defined in tft_master_fp.c)
            *          physical pin 22
            *  SDI:    RB8
            *          physical pin 17
            *  SCK:    RB15    (Defined in tft_master_fp.h)
            *          physical pin 26
            *  CS:     RB1     (TRIS defined in tft_master_fp.h)
            *          physical pin 5
            *  D/C:    RB0     (TRIS defined in tft_master_fp.h)
            *          physical pin 4
            *  RST:    RB2     (TRIS defined in tft_master_fp.h)
            *          physical pin 6
            * 
            * Resistive Touchscreen Pins
            * Digital:
            *  X+:     RB10    (Defined in touch_master.h)
            *          physical pin 21
            *  Y+:     RB9     (Defined in touch_master.h)
            *          physical pin 18
            * Analog:
            *  X-:     RB13    AN11    (Defined in touch_master.h)
            *          physical pin 24
            *  Y-:     RA0     AN0     (Defined in touch_master.h)
            *          physical pin 2
            * 
            * 
            */
           
           //////////////////////////////////// 
           // clock AND protoThreads configure!
           // You MUST check this file!
           //  TEST OLD CODE WITH NEW THREADS
           #include "config_1_3_2.h"
           // threading library
           #include "pt_cornell_1_3_2.h"
           ////////////////////////////////////
           // graphics libraries
           // SPI channel 1 connections to TFT
           #include "tft_master_fp.h"
           #include "tft_gfx_fp.h"
           #include "touch_master.h"
           //SD Card
           #include "FSIO.h"
           
           // need for rand function
           #include  <stdlib.h >
           // need for sin function
           #include < math.h & gt
           #include < stdio.h >
           ////////////////////////////////////
           
           /* Demo code for interfacing TFT (ILI9341 controller) to PIC32
            * The library has been modified from a similar Adafruit library
            */
           // Adafruit data:
           /***************************************************
             This is an example sketch for the Adafruit 2.2" SPI display.
             This library works with the Adafruit 2.2" TFT Breakout w/SD card
             ----> http://www.adafruit.com/products/1480
           
             Check out the links above for our tutorials and wiring diagrams
             These displays use SPI to communicate, 4 or 5 pins are required to
             interface (RST is optional)
             Adafruit invests time and resources providing this open source code,
             please support Adafruit and open-source hardware by purchasing
             products from Adafruit!
           
             Written by Limor Fried/Ladyada for Adafruit Industries.
             MIT license, all text above must be included in any redistribution
            ****************************************************/
           /*
            * 
            */
           
           /*DRAG_STATE: refers to current state regarding if touch screen is being touched
            *      or not. 
            * STATE_WAITING: currently waiting for user to initiate a gesture
            * STATE_TOUCHING: store first point of touch, remain on this state as long as
            *      there is a constant valid touch input
            * STATE_DONE: user has stopped touching the screen, store final point of touch
            *      Determine the gesture input
            * 
            */
           #define STATE_WAITING 0
           #define STATE_TOUCHING 1
           #define STATE_DONE 2
           
           volatile int DRAG_STATE;
           
           /*Gestures
            * First letter represents start
            * Second letter represents end
            * L = left = 0
            * R = right = 1
            * D = down = 2
            * U = up = 3
            * 
            * Value of gesture: initial touch point shall be GS, final touch point GE
            * GS*4 + GE
            * Example: RU = R*4 + U = 7
            */
           #define GESTURE_LL 0
           #define GESTURE_LR 1
           #define GESTURE_LD 2
           #define GESTURE_LU 3
           
           #define GESTURE_RL 4
           #define GESTURE_RR 5
           #define GESTURE_RD 6
           #define GESTURE_RU 7
           
           #define GESTURE_DL 8
           #define GESTURE_DR 9
           #define GESTURE_DD 10
           #define GESTURE_DU 11
           
           #define GESTURE_UL 12
           #define GESTURE_UR 13
           #define GESTURE_UD 14
           #define GESTURE_UU 15
           
           #define GESTURE_ILLEGAL 16
           
           //Directions for turning on Play mode
           #define DIRECTION_LEFT -1
           #define DIRECTION_RIGHT 1
           //Top is 0, bottom is 319
           #define DIRECTION_UP -1
           #define DIRECTION_DOWN 1
           #define DIRECTION_NONE 0
           
           // string buffer
           char buffer[60];
           //Gestures storage array, init to Illegal gesture
           int gestures[1024] = {16};
           //Gestures index, as well as how many gestures in gestures storage array
           volatile int gInd = 0;
           
           /*File pointer for SD card
            * FAT 32 format
            * Program will save and load from inter.txt in SD card root folder
            * First four characters indicate the amount of gestures from 0000 to 1024
            * Following are gestures in sets of two characters from 00 to 15
            * Example of file text:
            * 00050012051402
            * gInd = 5
            * Gestures: 00, 12, 05, 14, 02
            */
           FSFILE * pointer;
           
           //Protothreads
           static struct pt pt_gesture, pt_touch;
           
           /*Touch points for obtaining gestures
            * initial_point: first point of touch
            * last_point: last point of touch at a moment, used to determine if still being touched
            * final_point: actual final point of touch before screen is stopped being pressed
            */
           volatile point_t initial_point;
           volatile point_t last_point;
           volatile point_t final_point;
           //Gesture to draw, initial is illegal gesture
           volatile int drawGesture = 16;
           /*Draw gestures variables
            * done: has a gesture finished being drawn?
            * Idea is to draw a line, that erases itself as well
            * x, y: coordinates of where to draw
            * xclr, yclr: coordinate of where to erase, follows x, y at 100 pixels behind
            * xdir, ydir, xclrdir, yclrdir: direction the line (and its erasure) is moving
            */
           static int done;
           static int x;
           static int y;
           static int xclr;
           static int yclr;
           static int xdir;
           static int ydir;
           static int xclrdir;
           static int yclrdir;
           
           // === print a line on TFT =====================================================
           // Utilities to print a line on the TFT
           // Predefined colors definitions (from tft_master.h)
           //#define	ILI9340_BLACK   0x0000
           //#define	ILI9340_BLUE    0x001F
           //#define	ILI9340_RED     0xF800
           //#define	ILI9340_GREEN   0x07E0
           //#define ILI9340_CYAN    0x07FF
           //#define ILI9340_MAGENTA 0xF81F
           //#define ILI9340_YELLOW  0xFFE0
           //#define ILI9340_WHITE   0xFFFF
           
           
           void printLine(int line_number, char* print_buffer, short text_color, short back_color){
               // line number 0 to 31 
               /// !!! assumes tft_setRotation(0);
               // print_buffer is the string to print
               int v_pos;
               v_pos = line_number * 10 ;
               // erase the pixels
               tft_fillRoundRect(0, v_pos, 239, 8, 1, back_color);// x,y,w,h,radius,color
               tft_setTextColor(text_color); 
               tft_setCursor(0, v_pos);
               tft_setTextSize(1);
               tft_writeString(print_buffer);
           }
           
           void printLine2(int line_number, char* print_buffer, short text_color, short back_color){
               // line number 0 to 31 
               /// !!! assumes tft_setRotation(0);
               // print_buffer is the string to print
               int v_pos;
               v_pos = line_number * 20 ;
               // erase the pixels
               tft_fillRoundRect(0, v_pos, 239, 16, 1, back_color);// x,y,w,h,radius,color
               tft_setTextColor(text_color); 
               tft_setCursor(0, v_pos);
               tft_setTextSize(2);
               tft_writeString(print_buffer);
           }
           
           
           /*Using the start and end touch points determine the gesture for the appropriate
            * car movement on the intersection
            */
           int getGesture(point_t start, point_t end){
               /*Touch input does not extend from 0 to 1023 ADC units
                * After testing multiple testing, these were the ranges for it.
                * 320<x<600
                * 280<y<650
               */
               int gesture = GESTURE_ILLEGAL;
               //Gesture start
               int startG;
               //Gesture End
               int endG;
               
               if(start.X<413 && start.Y >400 && start.Y<520 ) // Left
                   startG = 0;
               else if(start.X>506 && start.Y>400 && start.Y<520 ) // Right
                   startG = 1;
               else if(start.Y>520 && start.X>413 && start.X<506 ) // Bottom
                   startG = 2; 
               else if(start.Y<400 && start.X>413 && start.X<506 ) // Top
                   startG = 3;
               //Illegal start point: corners or center
               else
                   return gesture;
               
               if(end.X<413 && end.Y >330 && end.Y<600 )
                   endG = 0;
               else if(end.X>506 && end.Y>330 && end.Y<600 )
                   endG = 1;
               else if(end.Y>520 && end.X>413 && end.X<506 )
                   endG = 2; 
               else if(end.Y<400 && end.X>413 && end.X<506 )
                   endG = 3;
               //Illegal end point: corners or center
               else
                   return gesture;
               
               gesture = startG*4 + endG;
               
               gestures[gInd] = gesture;
               gInd = (gInd + 1) % 1024;
               
               return gesture;
           }
           //Draw intersection and corner "buttons"
           void drawIntersection(){
               tft_drawFastVLine(79,0, 106, ILI9341_WHITE);
               tft_drawFastVLine(79,214, 106, ILI9341_WHITE);
               tft_drawFastVLine(159,0, 106, ILI9341_WHITE);
               tft_drawFastVLine(159, 214, 106, ILI9341_WHITE);
               
               tft_drawFastHLine(0, 105, 80, ILI9341_WHITE);
               tft_drawFastHLine(159, 105, 80, ILI9341_WHITE);
               tft_drawFastHLine(0, 214, 80, ILI9341_WHITE);
               tft_drawFastHLine(159, 214, 80, ILI9341_WHITE);
               
               // undo button
               tft_fillRect(160, 215, 79, 105, ILI9341_RED);
               tft_setTextColor(ILI9341_WHITE); 
               tft_setCursor(180, 260);
               tft_setTextSize(2);
               tft_writeString("UNDO");
               
               // clear button
               tft_fillRect(160, 0, 79, 105, ILI9341_ORANGE);
               tft_setTextColor(ILI9341_WHITE); 
               tft_setCursor(170, 45);
               tft_setTextSize(2);
               tft_writeString("CLEAR");
               
               // store button
               tft_fillRect(0, 0, 79, 52, ILI9341_BLUE);
               tft_setTextColor(ILI9341_WHITE); 
               tft_setCursor(10, 25);
               tft_setTextSize(2);
               tft_writeString("STORE");
               
               // load button
               tft_fillRect(0, 53, 79, 52, ILI9341_MAGENTA);
               tft_setTextColor(ILI9341_WHITE); 
               tft_setCursor(10, 65);
               tft_setTextSize(2);
               tft_writeString("LOAD");
               
               // play button
               tft_fillRect(0, 215, 79, 105, ILI9341_GREEN);
               tft_setTextColor(ILI9341_WHITE); 
               tft_setCursor(20, 260);
               tft_setTextSize(2);
               tft_writeString("PLAY");
           }
           
           //Draws a gesture for play mode
           void gesture_draw()
           {
               switch(drawGesture){
                   case 0: //LL
                       //draw u-turn
                       done = 0;
                       x = 0;
                       y = 200;
                       xclr = -100;
                       yclr = 200;
                       xdir = DIRECTION_RIGHT;
                       ydir = DIRECTION_NONE;
                       xclrdir = DIRECTION_RIGHT;
                       yclrdir = DIRECTION_NONE;
                       while(done == 0){
                           if(x>=0)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(xclr > =0)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           if(x + xdir>119){
                               xdir = DIRECTION_NONE;
                               ydir = DIRECTION_UP;
                           }
                           if(xclr + xclrdir> 119){
                               xclrdir = DIRECTION_NONE;
                               yclrdir = DIRECTION_UP;
                           }
                           if(y + ydir<120){
                               xdir = DIRECTION_LEFT;
                               ydir = DIRECTION_NONE;
                           }
                           if(yclr + yclrdir<120){
                               xclrdir = DIRECTION_LEFT;
                               yclrdir = DIRECTION_NONE;
                           }
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
           
                           if(xclr<0 && xclrdir == DIRECTION_LEFT)
                               done = 1;
                       }
                       break;
                   case 1: //LR
                       done = 0;
                       x = 0;
                       y = 200;
                       xclr = -100;
                       yclr = 200;
                       xdir = DIRECTION_RIGHT;
                       ydir = DIRECTION_NONE;
                       xclrdir = DIRECTION_RIGHT;
                       yclrdir = DIRECTION_NONE;
                       while(done == 0){
                           if(x>=0 && x < = 239)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(xclr > =0 && xclr < = 239)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
                           if(xclr>239)
                               done = 1;
                       }
                       break;
                   case 2: //LD
                       done = 0;
                       x = 0;
                       y = 200;
                       xclr = -100;
                       yclr = 200;
                       xdir = DIRECTION_RIGHT;
                       ydir = DIRECTION_NONE;
                       xclrdir = DIRECTION_RIGHT;
                       yclrdir = DIRECTION_NONE;
                       while(done == 0){
                           if(x>=0 && y < = 319)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(xclr > =0 && yclr < = 319)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           
                           if(x + xdir>145){
                               xdir = DIRECTION_NONE;
                               ydir = DIRECTION_DOWN;
                           }
                           if(xclr + xclrdir> 145){
                               xclrdir = DIRECTION_NONE;
                               yclrdir = DIRECTION_DOWN;
                           }
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
           
                           if(yclr>319)
                               done = 1;
                       }
                       break;
                   case 3: //LU
                       done = 0;
                       x = 0;
                       y = 200;
                       xclr = -100;
                       yclr = 200;
                       xdir = DIRECTION_RIGHT;
                       ydir = DIRECTION_NONE;
                       xclrdir = DIRECTION_RIGHT;
                       yclrdir = DIRECTION_NONE;
                       while(done == 0){
                           if(x>=0 && y > =20)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(xclr > =0 && yclr > =0)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           
                           if(x + xdir>145){
                               xdir = DIRECTION_NONE;
                               ydir = DIRECTION_UP;
                           }
                           if(xclr + xclrdir> 145){
                               xclrdir = DIRECTION_NONE;
                               yclrdir = DIRECTION_UP;
                           }
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
           
                           if(yclr<20)
                               done = 1;
                       }
                       break;
                   case 4: //RL
                       done = 0;
                       x = 239;
                       y = 120;
                       xclr = 339;
                       yclr = 120;
                       xdir = DIRECTION_LEFT;
                       ydir = DIRECTION_NONE;
                       xclrdir = DIRECTION_LEFT;
                       yclrdir = DIRECTION_NONE;
                       while(done == 0){
                           if(x > =0 && x < = 239)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(xclr > =0 && xclr < = 239)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
           
                           if(xclr<0)
                               done = 1;
                       }
                       break;
                   case 5: //RR
                       done = 0;
                       x = 239;
                       y = 120;
                       xclr = 339;
                       yclr = 120;
                       xdir = DIRECTION_LEFT;
                       ydir = DIRECTION_NONE;
                       xclrdir = DIRECTION_LEFT;
                       yclrdir = DIRECTION_NONE;
                       while(done == 0){
                           if(x > =0 && x < = 239)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(xclr > =0 && xclr < = 239)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           if(x + xdir<129){
                               xdir = DIRECTION_NONE;
                               ydir = DIRECTION_DOWN;
                           }
                           if(xclr + xclrdir<129){
                               xclrdir = DIRECTION_NONE;
                               yclrdir = DIRECTION_DOWN;
                           }
                           if(y + ydir>200){
                               xdir = DIRECTION_RIGHT;
                               ydir = DIRECTION_NONE;
                           }
                           if(yclr + yclrdir>200){
                               xclrdir = DIRECTION_RIGHT;
                               yclrdir = DIRECTION_NONE;
                           }
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
           
                           if(xclr>239 && xclrdir == DIRECTION_RIGHT)
                               done = 1;
                       }
                       break;
                   case 6: //RD
                       done = 0;
                       x = 239;
                       y = 120;
                       xclr = 339;
                       yclr = 120;
                       xdir = DIRECTION_LEFT;
                       ydir = DIRECTION_NONE;
                       xclrdir = DIRECTION_LEFT;
                       yclrdir = DIRECTION_NONE;
                       while(done == 0){
                           if(x>=0 && y < = 319)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(xclr > =0 && yclr < = 319)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           if(x + xdir<95){
                               xdir = DIRECTION_NONE;
                               ydir = DIRECTION_DOWN;
                           }
                           if(xclr + xclrdir<95){
                               xclrdir = DIRECTION_NONE;
                               yclrdir = DIRECTION_DOWN;
                           }
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
                           if(yclr>319)
                               done = 1;
                       }
                       break;
                   case 7: //RU
                       done = 0;
                       x = 239;
                       y = 120;
                       xclr = 339;
                       yclr = 120;
                       xdir = DIRECTION_LEFT;
                       ydir = DIRECTION_NONE;
                       xclrdir = DIRECTION_LEFT;
                       yclrdir = DIRECTION_NONE;
                       while(done == 0){
                           if(x>=0 && y > =20)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(xclr > =0 && yclr > =0)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           if(x + xdir<95){
                               xdir = DIRECTION_NONE;
                               ydir = DIRECTION_UP;
                           }
                           if(xclr + xclrdir<95){
                               xclrdir = DIRECTION_NONE;
                               yclrdir = DIRECTION_UP;
                           }
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
                           if(yclr<20)
                               done = 1;
                       }
                       break;
                   case 8: //DL
                       done = 0;
                       x = 145;
                       y = 319;
                       xclr = 145;
                       yclr = 419;
                       xdir = DIRECTION_NONE;
                       ydir = DIRECTION_UP;
                       xclrdir = DIRECTION_NONE;
                       yclrdir = DIRECTION_UP;
                       while(done == 0){
                           if(x>0 && y < = 319)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(xclr>0 && yclr < = 319)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           if(y + ydir<120){
                               xdir = DIRECTION_LEFT;
                               ydir = DIRECTION_NONE;
                           }
                           if(yclr + yclrdir<120){
                               xclrdir = DIRECTION_LEFT;
                               yclrdir = DIRECTION_NONE;
                           }
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
                           if(xclr<0)
                               done = 1;
                       }
                       break;
                   case 9: //DR
                       done = 0;
                       x = 145;
                       y = 319;
                       xclr = 145;
                       yclr = 419;
                       xdir = DIRECTION_NONE;
                       ydir = DIRECTION_UP;
                       xclrdir = DIRECTION_NONE;
                       yclrdir = DIRECTION_UP;
                       while(done == 0){
                           if(x < = 239 && y < = 319)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(xclr < = 239 && yclr < = 319)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           if(y + ydir<200){
                               xdir = DIRECTION_RIGHT;
                               ydir = DIRECTION_NONE;
                           }
                           if(yclr + yclrdir<200){
                               xclrdir = DIRECTION_RIGHT;
                               yclrdir = DIRECTION_NONE;
                           }
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
                           if(xclr>239)
                               done = 1;
                       }
                       break;
                   case 10: //DD
                       done = 0;
                       x = 145;
                       y = 319;
                       xclr = 145;
                       yclr = 419;
                       xdir = DIRECTION_NONE;
                       ydir = DIRECTION_UP;
                       xclrdir = DIRECTION_NONE;
                       yclrdir = DIRECTION_UP;
                       while(done == 0){
                           if(y < = 319)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(yclr < = 319)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           if(y + ydir<200){
                               xdir = DIRECTION_LEFT;
                               ydir = DIRECTION_NONE;
                           }
                           if(yclr + yclrdir<200){
                               xclrdir = DIRECTION_LEFT;
                               yclrdir = DIRECTION_NONE;
                           }
                           if(x + xdir<95){
                               xdir = DIRECTION_NONE;
                               ydir = DIRECTION_DOWN;
                           }
                           if(xclr + xclrdir<95){
                               xclrdir = DIRECTION_NONE;
                               yclrdir = DIRECTION_DOWN;
                           }
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
                           if(yclr>319 && yclrdir == DIRECTION_DOWN)
                               done = 1;
                       }
                       break;
                   case 11: //DU
                       done = 0;
                       x = 145;
                       y = 319;
                       xclr = 145;
                       yclr = 419;
                       xdir = DIRECTION_NONE;
                       ydir = DIRECTION_UP;
                       xclrdir = DIRECTION_NONE;
                       yclrdir = DIRECTION_UP;
                       while(done == 0){
                           if(y > =20)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(yclr > =0)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
                           if(yclr<20)
                               done = 1;
                       }
                       break;
                   case 12: //UL
                       done = 0;
                       x = 95;
                       y = 20;
                       xclr = 95;
                       yclr = -80;
                       xdir = DIRECTION_NONE;
                       ydir = DIRECTION_DOWN;
                       xclrdir = DIRECTION_NONE;
                       yclrdir = DIRECTION_DOWN;
                       while(done == 0){
                           if(x>0 && y >=20)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(xclr>0 && yclr > =20)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           if(y + ydir>120){
                               xdir = DIRECTION_LEFT;
                               ydir = DIRECTION_NONE;
                           }
                           if(yclr + yclrdir>120){
                               xclrdir = DIRECTION_LEFT;
                               yclrdir = DIRECTION_NONE;
                           }
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
                           if(xclr < 0)
                               done = 1;
                       }
                       break;
                   case 13: //UR
                       done = 0;
                       x = 95;
                       y = 20;
                       xclr = 95;
                       yclr = -80;
                       xdir = DIRECTION_NONE;
                       ydir = DIRECTION_DOWN;
                       xclrdir = DIRECTION_NONE;
                       yclrdir = DIRECTION_DOWN;
                       while(done == 0){
                           if(x < = 239 && y > =20)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(xclr < = 239 && yclr > =20)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           if(y + ydir > 200){
                               xdir = DIRECTION_RIGHT;
                               ydir = DIRECTION_NONE;
                           }
                           if(yclr + yclrdir > 200){
                               xclrdir = DIRECTION_RIGHT;
                               yclrdir = DIRECTION_NONE;
                           }
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
                           if(xclr>239)
                               done = 1;
                       }
                       break;
                   case 14: //UD
                       done = 0;
                       x = 95;
                       y = 20;
                       xclr = 95;
                       yclr = -80;
                       xdir = DIRECTION_NONE;
                       ydir = DIRECTION_DOWN;
                       xclrdir = DIRECTION_NONE;
                       yclrdir = DIRECTION_DOWN;
                       while(done == 0){
                           if(y >=20 && y < = 319)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(yclr >=20 && yclr < = 319)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
                           if(yclr>319)
                               done = 1;
                       }
                       break;
                   case 15: //UU
                       done = 0;
                       x = 95;
                       y = 20;
                       xclr = 95;
                       yclr = -80;
                       xdir = DIRECTION_NONE;
                       ydir = DIRECTION_DOWN;
                       xclrdir = DIRECTION_NONE;
                       yclrdir = DIRECTION_DOWN;
                       while(done == 0){
                           if(y > =20)
                               tft_drawCircle(x, y, 2, ILI9341_CYAN);
                           if(yclr > =20)
                               tft_drawCircle(xclr, yclr, 4, ILI9341_BLACK);
                           if(y + ydir>120){
                               xdir = DIRECTION_RIGHT;
                               ydir = DIRECTION_NONE;
                           }
                           if(yclr + yclrdir>120){
                               xclrdir = DIRECTION_RIGHT;
                               yclrdir = DIRECTION_NONE;
                           }
                           if(x + xdir>145){
                               xdir = DIRECTION_NONE;
                               ydir = DIRECTION_UP;
                           }
                           if(xclr + xclrdir>145){
                               xclrdir = DIRECTION_NONE;
                               yclrdir = DIRECTION_UP;
                           }
                           x += xdir;
                           y += ydir;
                           xclr += xclrdir;
                           yclr += yclrdir;
                           if(yclr < 20 && yclrdir == DIRECTION_UP)
                               done = 1;
                       }
                       break;
               }
           }
           /* Maintains gesture state
            * Get gesture based of initial and final points
            * If the gesture is not a valid traffic flow, determine if it was a "button" press
            * In case of button press, do action of the button
            */
           static PT_THREAD (protothread_gesture(struct pt *pt))
           {
               PT_BEGIN(pt);
                 while(1) {
                   // yield time 1 second
                   PT_YIELD_TIME_msec(32) ;
                   if(DRAG_STATE==STATE_DONE){
                       //Obtain a gesture based of the initial and final point
                       int gestureVal = getGesture(initial_point, final_point);
                       //Gesture 
                       if (gestureVal == GESTURE_ILLEGAL) {
                           //Undo Button
                           if ((initial_point.X > 506 && initial_point.Y > 520) && (final_point.X > 506 && final_point.Y>520)){
                               if ((gInd-1)%1024 > =0) {
                                   gInd=(gInd-1)%1024;
                                   #ifdef DEBUG_GESTURE
                                   sprintf(buffer,"Removing Gesture=%d", gestures[gInd]);
                                   printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW);
                                   #endif
                                   gestures[gInd] = 16;
                               } else {
                                   sprintf(buffer,"No gestures found");
                                   printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW);
                               }
                           } 
                           //Play back all gestures in storage
                           else if ((initial_point.X < 413 && initial_point.Y > 520) && (final_point.X < 413 && final_point.Y>520)){
                               if (gInd>0) {
                                   static int gi;
                                   for (gi = 0; gi < gInd; gi++){
                                       sprintf(buffer,"Playing Gesture=%d", gestures[gi]);
                                       printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW);
                                       drawGesture = gestures[gi];
                                       gesture_draw();
                                       PT_YIELD_TIME_msec(250) ;
                                   }
                               } else {
                                   sprintf(buffer,"No gestures found");
                                   printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW);
                               }
                           }
                           //Clear Gestures in storage
                           else if ((initial_point.X > 506 && initial_point.Y < 400) && (final_point.X>506 && final_point.Y < 400)){
                               gInd= 0;
                               sprintf(buffer,"Clearing Gestures");
                               printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW);
                           }
                           //Store from SD Card
                           else if ((initial_point.X < 413 && initial_point.Y < 330) && (final_point.X<413 && final_point.Y<330)){
           #ifdef DEBUG_GESTURE
                               sprintf(buffer,"Storing to SD Card");
                               printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW);
           #endif
                               //Either it creates or overwrites inter.txt if it already exists
                               pointer = FSfopen("inter.txt", FS_WRITE);
                               if(gInd < = 0){
                                   FSfprintf(pointer,"0000");
                               }
                               else{
                                   int ii;
                                   if(gInd < 10)
                                       FSfprintf(pointer, "000%i", gInd);
                                   else if(gInd < 100)
                                       FSfprintf(pointer, "00%i", gInd);
                                   //FSfprintf(pointer, "%i\n", gInd);
                                   else if(gInd < 1000)
                                       FSfprintf(pointer, "0%i", gInd);
                                   else 
                                       FSfprintf(pointer, "%i", gInd);
                               
                                   for(ii = 0; ii < gInd; ii++){
                                       if(gestures[ii] > 9)
                                           FSfprintf(pointer, "%i", gestures[ii]);
                                       else
                                           FSfprintf(pointer, "0%i", gestures[ii]);
                                   }
                               }
                               FSfclose(pointer);
                           }
                           //Load from SD Card
                           else if ((initial_point.X < 413 && initial_point.Y < 400) && (final_point.X<413 && final_point.Y<400)){
                               sprintf(buffer,"Loading to SD Card");
                               printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW);
                               pointer = FSfopen("inter.txt", FS_READ);
                               if(pointer != NULL){
                                   char gindin[2];
                                   int gind;
           
                                   FSfread(gindin, 2, 2, pointer);
                                   sscanf(gindin, "%d", &gind);
           #ifdef DEBUG_SD_LOAD
                                   sprintf(buffer, "stored gind:%d", gind);//gind);
                                   printLine2(7, buffer, ILI9341_BLACK, ILI9341_YELLOW);
                                   sprintf(buffer, gindin);
                                   printLine2(8, buffer, ILI9341_BLACK, ILI9341_YELLOW);
           #endif
                                   int ii;
                                   for(ii = 0; ii < gInd; ii++){
                                       gestures[ii] = 16;
                                   }
                                   gInd = gind;
                                   char junk[1];
                                   char ges_raw[1];
                                   int ges;
                                   for(ii = 0; ii < gInd; ii++){
                                       FSfread(ges_raw, 2, 1, pointer);
                                       sscanf(ges_raw, "%d", &ges);
                                       gestures[ii]=ges;
           #ifdef DEBUG_SD_LOAD
                                   sprintf(buffer, "%d", ges);//gind);
                                   printLine2(ii, buffer, ILI9341_BLACK, ILI9341_YELLOW);
           #endif
                                   }
                                   
                               }
                               FSfclose(pointer);
                           }
                       }
                       else {
                           
                           #ifdef DEBUG_GESTURE
                       
                           sprintf(buffer,"Gesture=%d", gestureVal);
                           printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW);
                           #endif
           
                       }
                       DRAG_STATE = STATE_WAITING;
                   }
                   // NEVER exit while
                 } // END WHILE(1)
             PT_END(pt);
           } 
           /*Thread to read touch points
            */
           static PT_THREAD (protothread_touch(struct pt *pt))
           {
               PT_BEGIN(pt);
           
                 while(1) {
                   PT_YIELD_TIME_msec(32) ;
                   touch_reset();
                   static int last_valid_touch = 0;
                   //Waiting for touch input to be processed as gesture
                   while(DRAG_STATE == STATE_DONE){
                       PT_YIELD_TIME_msec(5);
                   }
                   do{ // until touch is registered and holds a valid point
                       PT_YIELD_TIME_msec(5);
                       touch_getPoint();
                       //Debounce purposes, verify that there is no longer a touch
                       if(DRAG_STATE == STATE_TOUCHING && last_valid_touch>5){
                           if(touch_point.DONE == 0 || touch_point.Z == 0){ 
                               //touch point still being detected or touch not registered
                               DRAG_STATE = STATE_DONE;
                               final_point = last_point;
           #ifdef DEBUG_TOUCH
                               sprintf(buffer,"Final x=%d, y =%d", final_point.X, final_point.Y);
                               printLine2(3, buffer, ILI9341_BLACK, ILI9341_YELLOW);
           #endif
                           }
                       }
                       
                       last_valid_touch+=1;
           #ifdef DEBUG_TOUCH
                       sprintf(buffer,"Last valid touch: %d", last_valid_touch);
                       printLine2(6, buffer, ILI9341_BLACK, ILI9341_YELLOW);
           #endif
                   }while(touch_point.DONE == 0 || touch_point.Z == 0);
                   last_point = touch_point;
                   //Began touching screen, save initial point, change state
                   if(DRAG_STATE == STATE_WAITING){
                       DRAG_STATE = STATE_TOUCHING;
                       initial_point = touch_point;
                   }
                   last_valid_touch = 0;
           #ifdef DEBUG_TOUCH
                   sprintf(buffer,"x=%d, y =%d, z =%d", touch_point.X, touch_point.Y, touch_point.Z);
                   printLine2(1, buffer, ILI9341_BLACK, ILI9341_YELLOW);
                   sprintf(buffer,"Start x=%d, y =%d", initial_point.X, initial_point.Y);
                   printLine2(2, buffer, ILI9341_BLACK, ILI9341_YELLOW);
                   
                   sprintf(buffer,"Last x=%d, y =%d", (last_point).X, (last_point).Y);
                   printLine2(4, buffer, ILI9341_BLACK, ILI9341_YELLOW);
           #endif
                   // NEVER exit while
                 } // END WHILE(1)
             PT_END(pt);
           } // timer thread
           
           int main(int argc, char** argv) {
               //SYSTEMConfigPerformance(PBCLK);
           
               ANSELA = 0; ANSELB = 0; 
               // === setup system wide interrupts  ========
               INTEnableSystemMultiVectoredInt();
           
               // === TFT setup ============================
               // init the display in main since more than one thread uses it.
               // NOTE that this init assumes SPI channel 1 connections
               tft_init_hw();
               tft_begin();
               tft_fillScreen(ILI9341_BLACK);
               //240x320 vertical display
               tft_setRotation(0); // Use tft_setRotation(1) for 320x240
               touch_init();
               touch_reset();
               last_point = touch_point;
               DRAG_STATE = STATE_WAITING;
               
               // === config threads ==========
               // turns OFF UART support and debugger pin, unless defines are set
               PT_setup();
               
               sprintf(buffer,"Booting up");
               printLine2(0, buffer, ILI9341_BLACK, ILI9341_YELLOW);
               //SD Card Init
               while (!MDD_MediaDetect());
           #ifdef DEBUG_FSINIT
               sprintf(buffer,"Starting FS Init");
               printLine2(1, buffer, ILI9341_BLACK, ILI9341_YELLOW);
           #endif
               while (!FSInit());
           #ifdef DEBUG_FSINIT
               sprintf(buffer,"FS init: success");
               printLine2(2, buffer, ILI9341_BLACK, ILI9341_YELLOW);
           #endif
               tft_fillScreen(ILI9341_BLACK);
               drawIntersection();
               while (1){
                   PT_SCHEDULE(protothread_gesture(&pt_gesture));
                   PT_SCHEDULE(protothread_touch(&pt_touch));
               }
           }

Schematics

Source: Traffic Light Intersection Simulator

About The Author

Muhammad Bilal

I am a highly skilled and motivated individual with a Master's degree in Computer Science. I have extensive experience in technical writing and a deep understanding of SEO practices.