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
Diff: main.cpp
- 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