mbedica Graphing Calculator

Purpose & Description

The purpose of this project is to create a Mathematica-type calculator on the mbed.
This project is meant to take keyboard input for mathematical functions, display the input on a VGA screen, parse the input, evaluate the answer, and graph and print the result. Presently, this program does not parse inputs and thus does not return the output. The LEDs on the mbed will light up as follows:

  • LED1: blinks on and off when the program is running.
  • LED2: is on when the keyboard is connected (if it is off, disconnect and reconnect the keyboard).
  • LED3: is on when processing keyboard input. I will also rapidly blink during bitmap processing.
  • LED4: cycles when drawing on the VGA.

Function Parser

This originally used Bison and Flex(both tools to make compilers). Flex lexes (tags) tokens such as +,-,digits, and letters. Bison uses rules to write a tree which sits on a doubly linked list. It uses reverse post-order traversal: right sibling, left sibling, parent. Upon reaching a parent node, a function is evaluated and the parent node is replaced with that value. Bison needs Unix structures and would not fully run on the MBED, so the lexer and parser had to be re-written by hand.

VGA

On the left side of the screen, it will display the user input and result of the input. The screen is then divided by a red line, then the right side will display a graph on the function. Since VGA is slow, this graph will take time to complete. Also due to the speed of the display, the input characters are not displayed as they are typed, but rather once the user hits the enter key.

Thermal Printer

The printer prints every line that is input by keyboard, and can also print graphs that are displayed on the VGA display or the uLCD. Reading from the uLCD is much faster than the VGA, so we print each graph on both the VGA display and the uLCD, and read from the uLCD to print. To print, we read each pixel from the uLCD and store the result (0 for background, 1 for anything else) in bitmap format that the printer is able to read.

Parts List

Pinout

mbeduVGAThermal PrinterUSB KeyboarduLCDExternal 5V
GNDGNDGNDGNDGNDGND
--VH5V5V5V
Vu5V----
p9RX----
p10TX----
p11Reset----
p12---Reset-
p13---RX-
p14---TX-
D+--D+--
D---D---
p27-TX---
p28-RX---

Hardware Diagram

/media/uploads/sbongiovi3/hardwarediagram.png

USB Female Pinout

/media/uploads/sbongiovi3/usbtombed.jpeg

uVGA Pinout

/media/uploads/sbongiovi3/uvgatombed.jpeg

Thermal Printer Pinout

/media/uploads/sbongiovi3/thermalprintertombed.jpeg

Results

/media/uploads/sbongiovi3/groupme_20181213_3135.jpeg /media/uploads/sbongiovi3/groupme_20181213_3835.jpeg /media/uploads/sbongiovi3/groupme_20181213_11327.jpeg

Future Work

  • Implement function parsing
  • Balance thread times for increased performance
  • Optimize bitmap processing time
  • Update libraries for compatibility with newer RTOS

Program

Information

These are not the most up to date libraries; they are from 2013. They do not use modern rtos, and the libraries had to be modified for compatibility.

Information

The uLCD library may not correctly import with the rest of the code, so that maybe need to be imported manually:

Import programmbedica

Mathematica-like environment on the mbed using USB keyboard input, VGA output, and a thermal printer.

main.cpp

#include "mbed.h"
#include "Thermal.h"
#include "uLCD_4DGL.h"
#include "USBHostKeyboard.h"
#include "uVGAIII.h"

extern "C" struct node* scanner(const char* es);
extern "C" float eval_expr(struct node* node, float x_value);

#include <math.h>

#define SIZE_X       480
#define SIZE_Y       800

// Debugging LEDs
DigitalOut led(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);

uVGAIII ecran(p9,p10,p11); // serial tx, serial rx, reset pin;
uLCD_4DGL uLCD(p13,p14,p12); // serial tx, serial rx, reset pin;
Thermal printer(p28, p27, 19200);

// Variables to track cursor location on VGA
int verticalCursor = 2;
int horizontalCursor = 0;

