MP3-capable chair with sensor-embedded weight scale.

Dependencies:   ACM1602 SDFileSystem VS1053 mbed ClockControl PowerControl

main.cpp

Committer:
kayekss
Date:
2014-03-26
Revision:
0:d9789f57fd9d
Child:
1:ef257d63d970

File content as of revision 0:d9789f57fd9d:

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

// Pin settings for LPC1768(LPCXpresso)
SDFileSystem sd(/*MOSI*/ xp11, /*MISO*/ xp12, /*SCK*/ xp13, /*CS*/ xp53, /*Mountpoint*/ "sd");
VS1053       mp3(/*MOSI*/ xp11, /*MISO*/ xp12, /*SCK*/ xp13,
                 /*CS*/ xp52, /*BSYNC*/ xp51, /*DREQ*/ xp50, /*RST*/ xp49, /*SPI freq.*/ 6000000);
HysteresisIn sens3(p17, HIGH_TO_LOW, LOW_TO_HIGH, 1);  // Photo sensor #3 (Outer)
HysteresisIn sens2(p18, HIGH_TO_LOW, LOW_TO_HIGH, 1);  // Photo sensor #2
HysteresisIn sens1(p19, HIGH_TO_LOW, LOW_TO_HIGH, 1);  // Photo sensor #1
HysteresisIn sens0(p20, HIGH_TO_LOW, LOW_TO_HIGH, 1);  // Photo sensor #0 (Inner)
ACM1602      lcd(/*SDA*/ xp9, /*SCL*/ xp10, /*Address*/ 0xa0);
DigitalOut   led(LED0);
DigitalOut   audioPower(xp38);
Ticker       tic;

// Pin settings for LPC1114FN28 (SD card accessing does not work)
// SDFileSystem sd(/*MOSI*/ dp2, /*MISO*/ dp1, /*SCK*/ dp6, /*CS*/ dp4, /*Mountpoint*/ "sd");
// VS1053       mp3(/*MOSI*/ dp2, /*MISO*/ dp1, /*SCK*/ dp6,
//                  /*CS*/ dp14, /*BSYNC*/ dp17, /*DREQ*/ dp18, /*RST*/ dp25, /*SPI freq.*/ 4000000);
// HysteresisIn sens3(dp9,  HIGH_TO_LOW, LOW_TO_HIGH, 1);  // Photo sensor #3 (Outer)
// HysteresisIn sens2(dp10, HIGH_TO_LOW, LOW_TO_HIGH, 1);  // Photo sensor #2
// HysteresisIn sens1(dp11, HIGH_TO_LOW, LOW_TO_HIGH, 1);  // Photo sensor #1
// HysteresisIn sens0(dp13, HIGH_TO_LOW, LOW_TO_HIGH, 1);  // Photo sensor #0 (Inner)
// ACM1602      lcd(/*SDA*/ dp5, /*SCL*/ dp27, /*Address*/ 0xa0);
// 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;

/** Setup and initializations */
void setup(void) {
    // Initialize VS1053
    mp3.hardwareReset();
    mp3.clockUp();
    wait(0.1);
    
    // Initialize power supply for audio amplifier circuit
    audioPower = 0;
    
    // Setup LCD
    lcd.init();
    
    // Initialize LED
    led = 0;
}

/** 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
    };
    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;
            }
        }
    }
    
    // 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 - 1], "rb");
            if (fp) {
                // Power supply on
                audioPower = 1;
                led = 1;
                
                totalSizeSent = 0;
                state = (State) request;
                lcd.locate(15, 0);
                lcd.printf("*");
            } else {
                lcd.locate(0, 1);
                lcd.printf("File Open Error ");
            }
            // Clear play request
            request = NONE;
            break;
        default:
            break;
        }
        break;
    case PLAYING1: case PLAYING2: case PLAYING3: case PLAYING4:
        if (request == NONE) {
            sizeRead = fread(buf, sizeof(uint8_t), BLOCK_SIZE, fp);
            if (sizeRead) {
                totalSizeSent += mp3.sendDataBlock(buf, sizeRead);
            } else {
                // Close the track
                fclose(fp);
                lcd.locate(15, 0);
                lcd.printf(" ");
                
                // Invoke play request again
                request = (Request) state;
                state = READY;
            }
        } 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;
            
            // Power supply off
            audioPower = 0;
            led = 0;
        }
        break;
    default:
        break;
    }
}

/** Print build timestamp (in JST) */
void printTimestamp() {
    time_t    secBuild;
    struct tm *tmBuild;
    char      sbuf[11];
    
    secBuild = MBED_BUILD_TIMESTAMP + 9 * 60 * 60;
    tmBuild = localtime(&secBuild);
    
    strftime(sbuf, 11, "%Y-%m-%d", tmBuild);
    lcd.locate(0, 0);
    lcd.printf("Build %8s", sbuf);
    wait(0.0);
    strftime(sbuf, 11, "%H:%M:%S", tmBuild);
    lcd.locate(0, 1);
    lcd.printf("       T%8s", sbuf);
    wait(2.0);
}

/** Test files in the SD memory card */
uint8_t testFiles() {
    FILE *fpTest = NULL;
    bool sdTestResult = 0x00;
    
    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 - 1], "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    ");
        wait(1.0);
        lcd.cls();
    } else {
        lcd.printf("SD Test Fail    ");
    }
    return sdTestResult;
}

/** Main routine */
int main(void) {
    setup();
    
    // Print build timestamp
    printTimestamp();
    
    // Test files: Enter an infinite loop on failure
    if (testFiles()) {
        while (1) {
            led = 1;
            wait(0.5);
            led = 0;
            wait(0.5);
        }
    }
    
    // Set Ticker interrupt routine
    tic.attach(&pollAndRequest, POLL_INTERVAL_SEC);
    
    // Main loop
    while (1) {
        controlTrack();
    }
}