Analog Keypad (sometimes) returns wrong values - arduino

I use Arduino UNO plus Analog Keypad. I followed this instructions:
https://arduinogetstarted.com/tutorials/arduino-analog-keypad-library
Now, it all works, except sometimes when I press a button on the keyboard, a wrong value is returned. Looks like an error in Arduino's measuring A0 input (or button sending wrong data).
So if I run the calibration program (see the link), I'll get something like
1020
1020
1020
1020
1020
724
1020
1020
for the same button.
Any suggestions how to fix it?
Thanks.

The library you are using seems to be the following https://github.com/ArduinoGetStarted/Analog-Keypad
Both the calibration code and the ezAnalogKeypad::getKey() function base the keypress on a single analogRead call, which is susceptible to errors because you either pressed the key halfway through a read, or one of the electrical connections might not be so great, or maybe your finger wiggled a little while pressing the button.
You could simply call getKey multiple times, and only accept a keypress if a majority of the calls return the same value.
The library will actually return 0 if the pressed key is still the same and the second call is within the debounceTime.
So you if you have set debounceTime, then you could keep calling getKey until it returns 0, and then take the value it returned before that.
something like the following modification to the demo application could work? https://github.com/ArduinoGetStarted/Analog-Keypad/blob/main/examples/AnalogKeypad/AnalogKeypad.ino
// modify setup() to set the debounce time to 100 ms
keypad.setDebounceTime(100);
// modify the readKey part in loop() as follows
unsigned char key = 0; // this will contain the key value
unsigned char nextKey = 0;
do
{
key = nextKey;
nextKey = keypad.getKey();
} while (nextKey != 0);
if (key)
{
Serial.println(key);
}
You might also be able to get more stable readings by adding a small capacitor between the analog pin and ground, or a bigger capacitor between the ground and supply pins of the keypad.

Related

Arduino: while (Serial.available()==0) gives input

I am trying to input GPS coordinate into the serial monitor to use in my drone project
However, whenever I try to input GPS coordinate, it automatically writes one of the GPS coordinates without my input. For example, GPS latitude is shown as 0.00, but the program waits for GPS Longitude info.
For a detailed situation please look at the picture attached.
int GPSNumCor;
void setup() {
// put your setup code here, to run once:
Serial.begin (115200);
Serial.print("What is the number of your GPS Coordinate? ");
while (Serial.available() == 0);
GPSNumCor = Serial.parseInt();
Serial.println(GPSNumCor);
delay (200);
float GPSLat[GPSNumCor], GPSLon[GPSNumCor];
for (int i = 0; i < GPSNumCor; i++)
{
if (i == 0)
{
Serial.println("What is your 1st GPS Coordinate");
}
if (i == 1)
{
Serial.println("What is your 2nd GPS Coordinate");
}
if (i == 2)
{
Serial.println("What is your 3rd GPS Coordinate");
}
if (i > 2)
{
Serial.print("What is your ");
Serial.print(i + 1);
Serial.println(" th GPS Coordinate");
}
delay(200);
Serial.print ("Latitude: ");
while (Serial.available() == 0);
GPSLat[i] = Serial.parseFloat();
Serial.println(GPSLat[i]);
Serial.print("Longitude: ");
while (Serial.available() == 0);
GPSLon[i] = Serial.parseFloat();
Serial.println(GPSLon[i]);
}
}
It has to wait for all input until I make an input to the program, but it does not wait.
I know while (Serial.available()==0) is a way to go, but I do not know why it would not work.
First, there's no reason to use while (Serial.available() == 0);. The parseFloat function you are about to use waits for data to be available and, if it didn't, merely checking for zero wouldn't be sufficient anyway because that would stop waiting as soon as a single character was available.
So here's why that while loop is a bad idea:
If you really do need to wait for the input before calling parseFloat, this won't do it. It only waits until at least one character is received and the coordinates may be more than one character.
The parseFloat function doesn't return until it has read an entire float anyway. So it already waits for you.
But that's not your problem. Think about the input stream, say it's "11.0<newline>22.0newline44.0". Where is the code to read the spaces between those numbers? When parseFloat tries to read a space, it returns a zero, as the documentation says. That's why you're getting zeroes -- you don't have any code to do anything with the separators between the floats.
Think about how parseFloat must work when it reads "12.34newline". First it reads the 1 and has no idea whether that's the whole number of not, so it keeps checking. Then it reads the "2.34" and still has no idea it has the whole number. Not until it sees the newline does it know that 12.34 is the correct float to return. But it does not consume the newline. Why? Because that might mean something.
With the code you showed, your next call to parseFloat will then try to read the newline and see that this is not a valid character to be part of a floating point number. So, as the documentation says, it will return zero.
Look closely at parseFloat's documentation to find out how to correctly match the delimiters in your serial stream. The parseFloat function has the ability to behave differently, consuming and ignoring delimeters rather than returning zero.
I don't know how it work, I just add Serial.read() in every time I want to read.
If u don't want to add Serial.read(), u can also use old version like 1.6.0, it still work fine but it don't work when u make like C# Serial app.
Just add Serial.read(), it work fine every place.
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
while(Serial.available()==0){}
int r=Serial.parseInt();
Serial.println(r);
Serial.read(); // it work fine
while(Serial.available()==0){}
int g=Serial.parseInt();
Serial.println(g);
Serial.read();
}
In the Serial Monitor window, in the drop-down menu on the bottom-right, change from "Newline" to "No line ending" and that will solve the problem (by preventing the Serial Monitor from automatically entering zero value(s)).
Both the parseInt() and parseFloat() have a hard time reading other data types (this also includes white spaces such as new lines) than the ones specified, and as a result they automatically return zero.
Reference: This page on Programming electronics offers valuable, detailed explanations (look for a paragraph with bold text):
https://www.programmingelectronics.com/parseint/

