Arduino Library Causing SOS Usage Fault?

Hey, guys!

I have an AS5600 rotary magnetic position sensor & Arduino demo library that successfully compiles in the Particle DEV IDE but when the code runs it instantly throws the SOS Usage Fault Error for some reason.

This runs fine on an Arduino but I would like the ability to use this sensor with the Particle platform also.

Since the program compiles just fine and throws no errors I have no idea what is causing the SOS message. Any help is appreciated.

Here is the demo code ASM provides for this chip on their Arduino Shield demo board they sell here: http://ams.com/eng/Support/Demoboards/Magnetic-Position-Sensors/Angle-Position-On-Axis/AS5600-POTUINO

Main Demo.ino >


/****************************************************
FILE:  AMS_5600_example

Author: Tom Denton
www.ams.com
Date: 15 Dec 2014
Version 1.00

Description:  AS5600 "Potuino" demonstration application

AMS5600 Programming Sketch
/***************************************************/

#include <Wire.h>
#include "AMS_5600.h"

String lastResponse;
String noMagnetStr = "Error: magnet not detected";

AMS_5600 ams5600;

/*******************************************************
/* function: setup
/* In: none
/* Out: none
/* Description: called by system at startup
/*******************************************************/
void setup(){
 Serial.begin(9600);
 Wire.begin();
 printMenu();
}

/*******************************************************
/* function: printMenu
/* In: none
/* Out: none
/* Description: prints menu options and result of last
/* command
/*******************************************************/
void printMenu()
{
  for(int i =0; i<20;i++)
    Serial.println();
  Serial.println("AS5600 Serial Interface Program");
  Serial.println("");
  if(lastResponse.length()>0)
  {
    Serial.println(lastResponse);
    Serial.println("");
  }
  Serial.print("1 - Set start position\t|  "); Serial.println(" 6 - get MPOS");
  Serial.print("2 - Set end position\t|  ");   Serial.println(" 7 - get raw angle");
  Serial.print("3 - Set max angle range\t|  ");  Serial.println(" 8 - get scaled angle");
  Serial.print("4 - Get max angle range\t|  ");  Serial.println(" 9 - detect magnet");
  Serial.print("5 - Get ZPOS \t\t|  ");     Serial.println("10 - get magnet strength");
  Serial.println();
  Serial.print("Number of burns remaining: "); Serial.println(String(3 - ams5600.getBurnCount()));
  Serial.println("96 - Burn Angle");
  Serial.println("98 - Burn Settings (one time)");
}

/*******************************************************
/* Function: convertRawAngleToDegrees
/* In: angle data from AMS_5600::getRawAngle
/* Out: human readable degrees as float
/* Description: takes the raw angle and calculates
/* float value in degrees.
/*******************************************************/
float convertRawAngleToDegrees(word newAngle)
{
  /* Raw data reports 0 - 4095 segments, which is 0.087 of a degree */
  float retVal = newAngle * 0.087;
  return retVal;
}

/*******************************************************
/* Function: convertScaledAngleToDegrees
/* In: angle data from AMS_5600::getScaledAngle
/* Out: human readable degrees as float
/* Description: takes the scaled angle and calculates
/* float value in degrees.
/*******************************************************/
float convertScaledAngleToDegrees(word newAngle)
{
  word startPos = ams5600.getStartPosition();
  word endPos = ams5600.getEndPosition();
  word maxAngle = ams5600.getMaxAngle();

  float multipler = 0;

  /* max angle and end position are mutually exclusive*/
  if(maxAngle >0)
  {
    if(startPos == 0)
      multipler = (maxAngle*0.0878)/4096;
    else  /*startPos is set to something*/
      multipler = ((maxAngle*0.0878)-(startPos * 0.0878))/4096;
  }
  else
  {
    if((startPos == 0) && (endPos == 0))
      multipler = 0.0878;
    else if ((startPos > 0 ) && (endPos == 0))
      multipler = ((360 * 0.0878) - (startPos * 0.0878)) / 4096;
    else if ((startPos == 0 ) && (endPos > 0))
      multipler = (endPos*0.0878) / 4096;
    else if ((startPos > 0 ) && (endPos > 0))
      multipler = ((endPos*0.0878)-(startPos * 0.0878))/ 4096;
  }
  return (newAngle * multipler);
}

