Firmwire over the air

Hello

I am using a SODAQ Sara 412 AFF board. The ublox chip can be updatedd over the air, using proper AT commands.

However, Id like to know, if there are ways to implemented my software, that runs on the SAMD21 chip, (takes gps updates and saves in an SD card) over the air as well

Thank you.

Hi , i think you can use the arduinoota library as written in this post:https://forum.arduino.cc/index.php?topic=664694.0
is in italian but there is a sample sketch that show how it work.it’s the same samd chip so it should work.
if it work it will be usefull an update because i’m doing the same with arduino hardware but i will switch to sodaq for industrial version of my concept

Thank you.
i have tried to use the SDU library.

But the SDU libray fails with unknown chip error.
So I tried to force

  #include "boot/mzero.h"

and

  #include "boot/zero.h"

Neither works - what can be done in this case?

So, here is the update.

I have tested and tested with all possible combinations in the SDU.cpp file.

I had success with MKR1010.h - but only 2 times out of 26 tests.

What can be done.

How does the Community do OTA Updates?

Hi

You can use the WiFi101OTA library with some modifications to implement OTA for SAMD21.

in order to do that, i need to use the SDU library.
The SDU library complains :

“error unsupported board”

I rebuild the boot/xxxxxx.h files, using the build.sh file under extras. Unfortunately, I still dont see any update. Forcing the SDU library to choose ARDUINO_SAMD_ZERO , for example does not work.

Here is my Sketch :

// define Battery Sensor
#define ADC_AREF 3.3f
#define BATVOLT_R1 4.7f
#define BATVOLT_R2 10.0f
#define BATVOLT_PIN BAT_VOLT


// ##############################################
// define outputs
// ----------------------------------------------
// define SDCARD
//#include "FS.h"
#include "SD.h"
#include <SPI.h>

#define SD_CS 10
#define SD_SCLK 13
#define SD_MISO 12
#define SD_MOSI 11

// define Server

// define EEPROM
#include "FlashStorage.h"
FlashStorage(wakeup, int);
int wakeUpCnt = 0;


// ----------------------------------------------


// ##############################################
// define bridge / conns
// ----------------------------------------------
// define LTE
#include <Arduino.h>
#include "Sodaq_R4X.h"
#include "Sodaq_wdt.h"

#define powerPin SARA_ENABLE
#define enablePin SARA_TX_ENABLE

static Sodaq_R4X r4x;
static Sodaq_SARA_R4XX_OnOff saraR4xxOnOff;

bool modemstat = false;
uint32_t getNow();
const int urat = 7;                                                    // LTE Cat 1 dual mode not possible
const int cid = 1 ;                                                   // cell id set to 1
const int mnoProfile = 31;                                                // Deutsche Telekom
const int bandmask = 524420;
const int bandmaskRATind = 0;
const char * FOperator = "20404";

const char* APN = "";
const char* gprsUser = "";
const char* gprsPass = "";

#define MODEMBAUD 115200

#define R4XX // Uncomment when you use the ublox R4XX module

/* SODAQ SARA */
#define DEBUG_STREAM SerialUSB
#define MODEM_STREAM Serial1
#define powerPin SARA_ENABLE
#define enablePin SARA_TX_ENABLE

#define NETWORK_STATUS_GPIO_ID     16



#define TINY_GSM_MODEM_SARAR4                                             // Modem is SARA R4
#define TINY_GSM_RX_BUFFER   2048                                           // Set RX buffer to 1Kb
#include <TinyGsmClient.h>

#define TINY_GSM_DEBUG
#define TINY_GSM_TEST_GPRS    true
#define TINY_GSM_TEST_GPS     true
#define TINY_GSM_POWERDOWN    true
TinyGsm modemGSM(MODEM_STREAM);

TinyGsmClient client(modemGSM);
int uploadedFlag = 0;


// define server
const char server[] = "78.47.186.254";                                          // domain name: example.com, maker.ifttt.com, etc
const char authURL[] = "/auth";                                             // resource path, for example: /post-data.php
const char sodaqUploadPath[] = "/upload";                                         // new function for sodaq upload, does not touch the other one
const int  port = 5504;


// define fallback
// ----------------------------------------------

#define ARDUINO_SAMD_MKR1000

#include "SDU.h"

#if defined(ARDUINO_SAMD_ZERO)
  #define BOARD "arduino_zero_edbg"
#elif defined(ARDUINO_SAMD_MKR1000)
  #define BOARD "mkr1000"
#elif defined(ARDUINO_SAMD_MKRZERO)
  #define BOARD "mkrzero"
#else
  #error "Unsupported board!"
#endif

#define BOARD_LENGTH (sizeof(BOARD) - 1)