// Variables to hold user input & index
char currentLine[48];
int currentLineIndex = 0;

// Variables to track graph bitmaps
unsigned char bitmap[5000];
char temp[8];

// Mutexes for displays
Mutex VGA_Lock;
Mutex LCD_Lock;

char getPixel(int x, int y, int bk) {
    LCD_Lock.lock();
    int r = uLCD.read_pixel(x, y);
    LCD_Lock.unlock();
    
    if (r == bk) //same as background
        return '0';
    else
        return '1';
}

void makeBitmap() {
    LCD_Lock.lock();
    int bk = uLCD.read_pixel(127,127);
    LCD_Lock.unlock();
    for (int i = 0; i < 128*128/8; ++i) {
        for (int j = 0; j < 8; ++j) { //get next 8 bits and put them in temp array
            //temp[2*j] = getPixel((i%32)*4+j,i/32,bk);
            //temp[2*j+1] = temp[2*j];
            temp[j] = getPixel((i%16)*8+j,i/16,bk);
        }
       
        //need to convert to 0b format
        char * end;
        long int value = strtol(temp, &end, 2);
        //bitmap[64*(i/32)+(i%32)] = value;
        //bitmap[64*(i/32)+(i%32)+32] = value;
        bitmap[i] = value;
        led3 = !led3;
    }
    //printer.printf(bitmap);
    //printer.justify('C');
    printer.printBitmap(128,128,bitmap);
    printer.feed(2);
}


// hardcode plot x^2
void plotx2()
{
    LCD_Lock.lock();
    uLCD.cls();
    int xOffset = 0;
    int yOffset = 127;
    int pastX = 0;
    int pastY = 127;
    int currentX = 0;
    int currentY = 0;
    
    for(double i = 0; i < 11; i++) {
        //evaluate();
        currentX = i*10 + xOffset;
        currentY = yOffset-(i*i);
        if(pastX == 0){
            pastX = currentX;
            pastY = currentY;
            continue;
        }
        uLCD.line(pastX, pastY, currentX, currentY, RED);
        uLCD.line(pastX, pastY+1, currentX, currentY+1, RED);
        uLCD.line(pastX, pastY-1, currentX, currentY-1, RED);
        pastX = currentX;
        pastY = currentY;
    }
    LCD_Lock.unlock();
}

void plotsin()
{
    LCD_Lock.lock();
    uLCD.cls();
    int xOffset = 0;
    int yOffset = 64;
    int pastX = 0;
    int pastY = 64;
    int currentX = 0;
    int currentY = 0;
    
    for(double i = 0; i < 7; i+=.01) {
        //evaluate();
        currentX = i*16 + xOffset;
        currentY = yOffset-40*sin(i);
        if(pastX == 0){
            pastX = currentX;
            pastY = currentY;
            continue;
        }
        uLCD.line(pastX, pastY, currentX, currentY, RED);
        uLCD.line(pastX, pastY+1, currentX, currentY+1, RED);
        uLCD.line(pastX, pastY-1, currentX, currentY-1, RED);
        pastX = currentX;
        pastY = currentY;
    }
    LCD_Lock.unlock();
}


void plotx2vga(void const *)
{
    int xOffset = 600;
    int yOffset = 401;
    int pastX = 0;
    int pastY = 0;
    int currentX = 0;
    int currentY = 0;
    
    VGA_Lock.lock();
    
    for(double i = -20; i < 21; i++) {
        led4 = !led4;
        //evaluate(); // Where evaluate would go...if we could parse input        
        currentX = i*9 + xOffset;
        currentY = yOffset-(i*i);
        if(pastX == 0){
            pastX = currentX;
            pastY = currentY;
            continue;
        }
        ecran.line(pastX, pastY, currentX, currentY, RED);
        pastX = currentX;
        pastY = currentY;
    }
    
    VGA_Lock.unlock();
    
    while(1) {
        Thread::wait(500);
    }
    
}


