Can I measure my breath? (Part 2) #
For this week, I wanted to build off of my input board, and use the rubber sensor to drive the output servos for my fashion piece. For the micro-controller, I wanted to try using the ATtiny412 since it is much smaller and can draw less power than the Xiao rp2040s I usually prefer.
I started by adding to my original schematic, with ATtiny412 instead. I wanted 2 servos to have syncronized movements, so I connected them to the same PWM pin. I also included header pins with 5V, ground, and UPDI to program the ATtiny.
I kept the schematic for measuring the resistance from the rubber resistor the same, but now the analog input pins on the ATtiny can take 5V, so I didn’t have to worry about only sending 3.3V.
Here is the final board: #
I named my project dissection to reflect how the “lungs” would be worn externally. I also added header pins that include a 5V, ground, and UPDI programming pin to program the ATtiny.
I then milled the board and soldered the components.
Programming the ATtiny #
Programming the board turned out to be more of a challenge, however. First, I had to install a hardware package to Arduino IDE, then flash firmware onto an Arduino Micro Pro to turn it to a UPDI programmer, so it could then talk to the ATtiny. I had to add the Pro Micro as an ISP to a Programmer’s List and choose “Arduino as ISP (ATTiny core)”. I also made sure to install MegaTinyCore. I used a breadboard to connect the Arduino Micro Pro’s pins to the header pins I included on the PCB.
Then I was able to upload a simple servo code and move a servo.
But when I tried to control the servo using the stretch from the rubber resistor, I ran into some issues.
First, the servo was very noisy, but then I tried dividing the raw voltage by 1023, because the analog input pin is for 10 bits. I also used PA1 for analogRead rather than 2, which is the designated pin number. This seemed to solve some issues.
Then I ran into the issue where the servo was always rotating to 180 degrees, instead of an intermediary value controlled by the change in resistance from the rubber. I used the built in map() function for Arduino to try to map resistance values to values from 0 - 180, which would be angles for the servo position.
I then started to use the raw V_out, instead of the resistance I calculated to try to troubleshoot. Programming the ATtiny was especially difficult because my sensor (the resistive rubber) was a bit more unpredictable and ATtiny does not have the serial print because it uses UPDI. I played around with my numbers, and double checked my equation, and then I realized my math for calculating resistance was wrong. Oops!
This is the fixed equation:
This was because V_out came out from the first resistor, which is the rubber I was using. This meant I had to rearrange the numbers and flip the sign! The issue with the servo moving all the way to 180 degrees and not responding to further input was probably due to the fact that I was feeding the servo way too big of a number, and it was sensing it as a PWM signal rather than a fixed angle. From a quick search, it does seem that any values above 200 are sent to the servo as PWM signals, rather than positions in degrees. I also ensured that the internal reference voltage in the ATtiny was surely 5V with the line:
analogReference(VDD);
After this, it fixed some issues:
But the movement from the servo was still very small, and I wanted at least 90 degrees of rotation for my project. I tuned the numbers to around the nominal resistance for the unstretched cord (5k ohms) and divided by 180 to normalize to servo positions in degrees, then I also used the constrain function between 0 and 180 to ensure that only degrees, and not pwm signals, are sent to the servo:
myservo.write(constrain(r_stretch * 180.0 / 5000, 0, 180));
Then I also included a “reset” function in the setup of my code to ensure that the servo would return to its zero position every time it boots. This allows me to have a wider range of motion for the servo before hitting the 180 degree limit. I would take an initial measurement of where the servo moves to based on the nominal, unstretched resistance, then subtract that from the servo’s starting position to ensure that it would always start at 0 degrees.
#include <Servo.h>
Servo myservo; // create servo object to control a servo
// twelve servo objects can be created on most boards
int pos = 0; // variable to store the servo position
int v_out = 0;
float v_norm;
float r_stretch;
float strech_out;
float offset = 0;
void setup() {
myservo.attach(4); // attaches the servo on pin 9 to the servo object
analogReference(VDD);
v_out = analogRead(PIN_PA1);
// divide by 1023 for 10 bits
v_norm = (v_out / 1023.0);
// subtract offset so servo starts at 0
offset = (1 - v_norm) / v_norm * 11000;
}
void loop() {
v_out = analogRead(PIN_PA1);
v_norm = (v_out / 1023.0);
// calculating resistance based on 11k ohm resistor in voltage divider
r_stretch = ((1 - v_norm) / v_norm * 11000)-offset;
myservo.write(constrain(r_stretch * 180.0 / 5000, 0, 180));
// for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
// // in steps of 1 degree
// myservo.write(pos); // tell servo to go to position in variable 'pos'
// delay(15); // waits 15ms for the servo to reach the position
// }
// for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
// myservo.write(pos); // tell servo to go to position in variable 'pos'
// delay(15); // waits 15ms for the servo to reach the position
// }
}
After, the servo is looking great, and responds quite sensitively to the resistance changes in the rubber.