Mbed - elizhyu/smart-cube-prototype GitHub Wiki

The code can be downloaded in zip file(2020.11.28).

Header Codes

Libraries and Definitions

Include coding and IO devices libraries as well as define necessary constant(s).

// Mbed device basis library
#include "mbed.h"
// Mbed official rtos library
#include "rtos.h"
// uLCD-144-G2 Library
#include "uLCD_4DGL.h"
// LSM9DS1 IMU Library
#include "LSM9DS1.h"
// π defined for pose calculation
#define PI 3.14159

IO Declaration

Declare ports for IO devices.

// ========================================================== 
// =========== Control board devices declaration ============
// ==========================================================
// USB UART interface
Serial pc(USBTX, USBRX);
// UART interface to ESP32 board
Serial ESP(p9, p10); // TX, RX
// I2C interface to LSM9DS1 pose sensor
LSM9DS1 IMU(p28, p27, 0xD6, 0x3C); // SDA, SCL

// ========================================================== 
// ============ FRONT panel devices declaration =============
// ==========================================================
// uLCD screen
uLCD_4DGL uLCD(p13,p14,p8); // TX, RX, RESET;
// RGB Notification LED
DigitalOut LED_R(p7); // red
DigitalOut LED_G(p5); // green
DigitalOut LED_B(p6); // blue

// ========================================================== 
// ========== RIGHT panel IO devices declaration ============
// ==========================================================
// Green status LEDs
DigitalOut LED_TL(p21); // top-left
DigitalOut LED_TR(p23); // top-right
DigitalOut LED_BL(p25); // bottom-left
DigitalOut LED_BR(p26); // bottom-right
// Pushbuttons
DigitalIn PB_TL(p22); // top-left
DigitalIn PB_TR(p24); // top-right
DigitalIn PB_BL(p29); // bottom-left
DigitalIn PB_BR(p30); // bottom-right

Global Variables Declaration

Declare virtual Mutex to resolve RTOS multi-threading potential conflicts, and global variables used across threads.

// ========================================================== 
// ============ RTOS multi-thread mutex locks ===============
// ==========================================================
Mutex usage_LCD;
Mutex usage_UART;

// ========================================================== 
// ============ Global variables declaration ================
// ==========================================================
// local variables storing pulled SNTP time  
int prev_min;
int min;
int year;
int month;
int date;
char day[3];
// status indication variables for shake and orientation
bool shake;
int orientation;
// notification flag
bool new_notification;

Bootup Initiation Operations (main function before while loop)

This part of the code initialize essential IO devices, including:

  • initialize the UART interface
  • startup and clear LCD screen
  • reset all lights
  • startup and calibrate IMU

Also, global variables are given initial values in this section, while RTOS threads are declared and started.

    uLCD.cls();
    // set up UART interface BAUD rate to ESP32 board
    ESP.baud(9600);
    // debug only
    LED_R = 0;
    LED_G = 0;
    LED_B = 0;
    // LSM9DS1 pose sensor startup and calibration
    IMU.begin();
    Thread::wait(1000);
    IMU.calibrate(1);
    
    // preset values for global variables
    // pose and vibration status indicator variables
    orientation = 2;
    shake = false;
    // new notification flag
    new_notification = false;
    
    // RTOS multi-thread initialization
    //Thread thread1(time_update);  // obselete, dubug only
    Thread thread2(pose_update);    // pose and vibration detection
    Thread thread3(io_function);    // IO interaction
    
    char read_char;
    char msg_head[3];
    msg_head[2] = '\0';
    char msg[10];
    int index = 0;

RTOS Threads

Pose & Vibration Processing Thread

The thread reads in gyroscope and accelerometer values from the LSM9DS1 IMU, then calculates pose and pitch, and eventually update global variables for detected orientation change and vibration, executing at around 1 time per second.

