Trying to calculate the wind speed using a pitot tube MPXV7002DP.
We are getting the dynamic pressure from the sensor and then applying Bernoulli's equation:
pd = dynamic pressure
air density = 1.225kg/m^3
windspeed = sqrt(2*Pd/air density)
We are using an Arduino UNO.
We think that there is problem with reading the pressure from the sensor.
We don't know how to get the correct values.
#include <SoftwareSerial.h>
float Output=0;
void setup() {
Serial.begin(9600);
}
void loop() {
float sensorValue = analogRead(A0);
output=sqrt((2*sensorValue)/1.225);
Serial.println(output);
Serial.print("m/s");
delay(100);
}
As one commenter pointed out, the return value of analogRead is an integer from 0-1023. This is a scaling of the voltage on pin A0, from 0 to the comparison voltage. (If you're using a 5V Arduino, 1023 is 5V. 3V Arduino, 1023 is 3V. It doesn't look like you're doing anything complicated that alters what you're using as your comparison voltage, so this should be accurate.)
I'm going to assume that you're working with 5V, since that's what your sensor uses.
What you need to do is look at the data sheet for your device, to determine what the relationship between pressure and voltage is. Looking at the sheet, you have a complicated bugger here, but from the graph on page 5, it looks like you can assume that 0.5V (analogRead of around 102) is a pressure of -2kPa and a 4.5V (analogRead of around 921) is a pressure of 2kPa. You're in luck that the scaling is linear.
I say "around" because it's clear that the device has quite a bit of slop in it's response - at least plus-or-minus .5V or .2kPa! (In other words, 0kPa could read anywhere from 462 to 562.)
You should map analogRead values from 0-102 to -2kPa, from 921-1023 to 2kPa, and everything in between should be a lerp between -2 and 2. I don't have an arduino in front of me so I can't try it out, but it should be something like:
result = analogRead(A0);
if (result < 102) {
kPa = -2.0;
} else {
if (result > 921) {
kPa = 2.0;
} else {
kPa = map(result, 102, 921, -2000, 2000)/1000.0;
}
}
Let me know, in the comments, if I screwed something up and I'll see about fixing it. This is all without the benefit of being able to actually compile/test. =]
When you mention the dynamic pressure pd in Bernoulli' equation I suppose you mean the difference between the totalpressure and the static pressure, because the airspeed V is equal to : V = sqrt(2 * (p_total - p_static) / airdensity), so your pd should be (p_dynamic - p_static).
Related
Equipment:
Arduino ATmega2560
Rotary Encoder: https://www.amazon.ca/gp/product/B08RS6M32J/ref=ppx_yo_dt_b_asin_title_o06_s01?ie=UTF8&psc=1
Manual Curved Treadmill (what it looks like if you've never seen one before): https://www.youtube.com/watch?v=0oil6cmUCog&ab_channel=ResolutionFitness
Background:
I have a manual treadmill and I want to use my Arduino to capture distance and speed data. To do this, my approach was to use a rotary encoder, attach it to a skateboard wheel (older picture is attached, I'm using a bigger wheel now and there's no photo for that right now) such that when I run on the treadmill, the wheel spins proportionately.
In the code below, whenever I run it, the counter variable would accumulate to about 2000 (pulses) every time the wheel makes a full rotation. The wheel diameter = 6cm, therefore, it's circumference = 18.8495559215 cm.
This means for every centimeter I travel, there are about 106 pulses. (2000 pulses/18.8495559215 cm = ~106 pulses/cm).
Here's the code: (I got it from this website and only changed 2 lines of code because my sensor doesn't need to go in reverse; the treadmill only travels in one direction - https://electricdiylab.com/how-to-connect-optical-rotary-encoder-with-arduino/)
volatile long temp, counter = 0; //This variable will increase or decrease depending on the rotation of encoder
void setup() {
Serial.begin (9600);
pinMode(2, INPUT_PULLUP); // internal pullup input pin 2
pinMode(3, INPUT_PULLUP); // internalเป็น pullup input pin 3
//Setting up interrupt
//A rising pulse from encodenren activated ai0(). AttachInterrupt 0 is DigitalPin nr 2 on moust Arduino.
attachInterrupt(0, ai0, RISING);
//B rising pulse from encodenren activated ai1(). AttachInterrupt 1 is DigitalPin nr 3 on moust Arduino.
attachInterrupt(1, ai1, RISING);
}
void loop() {
// Send the value of counter
if( counter != temp && counter % 106 == 0 ){ // Only print something if the wheel travels in increments of 1 cm.
Serial.println (counter);
temp = counter;
}
}
void ai0() {
// ai0 is activated if DigitalPin nr 2 is going from LOW to HIGH
// Check pin 3 to determine the direction
if(digitalRead(3)==LOW) {
counter++;
}else{
counter++; // The original code said counter-- but my treadmill only goes one direction and the this variable starts to decrease when I reach a certain speed (around 5 kmk/h or (3.1 mph) or so). After I changed it to counter++, this issue was resolved however, the arduino serial monitor kept freezing at high speeds.
}
}
void ai1() {
// ai0 is activated if DigitalPin nr 3 is going from LOW to HIGH
// Check with pin 2 to determine the direction
if(digitalRead(2)==LOW) {
counter++; //// The original code said counter-- but my treadmill only goes one direction and the this variable starts to decrease when I reach a certain speed (around 5 kmk/h or (3.1 mph) or so). After I changed it to counter++, this issue was resolved however, the arduino serial monitor kept freezing at high speeds.
}else{
counter++;
}
}
The Problem
*To get the speed is easy, it's just not in this code right now. Accomplished by using t1 and t2 variables and the Millis() function.
When I walk on the treadmill, I typically walk at about 4 or 5 km/h (3.1 mph). This works fine. However, the issue is I want to be able to sprint over 30 km/h (18.6 mph) and have the code be able to support up to 50 km/h (31 mph). Right now, I'm unable to run at speeds over 5 km/h without the serial monitor freezing periodically, or there being inaccurate data.
At these speeds, I would need the sensor to support the wheel moving at over 4500 RPM -> 75 RPS or (75 RPS x 2000 P/R = 150,000 Pulses/Second)
I have absolutely no idea why this issue is happening and how I should approach this to achieve my desired speeds.
Possible reasons why this is happening:
I'm not an expert at Arduino at all and am just starting to understand things like interrupts. I have my pins in 2 and 3. Should I leave this alone or change it?
If you click the Amazon link, you'll see that there is a 3rd Orange "Z" wire which I have absolutely no idea what it does. The few tutorials I could find only involve the two "A" and "B" wires. Maybe incorporating the "Z" wire would point me in the right direction?
My Rotary encoder sensor is using the Arduino's 5V. If you click on the Amazon link, you'd see that it actually supports up to 26V. Should I find an external power source that allows up to 26V and pair it with a relay? Not sure if the extra voltage will help me.
I'm not sure why by default, the sensor counted a full rotation for the wheel to be 2000 pulses. On Amazon, you'll see that it supports the following Pulses/Revolution:
Resolution (pulse/rotation) : 100, 200, 300, 360, 400, 500, 600, 1000, 1024, 1200, 2000, 2500, 3600, 5000 (optional)
How can I change my 2000 P/R to 5000 P/R for example? Would this help?
Summary
I want to use my Arduino and rotary sensor to collect speed and distance data from my manual treadmill. At slow speeds (up to 5 km/h), the code works fine, but at high speeds, the data is highly inaccurate, and the serial monitor freezes every few seconds. I want the Arduino to support speeds up to 50 km/h which is about 4500 RPM. If my solution with the rotary sensor is not feasible, I am 100% open to other ideas as I just want the speed and distance data. I'll purchase any new equipment that's necessary but ideally, I'd like to work with what I have right now.
Thank you for your help!
Well I looked at the datasheet of your encoder and I was also very confused regarding the multiple values of resolution... what I suspect is that maybe there are different E6B2-CWZ6C models with differnt ppr's...
But what was useful was that I found out that your encoder is an incermental rotary encoder (this video explains better the inner workings of your type of encoder) what this means is that you have three wires A black, B white and Z the orange one.
As described on the video when your shaft completes 1 full rotation you will get N quantity of pulses from A and B, but depending on which pulse you get first would determines the rotation, according to the diagram below, if you first get a pulse from A it would mean that the rotation is CW but if you get B first it would then be CCW.
So for example lets say you start with a pulse_count=0 and start turning your shaft CW and you start counting the pulses on A, the count would go 0,1,2... and so on, but how would you know when to stop counting? that's when the Z orange cable comes in place, because it makes a pulse only after a whole rotation has been made so let's say you counted up to 200 and then you get a pulse from Z then you would know that you should restart your count back to the begining.
So to solve your problem and if you are only interested in the measured speed i would sugest to only measure how long does it take from one Z pulse to the next one, which would tell you how long it took for your encoder to complete one rotation, which could then be use to calculate the speed.
If we solve for rpms using this formula we get that RPM=(V60)/(2pir) if V=50km/h=13.88 m/s and r=3cm=0.03m we get that at 50km/h we would get 4418.14 rpm or 4418/60= 73.63 revs per sec what means 73 pulses of Z every second.
What was maybe happening is that for example if A had a resolution of 2000 ppr would imply that for every second you were receiving 73*2000 = 146000 pulses every second and as you configured your baudrate for your serial communication at Serial.begin(9600) you were only capable of reading at most 9600 pulses for each second which is way less than your 146000 pulses in A. However there are different baudrates you can set: 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, or 115200. Just be sure to select the same audrate when using the serial monitor.
For extremely high speeds you have to use bit operators and port registers.
Try this code with the equivalent pins CLK and DT connected to A2 and A1 receptively.
// Rotary Encoder Inputs
#define CLK A2
#define DT A1
int counter = 0;
String currentDir ="";
unsigned char currentPairBit = 0b0; // 8 bits
unsigned char lastPairBit = 0b0; // 8 bits
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
DDRC = 0b00000000; // Set Analog(C) encoder pins as inputs
}
void loop() {
while(true) { // while cycle is faster than loop!
// reads the analog states!
currentPairBit = PINC >> 1 & 0b11; // Gets A2(PC2) and A1(PC1) bits on C (analog input pins) (>> 1 = Jumps pin 0)
if ((lastPairBit & 0b11) != currentPairBit) {
lastPairBit = lastPairBit << 2 | currentPairBit;
// Bit Pairs Cyclic Sequence:
// 1. 2. 3. 4. 5.
// 11 | 01 | 00 | 10 | 11 for CCW
// 11 | 10 | 00 | 01 | 11 for CW
if (lastPairBit == 0b01001011 || lastPairBit == 0b10000111) {
if (lastPairBit == 0b01001011) {
currentDir = "CCW";
counter--;
} else {
currentDir = "CW";
counter++;
}
Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(counter);
}
}
}
}
Then check the Serial Monitor to see how fast it is. Note that you should avoid the delay() function in your code or any other that interrupts the cycle by too much time. Consider using a second auxiliary Arduino for anything else than counting.
Resulting Serial Output:
I am using Arduino Nano and various Li-Fe , Li-Po batteries of 9.9V , 6.6V and 3.7V.
I can read the voltage of the battery using Arduino . My Arduino works at 5V so for batteries like 9.9V and 6.6V I have used a voltage divider using two 10k resistors.But the problem is I need to read the the % of charged battery , I tried something in the code but I am not sure about it. Please anyone help me with it.
My code is:
#define cellPin A0
const float mvpc = 4.55 ; //measured voltage of arduino through voltmeter
float counts = 0; //battery volts in millivolts
float mv = 0;
float multiplier = 2;
float output = 0;
int charge = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
}
void loop() {
// put your main code here, to run repeatedly:
counts = analogRead(cellPin);
Serial.println(counts);
mv = counts * mvpc;
Serial.println(mv);
output = (mv * multiplier)/1000 ;
Serial.print(output);
Serial.println("V");
charge = (counts/1024)*100;
Serial.print(charge);
Serial.println("%");
delay(1000);
}
In order to accurately determine the % of the charged battery, you need the discharge graph for each of the batteries. The discharge graph is usually non-linear for lithium batteries. The discharge curve is basically the voltage versus the % charge and it is different for charging and discharging batteries.
If you have the discharge curve, you can create a map for each of the % to the corresponding voltage value. Then you can map each voltage to a % value from the map you created.
For example:
100% -> 5.00 V
99% -> 4.95 V
....
0% -> 3.23 V
Create an array to store the map of size 100 (for each %): [5.00, 4.95, ... 3.23]
You can then find the %s using the voltage. I hope you can find the discharge graph, otherwise, you can manually find it yourself by discharging the battery using a safe current
In addition to maheenul I'd like to add that you're calculations are a bit off.
const float mvpc = 4.55 ; //measured voltage of arduino through voltmeter
I presume you're supplying your Arduino via USB. This voltage is not very reliable.
float counts = 0; //battery volts in millivolts
counts = analogRead(cellPin);
analogRead returns a 10bit (0-1023) ADC reading. A fraction of your voltage reference. It is not a value in millivolts! So in the following you shoudl use 1024 instead of 1000.
output = (mv * multiplier)/1000 ;
charge = (counts/1024)*100; this calculates the percentage of your Vref. It is not a charge as explained by maheenul.
If you want accurate measurements you should use a better Vref. Either the internal 1.1Vref or some well regulated Vref. Both with appropriate voltage dividers.
But I guess being 5-10% off is not a big deal for a battery charge measurement.
I'm new here so, if I make any mistake, sorry. Well, I'm working with Arduino (Mega2560) to construct an Ammeter and found out a little problem... Arduino Mega measures voltage from 0 to 5V, and the AnalogPins return a 10-bit value according with the reading (that is, 1 bit represents 5/(2^10)=4mV (approximately)). But, in the case of ammeter, I need to use a resistor with small resistance so that my circuit don't get changes. So my objective is read the voltage drop and from V = R.I, calculate the current. But, as the voltage drop is such as slowly, the pin can't read any value.
Eg.: there is a current flowing from 2mA in the region that I would like to measure. With a resistance of 0.3 ohms (the lower value I found here) , would be: V = 2m . 0.3 = 0.6mV.
As I said, the lower posssible value of reading in analogPins is 4mV.
Thus, how to improve my precision of reading? For example, instead of 1023 represents only 5V, the same value represents around of 30 or 40mV...
0 - 0 V
1023 - 30/40 mV
You can use 1.1V internal voltage reference, or some more precise external one (This can be archieved by analogReference). BTW with such a small currents it would be more convenient to use bigger resistor.
Or, forget about limited functionality of analogRead and do it directly. For example 2.56V reference, differential input with 10x or 200x gain (but you'll get range -512 to 511 -> 2.56/512).
In below example, voltage_meter reads 500 samples in about 1 millisecond and returns the average. I set the reference to 1.1v for better precision.
int battery_pin = A3;
float voltage_meter()
{
//read battery voltage per %
long sum = 0; // sum of samples taken
float voltage = 0.0; // calculated voltage
float output = 0.0; //output value
for (int i = 0; i < 500; i++)
{
sum += analogRead(battery_pin);
delayMicroseconds(1000);
}
// calculate the voltage
voltage = sum / (float)500;
// voltage = (voltage * 5.0) / 1023.0; //for default reference voltage
voltage = (voltage * 1.1) / 1023.0; //for internal 1.1v reference
//round value by two precision
voltage = roundf(voltage * 100) / 100;
return voltage;
}
void setup()
{
analogReference(INTERNAL); //set reference voltage to internal
Serial.begin(9600);
}
void loop()
{
Serial.print("Voltage Level: ");
Serial.print(voltage_meter(), 4);
Serial.println(" V");
delay(1000);
}
On ATmega based boards (UNO, Nano, Mini, Mega), it takes about 100 microseconds (0.0001 s) to read an analog input, so the maximum reading rate is about 10,000 times a second.
100 Microsecond and no 1000 how the example
My LM35 connected to arduino decreases temperature value in celsius when near heat and increases value when far from heat. Could any one help or know why it's working other way round.
void setup() {
// put your setup code here, to run once:
//Start the serial connection with the computer
//to view the result open the serial monitor
// 9600 is the “baud rate”, or communications speed.
Serial.begin(9600);
}
void loop() {
delay(2000);
float tempValue = analogRead(A2);
// converting that reading to voltage
float tempVoltage = (tempValue/1024.0)*5.0;
float tempDegrees = (tempVoltage - 0.5) * 100.0 ;
//Multiplying tempDegrees by -1 to make it positive
tempDegrees =(tempDegrees * -1);
Serial.println("............................................");
Serial.println("Degrees");
Serial.println(tempDegrees);
delay(2000);
}
just randomly came accross your question. and it has been 6 years since I touched an LM35 :d
but I think you have a problem in that -0.5 thing. I did not really get that!
LM35's function as far as I remember was :
T = V/ 10mV
you might want to check the datasheet but I'm pretty positive this is the equation. when you get the voltage from ADC you have to put it in this equation and get the result.
be careful : you have to also attribute for the temperature error as well as ADC noise if temperature precision is important for you.
If you are using a 5 volts power supply to your arduino:
5 Volts in the Arduino are directly converted to 1023 in the output of the ADC
ADC_Outpput * 5000 / 1024, where 5000 is coming from 5volts as millivolts, 1024 is the 10 bist resolution
LM35 resolution is linearly generated with a rate of + 10-mV/°C
so the analogVolt = ADC_Outpput * 5000 / 1024
FinalTemperature = (analogVolt - 500) / 10
Hello
I am currently working on a project, where I want to measure the voltage and current in a 3-phase system with an Arduino Uno.
This is a small schoolproject and I've had the necessary course on AC-systems to know about safety around higher voltages. I've also have a little bit experience with microcontroller but I've never used ADC.
I have a problem when reading from the analog pins of the Arduino Uno. It seems like the analog pins are mixed which i believe is called ghosting. I've been searching the internet for some answers to this matter, but the proposed solutions didn't work for me. I tried to make a dummy measurement and also to make a small time delay between measurements but since it's about power monitoring timing is critical. I need at minimum 20 readings which needs to be done in 20ms
To test the code I used two function generators. Is this even possible or allowed? Is it best to have at minimum a resistance in between and maybe a capacitor to remove noise?
Is there something in the circuit when transforming the voltage/current to be between 0V-5V there can be done to prevent this ghosting-effect?
I am using a voltagetransformer for the voltage and a Hall-effect sensor for the current. Both circuits need offset.
This is the code that makes the measurements.
void measure(char pin_volt, char pin_curr, int *volt_rms, int *curr_rms, float *theta){
int i;
long squared_v, squared_c, sum_squared_v = 0, sum_squared_c = 0, inst_v, inst_c, mean_squared_v, mean_squared_c;
unsigned long time_v, time_c;
for(i = 0; i < samples; i++){
inst_v = analogRead(pin_volt) - volt_offset;
if(inst_v > -volt_varying && inst_v < volt_varying) {
time_v = micros();
}
inst_c = analogRead(pin_curr) - curr_offset;
if(inst_c >= -curr_varying && inst_c <= curr_varying) {
time_c = micros();
}
squared_v = inst_v * inst_v;
squared_c = inst_c * inst_c;
sum_squared_v += squared_v;
sum_squared_c += squared_c;
delayMicroseconds(80);
}
mean_squared_v = sum_squared_v / samples;
mean_squared_c = sum_squared_c / samples;
*volt_rms = sqrt(mean_squared_v);
*curr_rms = sqrt(mean_squared_c);
*theta = calculate_phase_difference(time_v,time_c);
}
Adding a capacitor can lower the problem.
Try to do the following:
No current or tension on the circuit, so the arduino should measure 0 values.
Run a sketch that reads values and prints max and min values to serial monitor; you will see that values will not be zero as expected, those are interferences.
Try and find a capacitor that can lower those values but don't exagerate.