The goal of Elevate is to create a soothing and calming experience for the user, while also giving the user an indirect sense of companionship. Since the beginning of the pandemic, many individuals have remained quarantined from social interaction. Elevate aims to address the lack of interaction by providing a soft, subtle presence in everyday life. Whether it is your morning coffee, or an afternoon book, Elevate acknowledges your actions and highlights your daily routines.
Due to limited access to on-campus resources, we continued the development of Elevate virtually. The next iteration of Elevate includes state-driven movement, to allow for simultaneous motion of multiple panels. Additionally, we looked to improve the mannerisms of the table, along with the user’s experience. As a result, we decreased the speed of the motors to provide the users a subtle and more relaxed interaction.
Updated Code
The updated code base includes state-machines for each panel. A panel consists of an LED, force-sensitive resistor (FSR), and a stepper motor. Each panel is either loaded or unloaded, based on the FSR reading. Each loop, the Master Arduino runs each panel’s state machine, which maintains the state of the panel, and then checks the state of the panel to see if it is loaded or unloaded. If the panel is loaded and was previously unloaded, the master controller sends the state change to the Stepper Driver Arduino. The Driver Arduino loop iterates through a list of steps needed until a panel is at its state. If a panel has steps remaining, each step is taken incrementally, allowing for simultaneous stepper motor movements. When data is received from the master controller, the steps array is updated, and the motor is moved accordingly.
The updated code is provided below:
Master Controller (Arduino Nano)
#include <Wire.h> const int aA = 0; const int aX = 1; const int aY = 2; const int aZ = 3; const int ledA = 2; const int ledX = 3; const int ledY = 4; const int ledZ = 5; // Array for LEDs in following order [a,x,y,z] boolean lights[4] = {false, false, false, false}; int val_a = 0; int state_a = 0; int state_prev_a = 0; unsigned long t_a = 0; unsigned long t0_a = 0; int val_x = 0; int state_x = 0; int state_prev_x = 0; unsigned long t_x = 0; unsigned long t0_x = 0; int val_y = 0; int state_y = 0; int state_prev_y = 0; unsigned long t_y = 0; unsigned long t0_y = 0; int val_z = 0; int state_z = 0; int state_prev_z = 0; unsigned long t_z = 0; unsigned long t0_z = 0; // Debounce delay to avoid false state changes unsigned long debounceDelay = 300; // Threshhold for Loaded/Unloaded reading of FSR // Depending on mechanical configuration, this value // may vary for each panel int fsr_a_threshhold = 500; int fsr_x_threshhold = 400; int fsr_y_threshhold = 600; int fsr_z_threshhold = 400; void setup() { Serial.begin(9600); Wire.begin(); pinMode(ledA, OUTPUT); pinMode(ledX, OUTPUT); pinMode(ledY, OUTPUT); pinMode(ledZ, OUTPUT); } void loop() { // Run the State Machine of each Motor/FSR/LED combo SM_a(); SM_x(); SM_y(); SM_z(); //Send updated states if applicable transmit_states(); } // Polling Functions // Polls the state of the lights in the lights array void poll_lights() { if (lights[0]){ digitalWrite(ledA, HIGH); }else{ digitalWrite(ledA, LOW); } if (lights[1]){ digitalWrite(ledX, HIGH); }else{ digitalWrite(ledX, LOW); } if (lights[2]){ digitalWrite(ledY, HIGH); }else{ digitalWrite(ledY, LOW); } if (lights[3]){ digitalWrite(ledZ, HIGH); }else{ digitalWrite(ledZ, LOW); } } //// sends State change to Stepper Driver controller and updates LED states void transmit_states(){ //Checks to see if state is loaded and was not previously loaded if (state_a == 5 && state_prev_a != 5){ Wire.beginTransmission(4); // transmit to device #4 Wire.write(0); Wire.endTransmission(); lights[0] = true; } //Checks to see if state is unloaded if (state_a == 4) { Wire.beginTransmission(4); // transmit to device #4 Wire.write(1); Wire.endTransmission(); lights[0] = false; } if (state_x == 5 && state_prev_x != 5){ Wire.beginTransmission(4); // transmit to device #4 Wire.write(2); Wire.endTransmission(); lights[1] = true; } if (state_x == 4) { Wire.beginTransmission(4); // transmit to device #4 Wire.write(3); Wire.endTransmission(); lights[1] = false; } if (state_y == 5 && state_prev_y != 5){ Wire.beginTransmission(4); // transmit to device #4 Wire.write(4); Wire.endTransmission(); lights[2] = true; } if (state_y == 4) { Wire.beginTransmission(4); // transmit to device #4 Wire.write(5); Wire.endTransmission(); lights[2] = false; } if (state_z == 5 && state_prev_z != 5){ Wire.beginTransmission(4); // transmit to device #4 Wire.write(6); Wire.endTransmission(); lights[3] = true; } if (state_z == 4) { Wire.beginTransmission(4); // transmit to device #4 Wire.write(7); Wire.endTransmission(); lights[3] = false; } poll_lights(); } // STATE MACHINES // Manages the state of the Motor/FSR/LED combo and updates according to // FSR reading void SM_z (){ state_prev_z = state_z; switch (state_z){ case 0: //RESET state_z = 1; break; case 1: //START val_z = analogRead(aZ); if (val_z >= fsr_z_threshhold) {state_z = 2;} break; case 2: // SET TIME t0_z = millis(); state_z = 3; break; case 3:// WAIT AND CHECK val_z = analogRead(aZ); t_z = millis(); if (val_z < fsr_z_threshhold) {state_z = 0;} if (t_z - t0_z > debounceDelay){ state_z = 5; } break; case 4: // UNLOADED state_z = 0; break; case 5: // LOADED val_z = analogRead(aZ); if (val_z < fsr_z_threshhold) {state_z = 4;} break; } } void SM_y (){ state_prev_y = state_y; switch (state_y){ case 0: //RESET state_y = 1; break; case 1: //START val_y = analogRead(aY); if (val_y >= fsr_y_threshhold) {state_y = 2;} break; case 2: // SET TIME t0_y = millis(); state_y = 3; break; case 3:// WAIT AND CHECK val_y = analogRead(aY); t_y = millis(); if (val_y < fsr_y_threshhold) {state_y = 0;} if (t_y - t0_y > debounceDelay){ state_y = 5; } break; case 4: // UNLOADED state_y = 0; break; case 5: // LOADED val_y = analogRead(aY); if (val_y < fsr_y_threshhold) {state_y = 4;} break; } } void SM_x (){ state_prev_x = state_x; switch (state_x){ case 0: //RESET state_x = 1; break; case 1: //START val_x = analogRead(aX); if (val_x >= fsr_x_threshhold) {state_x = 2;} break; case 2: // SET TIME t0_x = millis(); state_x = 3; break; case 3:// WAIT AND CHECK val_x = analogRead(aX); t_x = millis(); if (val_x < fsr_x_threshhold) {state_x = 0;} if (t_x - t0_x > debounceDelay){ state_x = 5; } break; case 4: // UNLOADED state_x = 0; break; case 5: // LOADED val_x = analogRead(aX); if (val_x < fsr_x_threshhold) {state_x = 4;} break; } } void SM_a (){ state_prev_a = state_a; switch (state_a){ case 0: //RESET state_a = 1; break; case 1: //START val_a = analogRead(aA); if (val_a >= fsr_a_threshhold) {state_a = 2;} break; case 2: // SET TIME t0_a = millis(); state_a = 3; break; case 3:// WAIT AND CHECK val_a = analogRead(aA); t_a = millis(); if (val_a < fsr_a_threshhold) {state_a = 0;} if (t_a - t0_a > debounceDelay){ state_a = 5; } break; case 4: // UNLOADED state_a = 0; break; case 5: // LOADED val_a = analogRead(aA); if (val_a < fsr_a_threshhold) {state_a = 4;} break; } }
Stepper Driver (Arduino Uno)
const int enable = 8; const int xDir = 5; const int yDir = 6; const int zDir = 7; const int aDir = 13; const int xStep = 2; const int yStep = 3; const int zStep = 4; const int aStep = 12; #include <Wire.h> // An array to keep track of number of steps needed until state completed // in the order of [a,x,y,z] int steps[4] = {0,0,0,0}; //Nema 17 and CNC Shield has 3200 steps per rotation // 800 steps is equal to 90 degrees of rotation int steps_revolution = 3200; //Sets the speed of the Stepper Motor int stepDelay = 800; void setup() { Serial.begin(9600); pinMode(xDir,OUTPUT); pinMode(xStep,OUTPUT); pinMode(yDir,OUTPUT); pinMode(yStep,OUTPUT); pinMode(zDir,OUTPUT); pinMode(zStep,OUTPUT); pinMode(aDir,OUTPUT); pinMode(aStep,OUTPUT); pinMode(enable,OUTPUT); digitalWrite(enable,LOW); Wire.begin(4); // join i2c bus with address #4 //Calls the receiveEvent function when data is received from Master Controller Wire.onReceive(receiveEvent); } void loop() { step_motors(); } //Steps a given motor one step in the given direction // True is clockwise, False is counterclockwise void step (bool dir, int dirPin, int stepperPin) { digitalWrite(dirPin,dir); digitalWrite(stepperPin, HIGH); digitalWrite(stepperPin, LOW); delayMicroseconds(stepDelay); } // Iterates through the steps array and steps each motor the required number of steps // This allows for simultaneous movement of stepper motors void step_motors() { for (int i = 0; i < 4; i++){ //Rotate clockwise to loaded state if (steps[i]>0){ //Case for A motor because A pins are not sequential on CNC Shield if (i == 0) { step (true, aDir,aStep); steps[i] -= 1; }else{ step (true, (4+i),(1+i) ); steps[i] -= 1; } //Rotate counterclockwise to unloaded state } else if (steps[i] < 0){ if (i == 0) { step (false, aDir,aStep); steps[i] += 1; }else{ step (false, (4+i),(1+i)); steps[i] += 1; } } } } void receiveEvent(int howMany) { // Reads value sent from Master Controller which reads the FSR's and updates LEDs int x = Wire.read(); int remaining = 0; //Updates the amount of steps remaining for each Stepper Motor based //on the state of the panel (loaded or unloaded) //Panel A : 0 is loaded, 1 is unloaded //Panel X : 2 is loaded, 3 is unloaded //Panel Y : 4 is loaded, 5 is unloaded //Panel Z : 6 is loaded, 7 is unloaded switch (x) { case 0: remaining = steps[0]; steps[0] = remaining + 800; break; case 1: remaining = steps[0]; steps[0] = (remaining - 800); break; case 2: remaining = steps[1]; steps[1] = remaining + 800; break; case 3: remaining = steps[1]; steps[1] = (remaining - 800); break; case 4: remaining = steps[2]; steps[2] = remaining + 800; break; case 5: remaining = steps[2]; steps[2] = (remaining - 800); break; case 6: remaining = steps[3]; steps[3] = remaining + 800; break; case 7: remaining = steps[3]; steps[3] = (remaining - 800); break; } }
Leave a Reply
You must be logged in to post a comment.