/*******************************************************
/* Function: burnAngle
/* In: none
/* Out: human readable string of success or failure
/* Description: attempts to burn angle data to AMS5600
/*******************************************************/
String burnAngle()
{
  int burnResult = ams5600.burnAngle();
  String returnStr = "Brun angle error: ";

  switch (burnResult)
  {
    case 1:
      returnStr = "Brun angle success";
      break;
    case -1:
      returnStr += "no magnet detected";
      break;
    case -2:
      returnStr += "no more burns left";
      break;
    case -3:
      returnStr += "no positions set";
      break;
    default:
      returnStr += "unknown";
      break;
  }
  return returnStr;
}

/*******************************************************
/* Function: burnMaxAngleAndConfig
/* In: none
/* Out: human readable string of sucess or failure
/* Description: attempts to burn max angle and config data
/* to AMS5600
/*******************************************************/
String burnMaxAngleAndConfig()
{
  int burnResult = ams5600.burnMaxAngleAndConfig();
  String retStr = "Brun max angle and config error: ";

  switch(burnResult)
  {
    case 1:
      retStr = "Brun max angle and config success";
      break;
    case -1:
      retStr += "chip has been burned once already";
      break;
    case -2:
      retStr += "max angle less than 18 degrees";
      break;
    default:
      retStr += "unknown";
      break;
  }
  return retStr;
}

/*******************************************************
/* Function: loop
/* In: none
/* Out: none
/* Description: main program loop
/*******************************************************/
void loop()
{

  if (Serial.available() > 0)
  {
    char incomingByteBuffer[2];
    char incomingByte;

    incomingByteBuffer[0] = NULL;
    incomingByteBuffer[1] = NULL;

    Serial.readBytes(incomingByteBuffer,2);

    if ((incomingByteBuffer[0] >= 48) && (incomingByteBuffer[0] < 60))
    {
      incomingByte = incomingByteBuffer[0] - 48;
    }

    if ((incomingByteBuffer[1] >= 48) && (incomingByteBuffer[1] < 60))
    {
      incomingByte *=10;
      incomingByte += incomingByteBuffer[1] - 48;
    }


    switch (incomingByte)
    {
      case 1:
      {
        if(ams5600.detectMagnet()==1)
          lastResponse = ("Start angle set to = "+String(convertRawAngleToDegrees(ams5600.setStartPosition()), DEC));  //Print Raw Angle Value
        else
          lastResponse = noMagnetStr;
      }
      break;

      case 2:
      {
        if(ams5600.detectMagnet()==1)
          lastResponse = ("End angle set to = "+String(convertRawAngleToDegrees(ams5600.setEndPosition()), DEC));
        else
          lastResponse = noMagnetStr;
      }
      break;

      case 3:
      {
        if(ams5600.detectMagnet()==1)
          lastResponse = ("Max angle range set to = "+String(convertRawAngleToDegrees(ams5600.setMaxAngle()), DEC));
        else
          lastResponse = noMagnetStr;
      }
      break;

      case 4:
      {
        lastResponse = ("Max angle range= "+String(convertRawAngleToDegrees(ams5600.getMaxAngle()), DEC));
      }
      break;

      case 5:
      {
        lastResponse = ("Start angle = "+String(convertRawAngleToDegrees(ams5600.getStartPosition()), DEC));
      }
      break;

      case 6:
      {
        lastResponse = "End angle = " + String(convertRawAngleToDegrees(ams5600.getEndPosition()),DEC);
      }
      break;

      case 7:
      {
        lastResponse = "Raw angle = "+ String(convertRawAngleToDegrees(ams5600.getRawAngle()),DEC);
      }
      break;

      case 8:
      {
        lastResponse = "Scaled angle = "+String(convertScaledAngleToDegrees(ams5600.getScaledAngle()),DEC);
      }
      break;

      case 9:
      {
        if(ams5600.detectMagnet()==1)
          lastResponse = "Magnet detected";
        else
          lastResponse = noMagnetStr;
      }
      break;

      case 10:
      {
        lastResponse = "Magnet strength ";
        if(ams5600.detectMagnet()==1)
        {
          int magStrength = ams5600.getMagnetStrength();

          if(magStrength == 1)
            lastResponse += "is weak";
          else if(magStrength == 2)
            lastResponse += "is acceptable";
          else if (magStrength == 3)
            lastResponse += "is to strong";
        }
        else
          lastResponse = noMagnetStr;
      }
      break;

      case 96:
      {
        Serial.println ("BURN ANGLE (Y/N)?");

        char answer;

        Serial.readBytes(&answer,1);

        if((answer == 'y') || (answer == 'Y'))
        {
          lastResponse = burnAngle();
        }
        else
          lastResponse = "Brun canceled";
      }
      break;

      case 98:
      {
        Serial.println ("BURN MAX ANGLE AND CONFIG (Y/N)?");

        char answer = Serial.read();

        if((answer == 'y') || (answer == 'Y'))
        {
          lastResponse = burnMaxAngleAndConfig();
        }
        else
          lastResponse = "Brun canceled";
      }
      break;

      default:
      {
          lastResponse = "Invalid Entry";
      }
      break;
    }
    /*end of menu processing */
    printMenu();
  }
}

