Making An Open Window More Accessible

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.');
}

 

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.