const uint32_t RESPONSE_TIMEOUT_MS = 5000;
const int updatePort = 4402;

// ##############################################
// define controls


#include "LedColor.h"
int wakeupCause = 0;
int debuglevel = 0;
#define CONSOLE_STREAM SerialUSB

#define CONSOLE_BAUD 115200

static uint8_t lastResetCause;


#define PROJECT_NAME "SODAQ - GPS Tracker Webaro"
#define VERSION "1.8"
#define STARTUP_DELAY 5000

bool uploadAborted;

int wpCounter = 1;

/*
 * 
 *  debuglevel = 0, say nothing
 *  debuglevel = 1, draw
 *  debuglevel = 2, text
 */
// ----------------------------------------------
// define dev configs

String devID = "GPS320024";
String fileID = "";
String authKEY = "uaVZHB67r55BfgjkikhFUZ64489533g6311GRDI908740HDWWlaykdtw2446jdsg";




// ##############################################
// define GSM functions




// ----------------------------------------------


// ##############################################


// ----------------------------------------------


// ##############################################
// define SERVER-CLIENT operation functions



// ----------------------------------------------



// ##############################################
// define EEPROM functions


void activate_EEPROM() {
  
  wakeUpCnt = wakeup.read();
  ++wakeUpCnt ;                                                   SerialUSB.print("Wake up counter at: ");SerialUSB.println(wakeUpCnt);
  
  wakeup.write(wakeUpCnt);
  
  
}

// ----------------------------------------------


// ##############################################
// define SDC  functions


void init_SDC() {
  
  Sd2Card card;
  SdVolume volume;
  SdFile root;
  
  SerialUSB.print("\nInitializing SD card...");
  // we'll use the initialization code from the utility libraries
  // since we're just testing if the card is working!
  
  if (!card.init(SPI_HALF_SPEED, SD_CS)) {                                      SerialUSB.println("initialization failed. Things to check:");
    SerialUSB.println("* is a card inserted?");
    SerialUSB.println("* is your wiring correct?");
    SerialUSB.println("* did you change the chipSelect pin to match your shield or module?");
    
    while (1);
  } else {                                                      SerialUSB.println("Wiring is correct and a card is present.");
  }
  
  // print the type of card
  SerialUSB.println();
  SerialUSB.print("Card type:         ");
  
  switch (card.type()) {
    case SD_CARD_TYPE_SD1:                                              SerialUSB.println("SD1");
    break;
    case SD_CARD_TYPE_SD2:                                              SerialUSB.println("SD2");
    break;
    case SD_CARD_TYPE_SDHC:                                             SerialUSB.println("SDHC");
    break;
    default:                                                    SerialUSB.println("Unknown");
  }
  
  // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
  
  while (!volume.init(card)) {                                              SerialUSB.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
    
  }
  
  SerialUSB.print("Clusters:          ");
  SerialUSB.println(volume.clusterCount());
  SerialUSB.print("Blocks x Cluster:  ");
  SerialUSB.println(volume.blocksPerCluster());
  SerialUSB.print("Total Blocks:      ");
  SerialUSB.println(volume.blocksPerCluster() * volume.clusterCount());
  SerialUSB.println();
  
  // print the type and size of the first FAT-type volume
  
  uint32_t volumesize;                                                SerialUSB.print("Volume type is:    FAT");
  SerialUSB.println(volume.fatType(), DEC);
  
  volumesize = volume.blocksPerCluster();                                         // clusters are collections of blocks
  volumesize *= volume.clusterCount();                                            // we'll have a lot of clusters
  volumesize /= 2;                                                                // SD card blocks are always 512 bytes (2 blocks are 1KB)
  
  SerialUSB.print("Volume size (Kb):  ");
  SerialUSB.println(volumesize);
  
  
  
}


// ----------------------------------------------

// ##############################################
// define ACCEL  functions



bool initialize_wakeUp() { return true;}

// ----------------------------------------------


// ##############################################
// define SENSOR  functions



// ----------------------------------------------



// ##############################################
// define GPS  functions



// ----------------------------------------------


// ##############################################
// define timer  functions

long lastTickTime;
long sleepTime = 600;

// ----------------------------------------------