CPP file here >

/****************************************************
/* AMS 5600 class for Arduino platform
/* Author: Tom Denton
/* Date: 15 Dec 2014 
/* File: AMS_5600.cpp
/* Version 1.00
/* www.ams.com
/*  
/* Description:  This class has been designed to
/* access the AMS 5600 “potuino” shield.
/*
/***************************************************/

#include "Arduino.h"
#include "AMS_5600.h"
#include "Wire.h"

/****************************************************
/* Method: AMS_5600
/* In: none
/* Out: none
/* Description: constructor class for AMS 5600
/***************************************************/
AMS_5600::AMS_5600()
{
  /* set i2c address */ 
  _ams5600_Address = 0x36;
 
  /* load register values*/
  /* c++ class forbids pre loading of variables */
  _zmco = 0x00;
  _zpos_hi = 0x01;
  _zpos_lo = 0x02;
  _mpos_hi = 0x03;
  _mpos_lo = 0x04;
  _mang_hi = 0x05;
  _mang_lo = 0x06;
  _conf_hi = 0x07;    
  _conf_lo = 0x08;
  _raw_ang_hi = 0x0c;
  _raw_ang_lo = 0x0d;
  _ang_hi = 0x0e;
  _ang_lo = 0x0f;
  _stat = 0x0b;
  _agc = 0x1a;
  _mag_hi = 0x1b;
  _mag_lo = 0x1c;
  _burn = 0xff;
}

/****************************************************
/* Method: AMS_5600
/* In: none
/* Out: i2c address of AMS 5600
/* Description: returns i2c address of AMS 5600
/***************************************************/
int AMS_5600::getAddress()
{
  return _ams5600_Address; 
}

/*******************************************************
/* Method: setMaxAngle
/* In: new maximum angle to set OR none
/* Out: value of max angle register
/* Description: sets a value in maximum angle register.
/* If no value is provided, method will read position of
/* magnet.  Setting this register zeros out max position
/* register.
/*******************************************************/
word AMS_5600::setMaxAngle(word newMaxAngle)
{
  word retVal;
  if(newMaxAngle == -1)
  {
    _maxAngle = getRawAngle();
  }
  else
    _maxAngle = newMaxAngle;

  writeOneByte(_mang_hi, highByte(_maxAngle));
  delay(2); 
  writeOneByte(_mang_lo, lowByte(_maxAngle)); 
  delay(2);         

  retVal = readTwoBytes(_mang_hi, _mang_lo);
  return retVal;
}

