This project allows for the sending of MIDI messages, and setting of variable resistances, controlled by a distance sensor. MIDI input messages can also be used to set the variable resistance.

Dependencies:   N5110 PinDetect SRF08 USBDevice mbed PowerControl

Revision:
0:39399720eaeb
Child:
1:802453187acf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sun May 10 01:58:51 2015 +0000
@@ -0,0 +1,488 @@
+/**
+@file main.cpp
+
+@brief Program implementation
+
+*/
+#include "main.h"
+
+int main() {
+    
+    ///Read parameter variables from VALS.csv
+    readNumberValuesFromFile();
+    ///Resise the smoothing values vector
+    smoothingValues.resize(numberValues[distanceSmoothing]+1);
+    
+    ///Set MCP4151 chip select high initially
+    CS = 1; 
+    /// Set the MCP4151 SPI for 16 bit mode 3
+    MCP4151.format(16,3);
+    /// Set MCP4151 clock frequency to 1000000
+    MCP4151.frequency(1000000);
+    
+    ///Initialise the screen and print splash screen
+    lcd.init(); 
+    lcd.printString("EXPRESSIONATOR",0,1);
+    lcd.printString("version 1.0",0,2);
+    lcd.printString("Toby O'Connell",0,4);
+    wait(0.5);
+    
+    ///Setup callback function for range finder ticker 
+    startRangingTicker.attach(&startRangingFlag, 0.1);
+
+    ///Set the initial range finder distance 
+    SRF08.setRangeRegister((int)((numberValues[maxDistance]-43.0)/43.0));
+    
+    ///Setup callback function for when MIDI messages are received
+    midi.attach(receivedMIDI);
+    
+    ///Setup callbacks functions for button presses
+    buttonA.attach_asserted(&aPressed); //Attach functions to button presses
+    buttonB.attach_asserted(&bPressed);
+    buttonC.attach_asserted(&cPressed);
+    buttonD.attach_asserted(&dPressed);
+    
+    ///Set button press doubounce freqencies to the default of 20ms
+    buttonA.setSampleFrequency();
+    buttonB.setSampleFrequency();
+    buttonC.setSampleFrequency();
+    buttonD.setSampleFrequency();
+    
+    ///Open the buttons.bmp image in binary, skip the header and copy into a buffer before closing
+    img = fopen("/local/BUTTONS.BMP", "rb"); 
+    fseek (img , 54 , SEEK_SET); 
+    fread(imgbuffer, (84*3*48), 1, img);
+    fclose(img);
+    
+    ///Show initial menu
+    showScreen(); 
+    ///Create infinite while loop to run program repeatedly
+    while(1) {
+        if(aButtonFlag){
+            if (menu[state].screenType == menuType || digitArrowPosition + 1 >= maxValLen) {
+                ///screenSelect() if button A is pressed and the screen type is a menu or a page type that is confirmed
+                screenSelect();
+            } else {
+                ///incrementDigitArrowPos() if button A is pressed and the screen type is not a menu or a page type that is confirmed 
+                incrementDigitArrowPos();
+            }
+            aButtonFlag = 0;
+        }
+        if(bButtonFlag){
+            if (menu[state].screenType == menuType || digitArrowPosition - 1 < 0) {
+                ///screenBack() if button B is pressed and the screen type is a menu or a page type that is reverted
+                screenBack();
+            } else {
+                ///decrementDigitArrowPos() if button B is pressed and the screen type is not a menu or a page type that is confirmed 
+                decrementDigitArrowPos();
+            }
+            bButtonFlag = 0;
+        }
+        if(cButtonFlag){
+            if (menu[state].screenType == menuType) {
+                ///menuUp() if button C is pressed and the screen type is a menu
+                menuUp();   
+            } else {
+                ///incrementValue() if button C is pressed and the screen type is not a menu
+                incrementValue();
+            }
+            cButtonFlag = 0;
+        }
+        if(dButtonFlag){
+            if (menu[state].screenType == menuType) {
+                ///menuDown() if button D is pressed and the screen type is a menu
+                menuDown();   
+            } else {
+                ///decrementValue() if button D is pressed and the screen type is not a menu
+                decrementValue();
+            }
+            dButtonFlag = 0;
+        }
+        if(rangingStartFlag) {
+            ///Start ranging if the startRangingTicker has fired
+            rangingStart();
+            rangingStartFlag = 0;
+        }
+        if(rangeGetFlag) {
+            ///Start ranging if the getRangeTimeout has fired
+            rangeGet();
+            rangeGetFlag = 0;
+        }
+        if(measuredDistanceFlag) {
+            if(numberValues[MIDIOutputOn]) { 
+                ///writeMIDI() if the distance has just been measured and MIDI output is on
+                writeMIDI(distanceFraction);
+            }
+            if(numberValues[resistanceSourceIndex] == sensorSource) {
+                ///writeMCP4151() if the distance has just been measured and the resistance source is the ultrasonic sensor
+                writeMCP4151(distanceFraction);
+            }
+            ///showScreen() if the distance has just been measured and the screen type is the display screen
+            if(menu[state].screenType == displayType) showScreen();
+            measuredDistanceFlag = 0;
+        }
+        if(receivedMIDIFlag) {
+            if(numberValues[resistanceSourceIndex] == MIDISource) {
+                 ///writeMCP4151() if a MIDI message has been received and the resistance source is the MIDI input
+                writeMCP4151(receivedMIDIValue/127.0);
+            }
+             ///showScreen() if a MIDI message has been received and the screen type is the display screen
+            if(menu[state].screenType == displayType) showScreen();
+            receivedMIDIFlag = 0;
+        }
+    }    
+}
+
+
+void aPressed() { aButtonFlag = 1; } //raise button flags
+void bPressed() { bButtonFlag = 1; }
+void cPressed() { cButtonFlag = 1; }
+void dPressed() { dButtonFlag = 1; }
+
+void startRangingFlag() { rangingStartFlag = 1;} //raise ranging star flag
+void getRangeFlag() { rangeGetFlag = 1;} //raise range get flag
+
+void screenSelect() {
+    if (menu[state].screenType == pageType) {
+        ///If exiting a page type screen checkValidity() and do the immediateUpdates()
+        checkValidity();
+        writeNumberValuesToFile();
+        immediateUpdates();
+    }
+    ///Change to the next state based on the menu arrow position
+    state = menu[state].nextState[menuArrowPosition]; //Set new menu state basted on arrow position
+    if (menu[state].screenType == pageType) {
+        ///if entering a page type screen save the associated value to oldValue for reverting back
+        oldValue = numberValues[menu[state].associatedValueIndex];
+    }
+    ///Reset menu and digit arrow posiitons
+    menuArrowPosition = 0; 
+    digitArrowPosition = 0;
+    ///Update the display
+    showScreen();
+}
+void screenBack() {
+    if (menu[state].screenType == pageType) {
+        ///If reverting back out of a page type screen set the assoicated value to its previous value
+        numberValues[menu[state].associatedValueIndex] = oldValue;
+    }
+    ///Go to FSM parent state
+    state = menu[state].previousState;
+     ///Reset menu and digit arrow posiitons
+    menuArrowPosition = 0; 
+    digitArrowPosition = 0;
+    ///Update the display
+    showScreen();
+}
+void menuUp() {
+    ///Decrement the menu arrow position
+    menuArrowPosition = (menuArrowPosition + menu[state].listLength - 1) % menu[state].listLength;
+    ///Update the display
+    showScreen(); 
+}
+void menuDown() {
+    ///Increment the menu arrow position
+    menuArrowPosition = (menuArrowPosition + 1) % menu[state].listLength;
+    ///Update the display
+    showScreen(); 
+}
+
+void incrementDigitArrowPos() {
+    ///Increment the digit arrow position
+    digitArrowPosition += 1;
+    ///Update the display
+    showScreen();
+}
+void decrementDigitArrowPos() {
+    ///Increment the digit arrow position
+    digitArrowPosition -= 1;
+    ///Update the display
+    showScreen(); 
+}
+
+void incrementValue() {
+    ///Set the increment to '1'
+    increment = 1;
+    ///amend the value
+    amendValue();
+}
+
+void decrementValue() {
+    ///Set the increment to '-1'
+    increment = -1;
+    ///amend the value
+    amendValue();
+}
+
+int amendNumberValue(int value) {
+    ///Get the digit at the current arrow position
+    int currentDigit = (int)(value / pow(10.0,maxValLen-1-digitArrowPosition))%10;
+    
+    if((currentDigit == 9 && increment == + 1) || (currentDigit == 0 && increment == - 1)) {
+        ///If the digit is will become greater than 9 or less then 0, wrap around
+        value = value - increment * 9 * pow(10.0,maxValLen-1-digitArrowPosition);
+    } else {
+        ///If not, increment / decrement the value
+        value = value + increment * pow(10.0,maxValLen-1-digitArrowPosition);
+    }
+    
+    ///return the value
+    return value;
+}
+
+void amendValue() {
+    
+    switch(menu[state].associatedValueType) {
+        case INT:
+            ///If the associated is an int style number amendNumberValue()
+            numberValues[menu[state].associatedValueIndex] = amendNumberValue(numberValues[menu[state].associatedValueIndex]);
+            break;
+        case INDEX:
+            ///If the associated is an index style number increment / decrement it
+            numberValues[menu[state].associatedValueIndex] = (maxNumberValues[menu[state].associatedMaxValueIndex] + numberValues[menu[state].associatedValueIndex] + increment) % maxNumberValues[menu[state].associatedMaxValueIndex];
+            break;
+        case BOOL:
+            ///If the associated is a boolean style number invert it
+            numberValues[menu[state].associatedValueIndex] = !numberValues[menu[state].associatedValueIndex];
+            break;
+    }
+    ///Update the display
+    showScreen();
+}
+
+
+
+void showScreen() {
+    ///Clear the screen
+    lcd.clear();
+    ///Show the title
+    lcd.printString(menu[state].title,0,0);
+    if(menu[state].screenType == menuType) {
+        for (int i = 0;i <= 3; i++) { 
+            ///If the screen type is a menu, print the items in its list
+            lcd.printString(menu[state].list[i],6,i+1);
+        }
+        if(menu[state].listLength > 0) {
+            ///if the list has items in it, then show show the menu arrow pointer
+            lcd.printString(">",0,menuArrowPosition+1); 
+        }
+    } else if(menu[state].screenType == pageType) {
+        switch (menu[state].associatedValueType) {
+        case INT:
+            ///If the screen type is a parameter editing page and the associated value is an int type number, displayNumber() and show the digit arrow pointer
+            displayNumber(numberValues[menu[state].associatedValueIndex],maxNumberValues[menu[state].associatedMaxValueIndex]);
+            lcd.printString("^",digitArrowPosition*6+7,3);
+            break;
+        case INDEX:
+            ///If the screen type is a parameter editing page and the associated value is an index type number, displayList()    
+            displayList(valueLists[menu[state].associatedValueList],numberValues[menu[state].associatedValueIndex]);
+            break;
+        case BOOL:
+            ///If the screen type is a parameter editing page and the associated value is a boolean type number, displayBool()
+            displayBool(numberValues[menu[state].associatedValueIndex]);
+            break;
+        }
+    } else {
+        ///If the screen type is the display screen, displayMeasured()
+        displayMeasured();
+    }
+    ///Draw the buttons.bmp image to the screen using drawBMP()
+    drawBMP();     
+}
+
+void displayNumber(int value, int maxValue) {
+    char buffer[14];
+    ///Find the maximum length the incoming value could be
+    maxValLen = sprintf(buffer,"%d",maxValue);
+    ///Find the actual length of the incoming value
+    valLen = sprintf(buffer,"%d",value);
+    ///Print the value to the screen, filling any unused digit spaces with zeros
+    for(int i = 0; i <= maxValLen - valLen; i++) {
+        lcd.printString("0",6*i+7,2);
+    }
+    lcd.printString(buffer,6*(maxValLen-valLen)+7,2);
+}
+
+void displayList(string list[], int listIndex) {
+    char buffer[14];
+    maxValLen = 1;
+    ///Display the list element at the list index
+    valLen = sprintf(buffer,"%s",list[listIndex].c_str());
+    lcd.printString(buffer,7,2);  
+}
+
+void displayBool(bool toggle) {
+    char buffer[14];
+    maxValLen = 1;
+    ///Print "On" if the boolean value is 1 and "Off" if the boolean value is 0
+    valLen = sprintf(buffer,"%s", toggle ? "On" : "Off");
+    lcd.printString(buffer,7,2);
+}
+
+void displayMeasured() {
+    char buffer[14];
+    ///Print the distance measured by the SRF08
+    sprintf(buffer,"D: %d",measuredDistance);
+    lcd.printString(buffer,7,1);
+    ///If the MIDI output is on, print the MIDI output value, else print "OFF"
+    if(numberValues[MIDIOutputOn] == 1) {
+        sprintf(buffer,"MO: %d",(int)(distanceFraction*127));
+    } else {
+        sprintf(buffer,"MO: OFF");
+    }
+    lcd.printString(buffer,7,2);
+    ///Print the input MIDI value
+    sprintf(buffer,"MI: %d",receivedMIDIValue);
+    lcd.printString(buffer,7,3);
+    if(numberValues[resistanceSourceIndex] == sensorSource) {
+        ///If the resistance source is the SRF08, print the resistance based on that distance
+        sprintf(buffer,"R: %d",(int)(numberValues[minResistance]+distanceFraction*(numberValues[maxResistance]-numberValues[minResistance])));
+    } else if(numberValues[resistanceSourceIndex] == MIDISource) {
+        ///If the resistance source is the the MIDI input, print the resistance based on the MIDI value
+        sprintf(buffer,"R: %d",(int)(numberValues[minResistance]+(receivedMIDIValue/127.0)*(numberValues[maxResistance]-numberValues[minResistance])));
+    } else {
+        ///If the resistance source is set to OFF, print "OFF"
+        sprintf(buffer,"R: OFF");
+    }
+    lcd.printString(buffer,7,4);
+}
+
+void rangingStart() {
+    ///detach the getRangeTimeout
+    getRangeTimeout.detach();
+    ///start the SRF08 ranging
+    SRF08.startRanging(CM);
+    ///reattach the getRangeTimeout
+    getRangeTimeout.attach(&getRangeFlag,0.07);
+}
+
+void rangeGet() { 
+    ///Get the measured distance from the SRF08
+    measuredDistance = SRF08.getRange();
+    //Convert the measuredDistance to a float between 0 and 1
+    distanceFraction = (measuredDistance * 10 - numberValues[minDistance]) / (float)(numberValues[maxDistance]-numberValues[minDistance]);
+    ///If the distanceFraction is > 1 or < 0, limit it to 1 or 0
+    if(distanceFraction > 1) distanceFraction = 1;
+    if(distanceFraction < 0) distanceFraction = 0;
+    ///smooth() the distance fraction 
+    distanceFraction = smooth(distanceFraction);
+    measuredDistanceFlag = 1;
+} 
+
+void receivedMIDI(MIDIMessage msg) {
+    if(msg.type() == MIDIMessage::ControlChangeType && msg.controller() == numberValues[MIDIInputController] && msg.channel() == numberValues[MIDIInputChannel]) {  
+        ///If the MIDI message is the right controller change on the right channel, save the value
+        receivedMIDIValue = msg.value();
+        receivedMIDIFlag = 1;
+    }
+}
+
+
+float smooth(float value) {
+    ///Erase the first element in the smoothing vector
+    smoothingValues.erase(smoothingValues.begin());
+    ///Add the value to the end of the smoothing vector
+    smoothingValues.push_back(value);
+    ///Sum the values in the smoothing vector
+    float sum = 0;
+    for(int i = 0; i < smoothingValues.size(); i++) {
+        sum += smoothingValues[i];
+    }    
+    ///Return the mean average of the smoothing vector contents
+    return sum/(smoothingValues.size());
+}
+
+
+
+void writeMCP4151(float value) {
+    ///Scale the max and min resistances for 0 to 10000 down to 0 to 255.
+    int scaledMax = (int)(numberValues[maxResistance]/(10000/255.0));
+    int scaledMin = (int)(numberValues[minResistance]/(10000/255.0));
+    ///Convert the 0 to 1 float value to a 0 to 255 int value for the MCP4151 SPI digital potentiometer
+    int writeValue = (int)(1 + scaledMin + value * (scaledMax - scaledMin));
+    ///Chip enable
+    CS = 0;
+    ///Write the new value
+    MCP4151.write(writeValue);
+    ///Chip disable
+    CS = 1;
+}
+
+void writeMIDI(float value) {
+    ///Convert the 0 to 1 float valye to 0 to 127 int value for the MIDI message
+    int writeValue = (int)(value * 127);
+    ///Write the 0 to 255 value to the MIDI output
+    midi.write(MIDIMessage::ControlChange(numberValues[MIDIOutputController],writeValue,numberValues[MIDIOutputChannel]));   
+}
+
+void drawBMP() {
+    ///Iterate through each pixel in the image buffer
+    for (int i = 0; i < 84*48 ; i++) {
+        if (imgbuffer[i*3] == 0) { 
+            ///If the pixel is not white, write it to the screen
+            lcd.setPixel(i%84,47-(int)(i/84));
+        }
+    }
+    lcd.refresh();
+}
+
+void readNumberValuesFromFile() {
+    ///Open the VALS.csv file
+    ifstream csvValuesRead("/local/VALS.csv");
+    for(int i = 0; i < 11; i++) {
+        ///Copy each line of the csv file to a each element in the numberValues array
+        string line;
+        getline(csvValuesRead, line);          
+        stringstream convertor(line);
+        convertor >> numberValues[i];
+    }
+    ///Close the VALS.csv file
+    csvValuesRead.close();
+}
+
+void writeNumberValuesToFile() {
+    ///Open the VALS.csv file
+    FILE *csvValuesWrite = fopen("/local/VALS.csv","w");
+    for(int i = 0; i < 11; i++) {
+        ///Write each element in the numberValues array to each line in the csv file
+        fprintf(csvValuesWrite,"%d\n",numberValues[i]);
+    }
+    ///Close the VALS.csv file
+    fclose(csvValuesWrite);
+}
+
+void checkValidity() {
+    if(numberValues[menu[state].associatedValueIndex] > maxNumberValues[menu[state].associatedMaxValueIndex]) {
+        ///If the associated value is greater than the associated max value after being altered, revert back to the original
+        numberValues[menu[state].associatedValueIndex] = oldValue;
+    }
+    if(numberValues[minResistance] >= numberValues[maxResistance]) {
+        if(menu[state].associatedValueIndex == minResistance) {
+            ///If the min resistance is greater than the max resistance make it 1 less than the maximum
+            numberValues[minResistance] = numberValues[maxResistance] - 1;
+        } else {
+            ///If the max resistance is less than the max resistance make it 1 more than the minimum
+            numberValues[maxResistance] = numberValues[minResistance] + 1;
+        }
+    }
+    if(numberValues[minDistance] >= numberValues[maxDistance]) {
+        if(menu[state].associatedValueIndex == minDistance) {
+            ///If the min distance is greater than the max distance make it 1 less than the maximum
+            numberValues[minDistance] = numberValues[maxDistance] - 1;
+        } else {
+            ///If the max distance is less than the max distance make it 1 more than the minimum
+            numberValues[maxDistance] = numberValues[minDistance] + 1;
+        }
+    }
+}
+
+void immediateUpdates() {
+    if(menu[state].associatedValueIndex == maxDistance) {
+        ///If the associated value is the max distance, set the range of the SRF08
+        SRF08.setRangeRegister((int)((numberValues[maxDistance]-43.0)/43.0));
+    }
+    if(menu[state].associatedValueIndex == distanceSmoothing) {
+        ///If the associated value is the distanceSmoothing value, resize the smoothingValues vector
+        smoothingValues.resize(numberValues[distanceSmoothing]+1);
+    }
+}
\ No newline at end of file