Calculate amplitude of a signal after application FFT - arduino

I'm using Arduino Due and sensor for noise and I applied FFT library to extract frequency and it's working perfectly. But I don't know how to calculate the amplitudes and print them on console?
Here is the code:
#include "arduinoFFT.h"
#define SAMPLES 32
#define SAMPLING_FREQUENCY 1000
arduinoFFT FFT = arduinoFFT();
unsigned int sampling_period_us;
unsigned long microseconds;
double vReal[SAMPLES];
double vImag[SAMPLES];
void setup() {
Serial.begin(9600);
sampling_period_us = round(1000000*(1.0/SAMPLING_FREQUENCY));
}
void loop() {
/*SAMPLING*/
for(int i=0; i<SAMPLES; i++)
{
microseconds = micros();
vReal[i] = analogRead(0);
vImag[i] = 0;
while(micros() < (microseconds + sampling_period_us)){
}
}
/*FFT*/
FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
double peak = FFT.MajorPeak(vReal, SAMPLES, SAMPLING_FREQUENCY);
Serial.println(peak);
for(int i=0; i<(SAMPLES/2); i++)
{
Serial.print((i * 1.0 * SAMPLING_FREQUENCY) / SAMPLES, 1);
Serial.print(" ");
//Serial.println(vReal[i], 1); //View only this line in serial plotter to visualize the bins
}
delay(1000);
while(1);
}

The serial plotter does not have a number of sample to plot functionality.
If you want to view the amplitudes for each window correctly, you will need to use the following code
for(int i=0; i<(SAMPLES/2); i++)
{
// Serial.print((i * 1.0 * SAMPLING_FREQUENCY) / SAMPLES, 1);
for(int i=0; i<20; i++)//Adjust the 20 to make space between each samples.
Serial.print(0);
Serial.print(vReal[i]);
}
And then adjust the size of the serial plotter to fit your window.
I hope this helps.

Related

Arduino loop stops after for loop in called function