/*******************************************************
/* Method: getMaxAngle
/* In: none
/* Out: value of max angle register
/* Description: gets value of maximum angle register.
/*******************************************************/
word AMS_5600::getMaxAngle()
{
  return readTwoBytes(_mang_hi, _mang_lo);
}

/*******************************************************
/* Method: setStartPosition
/* In: new start angle position
/* Out: value of start position register
/* Description: sets a value in start position register.
/* If no value is provided, method will read position of
/* magnet.  
/*******************************************************/
word AMS_5600::setStartPosition(word startAngle)
{
  if(startAngle == -1)
  {
    _rawStartAngle = getRawAngle();
  }
  else
    _rawStartAngle = startAngle;

  writeOneByte(_zpos_hi, highByte(_rawStartAngle));
  delay(2); 
  writeOneByte(_zpos_lo, lowByte(_rawStartAngle)); 
  delay(2);                
  _zPosition = readTwoBytes(_zpos_hi, _zpos_lo);
  
  return(_zPosition);
}

/*******************************************************
/* Method: getStartPosition
/* In: none
/* Out: value of start position register
/* Description: gets value of start position register.
/*******************************************************/
word AMS_5600::getStartPosition()
{
  return readTwoBytes(_zpos_hi, _zpos_lo);
}  

/*******************************************************
/* Method: setEndtPosition
/* In: new end angle position
/* Out: value of end position register
/* Description: sets a value in end position register.
/* If no value is provided, method will read position of
/* magnet.  
/*******************************************************/
word AMS_5600::setEndPosition(word endAngle)
{
  if(endAngle == -1)
    _rawEndAngle = getRawAngle();
  else
    _rawEndAngle = endAngle;
 
  writeOneByte(_mpos_hi, highByte(_rawEndAngle));
  delay(2); 
  writeOneByte(_mpos_lo, lowByte(_rawEndAngle)); 
  delay(2);                
  _mPosition = readTwoBytes(_mpos_hi, _mpos_lo);
  
  return(_mPosition);
}

/*******************************************************
/* Method: getEndPosition
/* In: none
/* Out: value of end position register
/* Description: gets value of end position register.
/*******************************************************/
word AMS_5600::getEndPosition()
{
  word retVal = readTwoBytes(_mpos_hi, _mpos_lo);
  return retVal;
}  

/*******************************************************
/* Method: getRawAngle
/* In: none
/* Out: value of raw angle register
/* Description: gets raw value of magnet position.
/* start, end, and max angle settings do not apply
/*******************************************************/
word AMS_5600::getRawAngle()
{
  return readTwoBytes(_raw_ang_hi, _raw_ang_lo);
}

/*******************************************************
/* Method: getScaledAngle
/* In: none
/* Out: value of scaled angle register
/* Description: gets scaled value of magnet position.
/* start, end, or max angle settings are used to 
/* determine value
/*******************************************************/
word AMS_5600::getScaledAngle()
{
  return readTwoBytes(_ang_hi, _ang_lo);
}

/*******************************************************
/* Method: detectMagnet
/* In: none
/* Out: 1 if magnet is detected, 0 if not
/* Description: reads status register and examines the 
/* MH bit
/*******************************************************/
int AMS_5600::detectMagnet()
{
  int magStatus;
  int retVal = 0;
  /*0 0 MD ML MH 0 0 0*/
  /* MD high = AGC minimum overflow, Magnet to strong */
  /* ML high = AGC Maximum overflow, magnet to weak*/ 
  /* MH high = magnet detected*/ 
  magStatus = readOneByte(_stat);
  
  if(magStatus & 0x20)
    retVal = 1; 
  
  return retVal;
}

