SAM-M8Q stops transmitting after ~10 hours

I’m relatively new to the Arduino ecosystem. I am prototyping a “sensor box” based off an ESP8266 Thing Dev board. Currently, I have a SAM-M8Q breakout board and a GY-521 (accel, gyro sensor) daisy-chained to the ESP8266 via I2C. I also have a DHT11 (Temp and Humidity sensor) and an SMD RGB LED connected to the thing on other pins.

The code I wrote sends the information via an HTTP POST request to an API I built, and stores the data in a database.

Everything works as expected. However, after about 10 hours, I stop getting GPS coordinates (see screenshot). Twice now, I have left the device plugged in to power and just let it sit overnight. Both times, GPS stopped for no obvious reason. Simply toggling the power switch on the ESP8266 board fixed the problem. It’s worth noting this happened once before I added in the second sensor on the I2C bus and the accompanying code.

I don’t really have an idea of where I should begin troubleshooting this issue. Does anyone have any suggestions?

Thanks!

(It seems my browser is failing to upload pictures, so I will add some in a reply shortly)

#include <SparkFun_Ublox_Arduino_Library.h>
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>
#include <DHTesp.h>
#include <Wire.h>

//Initialize DHTesp object called dht
DHTesp dht;
SFE_UBLOX_GPS myGPS;


//TODO: Make it try home wifi then phone wifi
/* Set these to your desired credentials. */
const char *ssid1 = "*****";
const char *password1 = "*****";
const char *ssid2 = "*****";
const char *password2 = "*****";

//Web/Server address to read/write from
const char *host = "********";
//const char *host ="badhost.int.lmp";
const int httpsPort = 443;  //HTTPS= 443 and HTTP = 80

//SHA1 finger print of certificate use web browser to view and copy
//You need to visit the site you are POSTing to, view certificate, copy SHA1 fingerprint below
//Replace colons with spaces, example BA:CB turns into BA CB
const char fingerprint[] PROGMEM = "**********";

//The following is for JSON code
const size_t capacity = JSON_OBJECT_SIZE(6);
DynamicJsonDocument doc(capacity);

//RGB Module
const int redpin = 4;
const int greenpin = 0;
const int bluepin = 5;


//MPU_Setup
const int MPU_ADDR = 0x68;
int16_t accelerometer_x, accelerometer_y, accelerometer_z; // variables for accelerometer raw data
int16_t gyro_x, gyro_y, gyro_z; // variables for gyro raw data
int16_t temperature; // variables for temperature data
char tmp_str[7]; // temporary variable used in convert function
char* convert_int16_to_str(int16_t i) { // converts int16 to string. Moreover, resulting strings will have the same length in the debug monitor.
  sprintf(tmp_str, "%6d", i);
  return tmp_str;
}

void setRgbRed() {
  analogWrite(bluepin, 0);
  analogWrite(redpin, 255);
  analogWrite(greenpin, 0);
}

void setRgbBlue() {
  analogWrite(bluepin, 255);
  analogWrite(redpin, 0);
  analogWrite(greenpin, 0);
}

void setRgbGreen() {
  analogWrite(bluepin, 0);
  analogWrite(redpin, 0);
  analogWrite(greenpin, 255);
}

void setRgbOff() {
  analogWrite(bluepin, 0);
  analogWrite(redpin, 0);
  analogWrite(greenpin, 0);
}

void blinkRgb() {
  setRgbRed();
  delay(500);
  setRgbOff();
  delay(500);
}

void showRgbError() {
  while (true) {
    setRgbRed();
    delay(100);
    setRgbOff();
    delay(100);
  }
}

