Exception during mqtt publish using ESP8266 - arduino

I am publishing sensor readings to thingSpeak using its mqttAPI. I have deployed my project on Huzzah esp8266 board. After just publishing one value, I'm getting ESP exception 9, 28. I guess I'm having problem with loop() function. Here's my code
void loop() {
if(!mqttCli.connected()){
reconnectMQTT();
}
mqttCli.loop();
if(millis()-mqttTimer>mqttInterval){
taskWiFiCallback();
}
}
void taskWiFiCallback(){
Serial.println("task WiFi Started");
if(!mqttCli.connected()){
Serial.println("mqtt not connected");
reconnectMQTT();
return;
}
Serial.println("mqtt Connection established");
Serial.println("State:" + mqttCli.connected());
String topicString ="channels/"+String(channelID)+"/publish/"+String(writeAPIKey);
int topicLength = topicString.length();
char topicBuffer[topicLength];
topicString.toCharArray(topicBuffer,topicLength+1);
Serial.println(topicBuffer);
String dataString = String("field1="+ String(tempC,1) + "&field2=" + String(tempF,1) + "&field3=" + String(humid,1));
int dataLength = dataString.length();
char dataBuffer[dataLength];
dataString.toCharArray(dataBuffer,dataLength+1);
Serial.println(dataBuffer);
Serial.println(mqttCli.publish(topicBuffer,dataBuffer) ? "Published" : "Not Published Bawa, Do Something");
mqttTimer = millis();
}
void reconnectMQTT(){
Serial.println("setting up mqtt");
WiFiClient wifiClient;
mqttCli.setServer(mqtt_server,1883);
mqttCli.setClient(wifiClient);
mqttCli.setCallback(&callback);
// Creating a random client ID
String clientId = "ESP8266Client-";
clientId += String(random(0xffff), HEX);
while(!mqttCli.connected()){
if(mqttCli.connect(clientId.c_str())){
String subTopic = String("channels/"+ String(channelID) +
"/subscribe/json/" + String(readAPIKey));
int subTopicLength = subTopic.length();
char subTopicBuffer[subTopicLength];
subTopic.toCharArray(subTopicBuffer,subTopicLength);
String pubTopic ="channels/" + String( channelID ) +
"/publish/"+String(writeAPIKey);
char pubTopicBuffer[pubTopic.length()];
pubTopic.toCharArray(pubTopicBuffer,pubTopic.length()+1);
String message = String("field1=" + String(tempC,1) + "&field2=" +
String(tempF,1) + "&field3=" + String(humid,1));
char messageBuffer[message.length()];
message.toCharArray(messageBuffer,message.length()+ 1);
Serial.println(messageBuffer);
Serial.println(mqttCli.publish(pubTopicBuffer,messageBuffer) ? "Published" : "Unpublished");
Serial.println(mqttCli.subscribe(subTopicBuffer) ? "Subscribed" : "Unsubscribed");
}else{
Serial.print("failed, rc=");
Serial.println(mqttCli.state());
delay(1000);
}
}
}
I have Subscribed to the topic and it is giving correct results and even publish in reconnectMQTT() function is being published.

There may be other problems, but to start:
int topicLength = topicString.length();
char topicBuffer[topicLength];
topicString.toCharArray(topicBuffer,topicLength+1);
You're declaring a buffer of topicLength bytes and then copying topicLength + 1 bytes into it. You'll always overrun the buffer by one byte. You need to declare it to be topicLength + 1 bytes long in order to have room for the C language \0 string terminator. So:
int topicLength = topicString.length();
char topicBuffer[topicLength+1];
topicString.toCharArray(topicBuffer,topicLength+1);
or, better:
int topicLength = topicString.length()+1;
char topicBuffer[topicLength];
topicString.toCharArray(topicBuffer,topicLength);
Same thing for dataBuffer later as well as pubTopic, messageBuffer and any other place that you turn a String into a char array.
Also, note that your line
subTopic.toCharArray(subTopicBuffer,subTopicLength);
doesn't add 1 to the length where you do add 1 to the length everywhere else.

Related

How to detect USB cable disconnect in blocking read() call?