void onKeyCode(uint8_t key, uint8_t modifier) {
    
    led3 = 1;
    
    // Skip spaces, empty spaces
    if(key == 0 || key == 0x20) {
        led3 = 0;
        return;
    }
    
    VGA_Lock.lock();
    
    // Handle newline
    if(key == 0x0A) {
        
        // Check if going off end of screen
        if(verticalCursor > 37) {
            ecran.filled_rectangle(0, 0 , 399, 480, DGREY);
            verticalCursor = 0;
        } else {
            // Move cursor to newline
            verticalCursor+=2;
        }
        
        horizontalCursor = 0;
        
        ecran.move_cursor(verticalCursor, horizontalCursor);
         
        ecran.puts(currentLine);  
        
        printer.printf(currentLine);
        printer.feed(2);
        
        struct node* expr = scanner(currentLine);
        float val = eval_expr(expr, 3.0);
        uLCD.printf("Evaluate: %f \n",val);
        
        for(size_t i = 0; i < 48; i++) {
            currentLine[i] = NULL;
        }
        
        currentLineIndex = 0;
        
        VGA_Lock.unlock();
        
        led3 = 0;
        return;
    }
    
    if(key == 0x08)
    {
        if(currentLineIndex != 0)
        {
            currentLineIndex--;
            currentLine[currentLineIndex] = NULL;
        }
        led3 = 0;
        
        VGA_Lock.unlock();
        
        return;
    }
    
    // Append character to curret line string
    currentLine[currentLineIndex] = (char)key;
    
    if(currentLineIndex < 47) {
        currentLineIndex++;
    }
    
    VGA_Lock.unlock();
    
    led3 = 0;
}

void keyboard_task(void const *) {
    
    USBHostKeyboard keyboard;
    
    while(1) {
        // try to connect a USB keyboard
        while(!keyboard.connect()) {
            Thread::wait(200);
        } 
        
        if(keyboard.connected()) led2 = 1;
        
        // When connected, attach handler called on keyboard event
        keyboard.attach(onKeyCode);
        
        // Wait until the keyboard is disconnected
        while(keyboard.connected())
            Thread::wait(500); 
            
        led2 = 0;
    }
}

int main() {  
    led = 1; 
    
    // Set up uLCD
    uLCD.baudrate(300000);
    uLCD.background_color(BLACK);
    
    // Set up VGA display
    ecran.baudrate(300000);
    ecran.screen_mode(LANDSCAPE);
    ecran.graphics_parameters(RESOLUTION, 2); 
    ecran.touch_status();
    ecran.background_color(DGREY);
    ecran.cls();

    ecran.move_cursor(0, 0);
    ecran.char_width('d');
    ecran.char_height('d');
    ecran.text_fgd_color(WHITE);
    ecran.text_bgd_color(DGREY);
    ecran.puts("Booting up...");   
    
    ecran.filled_rectangle(398,0,402,480,RED);
        
    // Use old style threading
    Thread keyboardTask(keyboard_task, NULL, osPriorityNormal, 256 * 4);   
    
    // Launch VGA plot through thread
    Thread vgaPlot(plotx2vga, NULL, osPriorityNormal, 256 * 4);

    plotx2();
    makeBitmap();
    led3 = 1;
    plotsin();
    makeBitmap();
    led3 = 1;
    
    // Clear currentLine array before using
    for(size_t i = 0; i < 48; i++) {
            currentLine[i] = NULL;
    }
    
    VGA_Lock.lock();
    ecran.move_cursor(2, 0);
    ecran.puts("Ready!");
    VGA_Lock.unlock();
    
    static const char es[] = "1 + 2 ^ 3 * 4 + 5 * x";
    struct node* expr = scanner(es);
    float val = eval_expr(expr, 3.0);
    
    while(1) {
        led=!led;
        Thread::wait(600); // Wait .6s in main thread
    }
}

Team Members

Spencer Bongiovi, Zachary Russell, Rodrigo Alarcon, Yehowshua Immanuel


Please log in to post comments.