Inaccurate results for arduino uno and ultrasonic sensor - arduino

Good day, I have the following code for an ultrasonic sensor that I'm using, but the result is not being accurate and I'd like to display the distance from the target in centimetres.
#include <LiquidCrystal.h> //Load Liquid Crystal Library
LiquidCrystal LCD(10, 9, 5, 4, 3, 2); //Create Liquid Crystal Object called LCD
int trigPin=13; //Sensor Trip pin connected to Arduino pin 13
int echoPin=11; //Sensor Echo pin connected to Arduino pin 11
int myCounter=0; //declare your variable myCounter and set to 0
int servoControlPin=6; //Servo control line is connected to pin 6
float pingTime; //time for ping to travel from sensor to target and return
float targetDistance; //Distance to Target in inches
float speedOfSound=776.5; //Speed of sound in miles per hour when temp is 77 degrees.
void setup() {
Serial.begin(9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
LCD.begin(16,2); //Tell Arduino to start your 16 column 2 row LCD
LCD.setCursor(0,0); //Set LCD cursor to upper left corner, column 0, row 0
LCD.print("Target Distance:"); //Print Message on First Row
}
void loop() {
digitalWrite(trigPin, LOW); //Set trigger pin low
delayMicroseconds(2000); //Let signal settle
digitalWrite(trigPin, HIGH); //Set trigPin high
delayMicroseconds(15); //Delay in high state
digitalWrite(trigPin, LOW); //ping has now been sent
delayMicroseconds(10); //Delay in high state
pingTime = pulseIn(echoPin, HIGH); //pingTime is presented in microceconds
pingTime=pingTime/1000000; //convert pingTime to seconds by dividing by 1000000 (microseconds in a second)
pingTime=pingTime/3600; //convert pingtime to hourse by dividing by 3600 (seconds in an hour)
targetDistance= speedOfSound * pingTime; //This will be in miles, since speed of sound was miles per hour
targetDistance=targetDistance/2; //Remember ping travels to target and back from target, so you must divide by 2 for actual target distance.
targetDistance= targetDistance*63360; //Convert miles to inches by multipling by 63360 (inches per mile)
LCD.setCursor(0,1); //Set cursor to first column of second row
LCD.print(" "); //Print blanks to clear the row
LCD.setCursor(0,1); //Set Cursor again to first column of second row
LCD.print(targetDistance); //Print measured distance
LCD.print(" inches"); //Print your units.
delay(250); //pause to let things settle
}
I will appreciate any help.

You are performing a lot of floating point calculations on every iteration of your loop. Rather than performing these calculations you can pre-compute a single multiplier that will convert the pingTime to a distance in centimetres.
First we need to calculate the metric speed of sound from your 776.5 mph.
776.5 miles/hour * 1609.34 (meters in a mile) = 1249652.51 metres/hour
1249652.51 metres/hour / 3600 = 347.126 metres/second
So we can precompute a multiplier as follows:
float speedOfSound = 347.126; // metres per second
float speedOfSound_cm_p_us = speedOfSound * 100 / 1000000; // speedOfSound in centimetres per microsecond
Obviously, this could be reduced to simply:
float speedOfSound_cm_p_us = 0.0347126; // cm per microsecond
Next we can precompute the multiplier by dividing speedOfSound_cm_p_us by 2 as the sound has to travel to the reflective surface and back again.
float pingTime_multiplier = speedOfSound_cm_p_us / 2; // The sound travels there and back so divide by 2.
Again, we could simply replace this step with:
float pingTime_multiplier = 0.0173563;
However, this magic number should be well documented in your code.
Once we have pingTime_multiplier calculated the loop function becomes much simpler. Simply multiply the pingTime by the pingTime_multiplier to get the distance in centimetres.
pingTime = pulseIn(echoPin, HIGH); // pingTime is presented in microseconds
targetDistance = pingTime * pingTime_multiplier;
This significantly reduces the amount of work that the Arduino has to do each time through the loop.

Convert inches to centimeters by multiplying by 2.54
float targetDistanceCentimeters = targetDistance*2.54;
Your code looks correct, so my guess is that this is a hardware problem. In my experience ultra sound sensors are typically quite bad at measuring distance to any object that is not a wall or other large object with relatively flat surface. So if you want to test the accuracy of your system, test it against such object i.e. wall or book.
If your sensor is moving while making the measurements make sure that it is not moving too fast.
If you are measuring distance to small or thin object you will need to filter and take the average of the measurements. It’s all up to you how accurate you want to be, I’d say that average of twenty or so of filtered measurements will give you appropriate result.
Another things to consider is that you have the correct supply voltage and that the sensor is not directed against the floor at some angle, as this might give unwanted reflections.

I'd like to display the distance from the target in centimetres.
Google says an inch is 2.54 cm, so just multipy your result by this.
the result is not being accurate
For more accurate results, take an average of a number of samples (3 or more), and you could implement some heuristics which discard grossly over/under range results.
Say if three readings are around 0-5cm apart, then a fourth reading 40cm away is most likely an error. So just do the average of the three similar results.

Related

Arduino interrupts

I am working on a smart greenhouse project using an ESP32 as a microcontroller.
Data comes from a DHT22 temperature and humidity sensor and a soil moisture sensor. Those two tend to use delay() functions to read, because they need some time to warm up.
Example:
void loop() {
// Wait a few seconds between measurements.
delay(2000);
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
float h = dht.readHumidity();
// Read temperature as Celsius (the default)
float t = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
float f = dht.readTemperature(true);
}
I am planning on posting this data on a web interface, which would have manual controls available too.
Since I am using delays, if I press the button on the website, first the delay executes, then the button press, so it's not instant. What could I do to fix that?
Don't use delay(). I'm not clear on exactly what you're trying to do here or how you're trying to handle the button, but in general you're better off doing something like this:
#define SENSOR_UPDATE_WARMUP 2000
#define SENSOR_UPDATE_INTERVAL 1000
void loop() {
static unsigned long next_sensor_update = SENSOR_UPDATE_WARMUP;
if(millis() > next_sensor_update) {
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
float h = dht.readHumidity();
// Read temperature as Celsius (the default)
float t = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
float f = dht.readTemperature(true);
next_sensor_update = millis() + SENSOR_UPDATE_INTERVAL;
}
}
This allows you to other processing in loop() while still having the warmup time and only updating the sensor readings periodically (adjust SENSOR_UPDATE_INTERVAL to the number of milliseconds between updates).
If you need to have other delays for other devices just repeat the pattern of having a static variable that tracks the timing for those devices.
Even simpler, you could just put the two second delay at the end of setup() rather than inside loop(), but I suspect you're not going to want to update the sensor values constantly, so you'll want to structure your program similarly to the code above.

How to make more precise the reading of AnalogPins in Arduino?

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

LM35 decreases when near heat

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

Speed measurement with arduino and ultrasonic hc-sr04 sensor?

i want made speed detection "device" using with Arduino and two ultrasonic hc-sr04 like this link. but I want to make it with ultrasonic instead by LDR.
from that link. how lasers and ldr work, like this
The resistors are used as pull-down resistors and I wired the sensors and put them in a case, to avoid them detecting surrounding light. For each case, a hole was drilled so that the laser beam can light the sensor while the ambient light does not affect the sensor.
The working principle is easy: an object that passes by will "cut" the laser beams, this means the LDR sensor will detect this sudden drop of light intensity. First I defined a threshold value under which the sensor is considered triggered, once the value is under threshold for the first sensor then Arduino waits for the second one to be triggered. During this waiting time it counts the elapsed time between the two events. When the second beam is interrupted, the timer stops and now is just simple math. The distance between the 2 sensors is known, the time between the two events is known, and speed can be computed as speed = distance/time.
Below the Arduino code:
/*
by Claudiu Cristian
*/
unsigned long time1;
int photocellPin_1 = 0; // 1st sensor is connected to a0
int photocellReading_1; // the analog reading from the analog port
int photocellPin_2 = 1; // 2nd sensor is connected to a1
int photocellReading_2; // the analog reading from the analog port
int threshold = 700; //value below sensors are trigerd
float Speed; // declaration of Speed variable
float timing;
unsigned long int calcTimeout = 0; // initialisation of timeout variable
void setup(void) {
// We'll send debugging information via the Serial monitor
Serial.begin(9600);
}
void loop(void) {
photocellReading_1 = analogRead(photocellPin_1); //read out values for sensor 1
photocellReading_2 = analogRead(photocellPin_2); //read out values for sensor 2
// if reading of first sensor is smaller than threshold starts time count and moves to calculation function
if (photocellReading_1 < threshold) {
time1 = millis();
startCalculation();
}
}
// calculation function
void startCalculation() {
calcTimeout = millis(); // asign time to timeout variable
//we wait for trigger of sensor 2 to start calculation - otherwise timeout
while (!(photocellReading_2 < threshold)) {
photocellReading_2 = analogRead(photocellPin_2);
if (millis() - calcTimeout > 5000) return;
}
timing = ((float) millis() - (float) time1) / 1000.0; //computes time in seconds
Speed = 0.115 / timing; //speed in m/s given a separation distance of 11.5 cm
delay(100);
Serial.print(Speed);
Serial.print("\n");
}
how to implement the code with ultrasonic HC-SR04 sensors?
the coding is problem for me. hopefully someone can help me...... :(
Please excuse my poor English !
There are already lots of examples on the internet, so if all you want to do is copy, google arduino sr04
But if you want to know how to do it...
The sr04 has 4 pins, vin, gnd, trigger, and echo.
Connect vin and ground to +5 and gnd
Connect trigger to a digital output pin
Connect echo to a digital input pin
Trigger by going low for 2 microseconds (us) and then high for 10 us then low again
Then get the results with a pulseIn from the echo pin
Read the data sheet for more information

Using arduino analog inputs

I am creating my first Arduino program on the UNO r3. I have played with the Arduino Uno before just with petty example programs, etc. I am using two analog inputs to sense distance using 2 laser sensors with 0-5vdc scaling. These two inputs are 0-5vdc and I have ensured common grounding throughout. The two sensors are named left and right and are input to A0 and A1 respectively. I also have a differential POT which uses a 10K ohm POT wiper voltage as an input on A2. The theory of the program is to take the absolute value of the difference in input voltages between the left and right lasers then determine if the result is greater than or equal to the voltage on pin A2 from the POT wiper. Based on the resulting math, turn on or off a relay interposed to pin D13 via a transistor driver circuit.
The PROBLEM: I cannot achieve accurate changes in voltage on the scale (0-1023) on pins A0, A1, or A2. I have utilized the serial monitor to diagnose this problem. Not sure what the problem is, any help would be great. Also, I cannot achieve a 0 value on any of the above analog pins, even the POT wiper!!!
Here's my code:
const int lf_dist = A0; //names A0
const int rt_dist = A1; //names A1
const int differential = A2; //names A2
const int relay = 13; // select the pin for the relay coil
unsigned int left = 0; // variable to store the value coming from the left sensor
unsigned int right = 0; // variable to store the value coming from the right sensor
unsigned int diff = 0; // variable to store the value coming from the differential POT for maximum distance differential
unsigned int offset = 0; // variable that stores the value between the two laser sensors
void setup() {
Serial.begin(9600);
pinMode(A0, INPUT);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
pinMode(relay, OUTPUT); // declare the relay pin as an OUTPUT:
analogReference(DEFAULT);
}
void loop()
{
unsigned int left = 0; // variable to store the value coming from the left sensor
unsigned int right = 0; // variable to store the value coming from the right sensor
unsigned int diff = 0; // variable to store the value coming from the differential POT for maximum distance differential
unsigned int offset = 0; // variable that stores the value between the two laser sensors
left = analogRead(A0); // read the value from the left laser
delay(5);
right = analogRead(A1); // read the value from the right sensor
delay(5);
diff = analogRead(A2); // read the value from the differential POT
delay(5);
offset = abs(left - right);
if(offset >= diff) // does math to check if left and right distances are greater than the value clocked in by the differential POT
{
digitalWrite(relay, LOW); // turns off the relay, opens the stop circuit, and turns on the yellow light
}
else
{
digitalWrite(relay, HIGH); // turns on the relay if all is good, and that keeps the machine running
}
Serial.print("\n left = " );
Serial.print(left);
Serial.print("\n right = " );
Serial.print(right);
Serial.print("\n differential = " );
Serial.print(diff);
delay(1000);
}
afaict, this should really be due to the floating pins surrounding the measuring pins, having erratic values, hence perturbating your measures. You should look at your values using arduinoscope, which will show you the interfering effects of the other floating pins on your measuring pins.
The easy workaround for this is to ground all analogical input pins you're not using, and put as much space as you can between both your inputs, so they don't interfere with each other.
I realize this thread is somewhat old not, but perhaps this will help someone. If you power the Arduino with only 5V, as you say you did with a regulator, you will get very erratic behavior, particularly from the analog pins. This is because you will start to brown out the internal voltage regulators that provide the AREF, 3.3, and 5.0 outputs. I've tested this for a robotics project I'm working on, and right around 6.5 volts, everything begins to go wrong. I suppose if you always provided 5.0 input voltage you could compensate for this effect, but in my case I used a LiPo battery that could range from 8.4 volts down to 6.0 volts, and everything goes crazy at 6.5 volts.
The minimum current that arduino sinks in during the sampling from potentiometer should not disturb the actual open input volts at the wiper.
Initialize the pins in pull up mode to avoid garbage values or 'floating' pins or use your own pull down/up resistors at the pins :)

Resources