MP3-capable chair with sensor-embedded weight scale.

Dependencies:   HysteresisIn LCD SDFileSystem VS1053 mbed

Revision:
0:0451ba2f1062
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Dec 20 21:35:07 2013 +0000
@@ -0,0 +1,269 @@
+#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();
+    }
+}