MP3-capable chair with sensor-embedded weight scale.

Dependencies:   HysteresisIn LCD SDFileSystem VS1053 mbed

main.cpp

Committer:
kayekss
Date:
2013-12-20
Revision:
0:0451ba2f1062

File content as of revision 0:0451ba2f1062:

#include <stdio.h>
#include "mbed.h"
#include "defs.h"
#include "pinalias.h"
#include "HysteresisIn.h"
#include "SDFileSystem.h"
#include "VS1053.h"
#include "LCD.h"

SDFileSystem sd(/*MOSI*/ D11, /*MISO*/ D12, /*SCK*/ D13, /*CS*/ D10, "sd");
VS1053       mp3(/*MOSI*/ D11, /*MISO*/ D12, /*SCK*/ D13,
                 /*CS*/ D9, /*BSYNC*/ D8, /*DREQ*/ D7, /*RST*/ D6, /*SPI freq.*/ 6000000);
HysteresisIn sens3(A3, HIGH_TO_LOW, LOW_TO_HIGH, 1);  // Photo sensor #3 (Outer)
HysteresisIn sens2(A2, HIGH_TO_LOW, LOW_TO_HIGH, 1);  // Photo sensor #2
HysteresisIn sens1(A1, HIGH_TO_LOW, LOW_TO_HIGH, 1);  // Photo sensor #1
HysteresisIn sens0(A0, HIGH_TO_LOW, LOW_TO_HIGH, 1);  // Photo sensor #0 (Inner)
LCD          lcd(/*SDA*/ D107, /*SCL*/ D106, /*RESET*/ D105, /*Backlight*/ NC,
                 /*Contrast*/ 40);
BusOut       led(/*Red*/ P3, /*Yellow*/ P4, /*Green*/ P5, /*Blue*/ P6, /*White*/ P7);
Ticker       tic;

const char *fileNameList[] = {
    "",
    "/sd/Track1.mp3",
    "/sd/Track2.mp3",
    "/sd/Track3.mp3",
    "/sd/Track4.mp3"
};

volatile State   state = READY;
volatile Request request = NONE;

/** Setups and initializations */
void setup(void) {
    // Initialize VS1053
    mp3.hardwareReset();
    mp3.clockUp();
    wait(0.1);
    
    // Setup LCD
    lcd.reset();
    lcd.cls();
    
    // Initialize LCD
    led = 0x00;
}

/** Read voltages from photo sensor pins and detect weightscale point code */
int readPhotoSensors(void) {
    uint8_t bitPattern;
    
    // Read all photo sensor inputs
    bitPattern =   sens3.read() << 3
                 | sens2.read() << 2
                 | sens1.read() << 1
                 | sens0.read();
    
    switch (bitPattern) {
    // 1 when open, 0 when shut
    // Higher bit is on outer side
    case 0xf:      // 1111: no load
        return 0;
    case 0xe:      // 1110: slight load
        return 1;
    case 0xc:      // 1100
        return 2;
    case 0x8:      // 1000
        return 3;
    case 0x0:      // 0000: heavy load
        return 4;
    default:       // Other than expectation: erroneous pattern
        return -1;
    }
}

/** Poll/sample weightscale inputs and issue requests */
void pollAndRequest() {
    const char* stateNameList[] = {
        "STOPPING",   // -2
        "CANCELING",  // -1
        "READY",      //  0
        "PLAYING1",   //  1
        "PLAYING2",   //  2
        "PLAYING3",   //  3
        "PLAYING4"    //  4
    };
    const uint8_t ledPatternList[] = {
        0x10,  // -1  STOP_REQUEST   W----
        0x00,  //  0  NONE           -----
        0x01,  //  1  PLAY1_REQUEST  ----R
        0x02,  //  2  PLAY2_REQUEST  ---Y-
        0x04,  //  3  PLAY3_REQUEST  --G--
        0x08   //  4  PLAY4_REQUEST  -B---
    };
    int            code;
    static int     codePrev = 0;
    static uint8_t holdTimes = 0;
    
    // Get weightscale point code by reading photo sensors
    code = readPhotoSensors();
    
    // Count the times that the given code persists
    if (code == codePrev && code != -1) {
        if (holdTimes < 99) {
            holdTimes++;
        }
    } else {
        holdTimes = 0;
    }
    lcd.locate(0, 0);
    lcd.printf("%-15s", stateNameList[state + 2]);
    lcd.locate(0, 1);
    lcd.printf("ps=%2d times=%2d", code, holdTimes);
    
    // Once the point is stable enough, make a request
    if (holdTimes == SETTLING_COUNT) {
        if (code == 0) {
            // Stable no load: stop request
            request = STOP_REQUEST;
        } else {
            // Certain stable load: play request 1..4
            // Ignore cases while playing the same track
            if (state != code) {
                request = (Request) code;
            }
        }
    }
    
    // Set LED as the request
    led = ledPatternList[request + 1];
    
    // Preserve this time's code
    codePrev = code;
}