I am trying to make a radio controlled car which reacts to the frequency of music. The Loop works as it should and it gives me the correct frequency. The function berekenGrootte works too. This gives me the size of the frequency (max frequency - min frequency). But as soon as I want to use the function verdeel() which divides the size of the frequency in 10 to make the car move smoother it does it one or two times and then I don't get any feedback anymore in my serial monitor. Do I use too much Serial.print or do I need to restart the loop after the for loop in the second function?
#include <FreqMeasure.h>
void setup()
{
Serial.begin(57600);
pinMode(3, OUTPUT);
pinMode(5, OUTPUT);
pinMode(2, OUTPUT);
pinMode(4, OUTPUT);
FreqMeasure.begin();
}
double sum = 0;
int count = 0;
float lowest = 50;
float highest = 0;
float grootte = 50;
float b[9][1];
void loop()
{
if (FreqMeasure.available()) {
// average several reading together
sum = sum + FreqMeasure.read();
count = count + 1;
if (count > 30) {
float frequency = FreqMeasure.countToFrequency(sum / count);
Serial.println(frequency);
sum = 0;
count = 0;
if (frequency < lowest) {
lowest = frequency;
Serial.print("new lowest: ");
Serial.println(lowest);
berekenGrootte();
}
else if (frequency > highest) {
highest = frequency;
Serial.print("new highest: ");
Serial.println(highest);
berekenGrootte();
}
/*if(frequency > 1000){
digitalWrite(3,HIGH);
delay(1000);
digitalWrite(3,LOW);
}
else{
digitalWrite(5,HIGH);
delay(1000);
digitalWrite(5,LOW);
}*/
}
}
}
void berekenGrootte()
{
grootte = highest - lowest;
Serial.print("new grootte: ");
Serial.println(grootte);
verdeel();
}
void verdeel()
{
float plength = grootte / 10;
b[0][0] = lowest;
b[0][1] = lowest + plength;
Serial.print("low: ");
Serial.println(b[0][0]);
Serial.print("high: ");
Serial.println(b[0][1]);
float startvalue = lowest + plength;
for (int i = 1; i < 10; i++) {
b[i][0] = startvalue;
b[i][1] = startvalue + plength;
startvalue = startvalue + plength;
Serial.print(i);
Serial.print(" low: ");
Serial.println(b[i][0]);
Serial.print(i);
Serial.print(" high: ");
Serial.println(b[i][1]);
}
}
You're overstepping the bounds of your b array.
float b[9][1];
The array is only 9 * 1.
void verdeel()
{
float plength = grootte / 10;
b[0][0] = lowest;
b[0][1] = lowest + plength;
So there is no element b[0][1]. The second number can only go up to 0 since the array is size 1 in that dimension. An array of size 1 is pretty useless as an array.
You have the same problem here in this loop at the end of that function:
for (int i = 1; i < 10; i++) {
b[i][0] = startvalue;
b[i][1] = startvalue + plength;
This for loop allows i to go to 9 which is too large for the array.

Arduino Wheel Speed Sensor Detect Direction

I'm trying to develop a wheel counter that determines both rotation rate, and the direction of rotation.
I have two magnetic pickup sensors mounted close together, and two metal chunks on the wheel 180 degrees apart.
Currently I have been able to measure rotation rate and convert this to distance, and the distance shown looks good. Here is the code for that:
volatile byte half_revolutions;
volatile byte half_revolutions_b;
volatile unsigned long last_time_a;
volatile unsigned long last_time_b;
volatile byte dir;
unsigned int rpm;
unsigned int rpm_b;
unsigned long timeold;
unsigned long timeold_b;
unsigned long time_print;
double distance = 0.0;
void setup()
{
Serial.begin(9600);
attachInterrupt(0, rpm_fun, RISING);
attachInterrupt(1, rpm_fun_b, RISING);
half_revolutions = 0;
half_revolutions_b = 0;
rpm = 0;
timeold = 0;
timeold_b = 0;
time_print = 0;
dir = 1;
}
void loop()
{
int rpm_guess;
if (half_revolutions >= 2)
{
rpm = 30*1000/(millis() - timeold)*half_revolutions;
timeold = millis();
half_revolutions = 0;
}
else if (millis() - timeold > 1000)
{
rpm = 0;
}
if (half_revolutions_b >= 2)
{
rpm_b = 30*1000/(millis() - timeold_b)*half_revolutions_b;
timeold_b = millis();
half_revolutions_b = 0;
}
else if (millis() - timeold_b > 1000)
{
rpm_b = 0;
}
if (millis() - time_print > 500)
{
rpm_guess = ((int)rpm + (int)rpm_b) / 2.0;
double rad_per_sec = (6.0*3.14159* rpm_guess)/180.0;
double metres_per_sec = rad_per_sec*0.038;
distance += metres_per_sec * 0.5;
Serial.print((int)last_time_b - (int)last_time_a);
Serial.print(",");
Serial.println(distance);
time_print = millis();
}
}
void rpm_fun()
{
half_revolutions++;
last_time_a = micros();
//Each rotation, this interrupt function is run twice
}
void rpm_fun_b()
{
half_revolutions_b++;
last_time_b = micros();
//Each rotation, this interrupt function is run twice
}
I was hoping to use the fact that sensor A should lead sensor B if rotation is clockwise, and vice-versa if anti-clockwise. However my logic doesn't seem to be working properly, Serial.print((int)last_time_b - (int)last_time_a); seems to switch between positive and negative no matter the direction I'm travelling.
I'd really appreciated any help with this.
I would rather do the direction guess in the interrupt handler:
void rpm_fun() {
if (last_time_a > last_time_b) dir = 0;
else dir = 1;
half_revolutions++;
last_time_a = micros();
}
void rpm_fun_b() {
if (last_time_a > last_time_b) dir = 0;
else dir = 1;
half_revolutions_b++;
last_time_b = micros();
}
If you experience "bounces", you could add some "debouncing" code.

Arduino sensor data

Hi I am using an Arduino Flex sensor to control a video game character.
The sensor data is being averaged and remapped to a value of between 0-6.
When the player flexes their bicep it reads the max value (this is perfect) however if the player flexes their arm hard and the sensor reads a max value of 6 for some reason as the player relaxes their arm the declining flex values are being passed into the game engine (instead of going from 6 to zero it drops from 6 to 5 to 4 to 3 to 2 to 1 before reaching zero). Can someone please advise how I should alter my code to make the sensor reading return to 0 instead of declining gradually?
#define NUM_LED 6 //sets the maximum numbers of LEDs
#define MAX_Low 75 //for people with low EMG activity
#define MAX_High 150//for people with high EMG activity
#define Threshold 3 // this sets the light to activate TENS
int reading[10];
int finalReading;
int MAX = 0;
int TENS =3;
int ledState = LOW;
byte litLeds = 0;
byte multiplier = 1;
byte leds[] = {8, 9, 10, 11, 12, 13};
char ch;
char contact;
void setup(){
Serial.begin(9600); //begin serial communications
digitalWrite(TENS, LOW);
for(int i = 0; i < NUM_LED; i++){ //initialize LEDs as outputs
pinMode(leds[i], OUTPUT);
pinMode(TENS, OUTPUT); // Set TENS output to StimPin
}
MAX = MAX_High; //This sets the default to people with high EMG activity.
}
void loop(){
for(int i = 0; i < 10; i++){ //take ten readings in ~0.02 seconds
reading[i] = analogRead(A0) * multiplier;
delay(2);
}
for(int i = 0; i < 10; i++){ //average the ten readings
finalReading += reading[i];
}
finalReading /= 10;
for(int j = 0; j < NUM_LED; j++)
{
digitalWrite(leds[j], LOW);//write all LEDs low and stim pin low
}
finalReading = constrain(finalReading, 0, MAX);
litLeds = map(finalReading, 0, MAX, 0, NUM_LED);
Serial.println(litLeds);
for(int k = 0; k < litLeds; k++){
digitalWrite(leds[k], HIGH); // This turns on the LEDS
}
{
// send data only when you receive data:
if (Serial.available() > 0)
{
ch = Serial.read();
contact=digitalRead(TENS);
if (ch == 'A' && contact==LOW)
{
digitalWrite(TENS, HIGH);
}
else if (ch == 'B' && contact==HIGH)
{
digitalWrite(TENS, LOW);
}
}
}
delay(80);
}
Your sensor doesn't read 6 and immediately 0, you are reading it so fast that you can read the intermediate values. For example: if player opens the arm very fast (250-300 ms, it is fast), you can take up to 3 readings then you can read 3 intermediate values.
You can add more time to the last delay() or use only a value that is the same that the 3 last readings to ensure that there isn't a fast change.

Arduino issues with fsr and LCD

I am doing a project which requires the use of arduino uno, 4 force sensors and a 16x2 LCD. I'm trying to display the readings of the force sensors onto the LCD with the implementation of the buttons. For eg. if I press the up button it should display the first force sensor reading. The problem is it just displays a huge number of 143164976.0000 even when no force is applied on it. Please advice on whether there is a problem to my coding.
int iForceSensorReading; // the analog reading from the FSR resistor divider
int iForceSensorReading1;
int iForceSensorReading2;
int iForceSensorReading3;
int iForceSensorVoltage; // the analog reading converted to voltage
int iForceSensorVoltage1;
int iForceSensorVoltage2;
int iForceSensorVoltage3;
unsigned long ulForceSensorResistance;// The voltage converted to resistance, can be very big so make "long"
unsigned long ulForceSensorResistance1;
unsigned long ulForceSensorResistance2;
unsigned long ulForceSensorResistance3;
unsigned long ulForceSensorConductance;
unsigned long ulForceSensorConductance1;
unsigned long ulForceSensorConductance2;
unsigned long ulForceSensorConductance3;
float FsrForce = 0; // Finally, the resistance converted to force
float FsrForce1 = 0;
float FsrForce2 = 0;
float FsrForce3 = 0;
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
#define RED 0x1
#define YELLOW 0x3
#define GREEN 0x2
#define TEAL 0x6
#define BLUE 0x4
#define VIOLET 0x5
#define WHITE 0x7
void setup(void){
Serial.begin(9600); // send debugging information via the Serial monitor
lcd.begin(16, 2);
lcd.print("Hand Muscle");
lcd.setCursor(0,1);
lcd.print("Meter");
lcd.setBacklight(WHITE);
}
uint8_t i=0;
void loop(void){
uint8_t buttons = lcd.readButtons();
iForceSensorReading = analogRead(A0);//read index finger pressure
delay(30);
// analog voltage reading ranges from about 0 to 1023 which maps to 0V to 5V (= 5000mV)
iForceSensorVoltage = map(iForceSensorReading, 0, 1023, 0, 5000);
ulForceSensorConductance = conductanceFunction(ulForceSensorResistance, iForceSensorVoltage);
if (ulForceSensorConductance <= 1000){
FsrForce = ulForceSensorConductance / 80;
}
else{
FsrForce = ulForceSensorConductance - 1000;
FsrForce /= 30;
}
iForceSensorReading1 = analogRead(A1);//read middle finger pressure
delay(30);
iForceSensorVoltage1 = map(iForceSensorReading1, 0, 1023, 0, 5000);
if (iForceSensorVoltage1 == 0){
}
else{
ulForceSensorConductance1 = conductanceFunction(ulForceSensorResistance1, iForceSensorVoltage1);
delay(30);
}
if (ulForceSensorConductance1 <= 1000){
FsrForce1 = ulForceSensorConductance1 / 80;
}
else{
FsrForce1 = ulForceSensorConductance1 - 1000;
FsrForce1 /= 30;
}
iForceSensorReading2 = analogRead(A2);// read ring finger pressure
delay(30);
iForceSensorVoltage2 = map(iForceSensorReading2, 0, 1023, 0, 5000);
ulForceSensorConductance2 = conductanceFunction(ulForceSensorResistance2, iForceSensorVoltage2);
if (ulForceSensorConductance2 <= 1000){
FsrForce2 = ulForceSensorConductance2 / 80;
}
else{
FsrForce2 = ulForceSensorConductance2 - 1000;
FsrForce2 /= 30;
}
iForceSensorReading3 = analogRead(A3);//read little finger pressure
delay(30);
iForceSensorVoltage3 = map(iForceSensorReading3, 0, 1023, 0, 5000);
ulForceSensorConductance3 = conductanceFunction(ulForceSensorResistance3, iForceSensorVoltage3);
if (ulForceSensorConductance3 <= 1000)
{FsrForce3 = ulForceSensorConductance3 / 80;
}else
{FsrForce3 = ulForceSensorConductance3 - 1000;
FsrForce3 /= 30;
}
if (buttons) {
lcd.clear();
lcd.setCursor(0,0);
if (buttons & BUTTON_UP) {
lcd.print("Index Finger: ");
lcd.setCursor(0,1);
lcd.print(FsrForce,DEC);
lcd.setBacklight(WHITE);
}
if (buttons & BUTTON_DOWN) {
lcd.print("Little Finger: ");
lcd.setCursor(0,1);
lcd.print(FsrForce3,DEC);
lcd.setBacklight(WHITE);
}
if (buttons & BUTTON_LEFT) {
lcd.print("Middle Finger: ");
lcd.setCursor(0,1);
lcd.print(FsrForce1,DEC);
lcd.setBacklight(WHITE);
}
if (buttons & BUTTON_RIGHT) {
lcd.print("Ring Finger: ");
lcd.setCursor(0,1);
lcd.print(FsrForce2,DEC);
lcd.setBacklight(WHITE);
}
if (buttons & BUTTON_SELECT) {
lcd.print("SELECT ");
lcd.setBacklight(WHITE);
}
}
}
// The voltage = Vcc * R / (R + FSR) where R = 10K and Vcc = 5V
// so FSR = ((Vcc - V) * R) / V
int conductanceFunction(long x, long y)
{long result;
x = 5000 - y; // fsrVoltage is in millivolts so 5V = 5000mV
x *= 10000; // 10K resistor
x /= y;
result = 1000000/x; //ulForceSensorConductance2 = 1000000 measured in micromhos
return result;
}
first likely issue, is this symptom sounds like a signed vs un-signed conflict. I see in your code the following calls
ulForceSensorConductance = conductanceFunction(ulForceSensorResistance, iForceSensorVoltage);
where
int conductanceFunction(long x, long y)
but
int iForceSensorVoltage;
You are passing an int(signed) into the second argument. Using it on math and creating a resultant variable of
long result;
and returning that as a INT.
Where you may have other problems.
You should follow some basic diagnostic procedures.
Assuming this problem is on ALL channels. if not then it is likely electrical.
Focusing on one channel.
PUT IN PRINTS of the various variables, to see what is moving as expected and validate the math is as expected

Arduino - PD - Line Following

#include <QTRSensors.h>
#define NUM_SENSORS 5 //Number of sensors used
#define NUM_SAMPLES_PER_SENSOR 4 //Average 4 analog samples per sensor reading
#define EMITTER_PIN 13 //Emitter is controlled by digital pin 2
QTRSensorsAnalog qtra((unsigned char[]) {0,1,2,3,4},
NUM_SENSORS, NUM_SAMPLES_PER_SENSOR, EMITTER_PIN);
unsigned int sensorValues[NUM_SENSORS]; //Variable Array for sensor values
const double kp = .505; //Variable for adjusting KP Value
const double kd = 150; //Variable for adjusting KD Value
const int max_speed = 255; //Variable for the Maximum Speed of the Motors
unsigned int position = 0; //Variable for holding the calculated position
int set_point = 1500;//Variable for holding the value of the center of the line
int error = 0; //Variable for holding the error from center of line based on position
int last_error = 0; //Variable for holding the previous error
int white = 0; //Variable for reading the line over white or black
int max_difference = 80 ;
double spd_right; //Speed for the Right Motor
double spd_left; //Speed for the Left Motor
int derivative = 0; //Value for the derivative
int error_value = 0; //Value for the error_value calculated from the pd function
int key_s6 = 2;//Declaring Digital Push Button
int E1 = 5; //M1 Speed Control
int E2 = 6; //M2 Speed Control
int M1 = 4; //M1 Direction Control
int M2 = 7; //M2 Direction Control
int sensors_sum = 0;
//Setup Method that includes calibration
void setup(){
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(13, OUTPUT);
calibration();
set_Point();
}//Ends Setup
void loop (){
get_sum();
white_or_black();
pid_calc();
adjust_control();
set_motors();
}//Ends Loop*/
/*Method for calibrating set_point on individual tracks
*/
void set_Point(){
qtra.readCalibrated(sensorValues);
for (int i = 0; i <= 5; i++){
set_point = sensorValues[i] + set_point;
}//Ends For
}//Ends set_point Method
/*Method for Calibrating the sensors to the ambiance lighting and
* varying contrasts of each individual track.
*/
void calibration (){
for (int i = 0; i<250; i++){
qtra.calibrate();
delay(20);
}//Ends For
}//Ends Calibration Method
/*Method for calculating the sum of all the sensors to be used in the
* white_or_black method for determining the background color of the track
*/
void get_sum(){
qtra.readCalibrated(sensorValues);
for (int i = 0; i <= 5; i++){
sensors_sum = sensorValues[i] + sensors_sum;
}//Ends For
}//Ends get_sum Method
/*Method for determining the background color of the current portion of the
* track based off of the value returned by get_sum and adjusting a variable
* to follow a white of black line as necessary.
*/
void white_or_black(){
if (sensors_sum > 2000){
white = 1;
}//Ends If
else{
white = 0;
}//Ends Else
}//Ends white_or_black Method
/*Method for following the line based of the position, proportional,
* and derivative
*/
void pid_calc(){
position = qtra.readLine(sensorValues, QTR_EMITTERS_ON, white);
error = position - set_point;
derivative = error-last_error;
last_error = error;
error_value = int(error*kp+derivative*kd);
}//Ends pid_calc Method
void adjust_control(){
if (error_value>max_difference){
error_value = max_difference;
}//Ends If
else if(error_value < max_difference){
error_value = -max_difference;
}//Ends If
if (error_value < 0 ){
spd_right = max_speed + error_value;
spd_left = max_speed;
}//Ends If
else{
spd_right = max_speed;
spd_left = max_speed - error_value;
}//Ends Else
}//Ends adjust_control Method
void set_motors(){
analogWrite(M1, HIGH);
analogWrite(M2, HIGH);
analogWrite(E1, constrain(spd_right, 0, max_speed));
analogWrite(E2, constrain(spd_left, 0, max_speed));
}//Ends set_motors Method
void stop(){
analogWrite(E1, 0);
analogWrite(E2, 0);
}//Ends Stop Method
I'm new to the arduino language. This is my first program and I wrote it last night. It's a PD Line Following example.
I'm currently having issues with the motors adjusting. Simply put it just goes straight.
I'm using a QTR Analog Sensor Array. I might switch to digital because one of the analog inputs on the romeo board I have is not working. Does anyone one have any suggestions or improvements to my logic?
void set_Point(){
qtra.readCalibrated(sensorValues);
for (int i = 0; i < NUM_SENSORS; i++){
set_point += sensorValues[i];
}
}
A strange thing about this loop is that is set_point is not initialised (to zero) before adding, and not divided (by i==NUM_SENSORS-=5) after the summation.

Resources