My Qt app does not recieve all the data sent by arduino

I'll go right to the point. My arduino reads values from the adc port and send them via serial port(values from 0 to 255). I save them in a byte type vector. After sending an specific signal to arduino, it starts to send to Qt app the data saved in the vector. Everything is working except that the arduino should send 800 values and the app receives less values than that. If I set the serial baud rate to 9600, I get 220 values. If, instead, I set the baud rate to 115200, I get only 20 values. Can you guys help me to fix this? I would like to use 115200 baud rate, because I need a good trasmision speed at this project(Real time linear CCD). I'll leave some code below:
Arduino code:
void sendData(void)
{
int x;
for (x = 0; x < 800; ++x)
{
Serial.print(buffer[x]);
}
}
This is the function that sends the values. I think is enough information, so I summarized it. If you need more code, please let me know.
Qt serial port setting code:
...
// QDialog windows private variables and constants
QSerialPort serial;
QSerialPortInfo serialInfo;
QList<QSerialPortInfo> listaPuertos;
bool estadoPuerto;
bool dataAvailable;
const QSerialPort::BaudRate BAUDRATE = QSerialPort::Baud9600;
const QSerialPort::DataBits DATABITS = QSerialPort::Data8;
const QSerialPort::Parity PARITY = QSerialPort::NoParity;
const QSerialPort::StopBits STOPBITS = QSerialPort::OneStop;
const QSerialPort::FlowControl FLOWCONTROL = QSerialPort::NoFlowControl;
const int pixels = 800;
QVector<double> data;
unsigned int dataIndex;
QByteArray values;
double maximo;
...
// Signal and slot connection.
QObject::connect(&serial, SIGNAL(readyRead()), this,SLOT(fillDataBuffer()));
...
// Method called when there's data available to read at the serial port.
void Ventana::fillDataBuffer()
{
dataIndex++;
data.append(QString::fromStdString(serial.readAll().toStdString()).toDouble());
if(data.at(dataIndex-1) > maximo) maximo = data.at(dataIndex-1);
/* The following qDebug is the one I use to test the recieved values,
* where I check that values. */
qDebug() << data.at(dataIndex-1);
}
Thanks and sorry if it's not so clear, it has been an exhausting day :P
Ok... I see two probelms here:
Arduino side: you send your data in a decimal form (so x = 100 will be sent as 3 characters - 1, 0 and 0. You have no delimiter between your data, so how your receiver will know that it received value 100 not three values 1, 0 and 0? Please see my answer here for further explanation on how to send ADC data from Arduino.
QT side: There is no guarantee on the moment when readyRead() signal will be triggered. It may be immediately after first sample arrives, but it may be raised after there are already couple of samples inside the serial port buffer. If that happens, your method fillDataBuffer() may process string 12303402 instead of four separate strings 123, 0, 340 and 2, because between two buffer reads four samples arrived. The bigger the baudrate, the more samples will arrive between the reads, which is why you observe less values with a bigger baud rate.
Solution for both of your problems is to append some delimiting byte for your data, and split the string in the buffer on that delimiting byte. If you don't want to have maximum data throughput, you can just do
Serial.print(buffer[x]);
Serial.print('\n');
and then, split incoming string on \n character.
Thank you very much! I did what you said about my arduino program and after solving that, I was still not getting the entire amount of data. So the the problem was in Qt. How you perfectly explain, the serial buffer was accumulating the values too fast, so the slot function "fillDataBuffer()" was too slow to process the arriving data. I simplified that function:
void Ventana::fillDataBuffer()
{
dataIndex++;
buffer.append(serial.readAll());
}
After saving all the values in the QByteArray buffer, I process the data separately.
Thanks again man. Your answer was really helpful.

Arduino sketch with Serial.available() passes twice

When I test Serial.available() or Serial.available() > 0 in my loop() function, it appears to return true twice each time I enter serial data. The second time, it sets the throttle value in my code to 0. Here is the code:
#include <Servo.h>
Servo rc_rotor;
int throttle = 0; // servo position indicates throttle position.
String s_throttle_set = "Throttle set to: ";
String s_throttle_read = "Reading throttle value: ";
String s_action_error = "No action known for input value: ";
void setup()
{
rc_rotor.attach(9);
Serial.begin(9600);
while(! Serial);
Serial.println("Throttle value: 0 through 255, or 999 to read current value.");
}
void loop()
{
rc_rotor.write(throttle);
delay(20);
if (Serial.available())
{
int temp_throttle = Serial.parseInt();
if (temp_throttle >= 0 && temp_throttle <= 180)
{
throttle = temp_throttle;
Serial.println(s_throttle_set + throttle);
}
else if (temp_throttle == 999)
{
Serial.println(s_throttle_read + throttle);
}
else
{
Serial.println(s_action_error + temp_throttle);
}
}
}
Please note this code is not my final masterpiece. Much of it is from publicly available examples. Anyway, the statement if (Serial.available()) succeeds twice. By that I mean, it is true when I type in a value such as 125, and a moment later it will be 'true' again when I have typed in nothing additional. I only expect one value to go through this way. The result is that my throttle is being set to the value I enter, and then almost immediately re-set to 0. Why would something like this happen?
It turns out there is no mysterious problem with the hardware or the code, as I first suspected there was. Actually, the solution is simply to select "no line ending" in the Arduino Serial Monitor's dropdown option (by default, I guess mine was set to "New Line"). Without the additional character being inserted by the Serial Monitor, everything behaves as expected.
One thing I did not expect is how the Arduino software interprets the newline. I debugged by printing the ascii values that were making it through my if-statement. First of all, the Serial Monitor sent the number I typed in, followed moments later by ascii 10, which is the line feed character. Fine, no problem. But then Serial.parseInt() chewed on that line feed for a moment (there was a slight but noticeable delay), then fed the numeral 0 to my function. It took me a little while to figure out why. Here is an explanation from the Serial part of the Arduino Language Reference:
parseInt()
Description
Looks for the next valid integer in the incoming serial stream.
parseInt() inherits from the Stream utility class.
In particular:
Initial characters that are not digits or a minus sign, are skipped;
Parsing stops when no characters have been read for a configurable
time-out value, or a non-digit is read;
If no valid digits were read
when the time-out (see Serial.setTimeout()) occurs, 0 is returned;
So Serial.available() is true after the line feed character enters the buffer, but there is no "valid digit" according to Serial.parseInt(). Understandably... we are looking at an empty buffer that finally times out. Therefore Serial.parseInt() returns 0 and the code that follows proceeds with that value.
The code in the question assumed that the only input would be integers coming over the serial connection, which is actually a pretty fragile assumption. If you need to use Serial.parseInt in a situation where empty buffers (null), line feeds or other unexpected characters might come through, it should just be a matter of filtering the input better than I did in the example code.

Basic PIC Programming issues

I am writing PIC code in C and encountered the following problems:
When I write my delay as _delay_ms(500), my code doesn't compile, it says it didn't recognize this instruction. I am using MPLAB.
I want to write a program that would count how many time the push button is pressed then return that value and display it using LED's. I know how to display it, but not how to make the program to wait for the push of the push button on the pickit.
main()
{
TRISA=0;//Sets all ports on A to be outputs
TRISB=1;//Sets all ports on B to be inputs
for(;;){
if(PORTBbits.RB0==1){//When the button is pressed the LED is off
PORTAbits.RA1 =0;
count=count+1;
}
else{
PORTAbits.RA1=1;
count = count +1;
}
if (count > 20){//if count =20 aka 20 button presses the LED turns on
PORTAbits.RA0=1;
}
else{
PORTAbits.RA0=0;
}
}
}
There are a few issues:
Assuming you're using a PIC24 or a dsPIC, you need to include libpic30.h
Before you include libpic30.h you need to #define FCY to be your instruction rate so that the delay takes the correct number of cycles. See the comments in the libpic30.h file for details.
The function is __delay_ms not _delay_ms. Note that there are two underscores at the beginning.
The name is all lower case, not Delay_ms as in your comment.
You need to add delay in your code when you detect a key is pressed. As you are saying the _delay_ms(500) is not recognized, You can try something like following:
unsigned char x;
// Just waste a few cycles to create delay
for (x = 0; x < 100; x++)
{
// No operation instruction
Nop();
}
You can create your own delay function with specific number of iterations of this for loop. Measure the exact delay created by this function using a profiler if you need. IMO any arbitrary delay, like say 100 iterations as stated above shall work.

Are interrupts the right thing to use for my robot?

So I have this old motorized wheelchair that I am trying to convert into a robot. I replaced the original motor driver with a sabertooth 2x12 and I’m using an Arduino micro to talk to it. The motors shaft goes all the way threw so I attached a magnet and a Hall Effect sensor to the back side to act as a rotary encoder. My current goal is to be able to tell the robot to move forward a certain amount of feet then stop. I wrote some code to do this linearly however this didn't work so well. Then I learned about interrupts and that sounded like exactly what I needed. So I tried my hand at that and things went wrong on several different levels.
LEVEL ONE: I have never seemed to be able to properly drive the motors it seems like any time I put the command to turn them on inside of a loop or if statement they decide to do what they want and move sporadically and unpredictably
LEVEL TWO: I feel like the interrupts are interrupting themselves and the thing I set in place to stop the wheels from moving forward because I can tell it to move 14 rotary encoder clicks forward and one wheel will continue moving way past 1000 clicks while the other stops
LEVEL THREE: couple times I guess I placed my interrupts wrong because when I uploaded the code windows would stop recognizing the Arduino and my driver would break until I uploaded the blink sketch after pressing the reset button which also reloaded and fixed my drivers. Then if I deleted one of my interrupts it would upload normally.
LEVEL FOUR: my Hall Effect sensors seem to not work right when the motors are on. They tend to jump from 1 to 200 clicks in a matter of seconds. This in turn floods my serial port and crashes the Arduino ide.
So as you can see there are several flaws somewhere in the system whether it’s hardware or software I don't know. Am I approaching this the right way or is there some Arduino secret I don’t know about that would make my life easier? If I am approaching this right could you take a look at my code below and see what I’m doing wrong.
#include <Servo.h>//the motor driver uses this library
Servo LEFT, RIGHT;//left wheel right wheel
int RclickNum=0;//used for the rotory encoder
int LclickNum=0;//these are the number of "clicks" each wheel has moved
int D =115;//Drive
int R =70;//Reverse
int B =90;//Break
int Linterrupt = 1;//these are the interrupt numbers. 0 = pin 3 and 1 = pin 2
int Rinterrupt = 0;
int clickConvert = 7;// how many rotery encoder clicks equal a foot
void setup()
{
Serial.begin(9600); //starting serial communication
LEFT.attach( 9, 1000, 2000);//attaching the motor controller that is acting like a servo
RIGHT.attach(10, 1000, 2000);
attachInterrupt(Linterrupt, LclickCounter, FALLING);//attaching the rotory encoders as interrupts that will
attachInterrupt(Rinterrupt, RclickCounter, FALLING);//trip when the encoder pins go from high to low
}
void loop()
{//This is for controling the robot using the standard wasd format
int input= Serial.read();
if(input == 'a')
left(2);
if(input == 'd')
right(2);
if(input == 'w')
forward(2);
if(input == 's')
backward(2);
if(input == 'e')
STOP();
}
void forward(int feet)//this is called when w is sent threw the serial port and is where i am testing all of my code.
{
interrupts(); //turn on the interrupts
while(RclickNum < feet * clickConvert || LclickNum < feet * clickConvert)// while either the left or right wheel hasnt made it to the desired distance
{
if(RclickNum < feet * clickConvert)//check if the right wheel has gone the distance
RIGHT.write(D); //make the right wheel move
else
RIGHT.write(B);//stop the right wheel
if(LclickNum < feet * clickConvert)
LEFT.write(D);
else
LEFT.write(B);
}
noInterrupts();//stop the interrupts
resetCount();//set the click counters back to zero
}
//once i have the forward function working i will implament it through out the other functions
//----------------------------------------------------------------------
void backward(int feet)
{
RIGHT.write(R);
LEFT.write(R);
}
void left(int feet)
{
RIGHT.write(D);
LEFT.write(R);
}
void right(int feet)
{
RIGHT.write(R);
LEFT.write(D);
}
void STOP()
{
resetCount();
RIGHT.write(B);
LEFT.write(B);
}
void LclickCounter()//this is called by the left encoder interrupt
{
LclickNum++;
Serial.print("L");
Serial.println(LclickNum);
}
void RclickCounter()//this is called by the right encoder interrupt
{
RclickNum++;
M Serial.print("R");
Serial.println(RclickNum);
}
void resetCount()
{
RclickNum=0;
LclickNum=0;
}
don't use interrupt() and nointerrupt() (or cli() and sei()) as they will stop timer and serial interrupt, breaking a lot of things. Just set to 0 the counting variable OR use detachInterrupt and attachInterrupt.
variable used inside interrupt AND normal execution flow should be declared as volatile, or their value my be unsyncornized. So declare them like volatile int RclickNum=0;
interrupt should be fast to execute, as by default other interrupt will NOT execute while inside an interrupt.
NEVER use Serial inside interrupt; if Serial buffer is full, it will call Serial.flush(), that will wait for Serial interrupt of byte written, but because you are alreadi inside an interrupt will never happen...dead lock aka you code hangs forever!
because your "moving" function use quite a long time to execute, if multiple command arrive to the serial, thay will remain isnode the buffer until readed. So if in the terminal you write "asd" and then "e", you will see robot go left, backward, right, stop (yes, actually the stop function is not usefull as it does nothing because your "moving" function are "blocking", that mean they won't return until they ended, so the loop() code (and the read of "e") will not execute until the buffer of serial has been processed.

Resources