void pose_update(void const *argument)
{
    float ax;
    float ay;
    float az;
    float gx;
    float gy;
    float gz;
    float roll;
    float pitch;
    bool shake_count;
    int orientation_count;  // -1 for LEFT, 1 for RIGHT, 0 for UP
    while(1)
    {
        // debug only
        //myled2 = 1;
        
        // Read from sensor
        while(!IMU.accelAvailable());
        IMU.readAccel();
        while(!IMU.gyroAvailable());
        IMU.readGyro();
        
        // Calculate
        ax = IMU.calcAccel(IMU.ax);
        ay = IMU.calcAccel(IMU.ay);
        az = IMU.calcAccel(IMU.az);
        gx = IMU.calcGyro(IMU.gx);
        gy = IMU.calcGyro(IMU.gy);
        gz = IMU.calcGyro(IMU.gz);
        
        // Calculate roll and pitch
        roll = atan2(ay, az);
        pitch = atan2(-ax, sqrt(ay * ay + az * az));
        
        // Convert from radians to degrees
        pitch *= 180.0 / PI;
        roll  *= 180.0 / PI;
        
        // Shake Detection
        if(abs(gx) > 10 || abs(gy) > 10 || abs(gz) > 10)
        {
            if(shake_count && !shake)
            {
                shake = true;
                usage_UART.lock();
                ESP.printf("\r*VI=Y#\r");
                usage_UART.unlock();
                if(new_notification)
                {
                    new_notification = false;
                    LED_R = 0;
                    LED_G = 0;
                    LED_B = 0;
                    usage_UART.lock();
                    ESP.printf("\r*NOT DIS#\r");
                    usage_UART.unlock();
                }
            }
            else    shake_count = true;
        }
        else
        {
            shake_count = false;
            if(shake)
            {
                shake = false;
                usage_UART.lock();
                ESP.printf("\r*VI=N#\r");
                usage_UART.unlock();
            }            
        }
        
        // Orientation Detection
        if(pitch > -120 && pitch < -60)
        {
            if(orientation_count == -1 && orientation != -1)
            {
                orientation = -1;
                usage_UART.lock();
                ESP.printf("\r*ORI=L#\r");
                usage_UART.unlock();
            }
            else    orientation_count = -1;
        }
        else if(pitch > 60 && pitch < 120)
        {
            if(orientation_count == 1 && orientation != 1)
            {
                orientation = 1;
                usage_UART.lock();
                ESP.printf("\r*ORI=R#\r");
                usage_UART.unlock();
            }
            else    orientation_count = 1;
        }
        else
        {
            if(orientation_count == 0 && orientation != 0)
            {
                orientation = 0;
                usage_UART.lock();
                ESP.printf("\r*ORI=U#\r");
                usage_UART.unlock();
            }
            else    orientation_count = 0;
        }
        
        // Thread execution frequency
        Thread::wait(1000);
    }
}

IO Interaction Thread

The thread reacts differently at different orientations. At orientation with left panel facing up, the thread asks for smart home device status through UART and toggle devices once corresponding push buttons being pressed, executing at around 1 time per second.

void io_function(void const *argument)
{
    while(1)   
    {
        // pose activate LEFT panel
        if(orientation == -1)
        {
            usage_UART.lock();
            ESP.printf("\r*DE=?#\r");
            usage_UART.unlock();
            while(orientation == -1)
            {
                if(PB_TL)
                {
                    LED_TL = !LED_TL;
                    usage_UART.lock();
                    if(LED_TL)  ESP.printf("\r*L1=1#\r");
                    else    ESP.printf("\r*L1=0#\r");
                    usage_UART.unlock();
                }
                else if(PB_TR)
                {
                    LED_TR = !LED_TR;
                    usage_UART.lock();
                    if(LED_TR)  ESP.printf("\r*L2=1#\r");
                    else    ESP.printf("\r*L2=0#\r");
                    usage_UART.unlock();
                }
                else if(PB_BL)
                {
                    LED_BL = !LED_BL;
                    usage_UART.lock();
                    if(LED_BL)  ESP.printf("\r*L3=1#\r");
                    else    ESP.printf("\r*L3=0#\r");
                    usage_UART.unlock();
                }
                else if(PB_BR)
                {
                    LED_BR = !LED_BR;
                    usage_UART.lock();
                    if(LED_BR)  ESP.printf("\r*L4=1#\r");
                    else    ESP.printf("\r*L4=0#\r");
                    usage_UART.unlock();
                }
            }
        }
        // pose activate RIGHT panel
        else if(orientation == 1)   
        {
            
        }
        // deactivate LEFT and RIGHT panels
        else    
        {
            LED_TL = 0;
            LED_TR = 0;
            LED_BL = 0;
            LED_BR = 0;
        }
        
        // Thread execution frequency
        Thread::wait(1000);
    }
}

UART Thread