/*******************************************************
/* Method: getMagnetStrength
/* In: none
/* Out: 0 if no magnet is detected
/*      1 if magnet is to weak
/*      2 if magnet is just right
/*      3 if magnet is to strong
/* Description: reads status register andexamins the MH,ML,MD bits
/*******************************************************/
int AMS_5600::getMagnetStrength()
{
  int magStatus;
  int retVal = 0;
  /*0 0 MD ML MH 0 0 0*/
  /* MD high = AGC minimum overflow, Magnet to strong */
  /* ML high = AGC Maximum overflow, magnet to weak*/ 
  /* MH high = magnet detected*/ 
  magStatus = readOneByte(_stat);
  if(detectMagnet() ==1)
  {
      retVal = 2; /*just right */
      if(magStatus & 0x10)
        retVal = 1; /*to weak */
      else if(magStatus & 0x08)
        retVal = 3; /*to strong */
  }
  
  return retVal;
}

/*******************************************************
/* Method: get Agc
/* In: none
/* Out: value of AGC register
/* Description: gets value of AGC register.
/*******************************************************/
int AMS_5600::getAgc()
{
  return readOneByte(_agc);
}

/*******************************************************
/* Method: getMagnitude
/* In: none
/* Out: value of magnitude register
/* Description: gets value of magnitude register.
/*******************************************************/
word AMS_5600::getMagnitude()
{
  return readTwoBytes(_mag_hi, _mag_lo);  
}

/*******************************************************
/* Method: getBurnCount
/* In: none
/* Out: value of zmco register
/* Description: determines how many times chip has been
/* permanently written to. 
/*******************************************************/
int AMS_5600::getBurnCount()
{
  return readOneByte(_zmco);
}

/*******************************************************
/* Method: burnAngle
/* In: none
/* Out: 1 success
/*     -1 no magnet
/*     -2 burn limit exceeded
/*     -3 start and end positions not set (useless burn)
/* Description: burns start and end positions to chip.
/* THIS CAN ONLY BE DONE 3 TIMES
/*******************************************************/
int AMS_5600::burnAngle()
{
  int retVal = 1;
  _zPosition = getStartPosition();
  _mPosition = getEndPosition();
  _maxAngle  = getMaxAngle();
  
  if(detectMagnet() == 1)
  {
    if(getBurnCount() < 3)
    {
      if((_zPosition == 0)&&(_mPosition ==0))
        retVal = -3;
      else
        writeOneByte(_burn, 0x80);
    }
    else
      retVal = -2;
  } 
  else
    retVal = -1;
    
  return retVal;
}

/*******************************************************
/* Method: burnMaxAngleAndConfig
/* In: none
/* Out: 1 success
/*     -1 burn limit exceeded
/*     -2 max angle is to small, must be at or above 18 degrees
/* Description: burns max angle and config data to chip.
/* THIS CAN ONLY BE DONE 1 TIME
/*******************************************************/
int AMS_5600::burnMaxAngleAndConfig()
{
  int retVal = 1;
  _maxAngle  = getMaxAngle();
  
  if(getBurnCount() ==0)
  {
    if(_maxAngle*0.087 < 18)
      retVal = -2;
    else
      writeOneByte(_burn, 0x40);    
  }  
  else
    retVal = -1;
    
  return retVal;
}


/*******************************************************
/* Method: readOneByte
/* In: register to read
/* Out: data read from i2c
/* Description: reads one byte register from i2c
/*******************************************************/
int AMS_5600::readOneByte(int in_adr)
{
  int retVal = -1;
  Wire.beginTransmission(_ams5600_Address);
  Wire.write(in_adr);
  Wire.endTransmission();
  Wire.requestFrom(_ams5600_Address, 1);
  while(Wire.available() == 0);
  retVal = Wire.read();
  
  return retVal;
}

