Resistance Varying Sensors:
Potentiometer
Flex Sensor
Force Sensor
Voltage Varying Sensors:
IR Distance Sensor
Accelerometer
Count-based Sensors
Rotary encoder
Create a data logger
Read/Write values to EEPROM
Upload your report as a wiki page to your student folder, and when you are ready to turn it in, only move the report wiki page to the 'Inbox' folder. Your images will remain embedded in the page.
Include your responses and uploads to the green questions.
Include snippets of code that explain what you did.
Include a link to a video demo of your data logger, and please
follow the Lab Report Guidelines.
Set up the LED output and potentiometer input circuits from the following schematic on your breadboard. Don't forget that there's a big difference between "analog pin 0" and "pin 0." This setup is much like lab 2's LED fade, except now we're using analogRead to control the fade.
Try the code in File->Examples->Analog->AnalogInput.
Change the code so that the LED fades and brightens with the analog value of the potentiometer, like a dimmer.
Post a copy of your new code in your lab writeup.
int sensorPin = A0; // select the input pin for the potentiometer
int ledPin = 9; // select the pin for the LED
int sensorValue = 0; // variable to store the value coming from the sensor
void setup() {
// declare the ledPin as an OUTPUT:
pinMode(ledPin, OUTPUT);
}
void loop() {
// read the value from the sensor (0 - 1024)):
// divide because we need range from 0 - 255
sensorValue = analogRead(sensorPin)/4;
// turn the ledPin on
analogWrite(ledPin, sensorValue)
}
✔
Use the code from File->Examples->Communication->Graph as a template to print data from your potentiometer to the serial monitor.
a. Based on the readings from the serial monitor, what is the range of the analog values being read?
According to the serial monitor, the digital values being read back span from 0 to 1023. Since that is the case, this potentiometer's output goes from 0 to +5V.
✔
b. How many bits of resolution does the analog to digital converter (ADC) on the Atmega32U4 have [hint: where might you look to find this sort of thing]? How many are you using with the range of values you're seeing?
According to the AVR atmega32U4 datasheet, the ADC provides 10 bits of resolution. That is precisely what the serial monitor readings show.
✔ Great that you read the datasheet :)
The Flex sensor changes resistance between 8k-10k Ohms (straight) and 20k-50k Ohms (bent). The datasheet is here -- note that the datasheet states they should be 10k Ohms straight and 60-110k Ohms while bent ±30%. We'll build a voltage divider circuit with a 22k resistor, using the Flex sensor with the fading LED/potentiometer code from the last exercise:
✔
a. What resistance do you see with a Multimeter when the sensor is flat? When it is bent?
When he sensor is flat, it measures at 14k. When the sensor is extremely bent, it measured as much as 82k.
✔
b. What kind of voltages should we expect for the Teensy analog pin based on the sensor resistance?
At 14k, the voltage at A0 will be about 3V. At the other end of the extreme (82k), the voltage at A0 will be about 1V.
✔
c. How does the range of the LED's brightness change compared to the potentiometer?
Compared to the potentiometer, the LED will not glow as bright and it won't dim as much either.
✔
Change the LED fading code values so that you get the full range of output voltages from using your Flex sensor.
d. Include a copy of your Lowly Multimeter code in your lab write-up.
/*
Lowly Multimeter
Demonstrates analog input by reading a flex sensor on Analog Pin 0,
and displayig the digital value representations on a 16x2 LCD
The circuit:
* Flex Sensor attached to analog input 0 on one end,
* and one side pin (either one) to ground
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
* Code adapted from efforts by:
* David A. MEllis, Tom Igoe, Limor Fried
*/
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int sensorPin = A0; // select the input pin for the flex sensor
int ledPin = 9; // select the pin for the LED
int sensorValue = 0; // variable to store the value coming from the sensor
void lcdprintSensorValue(int sensorValue) {
// go to (0,0):
lcd.clear();
// Print a header message to the LCD.
lcd.print("Sensor Value:");
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(0, 1);
// Print the sensor value to the LCD
lcd.print(sensorValue);
}
void setup() {
// declare the ledPin as an OUTPUT:
pinMode(ledPin, OUTPUT);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
}
void loop() {
// read the value from the sensor (0 - 1024)):
// divide because we need range from 0 - 255
// re-normalize to zero for the flex sensor by subtracting 50
// expand the measurement range by multiplying by 2.5
sensorValue = analogRead(sensorPin)/4;
sensorValue -= 50;
sensorValue *= 2.5;
// turn the ledPin on
analogWrite(ledPin, sensorValue);
// Print the sensor value to the LCD
lcdprintSensorValue(sensorValue);
// allow the changing sensor values to be visible
// instead of changing so quickly.
delay(100);
}
✔ Great commenting
Just like the Flex sensor, the FSR changes resistance - in this case, when pressure is applied to the FSR (Wikipedia page). Here's the datasheet. We can reuse the same circuit as before without LED circuit.
Build two FSR circuits to enable a game of thumb wrestling.
a. What resistance values do you see from your force sensor?
The sensor range changes very quickly from 1 megOhm, all the way down to almost 0 ohms.
b. What kind of relationship does the resistance have as a function of force applied? (eg, linear?)
It appears as if there is a power relationship between force and resistance.
c. Include a copy of your FSR thumb wrestling code in your lab write-up.
Thumb Wrestling
Demonstrates analog input by reading analog sensors on analog pins 4 and 5, and
summing the analog sensor values for each sensor and displaying each on an LCD.
The higher value "wins" the thumb wrestling match.
The circuit:
* Voltage divider circuit with FSR sensor on the "high side"
* one FSR pin attached to analog input 0
* other FSR pin attached to Vcc
* a 22K resistor on the "low side"
* one 22K end attached to FSR pin and analog input 0
* other 22K end attached to ground
* This code is based on the AnalogInput code example:
Created by David Cuartielles
Modified 4 Sep 2010
By Tom Igoe
http://arduino.cc/en/Tutorial/AnalogInput
*/
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int sensorPinA = A4; // select the input pin for force sensor 1
int sensorPinB = A5; // select the input pin for force sensor 2
int ledPin = 9; // select the pin for the LED
int sensorValueA = 0; // variable to store the value coming from the sensor
int sensorValueB = 0; // variable to store the value coming from the sensor
int lcd_columnA = 2;
int lcd_columnB = 12;
void lcdprintSensorValue(int sensorValue, int lcd_column) {
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(lcd_column, 1);
// Print the sensor value to the LCD
lcd.print(sensorValue);
}
void setup() {
// declare the ledPin as an OUTPUT:
pinMode(ledPin, OUTPUT);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
}
void loop() {
for (int i=0; i < 30; i++) {
// read both sensors values (0 - 1024)):
// divide because we need range from 0 - 255
// and create a running total of each amount
sensorValueA += analogRead(sensorPinA)/4;
sensorValueB += analogRead(sensorPinB)/4;
// go to (0,0):
lcd.clear();
// print a header line to lcd
lcd.print("TOTAL_A - TOTAL_B");
// Print the sensor value to the LCD
lcdprintSensorValue(sensorValueA, lcd_columnA);
// Print the sensor value to the LCD
lcdprintSensorValue(sensorValueB, lcd_columnB);
// allow the changing sensor values to be visible
// instead of changing so quickly.
delay(100);
}
}
Some more sophisticated sensors have ICs that measure physical phenomena and then output an analog voltage level, varying voltage much as a voltage divider circuit would.
The circuit for the distance sensor is simple: just connect red to 5V, black to ground, and yellow to analog input 0.
Use your Lowly Multimeter program to look at the data the sensor returns.
Move your hand or a piece of paper over the sensor and see how the readings vary with distance.
a. Describe the voltage change over the sensing range of the sensor.
When the field of view is clear, the ADC readings range between 0 and 15 (out of 255). As a paper is brought closer to the sensor, the values begin increasing in a linear-like fashion until a maximum of about 160 is generated. The values then plateau for a short distance before they begin dropping again as the paper is brought all the way up to the sensor. The value at that end is about 50.
b. Does it match up with what you expect from the datasheet?
The shape of the curve would be identical as that on the datasheet, though the values would be relative.
Start off with the example code that reads values from a 3-axis accelerometer out to a computer over the serial monitor. Use your mighty coding skills to indicate what your readings are on the X, Y and Z axes of the accelerometer on your LCD panel!
a. Include your accelerometer read-out code in your write-up.
/*
ADXL3xx
Reads an Analog Devices ADXL3xx accelerometer and communicates the
acceleration through a 16x2 LCD.
Accelerometer pinout:
1.G - Ground
2.Vin- Voltage, 5V
3.Xo- X axis output, 0 - 3.3V
4.Yo- Y axis output, 0 - 3.3V
5.Zo- Z axis output, 0 - 3.3V
6.G - Ground
7.3V0 - 3.3V output from the voltage regulator
8.GS - tie low for 1.5g sensing, high for 6g sensing
9.ST - self test pin (not needed for this assignment)
http://www.arduino.cc/en/Tutorial/ADXL3xx
The circuit:
analog 0: accelerometer self test
analog 1: z-axis
analog 2: y-axis
analog 3: x-axis
created 2 Jul 2008
by David A. Mellis
modified 4 Sep 2010
by Tom Igoe
This example code is in the public domain.
*/
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// these constants describe the pins. They won't change:
const int xpin = A1; // x-axis of the accelerometer
const int ypin = A2; // y-axis
const int zpin = A3; // z-axis (only on 3-axis models)
int xValue = 0;
int yValue = 0;
int zValue = 0;
void setup()
{
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
}
void loop()
{
// print the sensor values:
xValue = analogRead(xpin);
yValue = analogRead(ypin);
zValue = analogRead(zpin);
// go to (0,0):
lcd.clear();
// Print a header message to the LCD.
lcd.print("XXX - YYY - ZZZ");
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(0, 1);
lcd.print(xValue);
lcd.print(" - ");
lcd.print(yValue);
lcd.print(" - ");
lcd.print(zValue);
// delay before next reading:
delay(100);
}
Your data logger will have two main modes: one where it logs data and another where it plays the data back.
Create a state diagram sketch that indicates how you'd like to switch between one mode and the other, and also what you'd like the program to do in each state.
a. Turn in a copy of your final state diagram.
✔ Nice state diagram!
The Atmega32U4 on the Teensy has 1K bytes of internal EEPROM.
a. How many byte-sized data samples can you store on the Atmega32U4?
1024 samples.
✔
b. How would you get your analog data from the ADC to be byte-sized?
Cast the values as “char” prior to storing them into the EEPROM.
✔
Your logger should be able to record a stream of analog data (at a sample rate of your desire) and then play it back at some later point in time. You are welcome to play back to either the 16x2 LCD or the serial monitor.
a. Use the lab camera or your own camera/cell phone to record and upload a short demo video of your logger in action.
b. Here is the datalogger code:
#include <EEPROM.h>
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
#define PBUTTON_PRESS LOW
#define FLEX_THRESHOLD 100
#define IDLE 0x01
#define WAIT_CHANGE_STATE 0x02
#define RECORD_SITTING 0x04
#define RECORD_STANDING 0x08
#define PLAYBACK_STATE 0x10
#define CLEAR_EEPROM_STATE 0x20
#define SITTING 0
#define STANDING 1
#define MILLISECONDS_PER_SECOND 1000
#define EEPROM_ADDRESS_MAX 255
// set pin numbers - constants won't chang
const int PBUTTON_RECORD_PIN = 6; // the number of the pushbutton pin
const int PBUTTON_PLAY_PIN = 0; // the number of the pushbutton pin
const int FSR_SENSOR_PIN = A4; // select the input pin for the force sensor
const int LED_PIN = 15; // select the pin for the LED
// Initialize any sensor variables
int forceValue = 0; // variable to store the value coming from the sensor
int buttonState_play = 0; //
int buttonState_record = 0; //
// Initialize the state variables
int active_state = IDLE;
int next_state = IDLE;
int userPosition = STANDING;
int sitting_event = 0;
int standing_event = 0;
int writing_done_event = 0;
int playback_done_event = 0;
int clear_done_event = 0;
// Initialize other variables
int address = 0;
unsigned int timestamp_readvalue = 0x0000;
unsigned int timestamp_lsb = 0x0000;
unsigned int timestamp_msb = 0x0000;
unsigned char value = 0x00;
unsigned long initial_timestamp = 0;
unsigned long running_timestamp = 0;
char running_timestamp_lobyte = 0x00;
char running_timestamp_hibyte = 0x00;
void setup()
{
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// initialize the serial communication:
Serial.begin(9600);
// initialize the pushbutton pin as an input:
pinMode(PBUTTON_RECORD_PIN, INPUT);
// initialize the pushbutton pin as an input:
pinMode(PBUTTON_PLAY_PIN, INPUT);
}
void loop()
{
// read the state of the pushbutton value:
buttonState_play = digitalRead(PBUTTON_PLAY_PIN);
// read the state of the pushbutton value:
buttonState_record = digitalRead(PBUTTON_RECORD_PIN);
// read the flex sensor value (0 - 1024)
// divide because we need range from 0 - 255
forceValue = analogRead(FSR_SENSOR_PIN)/4;
// Print the forceValue
Serial.print("ForceValue:");
Serial.print("\t");
Serial.print(forceValue);
Serial.println();
// Print the userPosition
Serial.print("userPosition:");
Serial.print("\t");
Serial.print(userPosition);
Serial.println();
// determine if there has been a change in user position
if (userPosition == SITTING && forceValue > FLEX_THRESHOLD)
standing_event = 1;
if (userPosition == STANDING && forceValue < FLEX_THRESHOLD)
sitting_event = 1;
// check if the pushbutton is pressed.
// if it is, the buttonState is PRESSED:
if (buttonState_record == PBUTTON_PRESS) {
if(active_state == IDLE) {
initial_timestamp = millis();
next_state = WAIT_CHANGE_STATE;
}
if(active_state == WAIT_CHANGE_STATE)
next_state = IDLE;
}
if (buttonState_play == PBUTTON_PRESS) {
if(active_state == IDLE)
next_state = PLAYBACK_STATE;
}
if (sitting_event == 1 ) {
sitting_event = 0;
running_timestamp = (millis() - initial_timestamp)/MILLISECONDS_PER_SECOND;
if(active_state == WAIT_CHANGE_STATE)
next_state = RECORD_SITTING;
}
if (standing_event == 1 ) {
standing_event = 0;
running_timestamp = (millis() - initial_timestamp)/MILLISECONDS_PER_SECOND;
if(active_state == WAIT_CHANGE_STATE)
next_state = RECORD_STANDING;
}
if (writing_done_event == 1) {
writing_done_event = 0;
if(active_state == RECORD_SITTING)
next_state = WAIT_CHANGE_STATE;
if(active_state == RECORD_STANDING)
next_state = WAIT_CHANGE_STATE;
}
if (playback_done_event == 1) {
playback_done_event = 0;
if(active_state == PLAYBACK_STATE)
next_state = CLEAR_EEPROM_STATE;
}
if (clear_done_event == 1) {
clear_done_event = 0;
if(active_state == CLEAR_EEPROM_STATE)
next_state = IDLE;
}
active_state = next_state;
// print active state
Serial.println(active_state);
delay(250);
switch ( active_state )
{
case IDLE:
// turn the ledPin off
analogWrite(LED_PIN, 0x00);
break;
case WAIT_CHANGE_STATE:
// turn the ledPin on
analogWrite(LED_PIN, 0x80);
break;
case RECORD_SITTING:
// Set the new user position
userPosition = SITTING;
// create the two-byte timestamp. the msb of hibyte specifies
// sitting (0) or standing (1)
running_timestamp_lobyte = (char)running_timestamp >> 8;
running_timestamp_hibyte = (char)running_timestamp;
running_timestamp_hibyte &= 0x7F;
// write the two parts of the 16-bit timestamp to the EEPROM
EEPROM.write(address, running_timestamp_lobyte);
EEPROM.write(address+1, running_timestamp_hibyte);
// print timestamp to LCD
lcd.clear();
lcd.print("Timestamp:"); // print header on first row
lcd.setCursor(0, 1);
lcd.print(running_timestamp); // print timestamp on second row
// advance to the next address of the EEPROM.
address += 2;
writing_done_event = 1;
break;
case RECORD_STANDING:
// Set the new user position
userPosition = STANDING;
// create the two-byte timestamp. the msb of hibyte specifies
// sitting (0) or standing (1)
running_timestamp_lobyte = running_timestamp >> 8;
running_timestamp_hibyte = (char)running_timestamp;
running_timestamp_hibyte |= 0x80;
EEPROM.write(address, running_timestamp_lobyte);
EEPROM.write(address+1, running_timestamp_hibyte);
// print timestamp to LCD
lcd.clear();
lcd.print("Timestamp:"); // print header on first row
lcd.setCursor(0, 1);
lcd.print(running_timestamp); // print timestamp on second row
// advance to the next address of the EEPROM.
address += 2;
writing_done_event = 1;
break;
case PLAYBACK_STATE:
// reset the address to begin reading from the first sensor reading
address = 0;
// read back all 30 samples and print them to the serial monitor
for (int i=0; i < EEPROM_ADDRESS_MAX; i+=2)
{
// read a byte from the current address of the EEPROM
value = EEPROM.read(address);
timestamp_lsb += value;
value = EEPROM.read(address + 1);
timestamp_msb += value;
timestamp_msb <<= 8;
timestamp_readvalue = timestamp_msb + timestamp_lsb;
if(timestamp_readvalue == 0)
i = EEPROM_ADDRESS_MAX;
Serial.print(address);
Serial.print("\t");
if (timestamp_readvalue > 0x7FFF) {
timestamp_readvalue &= 0x7FFF;
Serial.print(timestamp_readvalue, DEC);
Serial.print(" - STANDING");
} else {
Serial.print(timestamp_readvalue, DEC);
Serial.print(" - SITTING");
}
Serial.println();
// advance to the next address of the EEPROM
address += 2;
delay(100);
timestamp_readvalue = 0x0000;
timestamp_lsb = 0x0000;
timestamp_msb = 0x0000;
}
playback_done_event = 1;
delay(10000);
break;
case CLEAR_EEPROM_STATE:
address = 0;
// clear all samples from EEPROM
for (int i=0; i < EEPROM_ADDRESS_MAX; i++)
{
// read a byte from the current address of the EEPROM
EEPROM.write(address, 0x00);
Serial.print(address);
Serial.print("\t");
Serial.print("- clear");
Serial.println();
// advance to the next address of the EEPROM
address++;
delay(100);
}
clear_done_event = 1;
break;
default:
active_state = IDLE;
next_state = IDLE;
userPosition = STANDING;
sitting_event = 0;
standing_event = 0;
writing_done_event = 0;
playback_done_event = 0;
initial_timestamp = 0;
running_timestamp = 0;
lcd.clear();
lcd.print("** STATE_ERROR ** "); // print header on first row
lcd.setCursor(0, 1);
lcd.print("Default state entered!"); // print timestamp on second row
delay(10000);
break;
}
}
✔ Nicely done.