I have a smart energy meter which sends energy consumption data every second. The daemon program I've written (C++/C Arch Linux) to read the data doesn't exit when the USB cable is disconnected and stalls indefinitely in the blocking read() call.
How to interrupt a blocking read() call (i.e. fail with EINTR instead of waiting for the next char)?
I extensively searched Google and looked here in SO and could not find an answer to this problem.
Details:
Smartmeter project source on Github
IR dongle with FT232RL USB to UART bridge
Datagrams have fixed length of 328 bytes sent every second
Read method detects beginning \ and end ! markers of a datagram
sigaction to catch CTRL+C SIGINT and SIGTERM signals
termios is setup to do blocking read() with VMIN = 1 and VTIME = 0.
Tried:
Playing with VMIN and VTIME
Removed SA_RESTART
Possible solution:
Use a non-blocking read method, maybe with select() and poll()
Or VMIN > 0 (datagram is longer than 255 characters and I would need to read the datagram in smaller chunks)
Not sure how to handle datagram begin/end detection and the one second interval between datagrams for a non-blocking read method
EDIT: The code below now buffers the read() call into an intermediate buffer of 255 bytes (VMIN = 255 and VTIME = 5) adapted from here. This avoids the small overhead of calling read() for every char. In practise this doesn't make a difference compared to reading one char at a time though. Read() still doesn't exit gracefully on cable disconnect. The daemon needs to be killed with kill -s SIGQUIT $PID. SIGKILL has no effect.
main.cpp:
volatile sig_atomic_t shutdown = false;
void sig_handler(int)
{
shutdown = true;
}
int main(int argc, char* argv[])
{
struct sigaction action;
action.sa_handler = sig_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_RESTART;
sigaction(SIGINT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
while (shutdown == false)
{
if (!meter->Receive())
{
std::cout << meter->GetErrorMessage() << std::endl;
return EXIT_FAILURE;
}
}
Smartmeter.cpp:
bool Smartmeter::Receive(void)
{
memset(ReceiveBuffer, '\0', Smartmeter::ReceiveBufferSize);
if (!Serial->ReadBytes(ReceiveBuffer, Smartmeter::ReceiveBufferSize))
{
ErrorMessage = Serial->GetErrorMessage();
return false;
}
}
SmartMeterSerial.cpp:
#include <cstring>
#include <iostream>
#include <thread>
#include <unistd.h>
#include <termios.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include "SmartmeterSerial.h"
const unsigned char SmartmeterSerial::BufferSize = 255;
SmartmeterSerial::~SmartmeterSerial(void)
{
if (SerialPort > 0) {
close(SerialPort);
}
}
bool SmartmeterSerial::Begin(const std::string &device)
{
if (device.empty()) {
ErrorMessage = "Serial device argument empty";
return false;
}
if ((SerialPort = open(device.c_str(), (O_RDONLY | O_NOCTTY))) < 0)
{
ErrorMessage = std::string("Error opening serial device: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
if(!isatty(SerialPort))
{
ErrorMessage = std::string("Error: Device ") + device + " is not a tty.";
return false;
}
if (flock(SerialPort, LOCK_EX | LOCK_NB) < 0)
{
ErrorMessage = std::string("Error locking serial device: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
if (ioctl(SerialPort, TIOCEXCL) < 0)
{
ErrorMessage = std::string("Error setting exclusive access: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
struct termios serial_port_settings;
memset(&serial_port_settings, 0, sizeof(serial_port_settings));
if (tcgetattr(SerialPort, &serial_port_settings))
{
ErrorMessage = std::string("Error getting serial port attributes: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
cfmakeraw(&serial_port_settings);
// configure serial port
// speed: 9600 baud, data bits: 7, stop bits: 1, parity: even
cfsetispeed(&serial_port_settings, B9600);
cfsetospeed(&serial_port_settings, B9600);
serial_port_settings.c_cflag |= (CLOCAL | CREAD);
serial_port_settings.c_cflag &= ~CSIZE;
serial_port_settings.c_cflag |= (CS7 | PARENB);
// vmin: read() returns when x byte(s) are available
// vtime: wait for up to x * 0.1 second between characters
serial_port_settings.c_cc[VMIN] = SmartmeterSerial::BufferSize;
serial_port_settings.c_cc[VTIME] = 5;
if (tcsetattr(SerialPort, TCSANOW, &serial_port_settings))
{
ErrorMessage = std::string("Error setting serial port attributes: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
tcflush(SerialPort, TCIOFLUSH);
return true;
}
char SmartmeterSerial::GetByte(void)
{
static char buffer[SmartmeterSerial::BufferSize] = {0};
static char *p = buffer;
static int count = 0;
if ((p - buffer) >= count)
{
if ((count = read(SerialPort, buffer, SmartmeterSerial::BufferSize)) < 0)
{
// read() never fails with EINTR signal on cable disconnect
ErrorMessage = std::string("Read on serial device failed: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
p = buffer;
}
return *p++;
}
bool SmartmeterSerial::ReadBytes(char *buffer, const int &length)
{
int bytes_received = 0;
char *p = buffer;
bool message_begin = false;
tcflush(SerialPort, TCIOFLUSH);
while (bytes_received < length)
{
if ((*p = GetByte()) == '/')
{
message_begin = true;
}
if (message_begin)
{
++p;
++bytes_received;
}
}
if (*(p-3) != '!')
{
ErrorMessage = "Serial datagram stream not in sync.";
return false;
}
return true;
}
Many thanks for your help.
While the code below is not a solution to the original question on how to interrupt a blocking read() call, at least it would be a vialble workaround for me. With VMIN = 0 and VTIME = 0 this is now a non-blocking read():
bool SmartmeterSerial::ReadBytes(char *buffer, const int &length)
{
int bytes_received = 0;
char *p = buffer;
tcflush(SerialPort, TCIOFLUSH);
bool message_begin = false;
const int timeout = 10000;
int count = 0;
char byte;
while (bytes_received < length)
{
if ((byte = read(SerialPort, p, 1)) < 0)
{
ErrorMessage = std::string("Read on serial device failed: ")
+ strerror(errno) + " (" + std::to_string(errno) + ")";
return false;
}
if (*p == '/')
{
message_begin = true;
}
if (message_begin && byte)
{
++p;
bytes_received += byte;
}
if (count > timeout)
{
ErrorMessage = "Read on serial device failed: Timeout";
return false;
}
++count;
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
if (*(p-3) != '!')
{
ErrorMessage = "Serial datagram stream not in sync.";
return false;
}
return true;
}
However, I am still curious to know if it is actually possible to interrupt a blocking `read()ยด as this workaround is constantly polling the serial port.
I believe reading one char at a time is not a problem as the received bytes from the UART are buffered by the OS - but constantly polling the buffer with read() is! Maybe I'll try a ioctl(SerialPort, FIONREAD, &bytes_available) before read(), although I don't know if this would actually make a difference.
Any suggestions?

Passing a string with multiple variables to anrduino using serial port

Im trying to pass a string generated using processing to the Arduino serial port but it doesn't seem to work.
Arduino Code:
String readString; //main captured String
String MotorChoice;
String AngleRange;
String FrequencyIN;
int ind1;
int ind2;
int ind3;
int MC;
int AR;
float FIN;
void setup() {
Serial.begin(9600);
Serial.println("Input command in the form of Motor Choice,Angle Range,Frequency*");
}
void loop()
{
if (Serial.available()) {
char c = Serial.read();
if (c == '*') {
Serial.println();
Serial.print("captured String is : ");
Serial.println(readString);
ind1 = readString.indexOf(','); //finds location of first ,
MotorChoice = readString.substring(0, ind1); //captures first data String
ind2 = readString.indexOf(',', ind1+1 ); //finds location of second ,
AngleRange = readString.substring(ind1+1, ind2); //captures second data String
ind3 = readString.indexOf(',', ind2+1 );
FrequencyIN = readString.substring(ind2+1);
//convert sring to int
MC = MotorChoice.toInt();
AR = AngleRange.toInt();
FIN = FrequencyIN.toFloat();
Serial.print("Motor Selected = ");
Serial.println(MC);
Serial.print("Angle Range = ");
Serial.println(AR);
Serial.print("Frequency Required = ");
Serial.println(FIN);
Serial.println();
Serial.println();
readString=""; //clears variable for new input
MotorChoice="";
AngleRange="";
FrequencyIN="";
}
else
{
readString += c; //makes the string readString
}
}
}
Processing Code:
import processing.serial.*;
Serial myPort;
println(Serial.list());
myPort = new Serial(this, Serial.list()[0], 9600);
int M =1;
int A =90;
float F =2.5;
String sM = str(M);
String sA = str(A);
String sF = str(F);
String sb;
String s1;
sb = sM +","+ sA+"," + sF+"*";
s1 = sb;
println(s1);
myPort.write(s1);
The above Arduino code has previously been tried out where an input was fed using the serial monitor in the format of motor,angle,frequency*
Any help would greatly be appreciated.
If I am interpreting your code correctly you are using the * character as a way of signifying the end of the list. But in your Arduino code you are checking for the * then you are printing the output string. Thus, the rest of your array is long gone by that point. Also, you set a variable at the beginning called readString but you never set it to anything. That's why you you're not seeing any output.
First I would start by changing your variable name from readString to something else (for the sake of clarity) then use Serial.readString() to get that string from the serial port. Then check the last character of that string for the * character.
if (Serial.available()) {
String str = Serial.readString(); // get string
char c = str[strlen(str)-1]; // find *
if (c == '*') {
Serial.print("Output: ");
Serial.println(str);
//The rest of your code
}

ESP8266 - Response from server gets cut

I'm using an ESP8266 connected to an Arduino one via SoftwareSerial to make a post request to a node web server. The ESP8266 sends some data to the server and it should get back other data. The data arrives at the server correctly, but the response from the server is incomplete (it gets cut each time in a different way) and I can't access the body of the response from my Arduino sketch. The server sends the response correctly, as i've checked with hurl.
This is my code:
#include "SoftwareSerial.h"
String ssid ="ssid";
String password="pwd";
SoftwareSerial esp(3, 2);// RX, TX
ESP8266_Simple wifi(3,2);
String data;
String server = "server";
String uri = "uri";
String token = "token";
float temp_set = 15; //standard values
float temp_rec = 15;
String temp_set_s;
String temp_rec_s;
int activate = LED_BUILTIN; //pin for relay
int button_up = 4;
int button_down = 5;
unsigned long time;
//LCD
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
// DHT11
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <DHT_U.h>
#define DHTPIN 6
#define DHTTYPE DHT22
DHT_Unified dht(DHTPIN, DHTTYPE);
void setup() {
esp.begin(9600);
Serial.begin(9600);
delay(10);
reset();
connectWifi();
pinMode(activate, OUTPUT);
pinMode(button_up, INPUT);
pinMode(button_down, INPUT);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
//DHT setup
dht.begin();
sensor_t sensor;
delay(500);
}
//reset the esp8266 module
void reset() {
esp.println("AT+RST");
delay(1000);
if(esp.find("OK") ) Serial.println("Module Reset");
}
//connect to your wifi network
void connectWifi() {
String cmd = "AT+CWJAP=\"" +ssid+"\",\"" + password + "\"";
esp.println(cmd);
delay(4000);
if(esp.find("OK")) {
Serial.println("Connected!");
time = millis();
} else {
connectWifi();
Serial.println("Cannot connect to wifi");
}
}
void loop () {
//temp_rec_s = String(temp_rec);
//temp_set_s = String(temp_set);
//data = "tempRec=" + temp_rec_s + "&tempSet=" + temp_set_s;
//httppost();
// dht data
sensors_event_t event;
dht.temperature().getEvent(&event);
temp_rec = event.temperature;
//temp_rec_s = String(temp_rec);
//temp_set_s = String(temp_set);
//data = "tempRec=" + temp_rec_s + "&tempSet" + temp_set_s;
// to activate
if(temp_set < temp_rec){
digitalWrite(activate, LOW);
} else{
digitalWrite(activate, HIGH);
}
//function for physical buttons
if((digitalRead(button_up)) == HIGH){
temp_set = temp_set + 0.5;
delay(100);
}
if((digitalRead(button_down)) == HIGH){
temp_set = temp_set - 0.5;
delay(100);
}
//shows temperature on display
lcd.setCursor(0, 0);
lcd.print("T rec " + String(temp_rec));
//shows temperature on display
lcd.setCursor(0, 1);
lcd.print("T set " + String(temp_set));
temp_rec_s = String(temp_rec);
temp_set_s = String(temp_set);
data = "tempRec=" + temp_rec_s + "&tempSet=" + temp_set_s + "&token=" + token;
//Serial.println(data);
if((millis() - time) >= 10000){
httppost();
}
delay(200);
}
void httppost () {
esp.println("AT+CIPSTART=\"TCP\",\"" + server + "\",80");//start a TCP connection.
if(esp.find("OK")) {
Serial.println("TCP connection ready");
}
delay(1000);
String postRequest =
"POST " + uri + " HTTP/1.0\r\n" +
"Host: " + server + "\r\n" +
"Accept: *" + "/" + "*\r\n" +
"Content-Length: " + data.length() + "\r\n" +
"Content-Type: application/x-www-form-urlencoded\r\n" +
"\r\n" + data;
String sendCmd = "AT+CIPSEND="; //determine the number of caracters to be sent.
esp.print(sendCmd);
esp.println(postRequest.length());
Serial.println(postRequest);
delay(500);
if(esp.find(">")) {
Serial.println("Sending..");
esp.print(postRequest);
String tmpResp = esp.readString();
Serial.println(tmpResp);
if(esp.find("SEND OK")) {
Serial.println("Packet sent");
while(esp.available()) {
String line = esp.readString();
Serial.print(line);
}
// close the connection
esp.println("AT+CIPCLOSE");
}
}
}
Put a delay(1) under the esp.readString() and use .read() instead with char like this:
while(esp.available())
{
char line = esp.read(); // read one char at a time
delay(1); // prevent freezing
Serial.print(line);
if (line == '\0') continue; // terminate the `while` when end of the data
}
The .readString() method as pointed out by #gre_gor reads until there is no incoming data for 1 second.
So the better method is to use read() and char since you can test the char to see if you have reached the end of data character \0.
When using .read() consider using a custom timeout, because data can be delivered with delays so you might want to keep trying for a certain period of time if you haven't yet reached the end of data character \0, like this:
long int time = millis(); // current time
long int wait = 1000 * 10; // wait 10 seconds before terminating the read process
while ((time + wait) > millis())
{
while (esp.available())
{
char line = esp.read();
delay(1);
Serial.print(line);
if (line == '\0') continue;
}
}

Arduino - Processing and serial communication

I need to get a serial signal from the Arduino board into a Processing sketch. I am sending values in string format, like "R12" "S40" "T80". When I capture those strings from Processing it will printing vertically (new line for each character). How can I get this as a string as it is what we sent?
I need code that can be put inside Processing's serialEvent method.
Arduino code
void loop() {
int r = random(10, 100);
int s = random(10, 100);
int t = random(10, 100);
Serial.print("R" + String(r));
Serial.print("S" + String(s));
Serial.print("T" + String(t));
delay(1000);
}
Processing Code
String data = "";
int R, S, T;
void serialEvent(Serial p) {
while(p.available() > 0) {
data = p.readString();
}
println(data);
}
I need to get the last received R S T values, respectively, to the R S T variables.
As mentioned previously, it is better to get the all the code to find the problem.
I suppose you did not add the function "bufferUntil()" in the Processing setup, so each time the serial received a value, it just displays it. I can propose you a solution. It is not the only one, but it seems to work.
In your Arduino code, instead of the Serial.print(), you should use Serial.println() to delimit your message.
void setup() {
Serial.begin(9600);
}
void loop() {
int r = random(10, 100);
int s = random(10, 100);
int t = random(10, 100);
Serial.println("R" + String(r));
Serial.println("S" + String(s));
Serial.println("T" + String(t));
delay(1000);
}
So you will send the message as
Rxx
Sxx
Txx
In Processing, you have to use the function "bufferUntil(lf)". It will save all messages from the serial port until it receives a "/n".
After that, you should use another function to associate your identifier with the value.
// Example by Tom Igoe
import processing.serial.*;
Serial myPort; // The serial port
PFont myFont; // The display font
String inString; // Input string from serial port
int lf = 10; // ASCII linefeed
String data = "";
int R, S, T;
void setup() {
size(400, 200);
// List all the available serial ports:
printArray(Serial.list());
// Open whatever port is the one you're using, mine is one.
myPort = new Serial(this, Serial.list()[1], 9600);
myPort.bufferUntil(lf); // Delimiter of serial buffer end line
}
void draw() {
background(0);
text("received: " + " R=" + R + " S=" + S + " T=" + T, 10, 50); // Display value on the window
}
void serialEvent(Serial p) {
while(p.available() > 0) {
String buf = p.readString(); // Read serial input
println(buf);
if(!buf.equals('\n')) // Delete null='/n' from arduino println()
data = buf; // Save buffer
}
/*
char id = data.charAt(0); // Get id from buffer
int value = parseInt(data.substring(1, 3)); // Get the value from buffer
identifyVariable(id, value); // Associate id and value
*/
}
void identifyVariable(char ID, int value) {
switch(ID) { // Associate id with value
case('R'):
R = value;
break;
case('S'):
S = value;
break;
case('T'):
T = value;
break;
default:
println("error " + ID + " " + value);
break;
}
}

How to take data from gps then send to my phone in arduino

I want to do project which will use GPS & GSM module, use Arduino, take data from GPS(GY-GPS/NEO6MV2) and send by GSM(SIM900 GSM/GPRS Module ) to my phone. I am using separate GPS module
I try this code but still have problem.
#include <SoftwareSerial.h>
#include "SIM900.h"
#include <TinyGPS.h>
#include "sms.h"
SMSGSM sms;
TinyGPS gps;
SoftwareSerial ss(4, 3);
SoftwareSerial SIM900(7, 8);
static void smartdelay(unsigned long ms);
static void print_float(float val, float invalid, int len, int prec);
static void print_int(unsigned long val, unsigned long invalid, int len);
static void print_date(TinyGPS &gps);
static void print_str(const char *str, int len);
String strL, strN, message, textForSMS;
char charL[10], charN[10], text[200];
int m = 1;
boolean started = false;
void setup()
{
Serial.begin(9600);
ss.begin(9600);
gsm.begin(9600);
}
void loop()
{
float flat, flon;
unsigned long age, date, time, chars = 0;
unsigned short sentences = 0, failed = 0;
gps.f_get_position(&flat, &flon, &age);
textForSMS = "Moosa Home"; //testing gps from here
Serial.println(textForSMS);
Serial.println(flat, 6);
Serial.println(flon, 6);
Serial.print("longitude: ");
print_float(flat, TinyGPS::GPS_INVALID_F_ANGLE, 10, 6);
Serial.println("");
Serial.print("latitude : ");
print_float(flon, TinyGPS::GPS_INVALID_F_ANGLE, 10, 6);
smartdelay(1000);
Serial.println(""); //till here
delay(1000);
if (m == 5) //send sms on third reading
{
Serial.println("XXXXXXXXX"); //to check whether 'if' works
dtostrf(flat, 4, 6, charL);
for (int i = 0; i < 10; i++)
{
strL += charL[i];
}
dtostrf(flon, 4, 6, charN);
for (int i = 0; i < 10; i++)
{
strN += charN[i];
}
message = "Home";
message = message + "/nLat: ";
message = message + strL;
message = message + "/nLon: ";
message = message + strN;
message.toCharArray(text, 250);
Serial.println(text);
if (sms.SendSMS("+999999999999999", text))
{
Serial.println("\nSMS sent OK.");
}
else
{
Serial.println("\nError sending SMS.");
}
do {} while (1);
}
m++;
}
static void smartdelay(unsigned long ms)
{
unsigned long start = millis();
do
{
while (ss.available())
gps.encode(ss.read());
} while (millis() - start < ms);
}
static void print_float(float val, float invalid, int len, int prec)
{
if (val == invalid)
{
while (len-- > 1)
Serial.print('*');
Serial.print(' ');
}
else
{
Serial.print(val, prec);
int vi = abs( val);
int flen = prec + (val < 0.0 ? 2 : 1); // . and -
flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
for (int i = flen; i < len; ++i)
Serial.print(' ');
}
smartdelay(0);
}
static void print_str(const char *str, int len)
{
int slen = strlen(str);
for (int i = 0; i < len; ++i)
Serial.print(i < slen ? str[i] : ' ');
smartdelay(0);
}
I receive SMS
Home/nLat:1000.00000/nLon:1000.00000`
where is my mistake in this code?
I am sure Gps & gsm work properly
Using TinyGPS on SoftwareSerial + a loop() structure using delay(1000), has very low odds of working. The m counter is really useless here, because loop() will execute 1000's of times while the GPS characters are arriving. And doing a get_position is useless, because you may not have any GPS data yet.
Basically, loop() should be constantly running, without delays. When something important happens (like getting all of a GPS sentence, or enough time has passed), that's when you do your other work (like send an SMS message). The TinyGPS smartDelay is not smart.
You should restructure the loop to look more like this example from the NeoGPS library. All the non-GPS work should be performed where the digitalWrite is in that example (line 62). That's where you would take the time to send an SMS.
The TinyGPS examples would require a complete rewrite to do what you want. They are fine by themselves, but it is difficult to extend them to do other things, like send an SMS. As I said, the loop structure must change.
I suggest taking a look at the NeoGPS library I wrote, as the examples are more robust. The library also uses much less RAM and CPU time, but that isn't a big problem if all you need to do is send an SMS message. If you do want to try it, be sure to review the default SoftwareSerial choice.
If you get the simple NMEAblink.ino example to work, I would suggest trying NMEA.ino. Then insert your code into the doSomeWork function. Most of what you have in loop needs to go in the doSomeWork function, which is called only when a complete RMC sentence is received.
Regardless of which library you use, you also need to check whether the data is valid. What if your GPS isn't receiving any satellites? It may still send an RMC sentence, but there won't be any lat/lon data. You probably shouldn't send an SMS if the location field is not valid.

Resources