/*******************************************************
/* Method: readOneByte
/* In: two registers to read
/* Out: data read from i2c as a word
/* Description: reads two bytes register from i2c
/*******************************************************/
word AMS_5600::readTwoBytes(int in_adr_hi, int in_adr_lo)
{
  word retVal = -1;
 
  /* Read Low Byte */
  Wire.beginTransmission(_ams5600_Address);
  Wire.write(in_adr_lo);
  Wire.endTransmission();
  Wire.requestFrom(_ams5600_Address, 1);
  while(Wire.available() == 0);
  int low = Wire.read();
 
  /* Read High Byte */  
  Wire.beginTransmission(_ams5600_Address);
  Wire.write(in_adr_hi);
  Wire.endTransmission();
  Wire.requestFrom(_ams5600_Address, 1);
  
  while(Wire.available() == 0);
  
  word high = Wire.read();
  
  high = high << 8;
  retVal = high | low;
  
  return retVal;
}

/*******************************************************
/* Method: writeOneByte
/* In: address and data to write
/* Out: none
/* Description: writes one byte to a i2c register
/*******************************************************/
void AMS_5600::writeOneByte(int adr_in, int dat_in)
{
  Wire.beginTransmission(_ams5600_Address);
  Wire.write(adr_in); 
  Wire.write(dat_in);
  Wire.endTransmission();
}

/**********  END OF AMS 5600 CALSS *****************/

And here is .H code >

/****************************************************
/* AMS 5600 class for Arduino platform
/* Author: Tom Denton
/* Date: 15 Dec 2014
/* File: AMS_5600.h 
/* Version 1.00
/* www.ams.com
/*  
/* Description:  This class has been designed to
/* access the AMS 5600 “potuino” shield.
/*
/***************************************************/

#ifndef AMS_5600_h
#define AMS_5600_h

#include <Arduino.h>

class AMS_5600
{
  public:
    
    AMS_5600(void);
    int getAddress();       
    
    word setMaxAngle(word newMaxAngle = -1);
    word getMaxAngle();
    
    word setStartPosition(word startAngle = -1);
    word getStartPosition();
    
    word setEndPosition(word endAngle = -1);
    word getEndPosition();
    
    word getRawAngle();
    word getScaledAngle();
    
    int  detectMagnet();
    int  getMagnetStrength();
    int  getAgc();
    word getMagnitude();
    
    int  getBurnCount();
    int  burnAngle();
    int  burnMaxAngleAndConfig();
    
  private:
  
    int _ams5600_Address;
      
    word _rawStartAngle;
    word _zPosition;
    word _rawEndAngle;
    word _mPosition;
    word _maxAngle;
    
    /* Registers */
    int _zmco;
    int _zpos_hi;    /*zpos[11:8] high nibble  START POSITION */
    int _zpos_lo;    /*zpos[7:0] */
    int _mpos_hi;    /*mpos[11:8] high nibble  STOP POSITION */
    int _mpos_lo;    /*mpos[7:0] */
    int _mang_hi;    /*mang[11:8] high nibble  MAXIMUM ANGLE */
    int _mang_lo;    /*mang[7:0] */
    int _conf_hi;    
    int _conf_lo;
    int _raw_ang_hi;
    int _raw_ang_lo;
    int _ang_hi;
    int _ang_lo;
    int _stat;
    int _agc;
    int _mag_hi;
    int _mag_lo;
    int _burn;
    
    int readOneByte(int in_adr);
    word readTwoBytes(int in_adr_hi, int in_adr_lo);
    void writeOneByte(int adr_in, int dat_in);
   
};
#endif

With usage fault I’d check the lib for any division that could cause a DIV/0 exception.

I’d also change this

  if (Serial.available() > 0)
  {
    char incomingByteBuffer[2];
    char incomingByte;

    incomingByteBuffer[0] = NULL;
    incomingByteBuffer[1] = NULL;

    Serial.readBytes(incomingByteBuffer,2);
    ...
  }
}

if (Serial.available() > 0) does not necessarily mean there will be two bytes to read. So I’d rather check for if (Serial.available() >= 2).

This might also need a Particle.process() in it

  while(Wire.available() == 0);

Also you are building for 0.6.2, right?