arduino: program with else not working - c++

#include <Servo.h>
Servo myservo; // create servo object to control a servo
int pos = 90;
String kontrolstr = "";
char kontrol;
void setup()
{
Serial.begin(9600);
myservo.attach(9);// attaches the servo on pin 9 to the servo object
}
void loop()
{
if(Serial.available())
{
kontrol=Serial.read(); // it reads from python voice recognition
kontrolstr.concat(kontrol);
}
if(kontrolstr== "right")
{pos += 30;
kontrol = '0';
kontrolstr = "";
}
else if(kontrolstr== "left")
{pos -= 30;
kontrol= '0';
kontrolstr = "";
}
myservo.write(pos);
delay(100);
}
It works with voice_command.py (which I wrote) on linux terminal. When code is like this, right after uploading this code to arduino, it works well until voice recognition understand a different word from "right" or "left". When voice command send to arduino another string different from "right" or "left", program still works without any error but after this point it starts not to respond "right" or "left" command anymore. To solve this I did this change. I put an 'else':
#include <Servo.h>
Servo myservo; // create servo object to control a servo
int pos = 90;
String kontrolstr = "";
char kontrol;
void setup()
{
Serial.begin(9600);
myservo.attach(9);// attaches the servo on pin 9 to the servo object
}
void loop()
{
if(Serial.available())
{
kontrol=Serial.read();
kontrolstr.concat(kontrol);
}
if(kontrolstr== "right")
{pos += 30;
kontrol = '0';
kontrolstr = "";
}
else if(kontrolstr== "left")
{pos -= 30;
kontrol= '0';
kontrolstr = "";
}
else { // I write this to make it work..
kontrol = '0';
kontrolstr = "";
}
myservo.write(pos);
delay(100);
}
However, now it is not responding "right" and "left" command too. How can I solve this problem?

The Problem
The problem you are having is that your Serial.available() block is only reading one byte from the Serial buffer in each iteration of the loop. As a consequence, when your servo sends the word "right", the Serial buffer is "right". The first iteration through loop() gives "r" as the value for kontrolstr.
Without the else block, on the second loop, kontrolstr is set to ri, then rig, then righ, etc, and is only reset when left or right are found. This is also what causes the problem of left and right not being reached if another word has been recognized - kontrolstr would have been set to , e.g. "horse", this is not recognized, so then when it sends "right", you get "horseright", etc.
With the else block, on the first loop, kontrolstr is "r", so it hits the else block, and resets the string. On the second loop, kontrolstr is "i", it hits the else block and resets the string, etc, never reaching the relevant control block.
Possible solutions
The start of the solution is to read the entire Serial buffer before processing it, so replace the block starting with if(Serial.available() to:
while(Serial.available())
{
kontrol = Serial.read();
kontrolstr.concat(kontrol);
}
This will read the entire buffer in the first loop, so as long as all the data has been sent between iterations of the loop, your problem will be solved. However, it takes a non-zero amount of time to send data over a Serial port, so it's possible that your loop() iteration triggers in the middle of a send, in which case the Serial buffer might be something like "rig", which won't match "right" or "left", will be reset, then in the next loop you'll get "ht", and again it will be reset - the trigger will be missed.
If possible, I think the best solution would be to have your servo send the control words with a delimiter between them, e.g. \n. If your servo sends "right\nanother word\nleft\n", then you can wait for entire words to come in before processing them. You can do this by changing your loop() to:
void loop()
{
kontrolstr = ""; // Reset on each iteration of the loop
while(Serial.available())
{
kontrol = Serial.read();
// If we reach the delimiter, stop reading from the Serial buffer
if (control == '\n') {
break;
}
kontrolstr.concat(kontrol);
}
if(kontrolstr== "right") {
pos += 30;
} else if(kontrolstr== "left") {
pos -= 30;
}
myservo.write(pos);
delay(100);
}
Of course, this assumes that you're OK with allowing extra words to accumulate in the Serial buffer (seems fine since the buffer wasn't filling up even when you were reading only 1 character every 100ms). However, if it does happen that the Serial buffer is overflowing, then you can create a second string bufferstring and always append whatever is in the Serial buffer to that string, then on each iteration of the loop, pull out the oldest command, giving:
#include <Servo.h>
Servo myservo; // create servo object to control a servo
int pos = 90;
String kontrolstr = "";
String bufferstring = "";
char kontrol;
void setup()
{
Serial.begin(9600);
myservo.attach(9);// attaches the servo on pin 9 to the servo object
}
void loop()
{
// Read whatever's in the Serial port into the buffer string
while(Serial.available())
{
kontrol = Serial.read();
// If we reach the delimiter, stop reading from the Serial buffer
bufferstring.concat(kontrol);
}
// Split the string by the delimiter
int delimiter_loc = bufferstring.indexOf('\n');
if (delimiter_loc != -1) {
// Get the first delimiter_loc characters (doesn't include the delimiter)
kontrolstr = bufferstring.substring(0, delimiter_loc);
// Remove all the characters up to and including the delimiter_loc
bufferstring.remove(0, delimiter_loc + 1);
}
if(kontrolstr== "right") {
pos += 30;
} else if(kontrolstr== "left") {
pos -= 30;
}
// Reset on each iteration of the loop
kontrolstr = "";
myservo.write(pos);
delay(100);
}