void setRgb(int r, int g, int b) {
  analogWrite(bluepin, b);
  analogWrite(redpin, r);
  analogWrite(greenpin, g);
}
void setup() {
  //Always good to have a delay
  delay(1000);
  Wire.begin();
  Wire.beginTransmission(MPU_ADDR); // Begins a transmission to the I2C slave (GY-521 board)
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0); // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  //Begin serial communication, might remove once pushing to web
  Serial.begin(115200);



  //Configure dht object for pin 14 and tell program it's a DHT11 (vs a DHT22)
  dht.setup(12, DHTesp::DHT11);

  //RGB Pins
  pinMode(redpin, OUTPUT);
  pinMode(bluepin, OUTPUT);
  pinMode(greenpin, OUTPUT);

  //Prevents reconnection issue (taking too long to connect)
  WiFi.mode(WIFI_OFF);
  delay(1000);

  //Only Station No AP, This line hides the viewing of ESP as wifi hotspot
  WiFi.mode(WIFI_STA);

  //Connect to your WiFi router
  //WiFi.begin(ssid, password);
  //Serial.println("");

  //Try network 1
  WiFi.begin(ssid1, password1);
  for (int i = 0; i < 5; i++) {
    if (WiFi.status() != WL_CONNECTED) {
      delay(1000);
      Serial.println("Trying network 1");
    }
  }

  //Try network 2 if not connected
  if (WiFi.status() != WL_CONNECTED) {
    WiFi.mode(WIFI_OFF);
    delay(1000);
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid2, password2);
    for (int i = 0; i < 5; i++) {
      if (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.println("Trying network 2");
      }
    }
  }

  Serial.print("Connecting");
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    setRgb(127, 0, 127);
    delay(500);
    setRgbOff();
    delay(500);
    Serial.print(".");
  }

  //If connection successful show IP address in serial monitor
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid2);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());  //IP address assigned to your ESP
}