The main function while loop is one of the two components of the thread, performing as the receiver of UART. UART data are read in byte by byte, being decoded and stored into two separate char arrays/buffers of command and payload for further operations. This thread is executed at around 2 times per second.

while(1)
{
    // lock UART mutex
    usage_UART.lock();
        
    // new UART data received
    if(ESP.readable())
    {
        // read and store received byte
        read_char = ESP.getc();
            
        // process received byte
        switch(read_char)
        {
            // start of message
            // clear buffer
            case '*':
                index = 0;
                break;
            // mid-point of message
            // store command from buffer
            case '=':
                index = 0;
                msg_head[0] = msg[0];
                msg_head[1] = msg[1];
                break;
            // end of message
            // store payload from buffer
            case '\r':
                msg[index] = '\0';
                index = 0;
                execute(msg_head, msg);
                break;
            // middle of command or payload
            // proceed buffer index
            default:
                msg[index] = read_char;
                index++;
                break;
        }
    }
    
    // unlock UART mutex
    usage_UART.unlock();
    
    // Thread execution frequency
    Thread::wait(500); 
}

The self-defined UART command execution function is the second component of the thread. Commands previously decoded and stored are executed and information in payload are extracted and stored.

void execute(char *head, char *load)
{
    // Transcode and store pulled SNTP time to local variables
    // and update LCD screen time display
    if(strcmp(head, "ti") == 0)
    {
        // store previous minute value
        prev_min = min;
        // transcode minute value from (char) to (int)
        min = (load[2] - 0x30) * 10 + (load[3] - 0x30);
        // update LCD time display only if next minute
        if(min != prev_min)
        {
            // lock LCD mutex
            usage_LCD.lock();
            // refresh time and date display
            uLCD.cls();
            uLCD.locate(4, 5);
            uLCD.printf("%04d-%02d-%02d", year, month, date);
            uLCD.locate(8, 7);
            uLCD.printf("%c%c%c", day[0], day[1], day[2]);
            uLCD.locate(6, 10);
            uLCD.printf("%c%c : %c%c", load[0], load[1], load[2], load[3]);
            // unlock LCD mutex
            usage_LCD.unlock();
        }
    }
    // Transcode and store pulled SNTP date to local variables
    else if(strcmp(head, "da") == 0)
    {
        // transcode date from (char) to (int)
        year = (((load[0] - 0x30) * 10 + (load[1] - 0x30)) * 10 + (load[2] - 0x30)) * 10 + (load[3] - 0x30);
        month = (load[4] - 0x30) * 10 + (load[5] - 0x30);
        date = (load[6] - 0x30) * 10 + (load[7] - 0x30);
        // transcode day of week from (int) to (char)
        switch(load[8])
        {
            case '0':
                day[0] = 'S';
                day[1] = 'U';
                day[2] = 'N';
                break;
            case '1':
                day[0] = 'M';
                day[1] = 'O';
                day[2] = 'N';
                break;
            case '2':
                day[0] = 'T';
                day[1] = 'U';
                day[2] = 'E';
                break;    
            case '3':
                day[0] = 'W';
                day[1] = 'E';
                day[2] = 'D';
                break;
            case '4':
                day[0] = 'T';
                day[1] = 'H';
                day[2] = 'U';
                break;
            case '5':
                day[0] = 'F';
                day[1] = 'R';
                day[2] = 'I';
                break;
            case '6':
                day[0] = 'S';
                day[1] = 'A';
                day[2] = 'T';
                break;
        }
    }
    // new notification received
    else if(strcmp(head, "no") == 0)
    {
        // raise new notification flag
        new_notification = true;
        
        // process notification color
        switch(load[0])
        {
            case 'r':   // red
                LED_R = 1;
                LED_G = 0;
                LED_B = 0;
                break;
            case 'g':   // green
                LED_R = 0;
                LED_G = 1;
                LED_B = 0;
                break;
            case 'b':   // blue
                LED_R = 0;
                LED_G = 0;
                LED_B = 1;
                break;    
            case 'p':   // purple
                LED_R = 1;
                LED_G = 0;
                LED_B = 1;
                break;
        }
    }
    // device status pulled
    else if(strcmp(head, "de") == 0)
    {
        ESP.printf("\r*DE GOT#\r");
        // update device indicator LEDs
        LED_TL = load[0] - 0x30;
        LED_TR = load[1] - 0x30;
        LED_BL = load[2] - 0x30;
        LED_BR = load[3] - 0x30;
    }
}