/** Player control in accordance with requests */
void controlTrack() {
    static FILE   *fp = NULL;
    size_t        sizeRead = 0;
    uint8_t       buf[BLOCK_SIZE];
    static size_t totalSizeSent = 0;
    
    switch (state) {
    case READY:
        switch (request) {
        case STOP_REQUEST:
            // Clear stop request
            request = NONE;
            break;
        case PLAY1_REQUEST: case PLAY2_REQUEST: case PLAY3_REQUEST: case PLAY4_REQUEST:
            fp = fopen(fileNameList[request], "rb");
            if (fp) {
                totalSizeSent = 0;
                state = (State) request;
                lcd.locate(15, 0);
                lcd.printf("*");
            }
            // Clear play request
            request = NONE;
            break;
        default:
            break;
        }
        break;
    case PLAYING1: case PLAYING2: case PLAYING3: case PLAYING4:
        if (request == NONE) {
            if (feof(fp)) {
                // Close the track
                fclose(fp);
                lcd.locate(15, 0);
                lcd.printf(" ");
                
                // Invoke play request again
                request = (Request) state;
                state = READY;
            } else {
                sizeRead = fread(buf, sizeof(char), BLOCK_SIZE, fp);
                totalSizeSent += mp3.sendDataBlock(buf, sizeRead);
            }
        } else {
            // Cancel current track when something's requested
            fclose(fp);
            lcd.locate(15, 0);
            lcd.printf(" ");
            state = CANCELING;
        }
        break;
    case CANCELING:
        if (mp3.sendCancel()) {
            state = STOPPING;
        }
        break;
    case STOPPING:
        if (mp3.stop()) {
            state = READY;
        }
        if (request == STOP_REQUEST) {
            // Clear stop request
            request = NONE;
        }
        break;
    default:
        break;
    }
}

/** Main routine */
int main(void) {
    FILE      *fpTest = NULL;
    time_t    secBuild;
    struct tm *tmBuild;
    char      sbuf[10];
    bool      sdTestResult = 0x00;
    
    setup();
    
    // Print build timestamp (in JST)
    secBuild = MBED_BUILD_TIMESTAMP + 9 * 60 * 60;
    tmBuild = localtime(&secBuild);
    
    strftime(sbuf, 10, "%Y-%m-%d", tmBuild);
    lcd.locate(0, 0);
    lcd.printf("Build %8s", sbuf);
    wait(0.0);
    strftime(sbuf, 10, "%H:%M:%S", tmBuild);
    lcd.locate(0, 1);
    lcd.printf("       T%8s", sbuf);
    wait(2.0);
    
    // SD card test
    lcd.cls();
    lcd.locate(0, 0);
    lcd.printf("No memory cards ");
    wait(0.0);
    lcd.locate(0, 0);
    lcd.printf("Track  / / /  Ok");
    for (uint8_t i = 1; i <= 4; i++) {
        fpTest = fopen(fileNameList[i], "rb");
        if (fpTest) {
            lcd.locate(2 * i + 4, 0);
            lcd.printf("%d", i);
            fclose(fpTest);
        } else {
            sdTestResult |= 0x01 << (i - 1);
        }
    }
    lcd.locate(0, 1);
    if (sdTestResult == 0x00) {
        lcd.printf("SD Test Pass    ");
    } else {
        lcd.printf("SD Test Fail    ");
        while (1) {
            led = sdTestResult;
            wait(0.5);
            led = 0x00;
            wait(0.5);
        }
    }
    wait(1.0);
    lcd.cls();
    
    // Set Ticker interrupt routine
    tic.attach(&pollAndRequest, POLL_INTERVAL_SEC);
    
    // Main loop
    while (1) {
        controlTrack();
    }
}