void loop() {

  //Gyro/Accelerometer code
  //TODO: Post to api and make useful
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H) [MPU-6000 and MPU-6050 Register Map and Descriptions Revision 4.2, p.40]
  Wire.endTransmission(false); // the parameter indicates that the Arduino will send a restart. As a result, the connection is kept active.
  Wire.requestFrom(MPU_ADDR, 7 * 2, true); // request a total of 7*2=14 registers

  // "Wire.read()<<8 | Wire.read();" means two registers are read and stored in the same variable
  accelerometer_x = Wire.read() << 8 | Wire.read(); // reading registers: 0x3B (ACCEL_XOUT_H) and 0x3C (ACCEL_XOUT_L)
  accelerometer_y = Wire.read() << 8 | Wire.read(); // reading registers: 0x3D (ACCEL_YOUT_H) and 0x3E (ACCEL_YOUT_L)
  accelerometer_z = Wire.read() << 8 | Wire.read(); // reading registers: 0x3F (ACCEL_ZOUT_H) and 0x40 (ACCEL_ZOUT_L)
  temperature = Wire.read() << 8 | Wire.read(); // reading registers: 0x41 (TEMP_OUT_H) and 0x42 (TEMP_OUT_L)
  gyro_x = Wire.read() << 8 | Wire.read(); // reading registers: 0x43 (GYRO_XOUT_H) and 0x44 (GYRO_XOUT_L)
  gyro_y = Wire.read() << 8 | Wire.read(); // reading registers: 0x45 (GYRO_YOUT_H) and 0x46 (GYRO_YOUT_L)
  gyro_z = Wire.read() << 8 | Wire.read(); // reading registers: 0x47 (GYRO_ZOUT_H) and 0x48 (GYRO_ZOUT_L)

  // print out data
  Serial.print("aX = "); Serial.print(convert_int16_to_str(accelerometer_x));
  Serial.print(" | aY = "); Serial.print(convert_int16_to_str(accelerometer_y));
  Serial.print(" | aZ = "); Serial.print(convert_int16_to_str(accelerometer_z));
  // the following equation was taken from the documentation [MPU-6000/MPU-6050 Register Map and Description, p.30]
  Serial.print(" | tmp = "); Serial.print(temperature / 340.00 + 36.53);
  Serial.print(" | gX = "); Serial.print(convert_int16_to_str(gyro_x));
  Serial.print(" | gY = "); Serial.print(convert_int16_to_str(gyro_y));
  Serial.print(" | gZ = "); Serial.print(convert_int16_to_str(gyro_z));
  Serial.println();

  // delay
  delay(1000);



  ////////RGB///////
  setRgbOff();
  delay(100);
  setRgbBlue();

  ////////GPS///////
  if (myGPS.begin() == false) {
    myGPS.setI2COutput(COM_TYPE_UBX);
    myGPS.saveConfiguration();
  }

  // Get and prepare latitude
  float latitude = myGPS.getLatitude();
  latitude = latitude / 10000000;

  // Get and prepare longitude
  float longitude = myGPS.getLongitude();
  longitude = longitude / 10000000;

  // Get and prepare speed
  float speedMMperS = myGPS.getGroundSpeed();
  float speedMPH = speedMMperS / 447.00;

  // Get and prepare sattelite count
  int numSats = myGPS.getSIV();

  //Delay loop for as long as minimum sampling period of DHT11
  delay(dht.getMinimumSamplingPeriod());

  //Record humidity, temperature C, and temperaure F
  float humidity = dht.getHumidity();
  float temperature = dht.getTemperature();
  float temperature_f = dht.toFahrenheit(temperature);

  //Repot to serial, remove this once all set up
  Serial.print(dht.getStatusString());
  Serial.print("\t");
  Serial.print("HUM: ");
  Serial.print(humidity, 1);
  Serial.print("\t\t");
  Serial.print("TempF: ");
  Serial.print(temperature_f, 1);
  Serial.print("\t\t");
  Serial.print("Heat Index F: ");
  Serial.println(dht.computeHeatIndex(dht.toFahrenheit(temperature), humidity, true), 1);
  delay(1000);
  ////////

  //Declare object of class WiFiClient
  WiFiClientSecure httpsClient;

  //Print the host you are connecting to, as defined above
  Serial.println(host);

  //Print the fingerprint of the site's cert
  Serial.printf("Using fingerprint '%s'\n", fingerprint);
  httpsClient.setFingerprint(fingerprint);
  httpsClient.setTimeout(15000); // 15 Seconds
  delay(1000);

  Serial.print("HTTPS Connecting");
  int r = 0; //retry counter
  while ((!httpsClient.connect(host, httpsPort)) && (r < 10)) {
    delay(100);
    Serial.print(".");
    r++;
  }
  if (r == 10) {
    Serial.println("Connection failed");
    showRgbError();

  }
  else {
    Serial.println("Connected to web");
  }

  String getData, Link;

  //POST Data
  //I believe this is the URI of where you want to send the post
  Link = "/api/sensorreading";

  Serial.print("requesting URL: ");
  Serial.println(host);

  ///////JSON CODE//////
  doc["tempF"] = temperature_f;
  doc["humidity"] = humidity;
  doc["longitude"] = longitude;
  doc["latitude"] = latitude;
  doc["speed"] = speedMPH;
  doc["satelliteCount"] = numSats;

  serializeJson(doc, Serial);
  String jsonOutput;
  serializeJson(doc, jsonOutput);
  int clength;
  clength = measureJson(doc);
  /////////////////////
  //Construct a valid POST header
  //Pay attention to Content-Length, it must match your content
  httpsClient.print(String("POST " + Link + " HTTP/1.1\r\n" +
                           "Host: " + host + "\r\n" +
                           "Content-Type: application/json" + "\r\n" +
                           "Content-Length: " + clength + "\r\n\r\n" +
                           jsonOutput + "\r\n\r\n"));
  Serial.println(" ");
  Serial.println("-------------------");
  Serial.println(String("POST " + Link + " HTTP/1.1\r\n" +
                        "Host: " + host + "\r\n" +
                        "Content-Type: application/json" + "\r\n" +
                        "Content-Length: " + clength + "\r\n\r\n" +
                        jsonOutput + "\r\n\r\n"));
  Serial.println("request sent");

  while (httpsClient.connected()) {
    String line = httpsClient.readStringUntil('\n');
    if (line == "\r") {
      Serial.println("headers received");
      setRgbGreen();
      break;
    }
  }

  Serial.println("reply was:");
  Serial.println("==========");
  String line;
  while (httpsClient.available()) {
    line = httpsClient.readStringUntil('\n');  //Read Line by Line
    Serial.println(line); //Print response
  }
  Serial.println("==========");
  Serial.println("closing connection");

  delay(1000);  //POST Data at every 2 seconds
}
//=======================================================================

