Making An Open Window More Accessible
Description:
The idea behind these two sketches is to have the ability to open a window via a slider on some interface. In place of a fully working window opening mechanism is a motor with an encoder wired up to an Arduino. This Arduino then uses serial communication to relay its current position back to a p5.js sketch and retrieve its goal position from that sketch as well. The goal position is determined by an on screen slider that moves around then the user clicks and drags the slider.
While I was able to get the motor moving to a specific degree based off of serial input through the Arduino serial command, I was not able to get the p5 sketch to communicate in such a way that the Arduino could read it and then interpret it into an actionable item. I was also not able to send data from the Arduino to the p5 sketch but instead was only able to communicate with the p5 serial application shown in the demo below. Despite all this non-functional code, however, I was able to code a working slider into the p5 sketch and convert the x position of the slider into a percent of 100 as it is dragged across the screen.
Demo:
Schematic:
Code:
* Making Things Interactive Assignment 4 Judson Kyle (judsonk) 02/07/2022 Collaboration and sources: - Encoder_Interrupts functions copied from 24-354 Gadgetry lab code */ // Pin Variables #define encoderPinA 2 #define encoderPinB 3 #define motorPin1 10 #define motorPin2 11 #define motorPWM 9 int encoderCount = 12 * 98; //12 encoder counts per revolution with 1:98 gear ratio int incomingByte = 0; // https://www.pololu.com/product/4824 float gearRatio = (22.0*20.0*22.0*22.0*23.0)/(12.0*12.0*10.0*10.0*10.0); bool motorSpinning = false; // Motor distance measuring variables volatile long currentEncoder_pos_f = 0, prevEncoder_pos = 0, revs = 0; float motorCurPos = 0; //PID Variables float ep = 0; //Proportional error float ei = 0; //Integral error float ed = 0; //Derivative error float prev_ep = 0; double curTime = 0; double prevTime = 0; int pwmCmd = 0; float motorGoalPos = 0; // desired angle [radians] float kp = 50; // P gain float ki = 0; // I gain float kd = 0; // D gain void setup() { //Initialize serial communication: Serial.begin(9600); //Initialize motor control pins pinMode(motorPin1, OUTPUT); pinMode(motorPin2, OUTPUT); pinMode(motorPWM, OUTPUT); //Initialize interrupts attachInterrupt(digitalPinToInterrupt(encoderPinA), encoderA, CHANGE); attachInterrupt(digitalPinToInterrupt(encoderPinB), encoderB, CHANGE); } void loop() { if (Serial.available() > 0){ incomingByte = Serial.parseInt(); if (incomingByte > 0 && incomingByte != motorGoalPos) { motorGoalPos = (float)incomingByte/100; } } //Update motorActualPos variable to reflect actual motor position in radians motorCurPos = (((float) currentEncoder_pos_f) / (4 * ((float) encoderCount))) * 3.14159*gearRatio/8; // Convert encoder counts to distance travelled Serial.println(motorCurPos); //PID controller to get motor to position // ----------------------- CONTROLLER CODE -------------- // PID Controller curTime = millis(); ep = (motorGoalPos - motorCurPos); // error in position (p) ei = ei + ep; // integral error in position (i) ed = (ep - prev_ep) / (curTime - prevTime); // derivative error in position (d) prev_ep = ep; prevTime = curTime; pwmCmd = (ep*kp + ei*ki + ed*kd); // switch directions if pass set point if(ep < 0) { // Turn on motor A & B digitalWrite(motorPin1, LOW); digitalWrite(motorPin2, HIGH); } else { // Turn on motor A & B digitalWrite(motorPin1, HIGH); digitalWrite(motorPin2, LOW); } pwmCmd = abs(pwmCmd); if (pwmCmd > 255) { pwmCmd = 255; } analogWrite(motorPWM, pwmCmd); // Serial.print("Motor Position: "); //Serial.println(motorCurPos, 7); } void encoderA(){ // look for a low-to-high on channel A if (digitalRead(encoderPinA) == HIGH) { // check channel B to see which way encoder is turning if (digitalRead(encoderPinB) == LOW) { currentEncoder_pos_f = currentEncoder_pos_f + 1; // CW } else { currentEncoder_pos_f = currentEncoder_pos_f - 1; // CCW } } else // must be a high-to-low edge on channel A { // check channel B to see which way encoder is turning if (digitalRead(encoderPinB) == HIGH) { currentEncoder_pos_f = currentEncoder_pos_f + 1; // CW } else { currentEncoder_pos_f = currentEncoder_pos_f - 1; // CCW } } } void encoderB(){ // look for a low-to-high on channel B if (digitalRead(encoderPinB) == HIGH) { // check channel A to see which way encoder is turning if (digitalRead(encoderPinA) == HIGH) { currentEncoder_pos_f = currentEncoder_pos_f + 1; // CW } else { currentEncoder_pos_f = currentEncoder_pos_f - 1; // CCW } } // Look for a high-to-low on channel B else { // check channel B to see which way encoder is turning if (digitalRead(encoderPinA) == LOW) { currentEncoder_pos_f = currentEncoder_pos_f + 1; // CW } else { currentEncoder_pos_f = currentEncoder_pos_f - 1; // CCW } } }
var serial; // variable to hold an instance of the serialport library // for macos and *nix, you'll do something like this: //var portName = '/dev/cu.usbserial-DN01DW79'; // fill in your serial port name here // for windows, just the COM port you're using var portName = '/dev/tty.usbmodem144101'; var inData; // variable to hold the input data from Arduino var minWidth = 600; //set min width and height for canvas var minHeight = 400; var width, height; // actual width and height for the sketch var squareWidth = 100; var sliderLength = 200; var sliderHeight = 100; var sliderBackgroundStrokeWeight = 4; var sliderBackgroundSideOffset = 100; var sliderOffset = sliderBackgroundSideOffset + (sliderBackgroundStrokeWeight + sliderLength)/2; var sliderX = sliderBackgroundSideOffset + sliderBackgroundStrokeWeight + sliderLength/2; var sliderY; var collision = false; var windowActualPos = 0; var windowGoalPos = 0; var i = 0; function setup() { // set the canvas to match the window size if (window.innerWidth > minWidth){ width = window.innerWidth; } else { width = minWidth; } if (window.innerHeight > minHeight) { height = window.innerHeight; } else { height = minHeight; } sliderX = sliderOffset + sliderLength/2; sliderY = height/2; //set up canvas createCanvas(width, height); noStroke(); //set up communication port serial = new p5.SerialPort(); // make a new instance of the serialport library serial.on('list', printList); // set a callback function for the serialport list event serial.on('connected', serverConnected); // callback for connecting to the server serial.on('open', portOpen); // callback for the port opening serial.on('data', serialEvent); // callback for when new data arrives serial.on('error', serialError); // callback for errors serial.on('close', portClose); // callback for the port closing serial.list(); // list the serial ports serial.open(portName); // open a serial port } function draw() { drawBackgroundCanvas(); //Draw slider background strokeWeight(sliderBackgroundStrokeWeight); stroke('grey'); fill('white'); rect(sliderBackgroundSideOffset, sliderY/2 - sliderHeight/2 - sliderBackgroundStrokeWeight, width - 2*sliderBackgroundSideOffset, sliderHeight + 2*sliderBackgroundStrokeWeight); //Draw actual slider strokeWeight(4); stroke('black'); fill('grey'); rect(sliderX - sliderLength/2, sliderY/2 - sliderHeight/2, sliderLength, sliderHeight); } //Initialize drawing canvas function drawBackgroundCanvas() { // set background to black background(0); fill(255); textSize(30); textAlign(CENTER); text("Window Opener", width/2, 80); textSize(16); text("Degree open: " + Number(serial.read()), width/2, 100); // displaying the input //Draw playing canvas strokeWeight(4); stroke('white'); fill('black'); noStroke(); } function updateSliderPosition() { if (mouseX <= sliderOffset) { sliderX = sliderOffset; } else if (mouseX >= width - sliderOffset) { sliderX = width - sliderOffset; } else { sliderX = mouseX; } windowGoalPos = (sliderX - sliderOffset)/(width - 2*sliderOffset); windowGoalPos = (windowGoalPos); serial.write(windowGoalPos); } // Following functions print the serial communication status to the console for debugging purposes function printList(portList) { // portList is an array of serial port names for (var i = 0; i < portList.length; i++) { // Display the list the console: print(i + " " + portList[i]); } } function mousePressed() { updateSliderPosition(); } function mouseDragged() { updateSliderPosition(); } function serverConnected() { print('connected to server.'); } function portOpen() { print('the serial port opened.') } function serialEvent() { // on the arduino we are using Serial.write to send an integer // so we have to use Number() to convert it to a number. // otherwise it would be a string inData = Number(serial.read()); motorActualPos = inData; // if you do this, the inData value will be a string, not a number // //inData = serial.read(); // // in arduino terms it's // int inData = 1; // vs // String inData = "1'; } function serialError(err) { print('Something went wrong with the serial port. ' + err); } function portClose() { print('The serial port closed.'); }