Related

Rotary encoder strange behaviour

I have problem with results (on serial monitor) of rotary encoder.
I am using Arduino UNO and RotaryEncoder library.
When I am running example code serial monitor show proper values when rotating with any speed.
I want to use encoder to change volume in Df-player.
Problem starts when I want to use this code together with more complicated one - Mp3 player.
It actually works only when I am rotating encoder very very slowly
#include <SPI.h>
#include <MFRC522.h>
#include <Arduino.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#include <RotaryEncoder.h>
#define RST_PIN 9 // Configurable, see typical pin layout above
#define SS_PIN 10 // Configurable, see typical pin layout above
#define PIN_IN1 2
#define PIN_IN2 3
#define ROTARYSTEPS 1
#define ROTARYMIN 0
#define ROTARYMAX 30
const int playPauseButton = 4;
const int shuffleButton = 5;
boolean isPlaying = false;
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance
SoftwareSerial mySoftwareSerial(5, 6); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);
// Setup a RotaryEncoder with 2 steps per latch for the 2 signal input pins:
RotaryEncoder encoder(PIN_IN1, PIN_IN2, RotaryEncoder::LatchMode::TWO03);
// Last known rotary position.
int lastPos = -1;
//*****************************************************************************************//
void setup() {
Serial.begin(9600); // Initialize serial communications with the PC, COMMENT OUT IF IT FAILS TO PLAY WHEN DISCONNECTED FROM PC
mySoftwareSerial.begin(9600);
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522 card
while (! Serial);
encoder.setPosition(5 / ROTARYSTEPS); // start with the value of 5.
pinMode(playPauseButton, INPUT_PULLUP);
pinMode(shuffleButton, INPUT_PULLUP);
Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
if (!myDFPlayer.begin(mySoftwareSerial)) { //Use softwareSerial to communicate with mp3.
Serial.println(F("Unable to begin:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
}
Serial.println(F("DFPlayer Mini online. Place card on reader to play a spesific song"));
//myDFPlayer.volume(15); //Set volume value. From 0 to 30
//volumeLevel = map(analogRead(volumePot), 0, 1023, 0, 30); //scale the pot value and volume level
myDFPlayer.volume(5);
//prevVolume = volumeLevel;
//----Set different EQ----
myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);
// myDFPlayer.EQ(DFPLAYER_EQ_POP);
// myDFPlayer.EQ(DFPLAYER_EQ_ROCK);
// myDFPlayer.EQ(DFPLAYER_EQ_JAZZ);
// myDFPlayer.EQ(DFPLAYER_EQ_CLASSIC);
// myDFPlayer.EQ(DFPLAYER_EQ_BASS);
}
//*****************************************************************************************//
void loop() {
encoder.tick();
// get the current physical position and calc the logical position
int newPos = encoder.getPosition() * ROTARYSTEPS;
if (newPos < ROTARYMIN) {
encoder.setPosition(ROTARYMIN / ROTARYSTEPS);
newPos = ROTARYMIN;
} else if (newPos > ROTARYMAX) {
encoder.setPosition(ROTARYMAX / ROTARYSTEPS);
newPos = ROTARYMAX;
} // if
if (lastPos != newPos) {
Serial.println(newPos);
myDFPlayer.volume(newPos);
lastPos = newPos;
} // if
// Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory.
MFRC522::MIFARE_Key key;
for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;
//some variables we need
byte block;
byte len;
MFRC522::StatusCode status;
if (digitalRead(playPauseButton) == LOW) {
if (isPlaying) {
myDFPlayer.pause();
isPlaying = false;
Serial.println("Paused..");
}
else {
isPlaying = true;
myDFPlayer.start();
Serial.println("Playing..");
}
delay(500);
}
if (digitalRead(shuffleButton) == LOW) {
myDFPlayer.randomAll();
Serial.println("Shuffle Play");
isPlaying = true;
delay(1000);
}
//-------------------------------------------
// Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
if ( mfrc522.PICC_IsNewCardPresent()) {
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
Serial.println(F("**Card Detected:**"));
//-------------------------------------------
mfrc522.PICC_DumpDetailsToSerial(&(mfrc522.uid)); //dump some details about the card
//mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); //uncomment this to see all blocks in hex
//-------------------------------------------
Serial.print(F("Number: "));
//---------------------------------------- GET NUMBER AND PLAY THE SONG
byte buffer2[18];
block = 1;
len = 18;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 1, &key, &(mfrc522.uid)); //line 834
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Authentication failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
status = mfrc522.MIFARE_Read(block, buffer2, &len);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Reading failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
//PRINT NUMBER
String number = "";
for (uint8_t i = 0; i < 16; i++)
{
number += (char)buffer2[i];
}
number.trim();
Serial.print(number);
//PLAY SONG
myDFPlayer.play(number.toInt());
isPlaying = true;
//----------------------------------------
Serial.println(F("\n**End Reading**\n"));
delay(1000); //change value if you want to read cards faster
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
}
}
Any ideas what is wrong?
You have a delay(1000) in your main loop, and since your RotaryEncoder object seems to need a tick() function, i am assuming that it is not interrupt driven. This means that it will check only once per second if it has moved to the next step.
If a rotary encoder is stepped twice, and the middle step is missed by the MCU, the latter has no way of knowing which way round the encoder has turned.
So in this case you can only turn it one step per second.
What you need is, either:
a free running main loop, which goes round at least 100 times per second. (less nice)
a rotary encoder driver that is interrupt driven. (very nice)
I don't know if such a library exists, because i tend not to use arduino libs, but it is a very good exercise to write your own using GPIO interrupts.