Here is a view of the DB which shows the GPS data stopping.

https://i.imgur.com/FVKKC38.jpg

Here is a view of the device (work in progress :shock: )

https://i.imgur.com/1put4iS.jpg

https://i.imgur.com/EvXATY2.jpg

At any point did it ever ‘restart’ getting values? For even one reading? If it did then it could be an issue with your GPS signal and your sky view of satellites.

Hi Brandon,

Thanks for your response. During these testing phases, the device hadn’t moved at all. Granted, it is indoors. However, the only thing that got the readings back was resetting the device.

I just double checked the database and that last time it had this problem, it was sending blank readings for a full hour. I don’t believe it was an issue with getting a view of the satellites based on that data.

When you say the GPS stops sending data do you mean it’s not sending anything at all or it’s sending zeros for position reports? it could be a reception or interference issue if you’re all the sudden getting zeros but the GPS is still sending data.

There’s no reason why the GPS would stop sending data alltogether. Could something in your code be overflowing and causing the issue? You might try connecting the GPS up to a serial logger or a PC and see if it stops sending data there when the rest of your system is indicating no data.

Hi YellowDog,

I can’t answer your question with 100% certainty. The behavior I see leads me to believe it’s sending all 0s. When this issue happens, both lights on the SAM-M8Q continue their normal function. The read light is solid and the yellow light keeps blinking.

It would make sense that the problem has something to with memory overflow. After X amount time, the memory gets full and we start getting 0s seems like a reasonable explanation. But I don’t know much about that topic, so I don’t know how to troubleshoot it.

Do you have any suggestions how I might confirm it’s a memory issue? Also, are there any best practices to avoid this problem?

Thanks for your time helping me troubleshoot!

I did a little research and discovered it may be my excessive use of Serial.print causing the issue. What I did to try to resolve the issue is replace all the plain strings in print calls (i.e. not the variables) with F() strings. I wish didn’t have to wait 10 hours to see if it worked, but at least I have something to try!

Dang…looks like its still happening.

https://i.imgur.com/cOQmH3o.jpg

As you can see from the screenshot above, at around 1:50 I plugged the device in and it started recording data. At around 12:00, it stopped reporting GPS data but kept reporting temp data.

Data is still being reported from the GPS module, but it’s all 0s. The whole project didn’t move at all. The SAM-M8Q breakout board still has the solid red light and blinking yellow light while sending 0s.

This time I will leave the ESP8266 connect to my PC so I monitor the serial output. I added a ```
Serial.println(ESP.getFreeHeap())


Is there anything else I should be looking for when this happens? Are there any print statements I can add that might be useful to know when this happens again?

Thanks!

I have a similar problem in that if I put any commands to retrieve data in the loop, it will eventually fail and give me no results. This can be anywhere from 1 minute to 1 hour. If I reset the arduino, it will always work again and then given some time, it will fail again. It seems to bring down the entire I2C bus because I have other shields talking on the same bus and it will then cause all the other shields to stop transmitting as well. So to recap, it doesn’t matter if I use the SAM-M8Q shield on its own or link multiple shields, the GPS shield will cause the bus to fail if I tell it to get me data in the loop. If I put it in setup, it will always work. I have tried the command of resetting the I2C bus if it detects an error as well as put delays down between 2 seconds to 1 minute. I have also toyed around with the powersave mode with no success for reliability. Anyone have any ideas?

Ok so it looks like the SAM-M8Q shield can work well on its own using example code but as soon as another shield is introduced the whole I2C bus collapses anywhere from a minute to a few hours. I still haven’t figured out what is causing it. On my system, I have 4 shields. They work well only if I do not include the GPS Shield. Its probably coding related but I am not sure what part of the code is causing it. I have a suspicion its related to the Setup section where I combine to example codes together. Could it be that I need a wire.begin() statement?

Do you not have a wire.begin() in the setup() at all? You only should need one.

Have you modified any of the pull-up resistors on any of the other boards? You may need to remove some by cutting trace jumpers.