{"id":2222,"date":"2022-02-07T23:19:18","date_gmt":"2022-02-08T04:19:18","guid":{"rendered":"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/?p=2222"},"modified":"2022-02-07T23:19:38","modified_gmt":"2022-02-08T04:19:38","slug":"making-an-open-window-more-accessible","status":"publish","type":"post","link":"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/?p=2222","title":{"rendered":"Making An Open Window More Accessible"},"content":{"rendered":"<h2><span style=\"text-decoration: underline\">Making An Open Window More Accessible<\/span><\/h2>\n<h3>Description:<\/h3>\n<p>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.<\/p>\n<p>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.<\/p>\n<h3>Demo:<\/h3>\n<div style=\"width: 840px;\" class=\"wp-video\"><!--[if lt IE 9]><script>document.createElement('video');<\/script><![endif]-->\n<video class=\"wp-video-shortcode\" id=\"video-2222-1\" width=\"840\" height=\"473\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/wp-content\/uploads\/2022\/02\/Assignment_4_demo.mp4?_=1\" \/><a href=\"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/wp-content\/uploads\/2022\/02\/Assignment_4_demo.mp4\">https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/wp-content\/uploads\/2022\/02\/Assignment_4_demo.mp4<\/a><\/video><\/div>\n<h3>Schematic:<\/h3>\n<p><img loading=\"lazy\" class=\"alignnone wp-image-2230 size-full\" src=\"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/wp-content\/uploads\/2022\/02\/MTI_Assignment_4_Schematic.jpg\" alt=\"\" width=\"853\" height=\"873\" srcset=\"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/wp-content\/uploads\/2022\/02\/MTI_Assignment_4_Schematic.jpg 853w, https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/wp-content\/uploads\/2022\/02\/MTI_Assignment_4_Schematic-293x300.jpg 293w, https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/wp-content\/uploads\/2022\/02\/MTI_Assignment_4_Schematic-768x786.jpg 768w\" sizes=\"(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/p>\n<h3>Code:<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">*\r\n  Making Things Interactive\r\n  Assignment 4\r\n  Judson Kyle (judsonk)\r\n  02\/07\/2022\r\n\r\n  Collaboration and sources:\r\n  - Encoder_Interrupts functions copied from 24-354 Gadgetry lab code\r\n\r\n*\/\r\n\r\n\/\/ Pin Variables\r\n\r\n#define encoderPinA   2\r\n#define encoderPinB   3\r\n#define motorPin1     10\r\n#define motorPin2     11\r\n#define motorPWM      9\r\n\r\nint encoderCount = 12 * 98; \/\/12 encoder counts per revolution with 1:98 gear ratio\r\nint incomingByte = 0;\r\n\r\n\/\/ https:\/\/www.pololu.com\/product\/4824\r\nfloat gearRatio = (22.0*20.0*22.0*22.0*23.0)\/(12.0*12.0*10.0*10.0*10.0); \r\n\r\nbool motorSpinning = false;\r\n\r\n\/\/ Motor distance measuring variables\r\nvolatile long currentEncoder_pos_f = 0, prevEncoder_pos = 0, revs = 0;\r\nfloat motorCurPos = 0;\r\n\r\n\/\/PID Variables\r\nfloat ep = 0; \/\/Proportional error\r\nfloat ei = 0; \/\/Integral error\r\nfloat ed = 0; \/\/Derivative error\r\nfloat prev_ep = 0;\r\ndouble curTime = 0;\r\ndouble prevTime = 0;\r\n\r\nint pwmCmd = 0;\r\n\r\nfloat motorGoalPos = 0; \/\/ desired angle [radians]\r\nfloat kp = 50; \/\/ P gain\r\nfloat ki = 0; \/\/ I gain\r\nfloat kd = 0; \/\/ D gain\r\n\r\nvoid setup() {\r\n\r\n  \/\/Initialize serial communication:\r\n  Serial.begin(9600);\r\n\r\n  \/\/Initialize motor control pins\r\n  pinMode(motorPin1, OUTPUT);\r\n  pinMode(motorPin2, OUTPUT);\r\n  pinMode(motorPWM, OUTPUT);\r\n\r\n  \/\/Initialize interrupts\r\n  attachInterrupt(digitalPinToInterrupt(encoderPinA), encoderA, CHANGE);\r\n  attachInterrupt(digitalPinToInterrupt(encoderPinB), encoderB, CHANGE);\r\n}\r\n\r\nvoid loop() {\r\n  if (Serial.available() &gt; 0){\r\n    incomingByte = Serial.parseInt();\r\n    if (incomingByte &gt; 0 &amp;&amp; incomingByte != motorGoalPos) {\r\n       motorGoalPos = (float)incomingByte\/100;\r\n    }\r\n  }\r\n  \r\n  \/\/Update motorActualPos variable to reflect actual motor position in radians\r\n  motorCurPos = (((float) currentEncoder_pos_f) \/ (4 * ((float) encoderCount))) * 3.14159*gearRatio\/8; \/\/ Convert encoder counts to distance travelled\r\n\r\n  Serial.println(motorCurPos);\r\n  \/\/PID controller to get motor to position\r\n  \/\/ ----------------------- CONTROLLER CODE --------------\r\n  \/\/ PID Controller\r\n  curTime = millis();\r\n  ep = (motorGoalPos - motorCurPos); \/\/ error in position (p)\r\n  ei = ei + ep; \/\/ integral error in position (i)\r\n  ed = (ep - prev_ep) \/ (curTime - prevTime); \/\/ derivative error in position (d)\r\n\r\n  prev_ep = ep;\r\n  prevTime = curTime;\r\n\r\n  pwmCmd = (ep*kp + ei*ki + ed*kd);\r\n\r\n  \/\/ switch directions if pass set point\r\n  if(ep &lt; 0) {\r\n    \/\/ Turn on motor A &amp; B\r\n    digitalWrite(motorPin1, LOW);\r\n    digitalWrite(motorPin2, HIGH);\r\n  }\r\n  else {\r\n    \/\/ Turn on motor A &amp; B\r\n    digitalWrite(motorPin1, HIGH);\r\n    digitalWrite(motorPin2, LOW);\r\n  }\r\n\r\n  pwmCmd = abs(pwmCmd);\r\n  if (pwmCmd &gt; 255) {\r\n    pwmCmd = 255;\r\n  }\r\n  analogWrite(motorPWM, pwmCmd);\r\n  \r\n\/\/  Serial.print(\"Motor Position: \");\r\n  \/\/Serial.println(motorCurPos, 7);\r\n}\r\n\r\nvoid encoderA(){\r\n  \/\/ look for a low-to-high on channel A\r\n  if (digitalRead(encoderPinA) == HIGH) { \r\n    \/\/ check channel B to see which way encoder is turning\r\n    if (digitalRead(encoderPinB) == LOW) {  \r\n      \r\n        currentEncoder_pos_f = currentEncoder_pos_f + 1;         \/\/ CW\r\n    } \r\n    else {\r\n        currentEncoder_pos_f = currentEncoder_pos_f - 1;        \/\/ CCW\r\n    }\r\n  }\r\n  else   \/\/ must be a high-to-low edge on channel A                                       \r\n  { \r\n    \/\/ check channel B to see which way encoder is turning  \r\n    if (digitalRead(encoderPinB) == HIGH) {   \r\n      \r\n        currentEncoder_pos_f = currentEncoder_pos_f + 1;          \/\/ CW\r\n    } \r\n    else {\r\n        currentEncoder_pos_f = currentEncoder_pos_f - 1;          \/\/ CCW\r\n    }\r\n  }\r\n}\r\n\r\nvoid encoderB(){\r\n\r\n  \/\/ look for a low-to-high on channel B\r\n  if (digitalRead(encoderPinB) == HIGH) {   \r\n   \/\/ check channel A to see which way encoder is turning\r\n    if (digitalRead(encoderPinA) == HIGH) {  \r\n       \r\n        currentEncoder_pos_f = currentEncoder_pos_f + 1;         \/\/ CW\r\n    } \r\n    else {\r\n        currentEncoder_pos_f = currentEncoder_pos_f - 1;         \/\/ CCW\r\n    }\r\n  }\r\n  \/\/ Look for a high-to-low on channel B\r\n  else { \r\n    \/\/ check channel B to see which way encoder is turning  \r\n    if (digitalRead(encoderPinA) == LOW) {   \r\n        currentEncoder_pos_f = currentEncoder_pos_f + 1;          \/\/ CW\r\n    } \r\n    else {\r\n        currentEncoder_pos_f = currentEncoder_pos_f - 1;         \/\/ CCW\r\n    }\r\n  }\r\n}<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">var serial;   \/\/ variable to hold an instance of the serialport library\r\n\r\n\/\/ for macos and *nix, you'll do something like this:\r\n\/\/var portName = '\/dev\/cu.usbserial-DN01DW79';    \/\/ fill in your serial port name here\r\n\r\n\/\/ for windows, just the COM port you're using\r\nvar portName = '\/dev\/tty.usbmodem144101';\r\n\r\nvar inData;   \/\/ variable to hold the input data from Arduino\r\n\r\nvar minWidth = 600;   \/\/set min width and height for canvas\r\nvar minHeight = 400;\r\nvar width, height;    \/\/ actual width and height for the sketch\r\nvar squareWidth = 100;\r\n\r\nvar sliderLength = 200;\r\nvar sliderHeight = 100;\r\nvar sliderBackgroundStrokeWeight = 4;\r\nvar sliderBackgroundSideOffset = 100;\r\nvar sliderOffset = sliderBackgroundSideOffset + (sliderBackgroundStrokeWeight + sliderLength)\/2;\r\nvar sliderX = sliderBackgroundSideOffset + sliderBackgroundStrokeWeight + sliderLength\/2;\r\nvar sliderY;\r\nvar collision = false;\r\nvar windowActualPos = 0;\r\nvar windowGoalPos = 0;\r\n\r\nvar i = 0;\r\n\r\nfunction setup() {\r\n  \/\/ set the canvas to match the window size\r\n  if (window.innerWidth &gt; minWidth){\r\n    width = window.innerWidth;\r\n  } else {\r\n    width = minWidth;\r\n  }\r\n  if (window.innerHeight &gt; minHeight) {\r\n    height = window.innerHeight;\r\n  } else {\r\n    height = minHeight;\r\n  }\r\n\r\n  sliderX = sliderOffset + sliderLength\/2;\r\n  sliderY = height\/2;\r\n\r\n  \/\/set up canvas\r\n  createCanvas(width, height);\r\n  noStroke();\r\n\r\n  \/\/set up communication port\r\n  serial = new p5.SerialPort();       \/\/ make a new instance of the serialport library\r\n  serial.on('list', printList);  \/\/ set a callback function for the serialport list event\r\n  serial.on('connected', serverConnected); \/\/ callback for connecting to the server\r\n  serial.on('open', portOpen);        \/\/ callback for the port opening\r\n  serial.on('data', serialEvent);     \/\/ callback for when new data arrives\r\n  serial.on('error', serialError);    \/\/ callback for errors\r\n  serial.on('close', portClose);      \/\/ callback for the port closing\r\n\r\n  serial.list();                      \/\/ list the serial ports\r\n  serial.open(portName);              \/\/ open a serial port\r\n}\r\n\r\nfunction draw() {\r\n  drawBackgroundCanvas();\r\n\r\n  \/\/Draw slider background\r\n  strokeWeight(sliderBackgroundStrokeWeight);\r\n  stroke('grey');\r\n  fill('white');\r\n  rect(sliderBackgroundSideOffset, sliderY\/2 - sliderHeight\/2 - sliderBackgroundStrokeWeight, width - 2*sliderBackgroundSideOffset, sliderHeight + 2*sliderBackgroundStrokeWeight);\r\n\r\n  \/\/Draw actual slider\r\n  strokeWeight(4);\r\n  stroke('black');\r\n  fill('grey');\r\n  rect(sliderX - sliderLength\/2, sliderY\/2 - sliderHeight\/2, sliderLength, sliderHeight);\r\n}\r\n\r\n\/\/Initialize drawing canvas\r\nfunction drawBackgroundCanvas() {\r\n  \/\/ set background to black\r\n  background(0);\r\n    fill(255);\r\n  textSize(30);\r\n  textAlign(CENTER);\r\n  text(\"Window Opener\", width\/2, 80);\r\n  textSize(16);\r\n  text(\"Degree open: \" + Number(serial.read()), width\/2, 100);    \/\/ displaying the input\r\n\r\n  \/\/Draw playing canvas\r\n  strokeWeight(4);\r\n  stroke('white');\r\n  fill('black');\r\n  noStroke();\r\n}\r\n\r\nfunction updateSliderPosition() {\r\n  if (mouseX &lt;= sliderOffset) {\r\n    sliderX = sliderOffset;\r\n  }\r\n  else if (mouseX &gt;= width - sliderOffset) {\r\n    sliderX = width - sliderOffset; \r\n  }\r\n  else {\r\n    sliderX = mouseX;\r\n  }\r\n  windowGoalPos = (sliderX - sliderOffset)\/(width - 2*sliderOffset);\r\n  windowGoalPos = (windowGoalPos);\r\n  serial.write(windowGoalPos);\r\n}\r\n\r\n\/\/ Following functions print the serial communication status to the console for debugging purposes\r\n\r\nfunction printList(portList) {\r\n \/\/ portList is an array of serial port names\r\n for (var i = 0; i &lt; portList.length; i++) {\r\n \/\/ Display the list the console:\r\n print(i + \" \" + portList[i]);\r\n }\r\n}\r\n\r\nfunction mousePressed() {\r\n  updateSliderPosition();\r\n}\r\n\r\nfunction mouseDragged() {\r\n  updateSliderPosition();\r\n}\r\n\r\nfunction serverConnected() {\r\n  print('connected to server.');\r\n}\r\n\r\nfunction portOpen() {\r\n  print('the serial port opened.')\r\n}\r\n\r\nfunction serialEvent() {\r\n    \/\/ on the arduino we are using Serial.write to send an integer\r\n    \/\/ so we have to use Number() to convert it to a number.\r\n    \/\/ otherwise it would be a string\r\n    inData = Number(serial.read());\r\n    motorActualPos = inData;\r\n    \/\/  if you do this, the inData value will be a string, not a number\r\n    \/\/ \r\n    \/\/inData = serial.read();\r\n    \/\/\r\n    \/\/ in arduino terms it's\r\n    \/\/ int inData = 1;\r\n    \/\/ vs\r\n    \/\/ String inData = \"1';\r\n}\r\n\r\nfunction serialError(err) {\r\n  print('Something went wrong with the serial port. ' + err);\r\n}\r\n\r\nfunction portClose() {\r\n  print('The serial port closed.');\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 &hellip; <a href=\"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/?p=2222\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Making An Open Window More Accessible&#8221;<\/span><\/a><\/p>\n","protected":false},"author":30,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[11,1],"tags":[],"_links":{"self":[{"href":"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/index.php?rest_route=\/wp\/v2\/posts\/2222"}],"collection":[{"href":"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/index.php?rest_route=\/wp\/v2\/users\/30"}],"replies":[{"embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2222"}],"version-history":[{"count":2,"href":"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/index.php?rest_route=\/wp\/v2\/posts\/2222\/revisions"}],"predecessor-version":[{"id":2237,"href":"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/index.php?rest_route=\/wp\/v2\/posts\/2222\/revisions\/2237"}],"wp:attachment":[{"href":"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2222"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2222"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/48-339\/s2022\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2222"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}