Alice’s Board

Overall photo
Video of working machine: when rotational speed is at its highest, the propeller spins fast and presses down on the force sensor a lot, which translates to the maximum rotation of servo motors bringing the magnet all the way to the right edge of the board.
Detail photos of parts

Servo motors holding the magnet (our output)

Propeller connected to a DC motor, standing upright with force sensor at the bottom of the propeller arm (our middle step)
Noel’s Board

Video
The encoder constantly samples rotational speed and determines the voltage passed on to the motor. The greater force translates across the FSR and is translated into servo position. Jittery human input produces similarly jittery magnet position.
Detail photos of parts

Our solution to the servo-encoder interface between projects, featuring hot glue and luck

The DRV8833, the chip that allows us to drive the DC motor with more than on and off
Narrative Description:
Our input is rotational speed. That is measured by how fast the knob (rotary encoder) gets turned at the left of our boards. This rotational speed is then used to calculate how fast we turn our DC motor, which is connected to a propeller. The propeller sits upright in the middle of our board and it is built in a way so that depending on the spinning speed, the propeller arm tilts in differing amounts to press onto the square force sensor resistor at the bottom. Based on the force reading, degree of rotation for the servo motors are determined. The servos are holding a piece of magnet and greater rotation equals to further distance of the magnet to the next team’s board. With different distances, we create different strengths of magnetic field which is our output.
Progress Images:

An early mock up of the motor wiring. Using a motor with an encoder and voltage regulator board. Replaced by simple motor and motor driver.

Force Sensor Testing: wiring up the fsr to see how it reads by making it print in Serial Monitor
Testing out the positions of servo motors. This was a critical step as we changed from our original design of using rubber bands arms sliding the magnet along a track to using a rigid arm.

