Arduino MKR NB 1500 with water level sensor (zbelding)

Hi,

I'm Fairly new to arduino programming and would like to get some help.

My goal with this project is to messure the water level at a dam so i can see when i need to open or close the gates.
I have the Arduino MKR NB 1500 board and a water pressure sensor which read 0 - 2M and output 4-20mA readings.
I'll use a voltage divider to get 3,3volt for the analogread.

My goal is to read the sensor every 1 minute and store the values and take the mean value and post it every 10 minutes

Now to the problem.
I have found a awesome code made by person named zbelding here on the forum that works great to post the data to my thingspeak channel.

But i can't figure out where in the code i should put my new code. I have tried to put the reading of the sensor in the void setup but then it slows down the whole code and the modem times out.
I have also tried put code in the if statement where he output Data =4; in the code(page 21) but with out any luck.

I hope someone could help me find a way to read my sensor.

Here is the code which Zbleding made.
Http post to thingspeak

Thanks! :slight_smile:

1 Like

Use a single resistor between the analog input and GND, not a voltage divider. Value: 3.3 / 0.020 = 165 Ohm.

Thanks for your answear!

Yes, i already have a 165Ohm resistor but the problem is to write code that works together with (Zbleding) code so i can post it to thingspeak.

If i put any code in the Void setup the modem fail.
Some how i need to let the modem code run and at the same time have a code for the sensor that collects data and send it every 10 min.

Then You have a very bad setup.
Please post the code here using the symbol.

Like i said in the first post, it's not my code but i'm using it and it works very well to post to thingspeak. But i need help to find a way to add the sensor code.

/*
  This sketch works with the MKR NB 1500 without the need for the MKRNB library.


  It updates thingspeak fields using http POST every 10 mins.


  Field1 = Battery Voltage
  Field2 = Example data(4)
  Field3 = Modem Reset Count
  Field4 = Modem signal, rsrq in dB


  It also reads command from Field5 using http GET. In the sample program I have provided,
  a command of "ledon" will turn on the onboard LED and "ledoff" will turn off the onboard LED.
  You can use another arduino or a desktop/android app like Postman to send commands
  to Field5.
  With this program, these commands are only updated when the thingspeak channel is updated.
  By default, it updates every 10 mins. Therefore, it only executes commands every 10 mins.
  This can be easily changed by changing the thingspeakPostInterval.


*/


#include <avr/dtostrf.h>
#include "ArduinoLowPower.h"


const char apn[] = "hologram";  //"m2m64.com.attz";
const char simPIN[] = "1234";   //If the sim card needs a PIN, this will be used.
/*
  Network profile ID, see AT Manual Appendix C.4:


  C.4  SARA-R410M-02B Americas MNO profile tables
  Network     Profile ID
  --------     ----------
  Undefined     0
  AT&T          2
  Verizon       3(won't register unless device is approved by Verizon)
  T-mobile US   5
  Sprint        8
  Telus         21
*/
const char networkProfile[] = "2";  //Network profile ID


const char thingspeakKey[] = "KEYYYYYYY";      //Thingspeak api write key
const char thingspeakReadKey[] = "KEYYYYYY";  //Thingspeak api read key
const char thingspeakChannelId[] = "1234567";
int thingspeakCommandField = 5;  //Thingspeak field used to read commands
bool mcuLowPower = false;        //If true, the Microcontroller will be put into sleep after each post.


const char thingspeakUrl[] = "api.thingspeak.com";
const unsigned int thingspeakPort = 80;  //80 or 443 for TLS
const unsigned long thingspeakPostInterval = 600000;


//System variables below ------------------------------------


unsigned long lastThingspeakPostTime = 0;
unsigned long commandStartTime = 0;
unsigned long delayStartTime = 0;
unsigned long postStartTime = 0;


int resetCount = 0;
int responsePosition = 0;
int sendCommandStage = 0;
int stage = 0;
int timeoutCount = 0;
int socket = -1;
int socketErrorCount = 0;
int httpErrorCount = 0;
int loopCount = 0;
int rsrq_dB = 0;
int dnsCheckPeriod = 0;
int dnsCheckCount = 0;
int consumeResponseChars = 0;


bool modemSetupFlag = false;
bool thingspeakSuccessFlag = false;
bool modemHardResetAndPowerOnPending = true;
bool modemReset = false;
bool modemSoftResetPending = false;
bool responseError = false;
bool thingspeakCommand = false;
bool continueResponse = false;
bool smsPending = false;


char response[2000];
char formattedCommand[500];
char formattedHttp[500];
char thingspeakIp[16];
char socketCloseIndicator[11];


void setup() {


  pinMode(SARA_PWR_ON, INPUT);
  pinMode(SARA_RESETN, INPUT);


  Serial.begin(115200);
  SerialSARA.begin(115200);


  dnsCheckPeriod = round(3600000 / thingspeakPostInterval);
  if (dnsCheckPeriod < 1) {
    dnsCheckPeriod = 1;
  }
  dnsCheckCount = dnsCheckPeriod + 1;
}


void loop() {
  if (timeoutCount > 20) {
    timeoutCount = 0;
    resetCount++;
    modemReset = true;
    modemHardResetAndPowerOnPending = true;
  }
  if (modemHardResetAndPowerOnPending) {
    modemHardResetAndPowerOn();
  }
  if (responsePosition > 1999) {
    Serial.print(response);
    memset(response, 0, sizeof(response));
    responsePosition = 0;
  }
  if (SerialSARA.available()) {
    response[responsePosition] = SerialSARA.read();
    if (consumeResponseChars > 0) {
      consumeResponseChars--;
    } else {
      responsePosition++;
    }
  }
  if (!modemSetupFlag && !modemHardResetAndPowerOnPending) {
    modemSetup();
  }
  if (!mcuLowPower && millis() - lastThingspeakPostTime > thingspeakPostInterval) {
    thingspeakSuccessFlag = false;
    lastThingspeakPostTime = millis();
  }
  if (!thingspeakSuccessFlag && modemSetupFlag && !modemHardResetAndPowerOnPending) {
    rawHttpPost();
  }
}


void sendCommand(const char command[500], unsigned long timeout, bool acceptError) {


  if (sendCommandStage == 0) {
    if (!continueResponse) {
      memset(response, 0, sizeof(response));
      responsePosition = 0;
    } else {
      continueResponse = false;
    }
    Serial.println(command);
    SerialSARA.println(command);
    responseError = false;
    commandStartTime = millis();
    sendCommandStage++;
    loopCount = 0;
  }
  if (sendCommandStage == 1) {
    loopCount++;
    if (loopCount > 1000) {
      loopCount = 0;
      if (strstr(response, "\nOK\r") != NULL || strstr(response, "\r\n@") != NULL) {
        delayStartTime = millis();
        sendCommandStage++;
      }
      if (strstr(response, "\nERROR\r") != NULL || strstr(response, "+CME ERROR") != NULL) {
        responseError = true;
        delayStartTime = millis();
        sendCommandStage++;
      }
    }
    if (millis() - commandStartTime > timeout) {
      Serial.println(response);
      Serial.println("Response timeout");
      sendCommandStage = 0;
      stage = 0;
      timeoutCount++;
    }
  }
  if (sendCommandStage == 2) {
    if (millis() - delayStartTime > 25) {
      Serial.println(response);
      timeoutCount = 0;
      sendCommandStage = 0;
      if (responseError && !acceptError) {
        stage = 0;
      } else {
        stage++;
      }
    }
  }
}