void setup() {
  
  CONSOLE_STREAM.begin(CONSOLE_BAUD);
  
  
  
  lastResetCause = PM->RCAUSE.reg;                                          debug2(String(lastResetCause));
  
  // In case of reset (this is probably unnecessary)
  sodaq_wdt_disable();
  
  // Setup the BOD33
  //setupBOD33();
  
  //sodaq_wdt_enable(WDT_PERIOD_8X);
  //sodaq_wdt_reset();
  
  
  
  sodaq_wdt_safe_delay(STARTUP_DELAY);
  printBootUpMessage(CONSOLE_STREAM);
  
  
  //Serial.begin(115200);                                               /** set serial output **/
  //                                                          CONSOLE_STREAM.println("waiting...");
  //while(!CONSOLE_STREAM.available());                                         // wait until get debug level
  debuglevel = 2;//CONSOLE_STREAM.parseInt();                                       // parse as integer
  CONSOLE_STREAM.println(debuglevel);
  
  // get_wakeupReason()                                               // already set via callbacks
  
  switch(wakeupCause) {                                               // branch wakeup reason
    
    case 0:                                                     // FIRST POWER UP
      activate_initialProtocol();
      break;
    case 1:                                                     // ACCELEROMETER trigger
      //activate_standardProtocol();
      break;
    case 2:                                                     // timer expired
      //activate_sporadicProtocol();
      break;
  }
  
  // shutdown_Peripherals();                                              // send unnecessary components to sleep
  // activate_sensors();
  
  
  lastTickTime = millis();
  
}


void activate_initialProtocol() {
  
 
  bool initialized_wakeUP = initialize_wakeUp();
  
  if (! initialized_wakeUP) return;
  
  setLedColor(YELLOW);
  sodaq_wdt_safe_delay(650);
  
  
  
  init_SDC();
  setLedColor(CYAN);
  sodaq_wdt_safe_delay(650);
  
  
  
  
  
  
  debug2("handling update ...");
  sodaq_wdt_safe_delay(5000);
  
}


void handleUpdate() {
  /*
  bool isValidContentType = false;
  
  const char* PATH = "http://78.47.186.254:4402/update.bin";
  const unsigned long CHECK_INTERVAL = 5000;
  
  
  
  
  if (!modemGSM.gprsConnect(APN, gprsUser, gprsPass)) {                             debug2(" fail apn");
  } else {
    if (!client.connect(server, updatePort))  {                               debug2("Cannot connect to " + String(server));
      return;
    }
  }
  
  client.print(String("GET ") + PATH + " HTTP/1.1\r\n");
  client.print(String("Host: ") + server + "\r\n");
  client.print("Connection: close\r\n\r\n");
  
  unsigned long timeout = millis();
  while (client.available() == 0) {
    if (millis() - timeout > RESPONSE_TIMEOUT_MS) {                             debug2("Client Timeout !");
      client.stop();
      return;
    }
  }
  
  if(!SD.begin(SD_CS)) {                                              debug2("SD FAILURE ::: ");
    return;
  }
  
  File file = SD.open(BIN_FILENAME, O_CREAT | O_WRITE);
  if (!file) {
    client.stop();                                              debug2("Could not create bin file. Can't continue with update.");
    return;
  }
  
  
  const uint32_t clientReadTimeout = 5000;
  uint32_t clientReadStartTime = millis();
  String headerBuffer;
  bool finishedHeader = false;
  uint32_t contentLength = 0;
  uint32_t knownFileSize = 1024; 
  uint32_t timeElapsed = millis();
  
  while (!finishedHeader) {
    int nlPos;
    
    if (client.available()) {
      clientReadStartTime = millis();
      while (client.available()) {
        char c = client.read();
        headerBuffer += c;
        
        // Uncomment the lines below to see the data coming into the buffer
        if (c < 16)
          //SerialUSB.print('0');
          //SerialUSB.print(c, HEX);
          //SerialUSB.print(' ');
          if (isprint(c))
            SerialUSB.print(reinterpret_cast<char> (c));
          else
            SerialUSB.print('*');
          SerialUSB.print(' ');
        
        // Let's exit and process if we find a new line
        if (headerBuffer.indexOf(F("\r\n")) >= 0)
          break;
      }
    }
    else {
      if (millis() - clientReadStartTime > clientReadTimeout) {
        // Time-out waiting for data from client
        SerialUSB.println(F(">>> Client Timeout !"));
        break;
      }
    }
    
    // See if we have a new line.
    nlPos = headerBuffer.indexOf(F("\r\n"));
    
    if (nlPos > 0) {
      headerBuffer.toLowerCase();
      // Check if line contains content-length
      if (headerBuffer.startsWith(F("content-length:"))) {
        contentLength = headerBuffer.substring(headerBuffer.indexOf(':') + 1).toInt();
        SerialUSB.print(F("Got Content Length: "));  // uncomment for
        SerialUSB.println(contentLength);            // confirmation
      }
      
      headerBuffer.remove(0, nlPos + 2);  // remove the line
    }
    else if (nlPos == 0) {
      // if the new line is empty (i.e. "\r\n" is at the beginning of the line), we are done with the header.
      finishedHeader = true;
    }
  }
  
  // The two cases which are not managed properly are as follows:
  // 1. The client doesn't provide data quickly enough to keep up with this loop.
  // 2. If the client data is segmented in the middle of the 'Content-Length: ' header,
  //    then that header may be missed/damaged.
  //
  
  uint32_t readLength = 0;
  
  
  if (finishedHeader && contentLength == knownFileSize) {
    SerialUSB.println(F("Reading response data"));
    clientReadStartTime = millis();
    
    printPercent(readLength, contentLength);
    while (readLength < contentLength && client.connected() && millis() - clientReadStartTime < clientReadTimeout) {
      while (client.available()) {
        uint8_t c = client.read();
        SerialUSB.print((c));  // Uncomment this to show data
        readLength++;
        if (readLength % (contentLength / 13) == 0) {
          printPercent(readLength, contentLength);
        }
        clientReadStartTime = millis();
      }
    }
    printPercent(readLength, contentLength);
  }
  
  timeElapsed = millis() - timeElapsed;
  SerialUSB.println();
  
  
  file.close();
  client.stop();
*/  
  
  #ifdef __AVR__
  wdt_enable(WDTO_15MS);
  while (true);
  #else
  NVIC_SystemReset();
  #endif
  
  
  
  //  if (contentLength && isValidContentType)  {
  // 
  //    
  //  }
  
}