One of the final troubleshooting phases. After re-seating every cable and testing every sensor, an incompatible power supply was the culprit.
Discussion:
The development of this project was a rollercoaster. Initial ambitions were blinding and made issues very easy to overlook, and there were several that we did. We made two simple backups and spent all of our creative power on integrating a windmill. We easily overlooked issues like creating linear motion with servos, balancing and connecting a propeller, and the interface between encoder and previous project’s servo. Our imagined outcome was not adherent to all the real physical limitations, and this would be apparent when we tried to share ideas. Solidworks, hand drawings, and real physical progress ultimately bridged the gap between the imagined and the real.
Through our build we were constantly reminded of the many tools that had already been developed before us. In an attempt to use as powerful a motor as we could find, we showed our lack of intuition for thrust and knowledge of available parts. Thanks to our professor, online resources, and physical testing, we derived something efficient and conceivable. These resources were invaluable, as neither of us had any idea a DRV8833 (dual motor driver carrier) was an available tool. Peers also helped ideation, and the learning environment was a significant help in understanding what we could accomplish. Miniature breadboards and switching to square FSRs were two results of the vibrant learning environment.
One of the most frustrating steps was connecting libraries and pre-existing code with our own. From completely unresponsive to mapped and tuned scaling, the troubleshooting often involved scrapping entire drafts and scouring for relevant sample code. Here it was obvious that we were “standing on the shoulders of giants.” With documentation from the course webpage and from online users with valuable experience with each part, we had plenty of comments to read and build our understanding of how we could connect each sensor and output device. Despite the plethora of technical documentation, we still sometimes found ourselves adjusting parameters through trial and error. In combining available resources and first hand experience, we were able to develop the system we imagined.
Functional Block Diagram:
Schematic:
Code:
/*Project Title: Double Transducer: Rotational Speed -> Magnetic Field Strength
Team Members: Noel Barnes, Yuqi (Alice) Zhou
Description: Arduino takes in input from a rotary encode to calculate the rotational speed.
This speed is then mapped into a speed that's feed into the DC motor to drive a propeller.
FSR then picks up a force reading from the tilt of the Propeller arm.
The force reading is then mapped to a rotation degree for the servo motor.
Servo motors are moved based on the rotation to move the magentic forward.
Pin mapping:
Arduino pin | role | description
------------|--------|-------------
A0 input FSR for force sensing
2 input rotary encoder pin A
3 input rotary encoder pin B
5 input drv8833 A2 IN
6 input drv8833 A1 IN
10 output servo motor #1
11 output servo motor #2
SCL SCL of LCD
SDA SDA of LCD
Rotoray Encoder Code adapted from https://dronebotworkshop.com/rotary-encoders-arduino/#Arduino_Motor_Encoder_Sketch DroneBot Workshop 2019
DC motor code adapted from https://courses.ideate.cmu.edu/16-223/f2016/text/lib/WheelDrive.html#wheeldrive-doxygen
*/
#include <Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C screen(0x27, 16, 2);
// Motor encoder output pulse per rotation (change as required)
#define ENC_COUNT_REV 374
// Encoder output to Arduino Interrupt pin
#define ENC_IN 3
// Pulse count from encoder
volatile long encoderValue = 0;
// interval for measurements of rotational speed
int interval = 100;
const int quaterSec = 250;
unsigned long timer = 0;
long previousMillis = 0;
long currentMillis = 0;
int rpm = 0;
int rpmDisplay; // RPM display for the LCD screen
int motorRPM; // DC motor speed
int dcDisplay; // DC motor speed display for LCD screen
//make a servo object
Servo lilMotor;
Servo secMotor;
const int LILMOTORPIN = 11; // Servo Motor to pin 11
const int SECMOTORPIN = 10; // Servo Motor to pin 10
int force; // the analog reading from the FSR
int forceDisplay; // force display for the LDC screen
int fsrPin = 0; // the FSR and 10K pulldown are connected to a0
int rotation; // rotation degree for lilMotor
int rotationTwo; // rotation degree for secMotor
int rotationDisplay; // servo motor rotation display for LDC screen
#define A1PIN 6
#define A2PIN 5
void setup() {
// Set encoder as input with internal pullup
pinMode(ENC_IN, INPUT_PULLUP);
// Attach interrupt
attachInterrupt(digitalPinToInterrupt(ENC_IN), updateEncoder, RISING);
// Setup initial values for timer
previousMillis = millis();
// initialize DC motor
pinMode(A1PIN, OUTPUT);
pinMode(A2PIN, OUTPUT);
digitalWrite(A1PIN, LOW);
digitalWrite(A2PIN, LOW);
// tell Arduino where the servo motors are plugged in
lilMotor.attach(LILMOTORPIN);
secMotor.attach(SECMOTORPIN);
// initialize the screen (only need to do this once)
screen.init();
// turn on the backlight to start
screen.backlight();
// lcd display template
screen.home();
screen.print("i:");
screen.setCursor(6, 0);
screen.print("m:");
screen.setCursor(12, 1);
screen.print("o:");
}
void loop() {
// Update RPM value
currentMillis = millis();
if (currentMillis - previousMillis > interval) {
previousMillis = currentMillis;
// Calculate RPM
rpm = (float)(encoderValue * 60 / ENC_COUNT_REV);
//follow the range of 20-60 for rotational speed
if (rpm > 60) {
rpm = 60;
}
if (rpm < 20) {
rpm = 20;
}
encoderValue = 0;
}
//rotational speed display
if (millis() - timer >= quaterSec) {
screen.setCursor(2, 0);
rpmDisplay = map(rpm, 20, 60, 0, 99);
screen.print(rpmDisplay);
}
// run DC motor with RPM
motorRPM = map(rpm, 20 , 60, 80, 255); //normalize rotational speed to input as DC motor speed
digitalWrite(A1PIN, LOW);
analogWrite(A2PIN, motorRPM);
delay(2);
// DC motor speed display
if (millis() - timer >= quaterSec) {
screen.setCursor(8, 0);
dcDisplay = map(motorRPM, 80, 255, 0, 99); // normalized for display
screen.print(dcDisplay);
}
// force reading
force = analogRead(fsrPin);
// limit to a force range that's appropriate for the propeller arm setup
if (force > 400) {
force = 400;
}
if (force < 50) {
force = 50;
}
// force sensor display
if (millis() - timer >= quaterSec) {
forceDisplay = map(force, 50, 400, 0, 99); // normalized force for display
screen.setCursor(8, 1);
screen.print(forceDisplay);
}
// calculate rotation for the two servo motors
rotation = map(force, 50, 400, 10, 170); // map force reading to motor rotation degree
rotationTwo = 180 - rotation;
// servo motor rotation degree display
if (millis() - timer >= quaterSec) {
rotationDisplay = map(rotation, 10, 170, 0, 99); // normalized rotation degree for display
screen.setCursor(14, 1);
screen.print(rotationDisplay);
timer = millis();
}
// rotate the servo motors
lilMotor.write(rotation);
secMotor.write(rotationTwo);
}
void updateEncoder()
{
// Increment value for each pulse from encoder
encoderValue++;
}