void modemHardResetAndPowerOn() {
  if (stage == 0) {
    modemSetupFlag = false;
    if (modemReset) {
      Serial.println("Modem resetting...");
      pinMode(SARA_RESETN, OUTPUT);
      digitalWrite(SARA_RESETN, HIGH);  //Logic high on Arduino pin = logic low on SARA R4
      delayStartTime = millis();
      stage++;
    } else {
      stage = 2;
    }
  }
  if (stage == 1) {
    if (millis() - delayStartTime > 12000) {
      pinMode(SARA_RESETN, INPUT);
      delayStartTime = millis();
      stage++;
    }
  }
  if (stage == 2) {
    if (millis() - delayStartTime > 500) {
      pinMode(SARA_PWR_ON, OUTPUT);
      digitalWrite(SARA_PWR_ON, HIGH);  //Logic high on Arduino pin = logic low on SARA R4
      delayStartTime = millis();
      stage++;
    }
  }
  if (stage == 3) {
    if (millis() - delayStartTime > 500) {
      pinMode(SARA_PWR_ON, INPUT);
      Serial.println("Modem on");
      delayStartTime = millis();
      stage++;
    }
  }
  if (stage == 4) {
    if (millis() - delayStartTime > 5000) {
      Serial.println("Reset and/or Power On complete");
      stage = 0;
      modemReset = false;
      modemHardResetAndPowerOnPending = false;
    }
  }
}