Incoming values to serial port are sometime corrupted or missing

I have written simple bluetooth transmitter and receiver on two arduino nano v3 boards. Bluetooth modules are HM-10 connected into hardware serial ports. It works, but on receiver side I often receive corrupted values and many values are missing. Where is a problem:
I am beginner in arduino. If it is possible a need to explain deeply. Thanks.
Transmitter code:
const long waitingInterval = 20000;
unsigned long lastSend = micros();
void setup()
{
Serial.begin(19200);
Serial.println("Started");
}
bool delay() {
if(micros() >= lastSend + waitingInterval) {
lastSend = micros();
return true;
}
return false;
}
void loop()
{
if(delay()) {
String mil = String(millis());
String sendingText = mil + ";" + mil + ";" + mil + ".";
Serial.println(sendingText);
}
}
Output of transmitter serial monitor interface:
10548;10548;10548.
10568;10568;10568.
10589;10589;10589.
10609;10609;10609.
10629;10629;10629.
10649;10649;10649.
10670;10670;10670.
10690;10690;10690.
10711;10711;10711.
10730;10730;10730.
10750;10750;10750.
10771;10771;10771.
10791;10791;10791.
10812;10812;10812.
10831;10831;10831.
10852;10852;10852.
10872;10872;10872.
10893;10893;10893.
10913;10913;10913.
10933;10933;10933.
10953;10953;10953.
10974;10974;10974.
10994;10994;10994.
11014;11014;11014.
11034;11034;11034.
11055;11055;11055.
11075;11075;11075.
11096;11096;11096.
11115;11115;11115.
Receiver code:
void setup() {
Serial.begin(19200);
Serial.println("Started");
}
void loop() {
if(Serial.available()) {
String incomingData = String();
char incomingChar = Serial.read();
if(incomingChar == '.') {
incomingData = bufferString;
Serial.print(bufferString);
bufferString = String();
} else {
bufferString += String(incomingChar);
return;
}
}
Output of receiver serial monitor interface:
10548;10548;10548
10568;10568;10568
10589;10589;10589
10609;10609;10609
10629;10629;10629
106410771
10791;10791;10791
10812;10812;10812
10831;10831;10831
10852;10852;10852
10872;10872;10872
10893;10893;11034;11034;11034
11055;11055;11055
11075;11075;11075
11096;11096;11096
11115;11115;11115
One problem is simply calling Serial.available() simply returns the number of bytes available to be read in the buffer; it could be exactly the number of bytes you need, it could be less, or more. Because of this, you might read extra data, too little, or too much data. More so, in higher level transmission protocols sometimes after a device receives data it will send an ACK(acknowledgement) back to the sender, saying it is ready for more data.
Edit** It should also be noted that the comment talking about mutex's isn't correct. Mutexes are typically used to synchronize code across multiple threads of execution on the same device. The key is that they are a shared resource across the thread's heap space. This is NOT the case when using two different arduino devices; thus, even if you could use them, it would be useless.
For your code, I would suggest the following edits to the transmitter:
#define MIN_TIMEOUT 3
void recieveAck(){
bool validAck = false;
uint8_t timeout_cnt = 0x00;
while(timeout_cnt < MIN_TIMEOUT){
//Wait for receiving device to respond with two bytes then send next
char incomingBytes[2];
Serial.readBytes(incomingBytes, 0x02);
if(incomingBytes[0] == 0xBB && incomingBytes[1] == 0xCC)
break;
timeout_cnt++;
}
}
void loop()
{
if(delay()) {
String mil = String(millis());
String sendingText = mil + ";" + mil + ";" + mil + ".";
Serial.println(sendingText);
recieveAck();
}
}
And to the receiver:
#define NEXT_INC_SIZE 2 //Expects one byte at a time
void sendAck(){
char outData[2];
outData[0] = 0xBB;
outData[1] = 0xCC;
Serial.write(outData, 2); //Write the ack data
}
void loop() {
if(Serial.available() >= NEXT_INC_SIZE){
char incomingBytes[2];
Serial.readByte(incomingBytes, NEXT_INC_SIZE); //Read exactly how many bytes you need
//Do stuff with the data here....
sendAck();
}
}

How to make an arduino serial loop (receive numbers continuously)

New to coding! I am trying to get text on my LED Matrix through serial data that is being sent in from processing.
My code works but the only problem is that though the serial data on processing is constant, the arduino code only reads the number once. This makes it so that the text will not scroll all the way through. How do I loop a serial read on arduino?
Here is the relevant portion of my code:
void loop()
{
if (Serial.available() > 0)
{
int matrixPinState = Serial.read();
// stage = Serial.read();
// analogWrite(matrixPin, stage);
if (matrixPinState == 1)
{
matrix.fillScreen(0);
matrix.setCursor(x, 0);
matrix.print(F("Im kind"));
if (--x < -30)
{
x = matrix.width();
if (++pass >= 8) pass = 0;
matrix.setTextColor(colors[pass]);
}
matrix.show();
delay(30);
}
}
}
When a byte(or you call it character, a data that is 8 bit long) is fetched by uart block(the hardware), it is buffered to input buffer so that programmer can read and process it.
In your case, when you send a character, it is fetched and put to the buffer and when you read it there is no more byte available to read unless you send to new one.
In short, read the pin state once. You can do something like:
int matrixPinState = 0
void setup() {
// do all your setup settings first
while (Serial.available() < 0) {
// wait here for the input
delay(30);
}
// got your input, read it
matrixPinState = Serial.read();
}
void loop()
{
if (matrixPinState == 1)
{
matrix.fillScreen(0);
matrix.setCursor(x, 0);
matrix.print(F("Im kind"));
if (--x < -30)
{
x = matrix.width();
if (++pass >= 8) pass = 0;
matrix.setTextColor(colors[pass]);
}
matrix.show();
delay(30);
}
}

Recieved String to useable variable in Arduino

I'm trying to use data sent via Bluetooth from a mobile app to the Arduino. There will be 2 strings received dependent on the function required.
First function: When the string "ledon" or "ledoff" received turn on or turn off an led.
Second Function: from the app I get the data a user has typed into 2 boxes, one a text box the other a password box, this is received by the Arduino as one string with comma's as a delimiter so the string can be separated into it's component parts, which in this case are an "ssid name" and a "network key". These details I will be intending to write to an onboard sd card so that the wifi module will be able to logon to the network that the ssid and network key are relevant too.
So far I can get the led light to do as it should, I can also get the string read, separated by the delimiter and printed out to the serial monitor. However when I try to combine the two codes the led function fails to turn on or off the led light although the correct command is printed into the serial monitor. I have researched how to solve this trying each requirement as a standalone function and calling them in the loop section, which individually they work but when called together again the led fails to come on.
This is the code i'm presently using:
const int ledPin = 11;
String readString;
void setup() {
Serial.begin(9600);
pinMode (ledPin, OUTPUT);
}
void loop() {
while (Serial.available()) {
delay(3);
char c = Serial.read();
readString += c;
}
//turns led on or off
if (readString.length() >0) {
Serial.println(readString);
if (readString == "ledon")
{
digitalWrite (ledPin, HIGH);
}
if (readString == "ledoff")
{
digitalWrite (ledPin, LOW);
}
readString="";
//seperates the string into 2 lines using the delimiter ","
String first = Serial.readStringUntil(',');
Serial.read(); //next character is comma, so skip it using this
String second = Serial.readStringUntil(',');
Serial.read();
Serial.println(first);
Serial.println(second);
readString="";
}
}
If I comment out the string part of the code the led turns on and off fine, if I try and run the code as is the code prints everything you would expect but the led doesn't come on.
I'm pretty new to Arduino,c and c++ and I cannot seem to figure this out so any help would be great.
The app I'm using to control this is being built in Mit's App Inventor 2
I have finally resolved this.
I decided to look at other logical steps to find a solution.
I considered the issue could be having different strings that did different things which may be conflicting in some way.
In my research I found reference to the fact that the Bluetooth module HC-05 has 2 pins that in my case were not being utilised, the key pin and the state pin, in this stack overflow discussion Tell when Bluetooth module connects on Arduino and decided this logic of using the "State pin" to signify Bluetooth connection would negate the possibility of any conflicts within the String logic.
Once I had figured out the best way to place and order the "where" statements I managed to get the functionality at this point in my project which then allows me to move on to the next step in the development.
The code I have now arrived at:
const int ledPin1 = 11;
const int ledPin2 = 4;
String readString;
boolean BTconnected = false;
void setup() {
Serial.begin(9600);
pinMode (ledPin1, OUTPUT);
pinMode (ledPin2, INPUT);
}
void loop() {
while (!BTconnected)
{
if ( digitalRead(ledPin2)==HIGH) { BTconnected = true;};
}
digitalWrite(ledPin1, HIGH);
Serial.println("HC-05 is now connected");
Serial.println("");
while (BTconnected)
{
if ( digitalRead(ledPin2)==LOW) { BTconnected = false;
digitalWrite(ledPin1, LOW);
Serial.println("HC-05 is now disconnected");
Serial.println("");
};
while (Serial.available())
{
delay(3);
char c = Serial.read();
readString += c;
}
if (readString.length() >0) {
//Serial.println(readString);
//readString="";
String first = Serial.readStringUntil(',');
Serial.read(); //next character is comma, so skip it using this
Serial.println(first);
//readString="";
String second = Serial.readStringUntil(',');
Serial.read();
Serial.println(second);
Serial.println("");
//readString="";
}
}
}

Arduino: AT Commands - Read the last line of the serial output using Serial.read()

I constantly pass AT commands to get GSM Signal Strength
My code copies the entire serial output
Kindly advise how to read the latest serial output (last line)
find the output below, in which i need to assign the output from last line (21,0) to the variable "signal"
My Output:
AT
OK
AT+CREG?
+CREG: 0,1
ok
AT+CSQ
+CSQ: 21,0
My code:
byte gsmDriverPin[3] = {
3,4,5};
char signal[10];
char inChar;
int index;
char inData[200];
void setup()
{
//Init the driver pins for GSM function
for(int i = 0 ; i < 3; i++){
pinMode(gsmDriverPin[i],OUTPUT);
}
digitalWrite(5,HIGH);//Output GSM Timing
delay(1500);
digitalWrite(5,LOW);
digitalWrite(3,LOW);//Enable the GSM mode
digitalWrite(4,HIGH);//Disable the GPS mode
delay(2000);
Serial.begin(9600); //set the baud rate
delay(5000);//call ready
delay(5000);
delay(5000);
start_GSM();
}
void loop()
{
Signal_Strength();
Serial.println("AT+CMGF=1");
delay(1000);
Serial.println("AT+CMGS=\"1234567890\"");//Change the receiver phone number
delay(1000);
Serial.println(signal);
delay(1000);
Serial.write(26);
}
void Signal_Strength(){
Serial.println("AT+CSQ");
delay(2000);
read_String();
strtok(inData, ",");
strcpy(signal,strtok(NULL, ","));
}
void read_String() {
index=0;
while(Serial.available() > 0) // Don't read unless
// there you know there is data
{
if(index < 199) // One less than the size of the array
{
inChar = Serial.read(); // Read a character
inData[index] = inChar; // Store it
index++; // Increment where to write next
inData[index] = '\0'; // Null terminate the string
}
}
}
void start_GSM(){
//Configuracion GPRS Claro Argentina
Serial.println("AT");
delay(2000);
Serial.println("AT+CREG?");
delay(2000);
}
First of all, you must seriously redo your AT command handling to
Read and parse the every single response line given back from the modem until you get a final result code. This applies for every single command line invocation, no exceptions whatsoever. See this answer for more details.
Never ever call delay in any code that handles AT commands. See this answer for more details about the risk of aborting the next command.
Before fixing these fundamental issues you cannot expect any successful behaviour. Parsing AT command responses is not that complicated, have a look at the source code for atinout for an example.