From 12a04887fd023ef66a6311dd15c79024015d683f Mon Sep 17 00:00:00 2001 From: Arne van Iterson Date: Wed, 20 May 2020 19:33:05 +0200 Subject: [PATCH] First commit Working on serial connection on Arduino side --- .gitignore | 2 + README.md | 2 + arduino-mixer.ino | 322 ++++++++++++++++++++++++++++++++++++++++++++++ python/start.py | 37 ++++++ 4 files changed, 363 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 arduino-mixer.ino create mode 100644 python/start.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..60ad5a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode +*.code-workspace \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ee07e15 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Arduino-mixer +Arduino gadget to control the Windows audio mixer using Python \ No newline at end of file diff --git a/arduino-mixer.ino b/arduino-mixer.ino new file mode 100644 index 0000000..729fabe --- /dev/null +++ b/arduino-mixer.ino @@ -0,0 +1,322 @@ +#include +#include +#include +#include +#include +#include + +Adafruit_PCD8544 display = Adafruit_PCD8544(14, 15, 16, 17, 18); + +Button button(4); +Encoder encoder(3, 2); + +unsigned long time; +int connected = 0; +int editing = 0; +int blink = 0; + +long position = 0; + + +// Set backlight +int light = 0; +int timeout = 10000; + +void toggleBacklight(int value = 128) { + if (light) { + analogWrite(5, 0); + light = 0; + } else { + analogWrite(5, value); + light = 1; + } +} + +// Set variables for menu +int programLenght; +int currentProgram = 0; +int menuUpdate = 1; +int volume; + +// Draw screen +void drawMenu(String name, float percentage) { + display.clearDisplay(); + display.setFont(&Picopixel); + + // Check for connection + if (connected == 1) + { + // Draw top bar + display.setCursor(0, 4); + display.print((String)(currentProgram + 1) + "/" + (String)(programLenght)); + + display.setCursor(73, 4); + display.print("USB"); + } + + display.drawLine(0, 6, 84, 6, BLACK); + display.setFont(NULL); + + // Check for connection + if (connected == 1) { + // Program name + drawName(name); + + // Volume Bar + drawBar(percentage); + } else { + // Blink connection message if not connected + if (time % 500 == 0) + { + blink = (blink == 0) ? 1 : 0; + } + + if (blink == 0) + { + display.setCursor(9, 10); + display.print("Waiting for"); + display.setCursor(12, 18); + display.print("Connection"); + } + } + + // Bottom Bar + display.drawLine(0, 38, 84, 38, BLACK); + if (connected == 1) + { + display.setCursor(0, 40); + if (editing == 1) + { + display.print(" - OK + "); + } else { + display.print(" < OK > "); + } + } + + display.display(); +} + +// Bar position +const int pos[2] = { 4, 26 }; + +// Draw volume bar and text +void drawBar(float percentage) { + // Set fonts and size + display.setFont(&Picopixel); + display.setCursor(4, pos[1] - 2); + display.print("Volume:"); + + // Blink percentage if editing + if (editing == 1 && time % 500 == 0) + { + blink = (blink == 0) ? 1 : 0; + } + + // Center percentage + if (percentage == 0) { + display.setCursor(39, pos[1] - 2); + } else if (percentage == 100) { + display.setCursor(36, pos[1] - 2); + } else { + display.setCursor(37, pos[1] - 2); + } + + if (blink == 0) + { + display.print((String)round(percentage) + "%"); + } + + // Draw volume bar + display.drawRect(pos[0], pos[1], 76, 8, BLACK); + display.drawLine(pos[0], pos[1] + 8, pos[0], pos[1] + 9, BLACK); + display.drawPixel(pos[0] + (76 / 4) * 1 - 1, pos[1] + 8, BLACK); + display.drawLine(pos[0] + (76 / 4) * 2 - 1, pos[1] + 8, pos[0] + (76 / 4) * 2 - 1, pos[1] + 9, BLACK); + display.drawPixel(pos[0] + (76 / 4) * 3 - 1, pos[1] + 8, BLACK); + display.drawLine(pos[0] + 75, pos[1] + 8, pos[0] + 75, pos[1] + 9, BLACK); + + display.fillRect(pos[0], pos[1], round(percentage / 100 * 76), 8, BLACK); + display.setFont(NULL); +} + +// Draw program name in the middle of the display or scroll it +void drawName(String name) { + int width = name.length() * 6; + if (width > 84) + { + // TODO: Make the text scroll if the name is too long + } else { + int x = (84 / 2) - (width / 2); + display.setCursor(x, 10); + } + display.print(name); +} + +// Check for button input +// TODO: Add rotary encoder support +void checkInput(unsigned long time) { + long newPosition = encoder.read(); + if (newPosition != position) { + menuUpdate = 1; + if (newPosition < position) // Rotating anti-clockwise + { + if (editing != 1) + { + if (currentProgram == 0) + { + currentProgram = programLenght - 1; + } else { + currentProgram--; + } + } else { + volume--; + } + } + if (newPosition > position) // Rotating clockwise + { + if (editing != 1) + { + if (currentProgram == programLenght - 1) + { + currentProgram = 0; + } else { + currentProgram++; + } + } else { + volume++; + } + } + position = newPosition; + } + + if (button.pressed()) + { + editing = (editing == 1) ? 0 : 1; + blink = 0; + } +} + +const byte numChars = 32; +char receivedChars[numChars]; +char tempChars[numChars]; + +char command[numChars] = {0}; +int data = 0; + +boolean newData = false; + +void processData() { + if (newData == true) { + Serial.print("Raw data recieved: "); + Serial.println(receivedChars); + + strcpy(tempChars, receivedChars); + + + + char * strtokIndx; // this is used by strtok() as an index + + strtokIndx = strtok(tempChars,","); // get the first part - the string + strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC + + strtokIndx = strtok(NULL, ","); // this continues where the previous call left off + integerFromPC = atoi(strtokIndx); // convert this part to an integer + + newData = false; + } +} + +void checkSerial() { + static boolean recvInProgress = false; + static byte ndx = 0; + char startMarker = '<'; + char endMarker = '>'; + char rc; + + while (Serial.available() > 0 && newData == false) { + rc = Serial.read(); + + if (recvInProgress == true) { + if (rc != endMarker) { + receivedChars[ndx] = rc; + ndx++; + if (ndx >= numChars) { + ndx = numChars - 1; + } + } + else { + receivedChars[ndx] = '\0'; // terminate the string + recvInProgress = false; + ndx = 0; + newData = true; + } + } + + else if (rc == startMarker) { + recvInProgress = true; + } + } +} + +// Init arduino +void setup() { + // Start serial connection + Serial.begin(115200); + + // Init button + button.begin(); + + // Init display + display.begin(); + display.setContrast(50); + toggleBacklight(); + display.clearDisplay(); + display.display(); + + // TODO: Remove testing variables + programLenght = 3; + currentProgram = 0; + volume = 50; + connected = 1; +} + +void loop() { + // Check time + time = millis(); + menuUpdate = (time % 1000 == 0) ? 1 : 0; + + // Check for serial data or commands + checkSerial(); + + // Check if serial connection is established + if (connected == 1) + { + // Draw menu every second or every 0.1 second when editing + if (menuUpdate == 1 || (editing == 1 && time % 100 == 0)) { + menuUpdate = 0; + // TODO: Get serial data for program + switch (currentProgram) + { + case 0: + drawMenu("Firefox", volume); + break; + case 1: + drawMenu("Discord", volume); + break; + case 2: + drawMenu("Steam", volume); + break; + } + } + + // Check input + checkInput(time); + + } else { + if (menuUpdate == 1) + { + // Draw menu with "Waiting for connection", name and volume are ignored bij drawMenu() + menuUpdate = 0; + drawMenu("", 0.0); + } + } +} diff --git a/python/start.py b/python/start.py new file mode 100644 index 0000000..dab664e --- /dev/null +++ b/python/start.py @@ -0,0 +1,37 @@ +from pycaw.pycaw import AudioUtilities +from pprint import pprint +import serial +import serial.tools.list_ports +import time + +def main(): + print ("Starting Arduino Mixer...\n") + + # Get audio sessions + sessions = AudioUtilities.GetAllSessions() + print (str(len(sessions)) + " audio sessions found:") + for session in sessions: + volume = session.SimpleAudioVolume + print (" " + str(session.Process)) + + # Init serial connection + ports = serial.tools.list_ports.comports() + if len(ports) == 0: + print("\nNo Serial ports Available") + exit() + else: + print ("\nSerial ports Available:") + for port, desc, hwid in sorted(ports): + print(" {}: {} [{}]".format(port, desc, hwid)) + + board = serial.Serial() + board.baudrate = 115200 + board.port = input("\nSelect COM port: ") + board.open() + + time.sleep(5) + board.write("HELLO".encode("utf-8")) + + +if __name__ == "__main__": + main() \ No newline at end of file