void modemSetup() {


  if (stage == 0) {
    sendCommand("ATV1", 1000, true);
  }
  if (stage == 1) {
    sendCommand("AT+IPR=115200", 1000, true);  //Baud rate set
  }
  if (stage == 2) {
    sendCommand("ATE0", 1000, true);  //Echo off
  }
  if (stage == 3) {
    sendCommand("AT+CMEE=2", 10000, true);
  }
  if (stage == 4) {
    sendCommand("AT+CPIN?", 10000, true);
  }
  if (stage == 5) {
    if (strstr(response, "SIM PIN") != NULL) {
      sprintf(formattedCommand, "AT+CPIN=\"%s\"", simPIN);
      stage++;
    } else {
      stage = 7;
    }
  }
  if (stage == 6) {
    sendCommand(formattedCommand, 10000, true);
  }
  if (stage == 7) {
    if (modemSoftResetPending == true) {
      sendCommand("AT+CFUN=15", 180000, true);
    } else {
      stage = 10;
    }
  }
  if (stage == 8) {
    delayStartTime = millis();
    stage++;
  }
  if (stage == 9) {
    if (millis() - delayStartTime > 5000) {
      modemSoftResetPending = false;
      stage = 0;
    }
  }
  if (stage == 10) {
    sendCommand("AT+UMNOPROF?", 1000, true);
  }
  if (stage == 11) {
    if (strstr(response, networkProfile) == NULL) {
      stage++;
    } else {
      stage = 16;
    }
  }
  if (stage == 12) {
    sendCommand("AT+CFUN=0", 180000, true);
  }
  if (stage == 13) {
    sprintf(formattedCommand, "AT+UMNOPROF=%s", networkProfile);
    stage++;
  }
  if (stage == 14) {
    sendCommand(formattedCommand, 1000, true);
  }
  if (stage == 15) {
    modemSoftResetPending = true;
    stage = 7;
  }
  if (stage == 16) {
    sendCommand("AT+CGDCONT?", 1000, true);
  }
  if (stage == 17) {
    if (strstr(response, apn) == NULL || strstr(response, "IP") == NULL) {
      stage++;
    } else {
      stage = 22;
    }
  }
  if (stage == 18) {
    sendCommand("AT+CFUN=0", 180000, true);
  }
  if (stage == 19) {
    sprintf(formattedCommand, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
    stage++;
  }
  if (stage == 20) {
    sendCommand(formattedCommand, 1000, true);
  }
  if (stage == 21) {
    modemSoftResetPending = true;
    stage = 7;
  }
  if (stage == 22) {
    sendCommand("AT+CPSMS?", 1000, true);
  }
  if (stage == 23) {
    if (strstr(response, "+CPSMS:0") == NULL) {
      stage++;
    } else {
      stage = 27;
    }
  }
  if (stage == 24) {
    sendCommand("AT+CFUN=0", 180000, true);
  }
  if (stage == 25) {
    sendCommand("AT+CPSMS=0", 10000, true);
  }
  if (stage == 26) {
    modemSoftResetPending = true;
    stage = 7;
  }
  if (stage == 27) {
    sendCommand("AT+COPS=0", 10000, true);
  }
  if (stage == 28) {
    sendCommand("AT+CEREG=0", 1000, true);
  }
  if (stage == 29) {
    sendCommand("AT+USOCLCFG=1", 1000, true);
  }
  if (stage == 30) {
    sendCommand("AT+UPSV=4", 1000, true);
  }
  if (stage == 31) {
    modemSetupFlag = true;
    stage = 0;
  }
}


void rawHttpPost() {
  if (stage == -1) {
    postStartTime = millis();
    stage++;
  }
  if (stage == 0) {
    sendCommand("ATE0", 1000, true);
  }
  if (stage == 1) {
    sendCommand("AT+CEREG?", 1000, false);
  }
  if (stage == 2) {
    if (strstr(response, ",1") != NULL || strstr(response, ",5") != NULL) {
      stage++;
      Serial.println("Registered");
    } else {
      if (millis() - commandStartTime > 500) {
        stage--;
      }
    }
  }
  if (stage == 3) {
    sendCommand("AT+CGATT?", 1000, false);
  }
  if (stage == 4) {
    if (strstr(response, "+CGATT: 1") != NULL) {
      stage++;
      Serial.println("Attached");
    } else {
      if (millis() - commandStartTime > 500) {
        stage--;
      }
    }
  }
  if (stage == 5) {
    sendCommand("AT+CGACT?", 1000, false);
  }
  if (stage == 6) {
    if (strstr(response, ",1") != NULL) {
      stage++;
      Serial.println("Context Activated");
    } else {
      if (millis() - commandStartTime > 500) {
        stage--;
      }
    }
  }
  if (stage == 7) {
    sendCommand("AT+CESQ", 1000, false);
  }
  if (stage == 8) {
    char *responsePointer;
    int rsrq = 0;


    responsePointer = strstr(response, "+CESQ:");
    if (responsePointer != NULL) {
      int rsrq1 = responsePointer[21] - '0';
      if (responsePointer[22] != ',') {
        int rsrq2 = responsePointer[22] - '0';
        rsrq = (rsrq1 * 10) + rsrq2;
        if (responsePointer[23] != ',') {
          rsrq = 0;
        }
      } else {
        rsrq = rsrq1;
      }
      rsrq_dB = -20 + (.5 * rsrq);


      Serial.print("Signal RSRQ:");
      Serial.print(rsrq_dB);
      Serial.println("dB (closer to zero is better)");
      socket = 0;
      stage++;
    } else {
      stage = 0;
    }
  }
  if (stage == 9) {
    sprintf(formattedCommand, "AT+USOCL=%i,1", socket);
    stage++;
  }
  if (stage == 10) {
    sendCommand(formattedCommand, 150000, true);
  }
  if (stage == 11) {
    socket++;
    if (socket <= 6) {
      stage = stage - 2;
    } else {
      socket = -1;
      stage++;
    }
  }
  if (stage == 12) {
    if (dnsCheckCount >= dnsCheckPeriod) {
      sprintf(formattedCommand, "AT+UDNSRN=0,\"%s\"", thingspeakUrl);
      stage++;
    } else {
      dnsCheckCount++;
      stage = stage + 3;
    }
  }
  if (stage == 13) {
    sendCommand(formattedCommand, 150000, true);
  }
  if (stage == 14) {
    if (responseError) {
      socketErrorCount++;
      stage = 0;
      if (socketErrorCount > 10) {
        modemSetupFlag = false;
        modemSoftResetPending = true;
        socketErrorCount = 0;
      }
    } else {
      socketErrorCount = 0;
      char *responsePointer;
      int pointerPosition = 10;


      responsePointer = strstr(response, "+UDNSRN:");
      if (responsePointer != NULL) {
        int thingspeakIpPosition = 0;
        while (responsePointer[pointerPosition] != '"' && thingspeakIpPosition <= 14) {
          thingspeakIp[thingspeakIpPosition] = responsePointer[pointerPosition];
          thingspeakIpPosition++;
          pointerPosition++;
        }
        thingspeakIp[thingspeakIpPosition] = '\0';
        dnsCheckCount = 0;
        stage++;
      } else {
        stage = 0;
      }
    }
  }
  if (stage == 15) {
    sendCommand("AT+USOCR=6", 120000, true);
  }
  if (stage == 16) {
    if (responseError) {
      modemSetupFlag = false;
      modemSoftResetPending = true;
      stage = 0;
    } else {
      char *responsePointer;


      responsePointer = strstr(response, "+USOCR:");
      if (responsePointer != NULL) {
        socket = responsePointer[8] - '0';
        int securitySetting = 0;
        if (thingspeakPort == 443) {
          securitySetting = 1;
        }
        sprintf(formattedCommand, "AT+USOSEC=%i,%i", socket, securitySetting);
        sprintf(socketCloseIndicator, "+UUSOCL: %i", socket);
        stage++;
      } else {
        stage = 0;
      }
    }
  }
  if (stage == 17) {
    sendCommand(formattedCommand, 1000, false);
  }
  if (stage == 18) {
    sprintf(formattedCommand, "AT+USOCO=%i,\"%s\",%i", socket, thingspeakIp, thingspeakPort);
    stage++;
  }
  if (stage == 19) {
    sendCommand(formattedCommand, 150000, true);
  }
  if (stage == 20) {
    if (responseError) {
      socketErrorCount++;
      stage = 0;
      if (socketErrorCount > 10) {
        modemSetupFlag = false;
        modemSoftResetPending = true;
        socketErrorCount = 0;
      }
    } else {
      socketErrorCount = 0;
      stage++;
    }
  }
  if (stage == 21) {
    char httpBody[500];
    int httpBodySize = 0;
    int formattedHttpSize = 0;


    if (thingspeakCommand) {
      formattedHttpSize = sprintf(formattedHttp, "GET /channels/%s/fields/field%i/last?api_key=%s&headers=false HTTP/1.1\r\n"
                                                 "Host: %s\r\n\r\n",
                                  thingspeakChannelId, thingspeakCommandField, thingspeakReadKey, thingspeakUrl);
    } else {
      int data = 4;
      float batVoltage = analogRead(ADC_BATTERY) * 3.3 / 1023.0 * 1.275;  //1.275 is derived from ADC_BATTERY voltage divider
      char batVolatgeCharArray[6];
      dtostrf(batVoltage, 0, 3, batVolatgeCharArray);


      httpBodySize = sprintf(httpBody, "headers=false&api_key=%s&field1=%s&field2=%i&field3=%i&field4=%i", thingspeakKey, batVolatgeCharArray, data, resetCount, rsrq_dB);


      formattedHttpSize = sprintf(formattedHttp, "POST /update HTTP/1.1\r\n"
                                                 "Host: %s\r\n"
                                                 "Content-Type: application/x-www-form-urlencoded\r\n"
                                                 "Content-Length: %i\r\n\r\n"
                                                 "%s",
                                  thingspeakUrl, httpBodySize, httpBody);
    }


    sprintf(formattedCommand, "AT+USOWR=%i,%i", socket, formattedHttpSize);
    stage++;
  }
  if (stage == 22) {
    sendCommand(formattedCommand, 120000, false);
  }
  if (stage == 23) {
    if (millis() - delayStartTime > 50) {
      stage++;
    }
  }
  if (stage == 24) {
    sendCommand(formattedHttp, 120000, false);
  }
  if (stage == 25) {
    char *responsePointer;
    responsePointer = strstr(response, "+USOWR:");
    if (responsePointer != NULL) {
      if (responsePointer[10] - '0' != 0) {
        loopCount = 0;
        stage++;
      } else {
        stage = 0;
      }
    } else {
      stage = 0;
    }
  }
  if (stage == 26) {
    loopCount++;
    if (loopCount > 1000) {
      loopCount = 0;
      char *responsePointer;
      responsePointer = strstr(response, "+UUSORD:");
      if (responsePointer != NULL) {
        delayStartTime = millis();
        sprintf(formattedCommand, "AT+USORD=%i,1024", socket);
        Serial.println(responsePointer);
        stage++;
      }
      responsePointer = strstr(response, socketCloseIndicator);
      if (responsePointer != NULL) {
        Serial.println(responsePointer);
        stage = 0;
      }
    }


    if (millis() - delayStartTime > 90000) {
      stage = 0;
    }
  }
  if (stage == 27) {
    if (millis() - delayStartTime > 500) {
      stage++;
    }
  }
  if (stage == 28) {
    sendCommand(formattedCommand, 5000, false);
  }
  if (stage == 29) {
    if (strstr(response, "+UUSORD:") != NULL) {
      char *responsePointer;
      char *endPointer;


      responsePointer = strrchr(response, '"');
      endPointer = strrchr(response, '\n');
      responsePosition = responsePosition - ((endPointer - responsePointer) + 1);
      consumeResponseChars = 19;
      continueResponse = true;
      stage = stage - 1;
    } else {
      if (strstr(response, "HTTP/1.1 200") != NULL || strstr(response, "HTTP/1.1 201") != NULL) {
        char *responsePointer;
        char *endPointer;
        int contentLength = 0;
        int receivedContentLength = 0;
        int ones;
        int tens;
        int hundreds;
        int thousands;


        responsePointer = strstr(response, "Content-Length");


        if (responsePointer == NULL) {
          responsePointer = strstr(response, "content-length");  //some server don't Captitalize
        }


        if (responsePointer != NULL) {
          thousands = responsePointer[16] - '0';
          if (responsePointer[17] != '\r') {
            hundreds = responsePointer[17] - '0';
            if (responsePointer[18] != '\r') {
              tens = responsePointer[18] - '0';
              if (responsePointer[19] != '\r') {
                ones = responsePointer[19] - '0';
                contentLength = (1000 * thousands) + (100 * hundreds) + (10 * tens) + ones;
              } else {
                contentLength = (100 * thousands) + (10 * hundreds) + tens;
              }
            } else {
              contentLength = (10 * thousands) + hundreds;
            }
          } else {
            contentLength = thousands;
          }
          Serial.print("Http Header Content Length:");
          Serial.println(contentLength);


          responsePointer = strstr(responsePointer, "\r\n\r\n");


          if (responsePointer != NULL) {
            endPointer = strrchr(responsePointer, '"');
            if (endPointer != NULL) {
              receivedContentLength = endPointer - responsePointer - 4;
              Serial.print("Received Content Length:");
              Serial.println(receivedContentLength);
              if (contentLength == receivedContentLength) {
                if (!thingspeakCommand) {
                  thingspeakCommand = true;
                  stage = 21;
                  httpErrorCount = 0;
                } else {
                  stage++;
                  httpErrorCount = 0;
                  thingspeakCommand = false;
                  if (strstr(responsePointer, "ledon") != NULL) {
                    digitalWrite(LED_BUILTIN, HIGH);
                  }
                  if (strstr(responsePointer, "ledoff") != NULL) {
                    digitalWrite(LED_BUILTIN, LOW);
                  }
                }
              } else {
                stage = 0;
              }
            } else {
              stage = 0;
            }
          } else {
            stage = 0;
          }
        } else {
          stage = 0;
        }
      } else {
        httpErrorCount++;
        stage = 0;
        if (httpErrorCount > 10) {
          stage = 0;
          modemSetupFlag = false;
          modemSoftResetPending = true;
          httpErrorCount = 0;
        }
      }
    }
  }
  if (stage == 30) {
    sprintf(formattedCommand, "AT+USOCL=%i,1", socket);
    stage++;
  }
  if (stage == 31) {
    sendCommand(formattedCommand, 150000, true);
  }
  if (stage == 32) {
    socket = -1;
    if (mcuLowPower) {
      stage = -1;
      unsigned long postDuration = millis() - postStartTime;
      while (postDuration > thingspeakPostInterval) {
        postDuration = postDuration - thingspeakPostInterval;
      }
      Serial.println("MCU sleep until next update...");
      LowPower.sleep(thingspeakPostInterval - postDuration);
    } else {
      stage = 0;
      thingspeakSuccessFlag = true;
    }
  }
}

at stage 21 in the void rawHttpPost() is where the magic happends.

@zbelding can help me :slight_smile:

I read some sensor data with my application of this as well. I will try to post an example of this tonight.

1 Like

Looking forward to get some example codes from you how to implement a sensor :slight_smile: @zbelding

@zbelding I have now tried adding code that read the sensor for just 10 samples every 1 second and take the mean value. (Stage 32) in the code.

It seems to work but in the serial monitor i get this response when it post again after 10 minutes to thingspeak.
Do i have to worry or is it fine?

14:25:19.031 -> ATE0
14:25:20.033 ->
14:25:20.033 -> Response timeout
14:25:20.033 -> ATE0

14:25:19.031 -> ATE0
14:25:20.033 -> 
14:25:20.033 -> Response timeout
14:25:20.033 -> ATE0
14:25:20.069 -> 
14:25:20.069 -> OK
14:25:20.069 -> 
14:25:20.069 -> AT+CEREG?
14:25:20.107 -> 
14:25:20.107 -> +CEREG: 0,1
14:25:20.107 -> 
14:25:20.107 -> OK
14:25:20.107 -> 
14:25:20.107 -> Registered
14:25:20.107 -> AT+CGATT?
14:25:20.145 -> 
14:25:20.145 -> +CGATT: 1
14:25:20.145 -> 
14:25:20.145 -> OK
14:25:20.145 -> 
14:25:20.145 -> Attached
14:25:20.145 -> AT+CGACT?
14:25:20.183 -> 
14:25:20.183 -> +CGACT: 1,1
14:25:20.183 -> 
14:25:20.183 -> OK
14:25:20.183 -> 
14:25:20.183 -> Context Activated
14:25:20.183 -> AT+CESQ
14:25:20.221 -> 
14:25:20.221 -> +CESQ: 99,99,255,255,10,47
14:25:20.221 -> 
14:25:20.221 -> OK
14:25:20.221 -> 
14:25:20.221 -> Signal RSRQ:-15dB (closer to zero is better)
14:25:20.221 -> AT+USOCL=0,1
14:25:20.255 -> 
14:25:20.255 -> OK
14:25:20.255 -> 
14:25:20.255 -> 
14:25:20.255 -> 
14:25:20.255 -> AT+USOCL=1,1
14:25:20.295 -> 
14:25:20.295 -> OK
14:25:20.295 -> 
14:25:20.295 -> 
14:25:20.295 -> 
14:25:20.295 -> AT+USOCL=2,1
14:25:20.332 -> 
14:25:20.332 -> OK
14:25:20.332 -> 
14:25:20.332 -> 
14:25:20.332 -> 
14:25:20.332 -> AT+USOCL=3,1
14:25:20.369 -> 
14:25:20.369 -> OK
14:25:20.369 -> 
14:25:20.369 -> 
14:25:20.369 -> 
14:25:20.369 -> AT+USOCL=4,1
14:25:20.406 -> 
14:25:20.406 -> OK
14:25:20.406 -> 
14:25:20.406 -> 
14:25:20.406 -> 
14:25:20.406 -> AT+USOCL=5,1
14:25:20.440 -> 
14:25:20.440 -> OK
14:25:20.440 -> 
14:25:20.440 -> 
14:25:20.440 -> 
14:25:20.440 -> AT+USOCL=6,1
14:25:20.480 -> 
14:25:20.480 -> OK
14:25:20.480 -> 
14:25:20.480 -> 
14:25:20.480 -> 
14:25:20.480 -> AT+USOCR=6
14:25:20.517 -> 
14:25:20.517 -> +USOCR: 0
14:25:20.517 -> 
14:25:20.517 -> OK
14:25:20.517 -> 
14:25:20.517 -> AT+USOSEC=0,0
14:25:20.551 -> 
14:25:20.551 -> OK
14:25:20.551 -> 
14:25:20.551 -> AT+USOCO=0,"3.230.206.56",80
14:25:21.080 -> 
14:25:21.080 -> OK
14:25:21.080 -> 
14:25:21.080 -> AT+USOWR=0,202
14:25:21.126 -> 
14:25:21.126 -> @
14:25:21.126 -> POST /update HTTP/1.1
14:25:21.126 -> Host: api.thingspeak.com
14:25:21.126 -> Content-Type: application/x-www-form-urlencoded
14:25:21.126 -> Content-Length: 82
14:25:21.126 -> 
14:25:21.126 -> headers=false&api_key=V5309F6616BPL0D5&field1=0.037&field2=786&field3=0&field4=-15
14:25:21.205 -> 
14:25:21.205 -> +USOWR: 0,202
14:25:21.205 -> 
14:25:21.205 -> OK
14:25:21.205 -> 
14:25:21.423 -> +UUSORD: 0,307
14:25:21.423 -> 
14:25:21.935 -> AT+USORD=0,1024
14:25:21.999 -> 
14:25:21.999 -> 
14:25:21.999 -> +USORD: 0,307,"HTTP/1.1 200 OK
14:25:21.999 -> Date: Fri, 22 Sep 2023 12:25:20 GMT
14:25:21.999 -> Content-Type: text/html; charset=utf-8
14:25:21.999 -> Content-Length: 4
14:25:21.999 -> Connection: keep-alive
14:25:21.999 -> Status: 200 OK
14:25:21.999 -> Cache-Control: max-age=0, private, must-revalidate
14:25:21.999 -> X-Request-Id: c35a9292-2c26-4116-b203-856721f703b0
14:25:21.999 -> ETag: W/"b1c04228b4390d096a51a514158dc786"
14:25:21.999 -> 
14:25:21.999 -> 2856"
14:25:21.999 -> 
14:25:21.999 -> OK
14:25:21.999 -> 
14:25:21.999 -> Http Header Content Length:4
14:25:21.999 -> Received Content Length:4
14:25:21.999 -> AT+USOWR=0,118
14:25:22.033 -> 
14:25:22.033 -> @
14:25:22.077 -> GET /channels/2268169/fields/field5/last?api_key=B43K7EJY4CUOUOM4&headers=false HTTP/1.1
14:25:22.077 -> Host: api.thingspeak.com
14:25:22.077 -> 
14:25:22.077 -> 
14:25:22.123 -> 
14:25:22.123 -> +USOWR: 0,118
14:25:22.123 -> 
14:25:22.123 -> OK
14:25:22.123 -> 
14:25:22.280 -> +UUSORD: 0,3
14:25:22.791 -> AT+USORD=0,1024
14:25:22.862 -> 
14:25:22.862 -> 
14:25:22.862 -> +USORD: 0,305,"HTTP/1.1 200 OK
14:25:22.862 -> Date: Fri, 22 Sep 2023 12:25:21 GMT
14:25:22.862 -> Content-Type: text/html; charset=utf-8
14:25:22.862 -> Content-Length: 2
14:25:22.862 -> Connection: keep-alive
14:25:22.862 -> Status: 200 OK
14:25:22.862 -> Cache-Control: max-age=0, private, must-revalidate
14:25:22.862 -> X-Request-Id: 3a0e6d43-ad53-4017-b97a-326c732baa3a
14:25:22.862 -> ETag: W/"1bad6b8cf97131fceab8543e81f77571"
14:25:22.862 -> 
14:25:22.862 -> -1"
14:25:22.862 -> 
14:25:22.862 -> OK
14:25:22.862 -> 
14:25:22.862 -> Http Header Content Length:2
14:25:22.862 -> Received Content Length:2
14:25:22.862 -> AT+USOCL=0,1
14:25:22.901 -> 
14:25:22.901 -> OK
14:25:22.901 -> 
14:25:32.904 -> Mean value: 860
/*
  This sketch works with the MKR NB 1500 without the need for the MKRNB library.


  It updates thingspeak fields using http POST every 10 mins.


  Field1 = Battery Voltage
  Field2 = Example data(4)
  Field3 = Modem Reset Count
  Field4 = Modem signal, rsrq in dB


  It also reads command from Field5 using http GET. In the sample program I have provided,
  a command of "ledon" will turn on the onboard LED and "ledoff" will turn off the onboard LED.
  You can use another arduino or a desktop/android app like Postman to send commands
  to Field5.
  With this program, these commands are only updated when the thingspeak channel is updated.
  By default, it updates every 10 mins. Therefore, it only executes commands every 10 mins.
  This can be easily changed by changing the thingspeakPostInterval.


*/


#include <avr/dtostrf.h>
#include "ArduinoLowPower.h"


const char apn[] = "online.telia.se";  //"m2m64.com.attz";
const char simPIN[] = "";   //If the sim card needs a PIN, this will be used.
/*
  Network profile ID, see AT Manual Appendix C.4:


  C.4  SARA-R410M-02B Americas MNO profile tables
  Network     Profile ID
  --------     ----------
  Undefined     0
  AT&T          2
  Verizon       3(won't register unless device is approved by Verizon)
  T-mobile US   5
  Sprint        8
  Telus         21
*/
const char networkProfile[] = "0";  //Network profile ID


const char thingspeakKey[] = "xxxx";      //Thingspeak api write key
const char thingspeakReadKey[] = "xxxx";  //Thingspeak api read key
const char thingspeakChannelId[] = "xxxx";
int thingspeakCommandField = 5;  //Thingspeak field used to read commands
bool mcuLowPower = false;        //If true, the Microcontroller will be put into sleep after each post.


const char thingspeakUrl[] = "api.thingspeak.com";
const unsigned int thingspeakPort = 80;  //80 or 443 for TLS
const unsigned long thingspeakPostInterval = 600000;

//SensorRead variables below ------------------------------------
const int sensorPin = A0;
const int sampleInterval = 1000; // Sample interval in milliseconds
const int numSamples = 10; // Number of samples to take (5 minutes * 60 seconds / sampleInterval)
int meanValue = 0;

//System variables below ------------------------------------


unsigned long lastThingspeakPostTime = 0;
unsigned long commandStartTime = 0;
unsigned long delayStartTime = 0;
unsigned long postStartTime = 0;


int resetCount = 0;
int responsePosition = 0;
int sendCommandStage = 0;
int stage = 0;
int timeoutCount = 0;
int socket = -1;
int socketErrorCount = 0;
int httpErrorCount = 0;
int loopCount = 0;
int rsrq_dB = 0;
int dnsCheckPeriod = 0;
int dnsCheckCount = 0;
int consumeResponseChars = 0;


bool modemSetupFlag = false;
bool thingspeakSuccessFlag = false;
bool modemHardResetAndPowerOnPending = true;
bool modemReset = false;
bool modemSoftResetPending = false;
bool responseError = false;
bool thingspeakCommand = false;
bool continueResponse = false;
bool smsPending = false;


char response[2000];
char formattedCommand[500];
char formattedHttp[500];
char thingspeakIp[16];
char socketCloseIndicator[11];


void setup() {


  pinMode(SARA_PWR_ON, INPUT);
  pinMode(SARA_RESETN, INPUT);


  Serial.begin(115200);
  SerialSARA.begin(115200);


  dnsCheckPeriod = round(3600000 / thingspeakPostInterval);
  if (dnsCheckPeriod < 1) {
    dnsCheckPeriod = 1;
  }
  dnsCheckCount = dnsCheckPeriod + 1;
}


void loop() {
  if (timeoutCount > 20) {
    timeoutCount = 0;
    resetCount++;
    modemReset = true;
    modemHardResetAndPowerOnPending = true;
  }
  if (modemHardResetAndPowerOnPending) {
    modemHardResetAndPowerOn();
  }
  if (responsePosition > 1999) {
    Serial.print(response);
    memset(response, 0, sizeof(response));
    responsePosition = 0;
  }
  if (SerialSARA.available()) {
    response[responsePosition] = SerialSARA.read();
    if (consumeResponseChars > 0) {
      consumeResponseChars--;
    } else {
      responsePosition++;
    }
  }
  if (!modemSetupFlag && !modemHardResetAndPowerOnPending) {
    modemSetup();
  }
  if (!mcuLowPower && millis() - lastThingspeakPostTime > thingspeakPostInterval) {
    thingspeakSuccessFlag = false;
    lastThingspeakPostTime = millis();
  }
  if (!thingspeakSuccessFlag && modemSetupFlag && !modemHardResetAndPowerOnPending) {
    rawHttpPost();
  }
}


void sendCommand(const char command[500], unsigned long timeout, bool acceptError) {


  if (sendCommandStage == 0) {
    if (!continueResponse) {
      memset(response, 0, sizeof(response));
      responsePosition = 0;
    } else {
      continueResponse = false;
    }
    Serial.println(command);
    SerialSARA.println(command);
    responseError = false;
    commandStartTime = millis();
    sendCommandStage++;
    loopCount = 0;
  }
  if (sendCommandStage == 1) {
    loopCount++;
    if (loopCount > 1000) {
      loopCount = 0;
      if (strstr(response, "\nOK\r") != NULL || strstr(response, "\r\n@") != NULL) {
        delayStartTime = millis();
        sendCommandStage++;
      }
      if (strstr(response, "\nERROR\r") != NULL || strstr(response, "+CME ERROR") != NULL) {
        responseError = true;
        delayStartTime = millis();
        sendCommandStage++;
      }
    }
    if (millis() - commandStartTime > timeout) {
      Serial.println(response);
      Serial.println("Response timeout");
      sendCommandStage = 0;
      stage = 0;
      timeoutCount++;
    }
  }
  if (sendCommandStage == 2) {
    if (millis() - delayStartTime > 25) {
      Serial.println(response);
      timeoutCount = 0;
      sendCommandStage = 0;
      if (responseError && !acceptError) {
        stage = 0;
      } else {
        stage++;
      }
    }
  }
}


void modemHardResetAndPowerOn() {
  if (stage == 0) {
    modemSetupFlag = false;
    if (modemReset) {
      Serial.println("Modem resetting...");
      pinMode(SARA_RESETN, OUTPUT);
      digitalWrite(SARA_RESETN, HIGH);  //Logic high on Arduino pin = logic low on SARA R4
      delayStartTime = millis();
      stage++;
    } else {
      stage = 2;
    }
  }
  if (stage == 1) {
    if (millis() - delayStartTime > 12000) {
      pinMode(SARA_RESETN, INPUT);
      delayStartTime = millis();
      stage++;
    }
  }
  if (stage == 2) {
    if (millis() - delayStartTime > 500) {
      pinMode(SARA_PWR_ON, OUTPUT);
      digitalWrite(SARA_PWR_ON, HIGH);  //Logic high on Arduino pin = logic low on SARA R4
      delayStartTime = millis();
      stage++;
    }
  }
  if (stage == 3) {
    if (millis() - delayStartTime > 500) {
      pinMode(SARA_PWR_ON, INPUT);
      Serial.println("Modem on");
      delayStartTime = millis();
      stage++;
    }
  }
  if (stage == 4) {
    if (millis() - delayStartTime > 5000) {
      Serial.println("Reset and/or Power On complete");
      stage = 0;
      modemReset = false;
      modemHardResetAndPowerOnPending = false;
    }
  }
}


void modemSetup() {


  if (stage == 0) {
    sendCommand("ATV1", 1000, true);
  }
  if (stage == 1) {
    sendCommand("AT+IPR=115200", 1000, true);  //Baud rate set
  }
  if (stage == 2) {
    sendCommand("ATE0", 1000, true);  //Echo off
  }
  if (stage == 3) {
    sendCommand("AT+CMEE=2", 10000, true);
  }
  if (stage == 4) {
    sendCommand("AT+CPIN?", 10000, true);
  }
  if (stage == 5) {
    if (strstr(response, "SIM PIN") != NULL) {
      sprintf(formattedCommand, "AT+CPIN=\"%s\"", simPIN);
      stage++;
    } else {
      stage = 7;
    }
  }
  if (stage == 6) {
    sendCommand(formattedCommand, 10000, true);
  }
  if (stage == 7) {
    if (modemSoftResetPending == true) {
      sendCommand("AT+CFUN=15", 180000, true);
    } else {
      stage = 10;
    }
  }
  if (stage == 8) {
    delayStartTime = millis();
    stage++;
  }
  if (stage == 9) {
    if (millis() - delayStartTime > 5000) {
      modemSoftResetPending = false;
      stage = 0;
    }
  }
  if (stage == 10) {
    sendCommand("AT+UMNOPROF?", 1000, true);
  }
  if (stage == 11) {
    if (strstr(response, networkProfile) == NULL) {
      stage++;
    } else {
      stage = 16;
    }
  }
  if (stage == 12) {
    sendCommand("AT+CFUN=0", 180000, true);
  }
  if (stage == 13) {
    sprintf(formattedCommand, "AT+UMNOPROF=%s", networkProfile);
    stage++;
  }
  if (stage == 14) {
    sendCommand(formattedCommand, 1000, true);
  }
  if (stage == 15) {
    modemSoftResetPending = true;
    stage = 7;
  }
  if (stage == 16) {
    sendCommand("AT+CGDCONT?", 1000, true);
  }
  if (stage == 17) {
    if (strstr(response, apn) == NULL || strstr(response, "IP") == NULL) {
      stage++;
    } else {
      stage = 22;
    }
  }
  if (stage == 18) {
    sendCommand("AT+CFUN=0", 180000, true);
  }
  if (stage == 19) {
    sprintf(formattedCommand, "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
    stage++;
  }
  if (stage == 20) {
    sendCommand(formattedCommand, 1000, true);
  }
  if (stage == 21) {
    modemSoftResetPending = true;
    stage = 7;
  }
  if (stage == 22) {
    sendCommand("AT+CPSMS?", 1000, true);
  }
  if (stage == 23) {
    if (strstr(response, "+CPSMS:0") == NULL) {
      stage++;
    } else {
      stage = 27;
    }
  }
  if (stage == 24) {
    sendCommand("AT+CFUN=0", 180000, true);
  }
  if (stage == 25) {
    sendCommand("AT+CPSMS=0", 10000, true);
  }
  if (stage == 26) {
    modemSoftResetPending = true;
    stage = 7;
  }
  if (stage == 27) {
    sendCommand("AT+COPS=0", 10000, true);
  }
  if (stage == 28) {
    sendCommand("AT+CEREG=0", 1000, true);
  }
  if (stage == 29) {
    sendCommand("AT+USOCLCFG=1", 1000, true);
  }
  if (stage == 30) {
    sendCommand("AT+UPSV=4", 1000, true);
  }
  if (stage == 31) {
    modemSetupFlag = true;
    stage = 0;
  }
}


void rawHttpPost() {
  if (stage == -1) {
    postStartTime = millis();
    stage++;
  }
  if (stage == 0) {
    sendCommand("ATE0", 1000, true);
  }
  if (stage == 1) {
    sendCommand("AT+CEREG?", 1000, false);
  }
  if (stage == 2) {
    if (strstr(response, ",1") != NULL || strstr(response, ",5") != NULL) {
      stage++;
      Serial.println("Registered");
    } else {
      if (millis() - commandStartTime > 500) {
        stage--;
      }
    }
  }
  if (stage == 3) {
    sendCommand("AT+CGATT?", 1000, false);
  }
  if (stage == 4) {
    if (strstr(response, "+CGATT: 1") != NULL) {
      stage++;
      Serial.println("Attached");
    } else {
      if (millis() - commandStartTime > 500) {
        stage--;
      }
    }
  }
  if (stage == 5) {
    sendCommand("AT+CGACT?", 1000, false);
  }
  if (stage == 6) {
    if (strstr(response, ",1") != NULL) {
      stage++;
      Serial.println("Context Activated");
    } else {
      if (millis() - commandStartTime > 500) {
        stage--;
      }
    }
  }
  if (stage == 7) {
    sendCommand("AT+CESQ", 1000, false);
  }
  if (stage == 8) {
    char *responsePointer;
    int rsrq = 0;


    responsePointer = strstr(response, "+CESQ:");
    if (responsePointer != NULL) {
      int rsrq1 = responsePointer[21] - '0';
      if (responsePointer[22] != ',') {
        int rsrq2 = responsePointer[22] - '0';
        rsrq = (rsrq1 * 10) + rsrq2;
        if (responsePointer[23] != ',') {
          rsrq = 0;
        }
      } else {
        rsrq = rsrq1;
      }
      rsrq_dB = -20 + (.5 * rsrq);


      Serial.print("Signal RSRQ:");
      Serial.print(rsrq_dB);
      Serial.println("dB (closer to zero is better)");
      socket = 0;
      stage++;
    } else {
      stage = 0;
    }
  }
  if (stage == 9) {
    sprintf(formattedCommand, "AT+USOCL=%i,1", socket);
    stage++;
  }
  if (stage == 10) {
    sendCommand(formattedCommand, 150000, true);
  }
  if (stage == 11) {
    socket++;
    if (socket <= 6) {
      stage = stage - 2;
    } else {
      socket = -1;
      stage++;
    }
  }
  if (stage == 12) {
    if (dnsCheckCount >= dnsCheckPeriod) {
      sprintf(formattedCommand, "AT+UDNSRN=0,\"%s\"", thingspeakUrl);
      stage++;
    } else {
      dnsCheckCount++;
      stage = stage + 3;
    }
  }
  if (stage == 13) {
    sendCommand(formattedCommand, 150000, true);
  }
  if (stage == 14) {
    if (responseError) {
      socketErrorCount++;
      stage = 0;
      if (socketErrorCount > 10) {
        modemSetupFlag = false;
        modemSoftResetPending = true;
        socketErrorCount = 0;
      }
    } else {
      socketErrorCount = 0;
      char *responsePointer;
      int pointerPosition = 10;


      responsePointer = strstr(response, "+UDNSRN:");
      if (responsePointer != NULL) {
        int thingspeakIpPosition = 0;
        while (responsePointer[pointerPosition] != '"' && thingspeakIpPosition <= 14) {
          thingspeakIp[thingspeakIpPosition] = responsePointer[pointerPosition];
          thingspeakIpPosition++;
          pointerPosition++;
        }
        thingspeakIp[thingspeakIpPosition] = '\0';
        dnsCheckCount = 0;
        stage++;
      } else {
        stage = 0;
      }
    }
  }
  if (stage == 15) {
    sendCommand("AT+USOCR=6", 120000, true);
  }
  if (stage == 16) {
    if (responseError) {
      modemSetupFlag = false;
      modemSoftResetPending = true;
      stage = 0;
    } else {
      char *responsePointer;


      responsePointer = strstr(response, "+USOCR:");
      if (responsePointer != NULL) {
        socket = responsePointer[8] - '0';
        int securitySetting = 0;
        if (thingspeakPort == 443) {
          securitySetting = 1;
        }
        sprintf(formattedCommand, "AT+USOSEC=%i,%i", socket, securitySetting);
        sprintf(socketCloseIndicator, "+UUSOCL: %i", socket);
        stage++;
      } else {
        stage = 0;
      }
    }
  }
  if (stage == 17) {
    sendCommand(formattedCommand, 1000, false);
  }
  if (stage == 18) {
    sprintf(formattedCommand, "AT+USOCO=%i,\"%s\",%i", socket, thingspeakIp, thingspeakPort);
    stage++;
  }
  if (stage == 19) {
    sendCommand(formattedCommand, 150000, true);
  }
  if (stage == 20) {
    if (responseError) {
      socketErrorCount++;
      stage = 0;
      if (socketErrorCount > 10) {
        modemSetupFlag = false;
        modemSoftResetPending = true;
        socketErrorCount = 0;
      }
    } else {
      socketErrorCount = 0;
      stage++;
    }
  }
  if (stage == 21) {
    char httpBody[500];
    int httpBodySize = 0;
    int formattedHttpSize = 0;


    if (thingspeakCommand) {
      formattedHttpSize = sprintf(formattedHttp, "GET /channels/%s/fields/field%i/last?api_key=%s&headers=false HTTP/1.1\r\n"
                                                 "Host: %s\r\n\r\n",
                                  thingspeakChannelId, thingspeakCommandField, thingspeakReadKey, thingspeakUrl);
    } else {
    int data = meanValue;
      float batVoltage = analogRead(ADC_BATTERY) * 3.3 / 1023.0 * 1.275;  //1.275 is derived from ADC_BATTERY voltage divider
      char batVolatgeCharArray[6];
      dtostrf(batVoltage, 0, 3, batVolatgeCharArray);


      httpBodySize = sprintf(httpBody, "headers=false&api_key=%s&field1=%s&field2=%i&field3=%i&field4=%i", thingspeakKey, batVolatgeCharArray, meanValue, resetCount, rsrq_dB);


      formattedHttpSize = sprintf(formattedHttp, "POST /update HTTP/1.1\r\n"
                                                 "Host: %s\r\n"
                                                 "Content-Type: application/x-www-form-urlencoded\r\n"
                                                 "Content-Length: %i\r\n\r\n"
                                                 "%s",
                                  thingspeakUrl, httpBodySize, httpBody);
    }


    sprintf(formattedCommand, "AT+USOWR=%i,%i", socket, formattedHttpSize);
    stage++;
  }
  if (stage == 22) {
    sendCommand(formattedCommand, 120000, false);
  }
  if (stage == 23) {
    if (millis() - delayStartTime > 50) {
      stage++;
    }
  }
  if (stage == 24) {
    sendCommand(formattedHttp, 120000, false);
  }
  if (stage == 25) {
    char *responsePointer;
    responsePointer = strstr(response, "+USOWR:");
    if (responsePointer != NULL) {
      if (responsePointer[10] - '0' != 0) {
        loopCount = 0;
        stage++;
      } else {
        stage = 0;
      }
    } else {
      stage = 0;
    }
  }
  if (stage == 26) {
    loopCount++;
    if (loopCount > 1000) {
      loopCount = 0;
      char *responsePointer;
      responsePointer = strstr(response, "+UUSORD:");
      if (responsePointer != NULL) {
        delayStartTime = millis();
        sprintf(formattedCommand, "AT+USORD=%i,1024", socket);
        Serial.println(responsePointer);
        stage++;
      }
      responsePointer = strstr(response, socketCloseIndicator);
      if (responsePointer != NULL) {
        Serial.println(responsePointer);
        stage = 0;
      }
    }


    if (millis() - delayStartTime > 90000) {
      stage = 0;
    }
  }
  if (stage == 27) {
    if (millis() - delayStartTime > 500) {
      stage++;
    }
  }
  if (stage == 28) {
    sendCommand(formattedCommand, 5000, false);
  }
  if (stage == 29) {
    if (strstr(response, "+UUSORD:") != NULL) {
      char *responsePointer;
      char *endPointer;


      responsePointer = strrchr(response, '"');
      endPointer = strrchr(response, '\n');
      responsePosition = responsePosition - ((endPointer - responsePointer) + 1);
      consumeResponseChars = 19;
      continueResponse = true;
      stage = stage - 1;
    } else {
      if (strstr(response, "HTTP/1.1 200") != NULL || strstr(response, "HTTP/1.1 201") != NULL) {
        char *responsePointer;
        char *endPointer;
        int contentLength = 0;
        int receivedContentLength = 0;
        int ones;
        int tens;
        int hundreds;
        int thousands;


        responsePointer = strstr(response, "Content-Length");


        if (responsePointer == NULL) {
          responsePointer = strstr(response, "content-length");  //some server don't Captitalize
        }


        if (responsePointer != NULL) {
          thousands = responsePointer[16] - '0';
          if (responsePointer[17] != '\r') {
            hundreds = responsePointer[17] - '0';
            if (responsePointer[18] != '\r') {
              tens = responsePointer[18] - '0';
              if (responsePointer[19] != '\r') {
                ones = responsePointer[19] - '0';
                contentLength = (1000 * thousands) + (100 * hundreds) + (10 * tens) + ones;
              } else {
                contentLength = (100 * thousands) + (10 * hundreds) + tens;
              }
            } else {
              contentLength = (10 * thousands) + hundreds;
            }
          } else {
            contentLength = thousands;
          }
          Serial.print("Http Header Content Length:");
          Serial.println(contentLength);


          responsePointer = strstr(responsePointer, "\r\n\r\n");


          if (responsePointer != NULL) {
            endPointer = strrchr(responsePointer, '"');
            if (endPointer != NULL) {
              receivedContentLength = endPointer - responsePointer - 4;
              Serial.print("Received Content Length:");
              Serial.println(receivedContentLength);
              if (contentLength == receivedContentLength) {
                if (!thingspeakCommand) {
                  thingspeakCommand = true;
                  stage = 21;
                  httpErrorCount = 0;
                } else {
                  stage++;
                  httpErrorCount = 0;
                  thingspeakCommand = false;
                  if (strstr(responsePointer, "ledon") != NULL) {
                    digitalWrite(LED_BUILTIN, HIGH);
                  }
                  if (strstr(responsePointer, "ledoff") != NULL) {
                    digitalWrite(LED_BUILTIN, LOW);
                  }
                }
              } else {
                stage = 0;
              }
            } else {
              stage = 0;
            }
          } else {
            stage = 0;
          }
        } else {
          stage = 0;
        }
      } else {
        httpErrorCount++;
        stage = 0;
        if (httpErrorCount > 10) {
          stage = 0;
          modemSetupFlag = false;
          modemSoftResetPending = true;
          httpErrorCount = 0;
        }
      }
    }
  }
  if (stage == 30) {
    sprintf(formattedCommand, "AT+USOCL=%i,1", socket);
    stage++;
  }
  if (stage == 31) {
    sendCommand(formattedCommand, 150000, true);
  }

  if (stage == 32) {
    int sum = 0;
    for (int i = 0; i < numSamples; i++) {
      int sensorValue = analogRead(A0);
      sum += sensorValue;
      delay(sampleInterval);
    }
    meanValue = sum / numSamples;
    Serial.print("Mean value: ");
    Serial.println(meanValue);
    stage++;
  }

  if (stage == 33) {
    socket = -1;
    if (mcuLowPower) {
      stage = -1;
      unsigned long postDuration = millis() - postStartTime;
      while (postDuration > thingspeakPostInterval) {
        postDuration = postDuration - thingspeakPostInterval;
      }
      Serial.println("MCU sleep until next update...");
      LowPower.sleep(thingspeakPostInterval - postDuration);
    } else {
      thingspeakSuccessFlag = true;
      stage = 0;
    }
  }
}

This is just the modem waking up from PSM(Power save mode). After a certain amount of AT terminal inactivity, the modem enters PSM. All it takes to wake the modem up is AT terminal activity. There is a response timeout because the first command is used to just wake the modem up.

The location of you sensor code may not be ideal because it will update your sensor variable at the end of the rawHttpPost(). This means you data will always be as old as the thingspeakPostInterval.

I read sensor data in the main loop(). You could use millis() or a loop counter to keep the sensor reading from slowing down the main loop. I used a loop counter. This is in the main loop() for my application:

  if (mainLoopCount > 5000) {
    for (int i = 0; i < 10; i++) {
      waterFlowRateSum = waterFlowRateSum + analogRead(waterFlowPin);
    }
    waterFlowRate = (log(waterFlowRateSum * 5 / 1023.0 / 10) * 4.355) + 3.299;
    waterFlowRateSum = 0;
    mainLoopCount = 0;
  }

  mainLoopCount++;

Thank you so much!

I have been stress testing the code every 2 min and it seems to work perfect.
i have added you code in to the void loop() and some global int.

//Variables below for sensor reading
int waterFlowPin = A1;
int mainLoopCount = 0;
int waterFlowRateSum = 0;
int waterFlowRate = 0;
//end sensor reading
void loop() {
  if (timeoutCount > 20) {
    timeoutCount = 0;
    resetCount++;
    modemReset = true;
    modemHardResetAndPowerOnPending = true;
  }
  if (modemHardResetAndPowerOnPending) {
    modemHardResetAndPowerOn();
  }
  if (responsePosition > 1999) {
    Serial.print(response);
    memset(response, 0, sizeof(response));
    responsePosition = 0;
  }
  if (SerialSARA.available()) {
    response[responsePosition] = SerialSARA.read();
    if (consumeResponseChars > 0) {
      consumeResponseChars--;
    } else {
      responsePosition++;
    }
  }
  if (!modemSetupFlag && !modemHardResetAndPowerOnPending) {
    modemSetup();
  }
  if (!mcuLowPower && millis() - lastThingspeakPostTime > thingspeakPostInterval) {
    thingspeakSuccessFlag = false;
    lastThingspeakPostTime = millis();
  }
  if (mainLoopCount > 5000) {
    for (int i = 0; i < 10; i++) {
      waterFlowRateSum = waterFlowRateSum + analogRead(waterFlowPin);
    }
    waterFlowRate = (log(waterFlowRateSum * 5 / 1023.0 / 10) * 4.355) + 3.299;
    waterFlowRateSum = 0;
    mainLoopCount = 0;
  }
  mainLoopCount++;
  if (!thingspeakSuccessFlag && modemSetupFlag && !modemHardResetAndPowerOnPending) {
    rawHttpPost();
  }
}

I'm working on code specific for my water level sensor but the sensor havn't arrived yet so no testing until then :slight_smile:

@zbelding
What i'm missing. I want field 2 to have decimals but once i change int data to float data the posts on thingspeak goes from 1 to 543456231. 9 digits long??

I have tried the dtostrf function without success :frowning:

when i change in the code so
float batVoltage = ADCVoltage; instead of
float batVoltage = analogRead(ADC_BATTERY) * 3.3 / 1023.0 * 1.275;

My thingspeak channel start to get decimal values from my ADCVoltage.

So it must be somewhere in the code that makes field 2,3,4 post without decimals.

float batVoltage = ADCVoltage;//analogRead(ADC_BATTERY) * 3.3 / 1023.0 * 1.275;  //1.275 is derived from ADC_BATTERY voltage divider

you got to love AI github copilot :smiley:

it found the solution for me.

it changed to code
&field2=%i
to
&field2=%s
in the httpbody

Solution

float data = ADCVoltage;
char dataCharArray[6];
dtostrf(data, 0, 2, dataCharArray);

httpBodySize = sprintf(httpBody, "headers=false&api_key=%s&field1=%s&field2=%s&field3=%i&field4=%i", thingspeakKey, batVolatgeCharArray, dataCharArray, resetCount, rsrq_dB);

Some references for sprintf format parameters:

https://cplusplus.com/reference/cstdio/sprintf/?kw=sprintf

https://cplusplus.com/reference/cstdio/printf/

Thank you, Zbleding!

I'm making good progress on the water level sensor now. I finally received the sensor from China, and it's very accurate, with an accuracy of approximately +/- 5mm, or even closer to +/- 2mm, depending on the quality of the resistors and the values I use in the code.

I'm using an MKR Relay Proto Shield to control the step-up regulator, which powers the 12v water sensor. I chose to use the relay shield because the step-up regulator and sensor together draw 26mA. To extend the battery life, I need to turn them off when not in use.

One important discovery I made is that I can't drive the step-up regulator directly from the 5v step-down regulator. When I attempted this, the MKR NB 1500 board kept resetting. Therefore, I had to separate the step-up regulator from the 5v step-down regulator, and I'll power it directly from the batteries.

With this code, the MKR NB 1500 board consumes only 9mA in sleep mode.

In a few weeks, I'll also be adding a 10W solar panel and a 6000mAh battery pack. I'm hopeful that this setup will provide around 18 days of battery life even without sunlight.

Certainly! To integrate your water level sensor readings into the existing code for posting to ThingSpeak, you need to find the appropriate place in the code to read the sensor data. Here's a basic outline of how you might modify the code:

#include <MKRNB.h>

// Add any necessary libraries for your water pressure sensor

// Pin configuration for voltage divider
const int sensorPin = A0;  // Analog input pin where your sensor is connected
const float voltageDividerRatio = 0.5;  // Adjust this ratio based on your voltage divider setup

// ThingSpeak settings
const char *server = "api.thingspeak.com";
const char *api_key = "YOUR_THINGSPEAK_API_KEY";
const char *field = "field1";

void setup() {
  Serial.begin(9600);
  delay(1500);
  NB.begin();
}

void loop() {
  // Read water level from sensor
  float sensorVoltage = analogRead(sensorPin) * (5.0 / 1023.0);  // Convert analog reading to voltage
  float waterLevel = sensorVoltage * voltageDividerRatio;  // Convert voltage to water level

  // Print the water level for debugging
  Serial.print("Water Level: ");
  Serial.println(waterLevel);

  // Delay for 1 minute
  delay(60000);

  // Send data to ThingSpeak every 10 minutes
  if (millis() % 600000 == 0) {
    // Format the data
    String data = String(field) + "=" + String(waterLevel);

    // Send data to ThingSpeak
    sendToThingSpeak(data);

    // Print a message for debugging
    Serial.println("Data sent to ThingSpeak");
  }
}

void sendToThingSpeak(String data) {
  String postStr = "api_key=" + String(api_key) + "&" + data;

  // Prepare the request
  NBClient client;
  if (client.connect(server, 80)) {
    client.println("POST /update HTTP/1.1");
    client.println("Host: " + String(server));
    client.println("Connection: close");
    client.println("Content-Type: application/x-www-form-urlencoded");
    client.print("Content-Length: ");
    client.println(postStr.length());
    client.println();
    client.print(postStr);

    // Wait for the server to respond
    while (!client.available()) {
      delay(10);
    }

    // Print the response from ThingSpeak for debugging
    while (client.available()) {
      char c = client.read();
      Serial.print(c);
    }
    Serial.println();

    // Close the connection
    client.stop();
  }
}

This modified code adds a section in the loop() function to read the water level from your sensor and print it for debugging purposes. Adjust the sensorPin and voltageDividerRatio variables based on your sensor and voltage divider configuration. The water level is then sent to ThingSpeak every 10 minutes, along with the existing data posting logic.

Remember to replace "YOUR_THINGSPEAK_API_KEY" with your actual ThingSpeak API key. Moreover,you can ask ChatGPT.

Make sure to test this code thoroughly to ensure it works with your specific sensor and setup.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.