void loop() {
  
  for(int i = 0; i < 10; i ++) { debug2(String(i) + "/10");
  setLedColor(GREEN);
  sodaq_wdt_safe_delay(1000);
  setLedColor(RED);
  sodaq_wdt_safe_delay(1000);
  }         debug2("handling update ");
  handleUpdate();
  
}








void setLONG_LedColor_(LedColor color) {
  
  setLedColor(color);
  sodaq_wdt_safe_delay(3000);
  
}

void debug2(String s) {
  if (debuglevel == 2){ CONSOLE_STREAM.println(s);}
}
void debug3(String f) {
  if (debuglevel == 3){ CONSOLE_STREAM.println(f);}
}


static void printBootUpMessage(Stream& stream) {
  stream.println("** " PROJECT_NAME " - " VERSION " **");
  
  stream.println();
  
  stream.print(" -> ");
  printCpuResetCause(stream);
  
  stream.println();
}


static void printCpuResetCause(Stream& stream){
  stream.print("CPU reset by");
  
  if (PM->RCAUSE.bit.SYST) {
    stream.print(" System reset request was performed, e.g. by rebooting....");
  }
  
  // Syntax error due to #define WDT in CMSIS/4.0.0-atmel/Device/ATMEL/samd21/include/samd21j18a.h
  // if (PM->RCAUSE.bit.WDT) {
  if ((PM->RCAUSE.reg & PM_RCAUSE_WDT) != 0) {
    stream.print(" Watchdog");
  }
  
  if (PM->RCAUSE.bit.EXT) {
    stream.print(" External - expect this for the accelerometer ");
  }
  
  if (PM->RCAUSE.bit.BOD33) {
    stream.print(" BOD33");
  }
  
  if (PM->RCAUSE.bit.BOD12) {
    stream.print(" BOD12");
  }
  
  if (PM->RCAUSE.bit.POR) {
    stream.print(" Power On Reset");
  }
  
  stream.print(" [");
  stream.print(PM->RCAUSE.reg);
  stream.println("]");
}




// OTHER FUNCTIONS

String split(String s, char parser, int index) {
  String rs = "";
  int parserIndex = index;
  int parserCnt = 0;
  int rFromIndex = 0, rToIndex = -1;
  while (index >= parserCnt) {
    rFromIndex = rToIndex + 1;
    rToIndex = s.indexOf(parser, rFromIndex);
    if (index == parserCnt) {
      if (rToIndex == 0 || rToIndex == -1) return "";
      return s.substring(rFromIndex, rToIndex);
    } else parserCnt++;
  }
  return rs;
}


inline String getHeaderValue(String header, String headerName)
{
  return header.substring(strlen(headerName.c_str()));
}

void printPercent(uint32_t readLength, uint32_t contentLength) {
  // If we know the total length
  if (contentLength != (uint32_t)-1) {
    SerialUSB.print("\r ");
    SerialUSB.print((100.0 * readLength) / contentLength);
    SerialUSB.print('%');
  } else {
    SerialUSB.println(readLength);
  }
}

The UPDATE.BIN file is in the SD card. The server communication is still in the sketch - but not implemented currently. The arduino compiler produced one file with bootloader, and one without. I tested both. The Chip select pin in the SDUBoot.ino has been updated.

But, the system does NOT pick up the new firmware. Please help.

Hi,

If you want SODAQ to implement the OTA service for you, please contact sales@sodaq.com
We can do projects to implement OTA services. We will then also provide a dashboard to manage all your devices.

We cannot share the source code of our OTA implementation.

Best regards,
Jan