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

main.cpp

Committer:
el13tjoc
Date:
2015-05-11
Revision:
1:802453187acf
Parent:
0:39399720eaeb

File content as of revision 1:802453187acf:

/**
@file main.cpp

@brief Program implementation

*/
#include "main.h"

int main() {
    
    ///Powerdown the Ethernet peripheral
    PHY_PowerDown();
    
    ///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(3);
    
    ///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);
    }
}