I'm trying to get up to 64 lines of max 16 characters to display on an LCD screen via serial. These lines have to be given during startup. I've got the following, which works in most cases:
unsigned char textMatrix[64][17];
unsigned char lineCount = 0;
void readLines(){
Serial.println("Send up to 64 lines of up to 16 characters. Send an empty line to stop sending lines. Make sure to use \\n (newline) as line terminator!");
Serial.setTimeout(10000);
bool receiving = true;
while (receiving){
if(Serial.available() > 0) {
textMatrix[lineCount][0] = '\0';
char res = Serial.readBytesUntil('\n',textMatrix[lineCount],16);
if (res == 0){
if (textMatrix[lineCount][0] != '\0'){
continue;
}
Serial.println("Received empty line");
receiving = false;
break;
}
textMatrix[lineCount][16] = '\0';
Serial.print("Received line: ");
Serial.println((const char*)textMatrix[lineCount]);
lineCount++;
if (lineCount >= 63){
receiving = false;
}
}
}
}
The problem occurs when I send the following line:
Okay, that's one
This line is exactly 16 characters long. I'm assuming that this causes readBytesUntil to trigger twice, causing it to be the same as pressing enter twice. I don't seem to be able to find a difference between seriously sending an empty line or sending a line of exactly 16 characters. What would be the best way to resolve this?
When you send the line
Okay, that's one
That's not 16 character but 17, because there is \n character in the end. What Serial.readBytesUntil('\n',textMatrix[lineCount],16); does in this case is it takes out first 16 characters out of the buffer. After that operation, the only thing left there, is the new line \n, which in the next iteration of the loop is read as an empty line.
To counter that, you can check res variable, and if you see that it equals 16, discard the next read. Or do some more checks in case there is a string longer than 16 + new line.
Here's one way... just read the chars yourself:
unsigned char textMatrix[64][17];
const uint8_t MAX_WIDTH = sizeof( textMatrix[0] );
const uint8_t MAX_LINES = sizeof( textMatrix ) / MAX_WIDTH;
unsigned char lineCount = 0;
void readLines(){
uint32_t startTime = millis();
uint8_t column = 0;
do {
if (Serial.available()) {
char c = Serial.read();
if (c == '\n') {
if ((lineCount >= MAX_LINES) || (column == 0))
break; // too many lines or empty line
textMatrix[ lineCount ] [ column ] = '\0'; // NUL-terminate
lineCount++; // start accumulating a new line
column = 0;
} else if (column < MAX_WIDTH-1) {
// room for another char on this line
textMatrix[ lineCount ] [ column ] = c;
column++;
} // else
// ignore rest of line
}
} while (millis() - startTime < 10000UL); // TIMEOUT == 10s
} // readLines
Related
I have an Arduino Leonardo and trying to use it as a serial to USB converter. On Serial1 I have a string ending on a number. This number I'm trying to get via USB to the PC. It works very fine but I need a '\n' at the end and I don't know how. When I try it in the line Keyboard.println or Keyboard.write, I get a various number of lines with the expected number in splitted.
#include <Keyboard.h>
String myEAN ="";
const int myPuffergrosse = 50;
char serialBuffer[myPuffergrosse];
void setup() {
Keyboard.begin();
Serial1.begin(9600);
delay(1000);
}
String getEAN (char *stringWithInt)
// returns a number from the string (positive numbers only!)
{
char *tail;
// skip non-digits
while ((!isdigit (*stringWithInt))&&(*stringWithInt!=0)) stringWithInt++;
return(stringWithInt);
}
void loop() {
// Puffer mit Nullbytes fuellen und dadurch loeschen
memset(serialBuffer,0,sizeof(myPuffergrosse));
if ( Serial1.available() ) {
int incount = 0;
while (Serial1.available()) {
serialBuffer[incount++] = Serial1.read();
}
serialBuffer[incount] = '\0'; // puts an end on the string
myEAN=getEAN(serialBuffer);
//Keyboard.write(0x0d); // that's a CR
//Keyboard.write(0x0a); // that's a LF
}
}
Since myEAN is a String, simply add the character...
myEAN += '\n';
Or, for a full carriage return/line feed combination:
myEAN += "\r\n";
See the doc: https://www.arduino.cc/en/Tutorial/StringAppendOperator
I suggest you use String in your getEAN function as well...
String getEAN(String s)
{
// returns the first positive integer found in the string.
int first, last;
for (first = 0; first < s.length(); ++first)
{
if ('0' <= s[first] && s[first] <= '9')
break;
}
if (first >= s.length())
return "";
// remove trailing non-numeric chars.
for (last = first + 1; last < s.length(); ++last)
{
if (s[last] < '0' || '9' < s[last])
break;
}
return s.substring(first, last - 1);
}
I have the following code which I need to execute quickly, yet its taking a lot of time to change the value, anyway over way of making this task quicker?
I am using indexOf() and substring() to accomplish this task.
This is for changing the strip LED colors.
// declare LED Series A Pins R-G-B (PWM Pins)
int const AledRedPin = 6;
int const AledGreenPin = 5;
int const AledBluePin = 3;
// declare LED Series B Pins R-G-B (PWM Pins)
int const BledRedPin = 10;
int const BledGreenPin = 11;
int const BledBluePin = 9;
// serial input variable & string
// initialise LED Series A Pins R-G-B (PWN Value: 0 to 255)
// initial value = 255
int AledRed = 255;
int AledGreen = 255;
int AledBlue = 255;
// initialise LED Series A Pins R-G-B (PWN Value: 0 to 255)
// initial value = 255
int BledRed = 255;
int BledGreen = 255;
int BledBlue = 255;
//serial input
String Command = "";
//string manipulation
int cmdindexval = 0;
String CommandType = "";
int CommandValue = 0;
String Series = "";
void setup() {
// put your setup code here, to run once:
// start serial
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB
}
// set LED Series A Pins as Output R-G-B
pinMode(AledRedPin, OUTPUT);
pinMode(AledGreenPin, OUTPUT);
pinMode(AledBluePin, OUTPUT);
// set LED Series B Pins as Output R-G-B
pinMode(BledRedPin, OUTPUT);
pinMode(BledGreenPin, OUTPUT);
pinMode(BledBluePin, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
// read from serial if it's available
if (Serial.available() > 0) {
Command = Serial.readString(); //read string from serial monitor
cmdindexval = Command.indexOf('='); //read characters until '=' then assign the value
CommandType = Command.substring(0, cmdindexval); //assign the value from 0 to cmdindexval
//Series = Command.substring(0, 1); //read first character
CommandValue = Command.substring(cmdindexval + 1).toInt(); //assign the value after '=' and convert string to Int
Serial.println(CommandType + " ,is equal to " + CommandValue + " ,Series: " + Series);
//if (Series == "A") {
if (CommandType == "ACledRed"){
AledRed = CommandValue;
}
else if (CommandType == "ACledGreen"){
AledGreen = CommandValue;
}
else if (CommandType == "ACledRedBlue") {
AledBlue = CommandValue;
}
//}
//else if (Series == "B") {
if (CommandType == "BCledRed") {
BledRed = CommandValue;
}
else if (CommandType == "BCledGreen") {
BledGreen = CommandValue;
}
else if (CommandType == "BCledBlue") {
BledBlue = CommandValue;
}
//}
} //end serial
analogWrite(AledRedPin, AledRed);
analogWrite(AledGreenPin, AledGreen);
analogWrite(AledBluePin, AledBlue);
analogWrite(BledRedPin, BledRed);
analogWrite(BledGreenPin, BledGreen);
analogWrite(BledBluePin, BledBlue);
}
From the Arduino docs on readString:
Serial.readString() reads characters from the serial buffer into a string. The function terminates if it times out (see setTimeout()).
and the docs on setTimeout:
Serial.setTimeout() sets the maximum milliseconds to wait for serial data when using Serial.readBytesUntil(), Serial.readBytes(), Serial.parseInt() or Serial.parseFloat(). It defaults to 1000 milliseconds.
This means that the readString is always waiting 1 sec to make sure that the sending of the string is finished and has the complete string.
Unfortunately that means it's slow to respond. You could lower the timeout with the setTimeout, but you would still have some delay, or if you set it too low you could potentially get incomplete stings.
The best solution would be to use readStringUntil, so you know you have a complete string when you get a terminator character (like a newline).
Replace
Command = Serial.readString();
with
Command = Serial.readStringUntil('\n');
and make sure you set the Serial monitor so send the newline character.
Edit: see important update at the end.
This can be made significantly faster, but first let's have a look at the work that has to be done in every loop iteration with the current code:
As #gre_gor already explained, you could be losing some time in readString().
for each value, between 15 and 20 bytes have to be sent, read, parsed and converted to int.
for each received value (R, G or B), analogWrite() is called 6 times (and analogWrite() isn't really fast). This means that in order to change the two series, analogWrite() is called 36 times (and this is probably where most time is lost). And if no serial data is available, analogWrite() is still called 6 times.
and also, Serial.println() is called each time in the example - so it would be best to turn this off.
To speed this up, the RGB values could be sent in a small buffer (assuming you have control over the sending side as well), and read with Serial.readBytesUntil().
If the values for both A and B are sent together, the 6 RGB values can be sent as 6 bytes:
byte rcvBuffer[7];
void loop() {
if (Serial.available() > 0) {
// message: RGBRGBx - but see update below
int numRead = Serial.readBytesUntil(0x78, rcvBuffer, 7); // 0x78 is 'x'
if (numRead == 7) { // or 6, see below
analogWrite(AledRedPin, rcvBuffer[0]);
analogWrite(AledGreenPin, rcvBuffer[1]);
analogWrite(AledBluePin, rcvBuffer[2]);
analogWrite(BledRedPin, rcvBuffer[3]);
analogWrite(BledGreenPin, rcvBuffer[4]);
analogWrite(BledBluePin, rcvBuffer[5]);
}
// else ignore this read - could be a first unaligned read
}
}
If only the values for A or B are sent together:
byte rcvBuffer[5];
void loop() {
// You could probably even remove the Serial.available() check
if (Serial.available() > 0) {
// message: TRGBx where T is Type ('A' or 'B')
int numRead = Serial.readBytesUntil(0x78, rcvBuffer, 5); // 0x78 is 'x'
if (numRead == 5) { // or 4, see below
switch (rcvBuffer[0]) {
case 'A':
analogWrite(AledRedPin, rcvBuffer[1]);
analogWrite(AledGreenPin, rcvBuffer[2]);
analogWrite(AledBluePin, rcvBuffer[3]);
break;
case 'B':
analogWrite(BledRedPin, rcvBuffer[1]);
analogWrite(BledGreenPin, rcvBuffer[2]);
analogWrite(BledBluePin, rcvBuffer[3]);
break;
default :
// do nothing, or send error message
}
}
}
}
I used 'x' as the stop byte to make it visible, but you could as well use a zero byte.
Now, I'm not really sure if readBytesUntil() also reads the terminating byte into the buffer or skips it, and can't test this right now. But I would think only the RGB values are read into the buffer. In this case you'll have to change those values to the ones I put in the comments.
To save even more time, you could check each value and only call analogWrite() if that value did change since the last call (for each R, G and B).
Update: Obviously we can't just use 'x' or a zero byte as the stop byte, because each of the RGB values could also be an 'x' or zero byte (it's getting late here :). And while ReadBytes() could be used instead, it's better to have a stop byte to keep the buffers aligned. So I would suggest to use 0xff (255) as the stop byte and make sure none of the RGB values can be 0xff.
And just in case there could be other message types in the future, each message could also be prepended with a message code (1 or 2 bytes).
I always use readBytesUntil()whenever I use the serial port for communication.
It gets the job done, it always gets the entire string, but the same problem as readString()takes at least 1000ms to complete.
Using both Serial.setTimeout() and Serial.readBytesUntil() worked fine for me, by reducing the delay.
Something like:
Serial.setTimeout(250);
inData = Serial.readStringUntil('\n');
I started a project quite recently were I intend to combine the arduino uno and the sim808 module which has gps/gsm/gprs all together in one.
Using just the gps functionality works fine, just the gsm both receiving and sending messages also works good. but combining them for what i intend makes the entire system falls apart.
I intend to be able to have some coordinates registered let's say position A position B and position c, and the system i intend to design is put on a moving, let's say car, now anyone on either of the positions will send a message with the alphabet denoting the position to a number lets say "A TO +23*******98 , AND WOULD get a reply of the cars current time from that position.
With all parts combined the program would sometimes not even indicate receiving any message or even if it did the gps part won't even work.
Is there something I am doing wrong or is it not just feasible with what the components I am using pls help as I am very confused right now.
`#include <SoftwareSerial.h>
SoftwareSerial GPRS(7, 8); // RX, TX
String BUSNUM="A"; // THIS SIM IS TO BE INSTALLED ON BUS A
static String sendersnumber;
char* key;
char MESSAGE[280];
String latitude;
String longitude;
String Speed;
enum _parseState
{
PS_DETECT_MSG_TYPE,
PS_IGNORING_COMMAND_ECHO,
PS_READ_CMTI_STORAGE_TYPE,
PS_READ_CMTI_ID,
PS_DETECT_MSG_TYPE2,
PS_IGNORING_COMMAND_ECHO2,
PS_READ_CMGR_STATUS2,
PS_READ_CMGR_CONTENT2,
PS_READ_CMGR_DATE2,
PS_READ_CMGR_STATUS,
PS_READ_CMGR_NUMBER2,
PS_READ_CMGR_NUMBER,
PS_READ_CMGR_SOMETHING,
PS_READ_CMGR_DATE,
PS_READ_CMGR_CONTENT,
PS_READ_VIDEO_CONTENT,
PS_READ_VIDEO2_CONTENT,
PS_READ_CMGR_SOMETHING2,
PS_READ_VIDEO_CONTENT2,
};
byte state = PS_DETECT_MSG_TYPE; // KEEP TRACK OF WHAT STATE WE ARE IN RIGHT NOW
char buffer[100]; // WHAT WE ARE READING INTO
byte pos = 0; //WHAT POSITION WE ARE AT IN THAT BUFFER
int lastReceivedSMSId = 0; // LAST RECIEVED SMS ID
boolean validSender = false; //SOO NOT JUST ANYONE HAS ACESS TO SEND A COMMAND
void resetBuffer()
{
memset(buffer, 0, sizeof(buffer));
pos = 0;
}//BASICALLY TO RESET THE BUFFER
void setup()
{
GPRS.begin(9600);
Serial.begin(9600);
GPRS.println("AT+CGNSPWR=1");
delay(290);
GPRS.println("AT+CGNSURC=0");
delay(300);
GPRS.println("AT");
delay(300);
GPRS.println("AT+CMGF=1"); // INITIALIZE SMS
delay(300);
for (int i = 1; i <= 15; i++)
{
GPRS.print("AT+CMGD=");
GPRS.println(i);
delay(300);
// Not really necessary but prevents the serial monitor from dropping any input SINCE WE KNOW LIMIT TO STORE IS 15, THIS JUST DELETES IT CLEARS IT
while(GPRS.available())
Serial.write(GPRS.read());
}
delay(2000);
GPRS.println("AT+CGNSURC=1");
}
void loop()
{
while(GPRS.available()) //ONLY WHEN THERE IS SOMETHING AVAILABLE,
{
GSM_NUM1(GPRS.read());
//delay(50);
GPRS.println("AT+CGNSINF");
// delay(40);
GPSAnalyzer(GPRS.read());
sendSMS();
}
}
// END OF VOID LOOP
void GSM_NUM1(byte b)
{
buffer[pos++] = b;
if ( pos >= sizeof(buffer) )
resetBuffer(); // just to be safe
switch (state)
{
case PS_DETECT_MSG_TYPE:
{
if ( b == '\n' )
resetBuffer();
else {
if ( pos == 3 && strcmp(buffer, "AT+") == 0 ) {
state = PS_IGNORING_COMMAND_ECHO;
}
else if ( pos == 6 ) {
//Serial.print("Checking message type: ");
//Serial.println(buffer);
if ( strcmp(buffer, "+CMTI:") == 0 ) {
Serial.println("Received CMTI");
state = PS_READ_CMTI_STORAGE_TYPE;
}
else if ( strcmp(buffer, "+CMGR:") == 0 ) {
Serial.println("Received CMGR");
state = PS_READ_CMGR_STATUS;
}
resetBuffer();
}
}
}
break;
case PS_IGNORING_COMMAND_ECHO:
{
if ( b == '\n' ) {
//Serial.print("Ignoring echo: ");
//Serial.println(buffer);
state = PS_DETECT_MSG_TYPE;
resetBuffer();
}
}
break;
case PS_READ_CMTI_STORAGE_TYPE:
{
if ( b == ',' ) {
Serial.print("SMS storage is ");
Serial.println(buffer);
state = PS_READ_CMTI_ID;
resetBuffer();
}
}
break;
case PS_READ_CMTI_ID:
{
if ( b == '\n' ) {
lastReceivedSMSId = atoi(buffer);
Serial.print("SMS id is ");
Serial.println(lastReceivedSMSId);
GPRS.print("AT+CMGR=");
GPRS.println(lastReceivedSMSId);
//delay(500); don't do this!
state = PS_DETECT_MSG_TYPE;
resetBuffer();
}
}
break;
case PS_READ_CMGR_STATUS:
{
if ( b == ',' ) {
Serial.print("CMGR status: ");
Serial.println(buffer);
state = PS_READ_CMGR_NUMBER;
resetBuffer();
}
}
break;
case PS_READ_CMGR_NUMBER:
{
if ( b == ',' ) {
Serial.print("CMGR MSSG SENDERS Number: ");
Serial.println(buffer);
String sendersnumber=buffer;
// Uncomment these two lines to check the sender's cell number
//validSender = false;
//if ( strcmp(buffer, "\"+0123456789\",") == 0 )
validSender = true;
state = PS_READ_CMGR_SOMETHING;
resetBuffer();
}
}
break;
case PS_READ_CMGR_SOMETHING:
{
if ( b == ',' ) {
Serial.print("CMGR something A.K.A SENDER'S NAME: ");
Serial.println(buffer);
state = PS_READ_CMGR_DATE;
resetBuffer();
}
}
break;
case PS_READ_CMGR_DATE:
{
if ( b == '\n' ) {
Serial.print("CMGR date: ");
Serial.println(buffer);
state = PS_READ_CMGR_CONTENT;
resetBuffer();
}
}
break;
case PS_READ_CMGR_CONTENT:
{
if ( b == '\n' ) {
Serial.print("CMGR MESSAGE Content: ");
Serial.print(buffer);
String key=buffer;
// sendSMS();
// GPSAnalyzer();
GPRS.print("AT+CMGD=");
GPRS.println(lastReceivedSMSId);
//delay(500); don't do this!
state = PS_DETECT_MSG_TYPE;
resetBuffer();
}
}
break;
}
}
void GPSAnalyzer(byte b)
{
buffer[pos++] = b;
if ( pos >= sizeof(buffer) )
resetBuffer();// just to be safe
switch (state)
{
case PS_DETECT_MSG_TYPE2:
{
if ( b == '\n' )
resetBuffer();
else {
if ( pos == 9 ) {
// Serial.print("Checking message type: ");
// Serial.println(buffer);
if ( strcmp(buffer, "+UGNSINF:") == 0 ) {
Serial.println("Received CGNSINF:");
state = PS_READ_CMGR_STATUS2;
}
resetBuffer();
}
}
}
break;
//CHECK
case PS_IGNORING_COMMAND_ECHO2:
{
if ( b == '\n' ) {
//Serial.print("Ignoring echo: ");
//Serial.println(buffer);
state = PS_DETECT_MSG_TYPE2;
resetBuffer();
}
}
break;
//THIS WOULD READ FROM +CGNSINF: (TO THE COMMA),
case PS_READ_CMGR_STATUS2:
{
if ( b == ',' ) {
Serial.print("RUN STATUS: ");
Serial.println(buffer);
String runstatus=buffer;
state = PS_READ_CMGR_NUMBER2;
resetBuffer();
}
}
break;
case PS_READ_CMGR_NUMBER2:
{
if ( b == ',' ) {
Serial.print("FIX STATUS : ");
Serial.println(buffer);
String fixstatus=buffer;
fixstatus.replace(","," ");
validSender = true;
state = PS_READ_CMGR_SOMETHING2;
resetBuffer();
}
}
break;
// this is DATE AND TIME i dont need this
case PS_READ_CMGR_SOMETHING2:
{
if ( b == ',' ) {
Serial.print("DATE AND TIME : ");
Serial.println(buffer);
String dateandtime=buffer;
state = PS_READ_CMGR_DATE2;
resetBuffer();
}
}
break;
case PS_READ_CMGR_DATE2:
{
if ( b == ',' ) {
Serial.print("LATITUDE: ");
Serial.println(buffer);
latitude=buffer;
latitude.replace(","," ");
state = PS_READ_CMGR_CONTENT2;
resetBuffer();
}
}
break;
case PS_READ_CMGR_CONTENT2:
{
if ( b == ',' ) {
Serial.print("LONGITUDE: ");
Serial.println(buffer);
longitude=buffer;
longitude.replace(","," ");
state = PS_READ_VIDEO_CONTENT2;
resetBuffer();
//delay(500); don't do this!
}
}
break;
case PS_READ_VIDEO_CONTENT2:
{
if ( b == ',' ) {
Serial.print("ALTITUDE: ");
Serial.println(buffer);
String Altitude=buffer;
state = PS_READ_VIDEO2_CONTENT;
resetBuffer();
//delay(500); don't do this!
}
}
break;
case PS_READ_VIDEO2_CONTENT:
{
if ( b == ',' ) {
Serial.print("SPEED(KM/HR): ");
Serial.println(buffer);
String Speed=buffer;
Speed.replace(","," ");
state =PS_DETECT_MSG_TYPE2;
resetBuffer();
}
}
break;
//use goto to put it at sms begining
}
}
void sendSMS()
{
if ( strcmp(key,"A") == 0 )
{
float lati=7.200970;
float longi=5.181782;
float Speed1 = atof(Speed.c_str());
float latituded = atof(latitude.c_str());
float longituded = atof(longitude.c_str());
float Distance = HaverSine(lati,longi,latituded,longituded);
float duration=Distance/Speed1;
const int StrLen = 10;
char * duration_new ;
double Value = duration;
(void) dtostrf (Value, StrLen, 6, duration_new);
String MESSAGE="BUS A";
MESSAGE+=duration_new ;
Serial.print("THE MESSAGE SENT IS ");
Serial.println(MESSAGE);
}
else if ( strcmp(key,"B") == 0 )
{
float lati=7.290970;
float longi=5.141782;
float Speed1 = atof(Speed.c_str());
float latituded = atof(latitude.c_str());
float longituded = atof(longitude.c_str());
float Distance = HaverSine(lati,longi,latituded,longituded);
float duration=Distance/Speed1;
const int StrLen = 10;
char * duration_new ;
double Value = duration;
(void) dtostrf (Value, StrLen, 6, duration_new);
String MESSAGE="BUS B";
MESSAGE+=duration_new ;
Serial.print("THE MESSAGE SENT IS ");
Serial.println(MESSAGE);
}
delay(300);
GPRS.print("AT+CMGF=1\r\n");
delay(100);
GPRS.println("AT+CMGS=\""+sendersnumber+"\"\r\n");
delay(100);
GPRS.println(MESSAGE);
delay(100);
GPRS.println((char)26);
delay(100);
GPRS.println();
delay(100);
}
float HaverSine(float lat1,float lon1,float lat2,float lon2)
{
String fixstatus;
float ToRad = PI / 180.0;
float R = 6371; // radius earth in Km
float dLat = (lat2-lat1) * ToRad;
float dLon = (lon2-lon1) * ToRad;
float a = sin(dLat/2) * sin(dLat/2) +
cos(lat1 * ToRad) * cos(lat2 * ToRad) *
sin(dLon/2) * sin(dLon/2);
String o= fixstatus + "8";
float c = 2 * atan2(sqrt(a), sqrt(1-a));
float d = R * c;
return d;
}
EXPLANATION After all declarations and initializations the first active part of the entire code is the for loop that just deletes all former messages from the sim card memory.it runs 15 times
for (int i = 1; i <= 15; i++) { GPRS.print("AT+CMGD="); GPRS.println(i); delay(300); while(GPRS.available()) Serial.write(GPRS.read()); }
Now, having functions GSM_NUM1(); GPSAnalyzer(); sendSMS(); Which are defined below in the code
GSM_NUM1(); GPSAnalyzer(); both work similarly they extract needed data which come in separated by commas. For the gps which comes in , in the form
+UGNSINF: ,,,,, ,,, ,,,,,,, ,,,,
lathitude, speed and any other important parameter is extracted.
For GSM_NUM1(); it gets the senders message and number into variable.
sendSMS(); this just does some calculation based on the message received and send a certain result to the number who requested it.
OBERVATIONS
When the program starts nothing happens which is good cause nothing is supposed to until something new comes in. Hence the void loop() { while(GPRS.available()) //ONLY WHEN THERE IS SOMETHING AVAILABLE, { GSM_NUM1(GPRS.read());
//delay(50); GPRS.println("AT+CGNSINF"); // delay(40);
GPSAnalyzer(GPRS.read()); sendSMS(); } Now this is were things go bad, as the first function works good, it waits gets the message and extracts the senders number and message"key" into variables but immediately after that I wantjust one return of the gps info shown below that's why I used ("AT+CGNSINF"); instead of ("AT+CGNSURC=1"); as the later gives continuous gps data such as
+UGNSINF: ,,,,, ,,, ,,,,,,, ,,,,
BUT instead of one line of the gps data to be gotten from were lathitude and some other parameters are extracted into variables "THE PROGRAM HANGS/STOPS for some unknown reason.
SUSPICIONS
Since the SIM808 possess gps/gsm on the same module. It makes it kind of tricky to separate them in the sense that.
IF I WANT THE GPS TO BE ON BUT NOT READ IN ANY DATA UNTIL A VALID REQUEST COMES IN , IN FORM OF AN SMS THEN THE NECESSARY GPS INFORMATION IS PARSED AND SOME CALCULATIONS ARE DONE TO GET A PARAMETER THAT WOULD BE SENT TO THE SAME NUMBER THAT REQUESTED IT.
PERSONNALY, I FEEL THAT GETTING THE GPS AND GSM TO WORK HAND IN HAND WITHOUT CANCELLING EACH OTHER OUT IN SOME WAY,SUCESSFULLY WOULD BE THE END OF MY PROBLEM. AND I THINK IT COMES DOWN TO THE POSITION OF THE AT COMMAND
GPRS.println("AT+CGNSURC=1"); AND/OR GPRS.println("AT+CGNSINF"); AS the former spits out the gps data every GNSS FIX continuously when called once and LATER spits it out just once per one command.
the attached picture just shows the continous nature of the output of ("AT+CGNSURC=1"); , just as ("AT+CGNSINF"); spits out just one line with +CGNSINF: at the begining instead
Please check that is not in overflow SRAM , that produces stranger things when you execute the program. This occur when you declare so much variables, you can use this library to check the use of sram.
I ran into this similar issue with trying to get GPS data from a module over serial but figured it out after painstaking hours. Essentially you need to validate every response from every command and flush the serial port before every write.
Flushing the serial port buffer before you write any command will ensure that you don't accidentally read left over characters from a previous command when you are trying to get the output of the one you just ran. Most TAs (Terminal Adapters) use EOL characters like OK which translates into "\r\nOK\r\n". If you do not read them all, they can hang around and interfere with the next command you send/ read a response from.
use GPRS.flush() to get rid of extra garbage characters in the serial buffer before sending commands.
Also, creating a generic command function to pass all AT commands and validate their output will save you a lot of sleep.
something like
int sendCommand(int fd, char* message, char* eol, char* response){
int wlen, rlen;
char buf[1024];
tcflush(fd, TCIOFLUSH); //flush the serial port to remove anything left over
wlen = write(fd, message, strlen(message)); //write command to terminal
tcdrain(fd); //delay until 1st readable character is written by modem
delay(100); //delay a little longer until all characters are written(hopefully)
rdlen = read(fd, buf, sizeof(buf) - 1); //read from serial port
if(rdlen > 0){ //if readable characters
if(strstr(buf, eol) != NULL){ //if my end of line character is present then
int i = 0; //output from TA is complete
while(buf[i] != '\0'){ //while buffer has more characters
response[i] = buf[i]; //copy string into the string I passed, char by char
}
return 1; //return 1 (i.e.true aka success)
}
}
return 0; //return 0 (i.e. false aka failure)
}
I wrote this for a communicating to a simcom5320 but I didnt use software serial so it'll be a little different for you. Use this function for all your at commands to ensure everything actually succeed like:
char commandReply[512];
if(sendCommand(myFileDescriptor, "AT+CGNSINF\r", "\r\nOK\r\n", commandReply)){
//do something with commandReply or not
} else {
//command failed
//do something else
}
Hope this helps!
I made G-code reader on Arduino, but it stops reading. It has many "break;" and I have also many while loops and switches, so I'm thinking that when I break on loop/switch, it will break all.
Another idea is that it goes some kind of loop, but I can't figure out where it loops.
Here is my code:
void Gcode(){
String yy,xx,gg;
char text[64];
int number=1;
while(number!=3){
while (Serial.available()>0) {
delay(3); //delay to allow buffer to fill
char c = Serial.read();
Serial.println(c);
switch(c){
case 'G':
//read_number()
while (Serial.available()>0) {
char k = Serial.read();
if(k==' ' || k=='\n'){
break;
}
else{
gg+=k;
}
}
switch(gg.toInt()){
case 1:
Serial.println(gg);
while (Serial.available()>0) {
c = Serial.read();
Serial.println(c);
switch(c){
case 'X':
while (Serial.available()>0) {
char k = Serial.read();
if(k==' ' || k=='\n'){
break;
}
else{
xx+=k;
}
}
char buf[xx.length()];
xx.toCharArray(buf,xx.length());
x2=atof(buf);
Serial.println(x2);
break;
case 'Y':
while (Serial.available()>0) {
char k = Serial.read();
if(k==' ' || k=='\n'){
break;
}
else{
yy+=k;
}
}
Serial.println(yy);
char buf2[yy.length()];
yy.toCharArray(buf2,yy.length());
y2=atof(buf2);
break;
case 'E':
break;
case 'F':
break;
default:
Serial.print("the end");
}
Serial.print("out of switch");
}
break;
case 2:
break;
default:
Serial.print("nothing");
}
break;
case '\n':
number=3;
break;
default:
Serial.print("default");
}
}
}
if(sizeof(yy)>0){
yy="";
xx="";
gg="";
}
Serial.print("quit");
}
When I send G1 X10.00 Y-100.00 \n It prints only:
G
1
X
10.00
out of s
one of your big problem is that your while end when thwere are nomore carachter. This mean that if your loop consume the buffer FASTER than it get written (remeber:9600 baud mean 960Byte/s, arduino even if slow but can compute 16.000.000 operation/s...).
Another big problem MAY bethe lack of ram, os your output is truncated. there is some function to check real time usage of ram, see http://playground.arduino.cc/Code/AvailableMemory, stopping to use String and even char array is a way better idea; the code is not that hard to write!
so the pc would send "G1 X10.00 Y-100.00 \n"
but at time X your aruino get "G1 X10.00", if you read really fast the buffer now (faster than 1/960 of a second, arduino is a way faster than that!)
so ideally you should change all your while condition removing serial available but instead putting the condition you use in the if with the break; sothe first while from
while (Serial.available()>0)
became
while ( (k=Serial.read()) != ' ' && k != '\n') //yes it is a bit weird like this
maybe a little better, with check that k is a valid caracter, AND timeout
unsigned long timeout_ms = 1000; //timeout after 1 seconds from NOW!
unsigned long start_ms = millis();
int k=Serial.read();
while ( k != ' ' && millis()-start_ms < timeout_ms){// because here we expect "Gx ", why are you was also using k != '\n'? removed, feel free to add it back
if (k == -1){
k=Serial.read(); //read the next char
continue; //return to the beginning of the while
}
[... do thigs...]
k=Serial.read(); //read the next char
//here you may add "start_ms = millis();" if you want to reset the timeout
}
I'm working on an interface that reads serial data from a single wire bus on a car (BMW IBUS). I'm using an Microchip MCP2025 LIN bus transceiver chip to convert the single line bus to regular Rx/Tx to feed into the hardware serial pins (0 & 1) of an Arduino (Nano V3).
BMW's IBUS protocol uses Serial 8E1 at 9600 baud. Messages are variable length (between 5 and 37 bytes) Bus messages are separated by a pause of around 11 milliseconds (considerably longer than the bus high time when receiving) and I need to use this gap to detect the end of one message and the beginning of the next.
1st byte is the Source ID,
2nd byte is length of the packet excluding the first two bytes (source & length),
3rd byte is the destination ID,
4th byte onwards is the actual data,
Last byte is the checksum
Checksum is generated by XOR of the entire packet excluding the checksum itself.
My problem is that I don't know how to measure the bus idle time between messages. I think I can use bitRead(PIND,0) to tell when the bus is idle (high), but I'm not sure how to time this and go and process the message after a suitable time has passed.
I'm currently using the following code to read messages from the IBUS. It works for the most part, but gets out of sync when connected to the car. It's doesn't really know when messages have finished, but relies on the second byte of each message (the length byte) to know how many bytes to read in. As long as it reads the length byte correctly, it's fine, but if it gets out of sync, and the length byte is wrong, it reads in the wrong number of bytes and eventually overruns the array and the Arduino resets. If I can detect the idle time between messages I will know for sure that I have a complete message.
void ReadIBUS()
{
boolean inSTATE = 0;
byte IBUSbyte[40];
while(Serial.available() > 0 && inSTATE == 0 )
{
IBUSbyte[0] = Serial.read(); //read source byte
while (inSTATE == 0)
{
if(Serial.available() > 0)
{
IBUSbyte[1] = Serial.read(); //read length byte
inSTATE = 1;
}
delay(10);
}
inSTATE == 0;
LENGTH = IBUSbyte[1];
int i = 2;
while(i <= LENGTH + 1)
{
if(Serial.available() > 0)
{
IBUSbyte[i] = Serial.read();
i++;
delay(10);
}
}
checksumBYTE = 0;
byteSTATE = 0;
for (int i = 0; i < LENGTH + 1; i++){
checksumBYTE ^= IBUSbyte[i];
}
if (IBUSbyte[LENGTH + 1] == checksumBYTE){
for(int i = 0; i <= LENGTH + 1; i++)
{
mySerial.print(IBUSbyte[i], HEX);
mySerial.print(" ");
}
mySerial.println();
}
else {
debug();
inSTATE = 0;
byteSTATE = 0;
}
}
}
First off, delay is rarely a good thing to use. In this case, you can use millis to measure how long it is between characters, but only if you lose the delay:
void ReadIBUS()
{
byte IBUSbyte[40];
do {
// First, make sure we're between frames, in the quiet interval
uint32_t last_rx = millis();
while (millis() - last_rx < 5) {
// Hasn't been long enough, see if more chars are coming
if (Serial.available()) {
Serial.read(); // throw away partial frame chars
last_rx = millis();
}
}
// Haven't received any chars for a while, we must be
// between frames. Now wait for a frame.
while (!Serial.available())
; // waiting...
IBUSbyte[0] = Serial.read(); //read source byte
while (!Serial.available())
; // waiting...
IBUSbyte[1] = Serial.read(); //read length byte
LENGTH = IBUSbyte[1];
// Check the LENGTH to make sure it's not bogus. If it is, try again.
} while (LENGTH > sizeof(IBUSbyte)-1);
// LENGTH is reasonable, wait for the payload
uint8_t bytes_received = 2;
while (bytes_received <= LENGTH + 1) {
if (Serial.available()) {
IBUSbyte[ bytes_received++ ] = Serial.read(); // read frame payload byte
}
}
// All here, verify the checksum
checksumBYTE = 0;
for (int i = 0; i < LENGTH + 1; i++) {
checksumBYTE ^= IBUSbyte[i];
}
if (IBUSbyte[LENGTH + 1] == checksumBYTE) {
for(int i = 0; i <= LENGTH + 1; i++) {
mySerial.print(IBUSbyte[i], HEX);
mySerial.print(" ");
}
mySerial.println();
} else {
debug();
}
}
And the state variables are no longer needed.
BTW, this routine "blocks" the rest of the program from doing anything until a complete frame is received. It should probably be rewritten so that it is called frequently (like from loop), and last_rx and bytes_received are remembered across calls. It could return true when a complete frame has been received. This'll work, it's just sub-optimal because it blocks anything else from happening, except interrupts.