I have a very short example program running on an Arduino Fio device. This program is sending serial data. A connected Xbee router device is receiving this data and sending it to a Xbee coordinator device connected to my notebook. The program also reading data from the serial port. I can send a 1 or a 0 to switch the LED of the Fio device on or off.
Switching the LED on or off by sending a 1 or a 0 from a terminal on my notebook is working well.
But when I try to read the data which the Fio device is sending, I get this:
his direction works
is direction works
s direction works
direction works
direction works
irection works
rection works
ection works
ction works
... and so on.
But I'm expecting a string ("This direction works " + counter++;) as you can see in the following code example.
Here is the short Arduino sketch:
int incomingByte = 0; // for incoming serial data
int counter = 0;
void setup()
{
Serial.begin(57600);
pinMode(13,OUTPUT);
// blink twice at startup
digitalWrite(13, LOW);
delay(1000);
digitalWrite(13, HIGH); // first blink
delay(50);
digitalWrite(13, LOW);
delay(200);
digitalWrite(13, HIGH); // second blink
delay(50);
digitalWrite(13, LOW);
}
void loop()
{
// send data only when you receive data:
if (Serial.available() > 0)
{
// read the incoming byte:
incomingByte = Serial.read();
if(incomingByte == '0')
{
digitalWrite(13, LOW);
}
else if(incomingByte == '1')
{
digitalWrite(13, HIGH);
}
// say what you got:
Serial.print("Fio received: ");
Serial.write(incomingByte); // Arduino 1.0 compatibility
Serial.write(10); // send a line feed/new line, ascii 10
}
else
{
String sendData = "This direction works " + counter++;
Serial.println(sendData);
delay(1500);
}
}
What I'm doing wrong? Why do I don't get:
This direction works 0
This direction works 1
This direction works 2
This direction works 3
... and so on?
As #Elric and #Olaf mentioned in the comments, it is not possible to do that like in my posted code.
After reading the documentation again I found an "explanation":
Caution: You should be careful about concatenating multiple variable
types on the same line, as you may get unexpected results. For
example:
int sensorValue = analogRead(A0); String stringOne = "Sensor
value: "; String stringThree = stringOne + sensorValue;
Serial.println(stringThree);
results in "Sensor Value: 402" or whatever the analogRead() result is,
but
int sensorValue = analogRead(A0); String stringThree = "Sensor
value: " + sensorValue; Serial.println(stringThree);
gives unpredictable results because stringThree never got an initial
value before you started concatenating different data types.
Firstly you have to initialize the String variable to assign two concatenated strings to it.
Related
I have an AI Thinker A9G module connected to an ESP8266 D1 mini. I have the following script to send and receive AT commands to/from the module. The idea is to get a SMS with the Google maps GPS location of the module onto my mobile phone.
// GPS tracker with AI Thinker A9G module and AZ Delivery D1 Mini ESP8266 module
#include <SoftwareSerial.h>
#define rxPin D7
#define txPin D8
SoftwareSerial A9modem(rxPin, txPin); // Pins D7 Rx and D8 Tx are used as used as software serial pins
String incomingData; // For storing incoming serial data
String gpsData; // For storing location data
char msg;
char call;
void setup()
{
Serial.begin(115200); // Baud rate for serial monitor
A9modem.begin(115200); // Baud rate for GSM shield
Serial.println("GPS/GSM A9G BEGIN");
Serial.println("Enter character for control option: ");
Serial.println("h : to disconnect a call");
Serial.println("s : to send a message");
Serial.println("r : to receive a message");
Serial.println("c : to make a call");
Serial.println("l : to read location");
Serial.println("d : to disconnect gps");
Serial.println();
delay(100);
// Set SMS mode to text mode
A9modem.print("AT+CMGF=1\r");
delay(100);
// Set GSM module to TP show the output on serial out
A9modem.print("AT+CNMI=2,2,0,0,0\r");
delay(100);
}
void loop()
{
ReceiveMessage();
gpsData = incomingData.substring(33, 52);
Serial.print("Location: ");
Serial.println(gpsData);
delay(2000);
if (Serial.available() > 0)
switch(Serial.read())
{
case 's':
SendMessage();
break;
case 'r':
ReceiveMessage();
break;
case 'c':
MakeCall();
break;
case 'h':
HangupCall();
break;
case 'l':
ReadLocation();
break;
case 'd':
DisconnectGps();
break;
}
}
void ReceiveMessage()
{
if (A9modem.available() > 0)
{
incomingData = A9modem.readString(); // Get the data from the serial port
Serial.print(incomingData);
delay(100);
}
}
void SendMessage()
{
A9modem.println("AT+CMGF=1"); // Sets the GSM Module into text mode
delay(1000); // Delay of one second
A9modem.println("AT+CMGS=\"xxxxxxxxxxxxx\"\r"); // Replace your mobile number here
delay(1000);
String sms = "http://maps.google.com/maps?q=" + gpsData; // Create the SMS location string
A9modem.println(sms);
delay(100);
A9modem.println((char)26); // ASCII code of CTRL+Z
delay(1000);
}
void MakeCall()
{
A9modem.println("ATD+xxxxxxxxxxxxx;"); // Replace your mobile number here
Serial.println("Calling "); // Print response over serial port
delay(1000);
}
void HangupCall()
{
A9modem.println("ATH");
Serial.println("Hangup Call");
delay(1000);
}
void DisconnectGps()
{
A9modem.println("AT+GPS=0");
Serial.println("Disconnect GPS");
delay(1000);
}
void ReadLocation()
{
A9modem.println("AT+GPS=1");
delay(1000);
A9modem.println("AT+LOCATION=2"); // Check location every two seconds
delay(2000);
}
So if I use the commands "l" and "s" in Arduino IDE serial monitor everything is working well but I don't know how to change the code in a way that I get a fully automated GPS tracker. The idea is the following: Power on starts the tracker. When gpsData string is not empty the first SMS will be send to my mobile phone. The next SMS follows 20 minutes later and so on until power is switched off. Could you help please?
Thanks in advance!
Finally I came up with this solution but I'm not very happy with it (it is working but I have to restart the module from time to time because there are no GPS data). There are 3 reasons for this: There is no error handling for the GPS function, I set the delays in a more ore less random way to get everything working but I don't know if the values make sense and I don't know if the substring() function is the best way to get the location from the serial response.
// GPS tracker with AI Thinker A9G module and AZ Delivery D1 Mini
ESP8266 module
#include <SoftwareSerial.h>
#define rxPin D7
#define txPin D8
SoftwareSerial A9modem(rxPin, txPin); // Pins D7 Rx and D8 Tx are used as used as software serial pins
String incomingData; // For storing incoming serial data
String gpsData; // For storing location data
int runGps = 1;
int sendSms = 0;
void setup()
{
pinMode(LED_BUILTIN, OUTPUT); // Use builtin LED for correct GPS status
Serial.begin(115200); // Baud rate for serial monitor
A9modem.begin(115200); // Baud rate for GSM shield
// Set SMS mode to text mode
A9modem.print("AT+CMGF=1\r");
delay(300);
// Set GSM module to TP show the output on serial out
A9modem.print("AT+CNMI=2,2,0,0,0\r");
delay(1000);
Serial.end();
delay(500);
}
void loop()
{
Serial.begin(115200);
ReceiveMessage(); // Start a new serial stream
gpsData = incomingData.substring(33, 52); // Strip all unnessesary data from the stream
Serial.print("Location: "); // Check if the location data are correct
Serial.println(gpsData);
delay(1000);
if(runGps == 1) { // Start GPS
ReadLocation();
delay(20000); // Wait for GPS to be ready
}
if(gpsData.length() == 19 && sendSms == 0) { // If the location string is correct send SMS
if (isdigit(gpsData.charAt(0))) { // Check if the stream starts with a number
SendMessage();
digitalWrite(LED_BUILTIN, HIGH);
delay(300);
digitalWrite(LED_BUILTIN, LOW);
delay(300);
digitalWrite(LED_BUILTIN, HIGH);
delay(300);
digitalWrite(LED_BUILTIN, LOW);
delay(300);
digitalWrite(LED_BUILTIN, HIGH);
}
}
if(runGps == 0 && sendSms == 1) {
runGps = 1;
sendSms = 0;
delay(6000); // Make sure that all SMS serial communication is over
while (A9modem.available()) {
A9modem.read(); // Delete all data from serial buffer
}
delay(300000); // If SMS was sent wait 5 minutes before running the main loop again
Serial.end();
delay(500);
}
}
void ReceiveMessage()
{
if (A9modem.available() > 0)
{
incomingData = A9modem.readString(); // Get the data from the serial port
Serial.print(incomingData);
delay(500);
}
}
void SendMessage()
{
sendSms = 1; // Stop starting the SendMessage function
A9modem.println("AT+CMGF=1"); // Sets the GSM Module into text mode
delay(1000);
A9modem.println("AT+CMGS=\"xxxxxxxxxx\"\r"); // Replace your mobile number here
delay(1000);
String sms = "http://maps.google.com/maps?q=" + gpsData; // Create the SMS string
A9modem.println(sms);
delay(500);
A9modem.println((char)26); // ASCII code of CTRL+Z
delay(500);
}
void ReadLocation()
{
runGps = 0; // Stop starting the ReadLocation function
A9modem.println("AT+GPS=1");
delay(1000);
A9modem.println("AT+LOCATION=2"); // Check location every two seconds
delay(2000);
}
Try turning ON the NMEA stream from the A9G. Say you want to get the NMEA data every t seconds, you can add and then call the following function once in the void setup()
void TurnOnNMEA(int t)
{
if(t > 3600)
t = 3600; // Because the max time is 3600s
A9modem.print("AT+GPSRD=");
A9modem.println(t);
while(!A9modem.available()); // wait till the A9G sends a response
char c;
while(A9modem.available())
{
c = A9modem.read();
Serial.print(c);
delay(2); // Time needed for the next character
}
}
This will start the NMEA data stream every t seconds which you can print using the following function,
void ReadNMEA()
{
char c; // Read each character from the stream as it comes
if(A9modem.available())
{
c = A9modem.read();
Serial.print(c);
delay(2); // Time needed for the next character
}
}
Parsing and decoding the NMEA is a bit tricky thing to do. There is a lot of information inside it and the decoding depends on what you want to do with it. If you just want the location, you can parse the $GNGGA line.
When parsing the data, DO NOT USE Arduino String() class. It is a very bad idea when handling this kind of stream. Instead, use char array.
I have the script below and it works.
But I want to control it with a word like on or off instead of one character.
I tried and searched a lot but without success.
/*
Simple LED sketch
*/
int led = 13; // Pin 13
void setup()
{
pinMode(led, OUTPUT); // Set pin 13 as digital out
// Start up serial connection
Serial.begin(115200); // baud rate
}
void loop()
{
if (Serial.available()) {
int ser = Serial.read(); //read serial as ascii integer
if (ser == 'a') { //is this serial byte the ASCII equivalent of 0 through 9?
digitalWrite(led, HIGH); // on
Serial.println("aan");
}
else if (ser == 'u') {
digitalWrite(led, LOW); // off
Serial.println("uit");
}
}
}
Use Serial.readStringUntil(terminator) to read a string from the serial.
The sent string needs to be terminated with a newline character.
Chose Newline in the Arduino IDE's Serial Monitor.
String cmd = "";
void loop()
{
if (Serial.available()) {
cmd = Serial.readStringUntil('\n');
if (cmd == "on") {
digitalWrite(led, HIGH); // on
Serial.println("aan");
}
else if (cmd == "off") {
digitalWrite(led, LOW); // off
Serial.println("uit");
}
}
}
Your problem is that you are using a char you need to declare a string so you can compare that string to the input, the guy above gave a ok/very slacky/too complex for you answer (sorry no offense I am just trying to show contrast between a complex and a easy answer not trying to offend you) and partially is because he did not explain what he is doing and because he is doing unnecessary/useless work. There is a function in c++ called Serial.readString(), much easier then the stuff up there. Assuming your level of programming (no offense) from your question here is a quick review on data types:
String = ""
int = integer number {1,2,3,4,5,6,...}
char = '' <- Notice the difference from String = ""
float = floating point number {1.2,4.5,...}
(These are not all of them, there is more like byte and so on but just make sure you know how to use the above first)
/*
Simple LED sketch
*/
int led = 7; // Pin 13
String ser; //Declare the string that is going to store what your are going to
//write in the serial
void setup(){
// Start up serial connection
//It's good convention to start the serial before pinMode or any other thing
//in setup
Serial.begin(9600); //I don't know why you need such an high baud rate in
//your thing, 9600 for what you have to do is more than fine, this is just at
//what speed the serial is read (in a very general explanation)
pinMode(led, OUTPUT); // Set pin 13 as digital out
//make sure you put in here wheter you want to start the led "on" or "off"
//Eg you want it to start on, then digitalWrite(led, HIGH);
}
void loop(){
ser = Serial.readString();
if(Serial.available() == 0) { //You can also use a while loop if you want to
//This is telling the arduino: if there is something in the serial, then do...
if(ser == "on"){
Serial.println("on");
digitalWrite(led, HIGH);
}else if(ser == "off"){
Serial.println("off");
digitalWrite(led, LOW);
}
}
}
Hope it helped!
Also notice how the above code with
if(Serial.available())
This is a quite WEIRD and SHADY statement and might not work. That's because you are not really telling the int value into the function Serial.available
As the arduino guidelines specify:
int incomingByte = 0; // for incoming serial data
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}
void loop() {
// send data only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte, DEC);
}
}
viewable here
I want to send processing IDE data to arduino. But led is not working. It worked fine once. But not working now :( Serial port name is exactly same in arduino as it is found by processing.
Processing code:
import processing.serial.*;
Serial myPort; // Create object from Serial class
void setup()
{
size(200,200); //make our canvas 200 x 200 pixels big
String portName = Serial.list()[0]; //change the 0 to a 1 or 2 etc. to match your port
myPort = new Serial(this, portName, 9600);
}
//send a 1
void draw() {
if (mousePressed == true)
{ //if we clicked in the window
myPort.write('1'); //send a 1
println("1");
} else
{ //otherwise
myPort.write('0'); //send a 0
}
}
Arduino code:
char val='0'; // Data received from the serial port
int ledPin = 13; // Set the pin to digital I/O 13
void setup() {
pinMode(ledPin, OUTPUT); // Set pin as OUTPUT
Serial.begin(9600); // Start serial communication at 9600 bps
}
void loop() {
//digitalWrite(ledPin, HIGH); // turn the LED on
if (Serial.available())
{ // If data is available to read,
val = Serial.read(); // read it and store it in val
}
if (val == '1')
{ // If 1 was received
digitalWrite(ledPin, HIGH); // turn the LED on
} else {
digitalWrite(ledPin, LOW); // otherwise turn it off
}
delay(10); // Wait 10 milliseconds for next reading
}
Processing
You can simply say if(mousePressed)..., there is no need to say == true (it's implied)
Arduino
You're correct to check if(Serial.available()) before trying to overwrite val with whatever character you read from there. However, the rest of your code inside loop() is executing regardless of this check. There is no reason to repeatedly write a pin to LOW or HIGH if it is already there. In fact, you will be more responsive if you only delay on loops where you find a character available for reading.
I'd recommend you add some print statements to your Arduino code so you can get a look at what you're reading.
Also, could it be that your hardware is connected improperly or that your LED is simply burnt out?
I'm trying to do a project where BLUNO (Arduino UNO + BLE) will connect to an iBeacon and make use of the RSSI detected.
I've already made contact between the BLUNO and the iBeacon through the AT commands. I can get RSSI result in the Arduino IDE serial monitor when I ping it with AT commands.
My problem now is in sending those AT commands through an Arduino sketch. I know I've to use Serial Communication, but my Serial.Available function never returns more than 0.
void setup() {
pinMode(13, OUTPUT);
Serial.begin(115200);
Serial.print("+++\r\n");
Serial.print("AT+RSSI=?\r\n");
}
void loop(){
if(Serial.available()){
digitalWrite(13, HIGH);
delay(5000);
}
}
What is irritating me is that I can connect BLUNO to my iPhone and get the RSSI on the serial monitor through AT commands. But that above code doesn't work!
Any help?
I'm almost done with the whole project for now.
my mistake in the last code was the initiation part that has to be done before the AT commands. The right way is
Serial.begin(115200); //Initiate the Serial comm
Serial.print("+");
Serial.print("+");
Serial.print("+"); // Enter the AT mode
delay(500); // Slow down and wait for connection establishment
instead of
Serial.print("+++\r\n");
so yeah the rest is kind of alright. Keep in mind that this BLE thing REALLY sucks in terms of accuracy in locating a beacon. The RSSI reading keep fluctuating and the calculated distance using the simplified equation here somewhere on Stack overflow is REALLY unreliable.
So yeah keep that in mind yo!
Here's my full code just for reference.
// while the AT connection is active, the serial port between the pc and the arduino is occuipied.
// You can manipluate the data on arduino, but to display on the serial monitor you need to exit the AT mode
char Data[100];
char RAW[3];
int INDEX;
char Value = '-';
void setup() {
pinMode(13, OUTPUT); // This the onboard LED
pinMode(8, OUTPUT); // This is connected to the buzzer
Serial.begin(115200); //Initiate the Serial comm
Serial.print("+");
Serial.print("+");
Serial.print("+"); // Enter the AT mode
delay(500); // Slow down and wait for connectin establishment
}
void loop(){
Serial.println("AT+RSSI=?"); // Ask about the RSSI
for(int x=0 ; Serial.available() > 0 ; x++ ){ // get the Enter AT mode words
//delay(20); // Slow down for accuracy
Data[x] = Serial.read(); // Read and store Data Byte by Byte
if (Data[x] == Value ) // Look for the elemnt of the array that have "-" that's the start of the RSSI value
{
INDEX=x;
}
}
//Serial.println("AT+EXIT");
RAW[0] = Data[INDEX]; // Copy the RSSI value to RAW Char array
RAW[1] = Data[INDEX+1];
RAW[2] = Data[INDEX+2];
RAW[3] = Data[INDEX+3];
int RSSI = atoi(RAW); //Convert the Array to an integer
//Serial.println(RSSI);
//delay(200); // Give the program time to process. Serial Comm sucks
double D = exp(((RSSI+60)/-10)); //Calculate the distance but this is VERY inaccurate
//Serial.println(D);
if (D>1.00) // If the device gets far, excute the following>>
{
digitalWrite(13, HIGH);
digitalWrite(8, HIGH);
delay(500);
digitalWrite(13, LOW);
digitalWrite(8, LOW);
delay(500);
}
}
As part of a simple Automation project, I was trying to control some LEDs through serial port. I cannot make the following code working
int pin =0;
int state = 0;
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}
void loop() {
// send data only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
if(Serial.read() == 'S' && Serial.read() == 'S') {
// Command to set the pin
pin = Serial.read() - 65;
state = Serial.read() - '0';
Serial.print("Set State Command received");
// Set the Pin
pinMode(pin, OUTPUT);
digitalWrite(pin, state == 0? LOW:HIGH);
}
}
}
I am sending "SSN1" from my python program to the Arduino serial port for testing, and nothing happens (I have an LED connected on pin 13)
SS - Set State Command
N - (pin no) + 'A' - Pin number 13
1 - State ( 0 = LOW, 1= HIGH)
You want to wait until 4 serial bytes accumulate on the serial buffer.
void loop() {
// polls the serial buffer
while (Serial.available() < 4);
if (Serial.read() == 'S' && Serial.read() == 'S') {
char type = Serial.read();
char pin = Serial.read() - 65;
// do something with the results
}
}
Note that you may want to implement some kind of padding (adding a fixed length of spaces, for example) between inputs, because the serial buffer may drop a byte or overflow, which can lead to unexpected results. Also, some people will complain about the while (Serial.available() < 4) command because computer scientists have been trained to think "polling = bad!", but in the case of an Arduino it makes no difference since it is only running a single task.
By the way, you can also use interrupts with Serial data, but that's out of the scope of this response.
actually I have also battled to get something similar right. due to the problems experienced with serial communication (sometime the messages are slow and sent in pieces, the ACII codes are difficult to remember) I went for a different solution. Basically I added a header ">" and a tail "<"to the message sent to the serial and I featured the Arduino code to consider the message between >< as command. I have then used ATOI to convert string to integer.
In the following code you will see that I have used the following numbers to get Arduino changing the status of pin2 and pin13.
Here are the commands associated to this code:
1000< set pin 13 HIGH,
2000< set pin 13 LOW,
3000< set pin 2 HIGH,
4000< set pin 2 LOW
So, simply send out to the serial those numbers between >< and it should work.
I have used the above also to set PWM speed simply manipulating the strings associated to the received messages. I have not included in this sample the PWM commands but only those related to pin 13 and pin 2. I thought that would be much simpler and safe to use numbers and string identifier to open and close the message.
Load the sketch, open the serial monitor and send >1000<, you should see the internal led on pin 13 lighting up, and so on. let me know if you need any additional help.
char inData[10];
int index;
boolean started = false;
boolean ended = false;
String message = "I am ready!, Send your command....";
void setup(){
Serial.begin(9600);
Serial.println(message);
pinMode (13, OUTPUT);
pinMode (2, OUTPUT);
}
void loop()
{
while(Serial.available() > 0)
{
char aChar = Serial.read();
if(aChar == '>')
{
started = true;
index = 0;
inData[index] = '\0';
}
else if(aChar == '<')
{
ended = true;
}
else if(started)
{
inData[index] = aChar;
index++;
inData[index] = '\0';
}
}
if(started && ended)
{
int inInt = atoi(inData);
// set pin 13 HIGH
if (inInt < 1000)
{
digitalWrite(13, HIGH);
}
//set pin 13 LOW
else if (inInt < 2000)
{
digitalWrite(13, LOW);
}
//set pin 2 HIGH
else if (inInt < 3000)
{
digitalWrite(2, HIGH);
}
//set il pin 2 LOW
else if (inInt < 4000)
{
digitalWrite(2, LOW);
}
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
}
Look at this example code from Arduino's Physical Pixel Tutorial
const int ledPin = 13; // the pin that the LED is attached to
int incomingByte; // a variable to read incoming serial data into
void setup() {
// initialize serial communication:
Serial.begin(9600);
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
}
void loop() {
// see if there's incoming serial data:
if (Serial.available() > 0) {
// read the oldest byte in the serial buffer:
incomingByte = Serial.read();
// if it's a capital H (ASCII 72), turn on the LED:
if (incomingByte == 'H') {
digitalWrite(ledPin, HIGH);
}
// if it's an L (ASCII 76) turn off the LED:
if (incomingByte == 'L') {
digitalWrite(ledPin, LOW);
}
}
Verify that you see SSN1 coming through on the serial monitor.
I improved my code as it look like this
int pin =0;
int state = 0;
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}
void loop() {
// send data only when you receive data:
if (Serial.available() > 3) { // I am using chunk of 4 characters/bytes as a command
// read the incoming byte:
if(Serial.read() == 'S' && Serial.read() == 'S') {
// Command to set the pin
pin = Serial.read() - 65;
state = Serial.read() - '0';
Serial.print("Set State Command received");
// Set the Pin
pinMode(pin, OUTPUT);
digitalWrite(pin, state == 0? LOW:HIGH);
}
delay(50);
}
}
It works perfectly for me. Due to the high frequency of looping, Arduino was not able pickup the bytes for consecutive read. So we are waiting for 4 bytes to accumulate in the buffer to read those (Arduino's serial buffer is 64 bytes).