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
Collaboration and sources:
- Encoder_Interrupts functions copied from 24-354 Gadgetry lab code
int encoderCount = 12 * 98; //12 encoder counts per revolution with 1:98 gear ratio
// 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 ep = 0; //Proportional error
float ei = 0; //Integral error
float ed = 0; //Derivative error
float motorGoalPos = 0; // desired angle [radians]
//Initialize serial communication:
//Initialize motor control pins
pinMode(motorPin1, OUTPUT);
pinMode(motorPin2, OUTPUT);
pinMode(motorPWM, OUTPUT);
attachInterrupt(digitalPinToInterrupt(encoderPinA), encoderA, CHANGE);
attachInterrupt(digitalPinToInterrupt(encoderPinB), encoderB, CHANGE);
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 --------------
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)
pwmCmd = (ep*kp + ei*ki + ed*kd);
// switch directions if pass set point
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, HIGH);
digitalWrite(motorPin1, HIGH);
digitalWrite(motorPin2, LOW);
analogWrite(motorPWM, pwmCmd);
// Serial.print("Motor Position: ");
//Serial.println(motorCurPos, 7);
// 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
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
currentEncoder_pos_f = currentEncoder_pos_f - 1; // CCW
// 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
currentEncoder_pos_f = currentEncoder_pos_f - 1; // CCW
// Look for a high-to-low on channel B
// check channel B to see which way encoder is turning
if (digitalRead(encoderPinA) == LOW) {
currentEncoder_pos_f = currentEncoder_pos_f + 1; // CW
currentEncoder_pos_f = currentEncoder_pos_f - 1; // CCW
*
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
}
}
}
*
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 width, height; // actual width and height for the sketch
var sliderBackgroundStrokeWeight = 4;
var sliderBackgroundSideOffset = 100;
var sliderOffset = sliderBackgroundSideOffset + (sliderBackgroundStrokeWeight + sliderLength)/2;
var sliderX = sliderBackgroundSideOffset + sliderBackgroundStrokeWeight + sliderLength/2;
// set the canvas to match the window size
if (window.innerWidth > minWidth){
width = window.innerWidth;
if (window.innerHeight > minHeight) {
height = window.innerHeight;
sliderX = sliderOffset + sliderLength/2;
createCanvas(width, height);
//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
strokeWeight(sliderBackgroundStrokeWeight);
rect(sliderBackgroundSideOffset, sliderY/2 - sliderHeight/2 - sliderBackgroundStrokeWeight, width - 2*sliderBackgroundSideOffset, sliderHeight + 2*sliderBackgroundStrokeWeight);
rect(sliderX - sliderLength/2, sliderY/2 - sliderHeight/2, sliderLength, sliderHeight);
//Initialize drawing canvas
function drawBackgroundCanvas() {
// set background to black
text("Window Opener", width/2, 80);
text("Degree open: " + Number(serial.read()), width/2, 100); // displaying the input
function updateSliderPosition() {
if (mouseX <= sliderOffset) {
else if (mouseX >= width - sliderOffset) {
sliderX = width - sliderOffset;
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() {
function mouseDragged() {
function serverConnected() {
print('connected to server.');
print('the serial port opened.')
// 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());
// if you do this, the inData value will be a string, not a number
//inData = serial.read();
function serialError(err) {
print('Something went wrong with the serial port. ' + err);
print('The serial port closed.');
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.');
}
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.');
}