MP3-capable chair with sensor-embedded weight scale.

Dependencies:   ACM1602 SDFileSystem VS1053 mbed ClockControl PowerControl

main.cpp

Committer:
kayekss
Date:
2014-04-01
Revision:
3:64f0ba828a2e
Parent:
2:844bedc9dc63

File content as of revision 3:64f0ba828a2e:

#include <stdio.h>
#include "mbed.h"
#include "defs.h"
#include "SDFileSystem.h"
#include "VS1053.h"
#include "ACM1602.h"
#include "ClockControl.h"
#include "EthernetPowerControl.h"
#include "PowerControl.h"

// Pin settings for mbed LPC1768
SDFileSystem sd(/*MOSI*/ p11, /*MISO*/ p12, /*SCK*/ p13, /*CS*/ p21, /*Mountpoint*/ "sd");
VS1053       mp3(/*MOSI*/ p11, /*MISO*/ p12, /*SCK*/ p13,
                 /*CS*/ p22, /*BSYNC*/ p23, /*DREQ*/ p24, /*RST*/ p25, /*SPI freq.*/ 4000000);
BusIn        sens(/*Out: 0*/ p20, /*1*/ p19, /*2*/ p18, /*In: 3*/ p17);
BusIn        test(/*Out: 0*/ p26, /*1*/ p27, /*2*/ p28, /*In: 3*/ p29);
ACM1602      lcd(/*SDA*/ p9, /*SCL*/ p10, /*Address*/ 0xa0);
DigitalOut   audioPower(p30);
DigitalIn    test3(p29);
DigitalIn    test2(p28);
DigitalIn    test1(p27);
DigitalIn    test0(p26);
DigitalIn    inputSelector(p8);
// Set unused pins to input when using the LPCXpresso board
BusIn        __dummy(xp21, xp22, xp23, xp24, xp49, xp50, xp51, xp52, xp53);
Ticker       tic;
bool         inputFrom;
size_t       totalSizeSent;

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) {
    // Reduce system clock frequency to 48 MHz
    setSystemFrequency(0x03, 0x01, 6, 1);
    
    // Power down Ethernet PHY
    PHY_PowerDown();
    
    // Set pull-up mode
    inputSelector.mode(PullUp);
    test3.mode(PullUp);
    test2.mode(PullUp);
    test1.mode(PullUp);
    test0.mode(PullUp);
    wait(0.1);
    
    // Initialize VS1053
    mp3.hardwareReset();
    mp3.clockUp();
    wait(0.1);
    
    // Initialize power supply for audio amplifier circuit
    audioPower = 0;
    
    // Setup LCD
    lcd.init();
    
    // Read input selector
    inputFrom = inputSelector;
}

/** Read voltages from photo sensor pins and detect weightscale point code */
int readPhotoSensors(void) {
    uint8_t bitPattern;
    
    // Read all photo sensor inputs
    if (inputFrom) {
        // Read from photo sensors when Input Selector reads high
        // (Input is inverted by 74HC14)
        bitPattern = ~sens & 0xf;
    } else {
        // Read from test pins when Input Selector reads low
        bitPattern = test;
    }
    
    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;
    }
    
    // Print status to LCD:
    // current state, photo sensor inputs, hold count,
    // file size sent to VS1053
    lcd.locate(0, 0);
    lcd.printf("%-9s  in=%2d", stateNameList[state + 2], code);
    lcd.locate(0, 1);
    lcd.printf("x%2d ", holdTimes);
    switch (state) {
    case PLAYING1: case PLAYING2: case PLAYING3: case PLAYING4:
        if (totalSizeSent >= 1000000) {
            lcd.printf("%9.2f MB", totalSizeSent / 1048576.0);
        } else if (totalSizeSent >= 1000) {
            lcd.printf("%9.2f KB", totalSizeSent / 1024.0);
        } else {
            lcd.printf("%6d bytes", totalSizeSent);
        }
        break;
    default:
        lcd.printf("   <No File>");
        break;
    }
    
    // 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];
    
    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) {
                clearerr(fp);
                
                // Power supply on
                audioPower = 1;
                
                totalSizeSent = 0;
                state = (State) request;
            }
            // Clear play request
            request = NONE;
            break;
        default:
            break;
        }
        break;
    case PLAYING1: case PLAYING2: case PLAYING3: case PLAYING4:
        if (request == NONE) {
            // Continue playback
            if (feof(fp)) {
                // Close when the track reaches the end
                fclose(fp);
                fp = NULL;
                
                // 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);
            fp = NULL;
            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;
        }
        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) {}
    }
    
    // Set Ticker interrupt routine
    tic.attach(&pollAndRequest, POLL_INTERVAL_SEC);
    
    // Main loop
    while (1) {
        controlTrack();
    }
}