Mobile Life IoT project using the AT&T IoT Starter Kit Software and files for my device to monitor the status or our Airstream travel trailer RV. A full description of the project is at Hackster.IO here as part of the Realtime AT&T IoT Starter Kit Challenge: https://www.hackster.io/Anubus/mobile-life-iot-9c10be

Dependencies:   FXOS8700CQ MODSERIAL mbed

Revision:
0:bd276b1f1249
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sensors.cpp	Sun Apr 02 12:28:21 2017 +0000
@@ -0,0 +1,419 @@
+/* ===================================================================
+Copyright © 2016, AVNET Inc.  
+
+Licensed under the Apache License, Version 2.0 (the "License"); 
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, 
+software distributed under the License is distributed on an 
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
+either express or implied. See the License for the specific 
+language governing permissions and limitations under the License.
+
+=======================================================
+Modified by Robert Bolling
+January 2017 
+for the Mobile Life IoT project
+  - refactored Si7020 to Si7021 sensor
+  - added voltage and intrusion sensor
+  - some other clean up including removing unused code
+    for virtual sensors and the Si1145
+=======================================================
+
+======================================================================== */
+
+#include "mbed.h"
+#include "sensors.h"
+#include "hardware.h"
+#include "config_me.h"
+#include "FXOS8700CQ.h"
+#include "HTS221.h"
+#include "xadow_gps.h"
+#include <string>
+
+#define Si7020_PMOD_I2C_ADDR   0x80 //this is for 7-bit I2C addr 0x4 for the Si7021 external sensor
+
+// Storage for the data from the motion sensor
+SRAWDATA accel_data;
+SRAWDATA magn_data;
+//InterruptIn fxos_int1(PTC6); // unused, common with SW2 on FRDM-K64F
+InterruptIn fxos_int2(PTC13); // should just be the Data-Ready interrupt
+
+bool fxos_int2_triggered = false;
+void trigger_fxos_int2(void)
+{
+    fxos_int2_triggered = true;
+
+}
+
+#define VOLTS_SCALE 18.6 //scale battery voltage measurement based on R1 and R2
+AnalogIn Analog0(PTC0); //  Had to use J1-11 for PTC0 to deconflict from wnc shield pins
+
+
+/*------------------------------------------------------------------------------
+ * Perform I2C single read.
+ *------------------------------------------------------------------------------*/
+unsigned char I2C_ReadSingleByte(unsigned char ucDeviceAddress)
+{
+    char rxbuffer [1];
+    i2c.read(ucDeviceAddress, rxbuffer, 1 );
+    return (unsigned char)rxbuffer[0];
+} //I2C_ReadSingleByte()
+
+/*------------------------------------------------------------------------------
+ * Perform I2C single read from address.
+ *------------------------------------------------------------------------------*/
+unsigned char I2C_ReadSingleByteFromAddr(unsigned char ucDeviceAddress, unsigned char Addr)
+{
+    char txbuffer [1];
+    char rxbuffer [1];
+    txbuffer[0] = (char)Addr;
+    i2c.write(ucDeviceAddress, txbuffer, 1 );
+    i2c.read(ucDeviceAddress, rxbuffer, 1 );
+    return (unsigned char)rxbuffer[0];
+} //I2C_ReadSingleByteFromAddr()
+
+/*------------------------------------------------------------------------------
+ * Perform I2C read of more than 1 byte.
+ *------------------------------------------------------------------------------*/
+int I2C_ReadMultipleBytes(unsigned char ucDeviceAddress, char *ucData, unsigned char ucLength)
+{
+    int status;
+    status = i2c.read(ucDeviceAddress, ucData, ucLength);
+    return status;
+} //I2C_ReadMultipleBytes()
+
+/*------------------------------------------------------------------------------
+ * Perform I2C write of a single byte.
+ *------------------------------------------------------------------------------*/
+int I2C_WriteSingleByte(unsigned char ucDeviceAddress, unsigned char Data, bool bSendStop)
+{
+    int status;
+    char txbuffer [1];
+    txbuffer[0] = (char)Data; //data
+    status = i2c.write(ucDeviceAddress, txbuffer, 1, !bSendStop); //true: do not send stop
+    return status;
+} //I2C_WriteSingleByte()
+
+/*------------------------------------------------------------------------------
+ * Perform I2C write of 1 byte to an address.
+ *------------------------------------------------------------------------------*/
+int I2C_WriteSingleByteToAddr(unsigned char ucDeviceAddress, unsigned char Addr, unsigned char Data, bool bSendStop)
+{
+    int status;
+    char txbuffer [2];
+    txbuffer[0] = (char)Addr; //address
+    txbuffer[1] = (char)Data; //data
+    //status = i2c.write(ucDeviceAddress, txbuffer, 2, false); //stop at end
+    status = i2c.write(ucDeviceAddress, txbuffer, 2, !bSendStop); //true: do not send stop
+    return status;
+} //I2C_WriteSingleByteToAddr()
+
+/*------------------------------------------------------------------------------
+ * Perform I2C write of more than 1 byte.
+ *------------------------------------------------------------------------------*/
+int I2C_WriteMultipleBytes(unsigned char ucDeviceAddress, char *ucData, unsigned char ucLength, bool bSendStop)
+{
+    int status;
+    status = i2c.write(ucDeviceAddress, ucData, ucLength, !bSendStop); //true: do not send stop
+    return status;
+} //I2C_WriteMultipleBytes()
+
+//********************************************************************************************************************************************
+//* Battery Voltage
+//********************************************************************************************************************************************
+
+void Read_Battery_Volts(void)
+{
+        float Volts;
+        Volts = Analog0 * VOLTS_SCALE;
+        PRINTF("Voltage: %0.3f Volts \r\n", Volts); 
+        sprintf(SENSOR_DATA.Battery_Voltage, "%0.2f", Volts);
+}
+
+//********************************************************************************************************************************************
+//* Si7020/Si7021 temperature & humidity sensor
+//********************************************************************************************************************************************
+
+bool bSi7020_present = false;
+void Init_Si7020(void)
+{
+    char SN_7020 [8];
+    //SN part 1:
+    I2C_WriteSingleByteToAddr(Si7020_PMOD_I2C_ADDR, 0xFA, 0x0F, false);
+    I2C_ReadMultipleBytes(Si7020_PMOD_I2C_ADDR, &SN_7020[0], 4);
+
+    //SN part 1:
+    I2C_WriteSingleByteToAddr(Si7020_PMOD_I2C_ADDR, 0xFC, 0xC9, false);
+    I2C_ReadMultipleBytes(Si7020_PMOD_I2C_ADDR, &SN_7020[4], 4);
+
+    char Ver_7020 [2];
+    //FW version:
+    I2C_WriteSingleByteToAddr(Si7020_PMOD_I2C_ADDR, 0x84, 0xB8, false);
+    I2C_ReadMultipleBytes(Si7020_PMOD_I2C_ADDR, &Ver_7020[0], 2);
+
+    if (SN_7020[4] != 0x15)  //Si7021 this is 0x15, for Si7020 this is 0x14
+    {
+        bSi7020_present = false;
+        PRINTF("Si7020 sensor not found. SN=0x%02X Si7020addr=0x%02X \r\n", SN_7020[4], Si7020_PMOD_I2C_ADDR  );  //TODO: take out SN print
+    }
+    else 
+    {
+        bSi7020_present = true;
+        PRINTF("Si7020 SN = 0x%02X%02X%02X%02X%02X%02X%02X%02X \r\n", SN_7020[0], SN_7020[1], SN_7020[2], SN_7020[3], SN_7020[4], SN_7020[5], SN_7020[6], SN_7020[7]);
+        PRINTF("Si7020 Version# = 0x%02X \r\n", Ver_7020[0]);
+    } //bool bSi7020_present = true
+
+} //Init_Si7020()
+
+void Read_Si7020(void)
+{
+    if (bSi7020_present)
+    {   
+        char Humidity [2];
+        char Temperature [2];
+        //Command to measure humidity (temperature also gets measured):
+        I2C_WriteSingleByte(Si7020_PMOD_I2C_ADDR, 0xF5, false); //no hold, must do dummy read
+        I2C_ReadMultipleBytes(Si7020_PMOD_I2C_ADDR, &Humidity[0], 1); //dummy read, should get an nack until it is done
+        wait (0.05); //wait for measurement.  Can also keep reading until no NACK is received
+        //I2C_WriteSingleByte(Si7020_PMOD_I2C_ADDR, 0xE5, false); //Hold mod, the device does a clock stretch on the read until it is done (crashes the I2C bus...
+        I2C_ReadMultipleBytes(Si7020_PMOD_I2C_ADDR, &Humidity[0], 2); //read humidity
+        //PRINTF("Read Si7020 Humidity = 0x%02X%02X\n", Humidity[0], Humidity[1]);
+        int rh_code = (Humidity[0] << 8) + Humidity[1];
+        float fRh = (125.0*rh_code/65536.0) - 6.0; //from datasheet
+        PRINTF("Si7020 Humidity = %0.1f %% \r\n", fRh); //double % sign for escape //PRINTF("%*.*f\n", myFieldWidth, myPrecision, myFloatValue);
+        sprintf(SENSOR_DATA.Humidity_Si7020, "%0.1f", fRh);
+        
+        //Command to read temperature when humidity is already done:
+        I2C_WriteSingleByte(Si7020_PMOD_I2C_ADDR, 0xE0, false);
+        I2C_ReadMultipleBytes(Si7020_PMOD_I2C_ADDR, &Temperature[0], 2); //read temperature
+        //PRINTF("Read Si7020 Temperature = 0x%02X%02X\n", Temperature[0], Temperature[1]);
+        int temp_code = (Temperature[0] << 8) + Temperature[1];
+        float fTemp = (175.72*temp_code/65536.0) - 46.85; //from datasheet in Celcius
+        PRINTF("Si7020 Temperature = %0.1f deg F \r\n", CTOF(fTemp));
+        sprintf(SENSOR_DATA.Temperature_Si7020, "%0.1f", CTOF(fTemp));
+    } //bool bSi7020_present = true
+
+} //Read_Si7020()
+
+
+//********************************************************************************************************************************************
+//* Read the FXOS8700CQ - 6-axis combo Sensor Accelerometer and Magnetometer
+//********************************************************************************************************************************************
+bool bMotionSensor_present = false;
+void Init_motion_sensor()
+{
+    // Note: this class is instantiated here because if it is statically declared, the cellular shield init kills the I2C bus...
+    // Class instantiation with pin names for the motion sensor on the FRDM-K64F board:
+    FXOS8700CQ fxos(PTE25, PTE24, FXOS8700CQ_SLAVE_ADDR1); // SDA, SCL, (addr << 1)
+    int iWhoAmI = fxos.get_whoami();
+
+    PRINTF("FXOS8700CQ WhoAmI = %X\r\n", iWhoAmI);
+    // Iterrupt for active-low interrupt line from FXOS
+    // Configured with only one interrupt on INT2 signaling Data-Ready
+    //fxos_int2.fall(&trigger_fxos_int2);
+    if (iWhoAmI != 0xC7)
+    {
+        bMotionSensor_present = false;
+        PRINTF("FXOS8700CQ motion sensor not found\r\n");
+    }
+    else
+    {
+        bMotionSensor_present = true;
+        fxos.enable();
+    }
+} //Init_motion_sensor()
+
+void Read_motion_sensor()
+{
+    // Note: this class is instantiated here because if it is statically declared, the cellular shield init kills the I2C bus...
+    // Class instantiation with pin names for the motion sensor on the FRDM-K64F board:
+    FXOS8700CQ fxos(PTE25, PTE24, FXOS8700CQ_SLAVE_ADDR1); // SDA, SCL, (addr << 1)
+    if (bMotionSensor_present)
+    {
+        fxos.enable();
+        fxos.get_data(&accel_data, &magn_data);
+        //PRINTF("Roll=%5d, Pitch=%5d, Yaw=%5d;\r\n", magn_data.x, magn_data.y, magn_data.z);
+        sprintf(SENSOR_DATA.MagnetometerX, "%5d", magn_data.x);
+        sprintf(SENSOR_DATA.MagnetometerY, "%5d", magn_data.y);
+        sprintf(SENSOR_DATA.MagnetometerZ, "%5d", magn_data.z);
+    
+        //Try to normalize (/2048) the values so they will match the eCompass output:
+        float fAccelScaled_x, fAccelScaled_y, fAccelScaled_z;
+        fAccelScaled_x = (accel_data.x/2048.0);
+        fAccelScaled_y = (accel_data.y/2048.0);
+        fAccelScaled_z = (accel_data.z/2048.0);
+        PRINTF("Acc: X=%2.3f Y=%2.3f Z=%2.3f;\r\n", fAccelScaled_x, fAccelScaled_y, fAccelScaled_z);
+        sprintf(SENSOR_DATA.AccelX, "%2.3f", fAccelScaled_x);
+        sprintf(SENSOR_DATA.AccelY, "%2.3f", fAccelScaled_y);
+        sprintf(SENSOR_DATA.AccelZ, "%2.3f", fAccelScaled_z);
+    } //bMotionSensor_present
+} //Read_motion_sensor()
+
+
+//********************************************************************************************************************************************
+//* Read the HTS221 temperature & humidity sensor on the Cellular Shield
+//********************************************************************************************************************************************
+// These are to be built on the fly
+string my_temp;
+string my_humidity;
+HTS221 hts221;
+
+//#define CTOF(x)  ((x)*1.8+32)
+bool bHTS221_present = false;
+void Init_HTS221()
+{
+    int i;
+    void hts221_init(void);
+    i = hts221.begin();
+    if (i)
+    {
+        bHTS221_present = true;
+        PRINTF(BLU "HTS221 Detected (0x%02X)\n\r",i);
+        PRINTF("  Temp  is: %0.2f F \n\r",CTOF(hts221.readTemperature()));
+        PRINTF("  Humid is: %02d %%\n\r",hts221.readHumidity());
+    }
+    else
+    {
+        bHTS221_present = false;
+        PRINTF(RED "HTS221 NOT DETECTED!\n\r");
+    }
+} //Init_HTS221()
+
+void Read_HTS221()
+{
+    if (bHTS221_present)
+    {
+        sprintf(SENSOR_DATA.Temperature, "%0.1f", CTOF(hts221.readTemperature()));
+        PRINTF("HTS221 Temp  is: %0.1f F \n\r",CTOF(hts221.readTemperature()));
+        sprintf(SENSOR_DATA.Humidity, "%02d", hts221.readHumidity());
+        PRINTF("HTS221 Humid is: %02d %%\n\r",hts221.readHumidity());     
+        
+    } //bHTS221_present
+} //Read_HTS221()
+
+//********************************************************************************************************************************************
+//* Read the xadow gps module connedted to i2c1
+//********************************************************************************************************************************************
+
+bool bGPS_present = false;
+void Init_GPS(void)
+{
+    char scan_id[GPS_SCAN_SIZE+2]; //The first two bytes are the response length (0x00, 0x04)
+    I2C_WriteSingleByte(GPS_DEVICE_ADDR, GPS_SCAN_ID, true); //no hold, must do read
+
+    unsigned char i;
+    for(i=0;i<(GPS_SCAN_SIZE+2);i++)
+    {
+        scan_id[i] = I2C_ReadSingleByte(GPS_DEVICE_ADDR);
+    }
+
+    if(scan_id[5] != GPS_DEVICE_ID)
+    {
+        bGPS_present = false;
+        PRINTF("Xadow GPS not found \r\n");
+    }
+    else 
+    {
+        bGPS_present = true;
+        PRINTF("Xadow GPS Scan ID response = 0x%02X%02X (length), 0x%02X%02X%02X%02X\r\n", scan_id[0], scan_id[1], scan_id[2], scan_id[3], scan_id[4], scan_id[5]);
+        char status = gps_get_status();
+        
+         
+ /*  rather not wait for valid GPS before reporting sensors       
+        if ((status != 'A') && (iSensorsToReport == TEMP_HUMIDITY_ACCELEROMETER_GPS))
+          { //we must wait for GPS to initialize
+              PRINTF("Waiting for GPS to become ready... ");
+              while (status != 'A')
+              {
+                  wait (5.0);        
+                  status = gps_get_status();
+                  unsigned char num_satellites = gps_get_sate_in_veiw();
+                  PRINTF("%c%d", status, num_satellites);
+              }
+              PRINTF("\r\n");
+          } //we must wait for GPS to initialize
+ */
+ 
+        PRINTF("gps_check_online is %d\r\n", gps_check_online());
+        unsigned char *data;
+        data = gps_get_utc_date_time();       
+        PRINTF("gps_get_utc_date_time : %d-%d-%d,%d:%d:%d\r\n", data[0], data[1], data[2], data[3], data[4], data[5]); 
+        PRINTF("gps_get_status        : %c ('A' = Valid, 'V' = Invalid)\r\n", gps_get_status());
+        PRINTF("gps_get_latitude      : %c:%f\r\n", gps_get_ns(), gps_get_latitude());
+        PRINTF("gps_get_longitude     : %c:%f\r\n", gps_get_ew(), gps_get_longitude());
+        PRINTF("gps_get_altitude      : %f meters\r\n", gps_get_altitude());
+        PRINTF("gps_get_speed         : %f knots\r\n", gps_get_speed());
+        PRINTF("gps_get_course        : %f degrees\r\n", gps_get_course());
+        PRINTF("gps_get_position_fix  : %c\r\n", gps_get_position_fix());
+        PRINTF("gps_get_sate_in_view  : %d satellites\r\n", gps_get_sate_in_veiw());
+        PRINTF("gps_get_sate_used     : %d\r\n", gps_get_sate_used());
+        PRINTF("gps_get_mode          : %c ('A' = Automatic, 'M' = Manual)\r\n", gps_get_mode());
+        PRINTF("gps_get_mode2         : %c ('1' = no fix, '1' = 2D fix, '3' = 3D fix)\r\n", gps_get_mode2()); 
+    } //bool bGPS_present = true
+} //Init_GPS()
+
+void Read_GPS()
+{
+    unsigned char gps_satellites = 0; //default
+    int lat_sign;
+    int long_sign;
+    if (bGPS_present)
+    {
+        if ((gps_get_status() == 'A') && (gps_get_mode2() != '1'))
+        {
+            gps_satellites = gps_get_sate_in_veiw(); //show the number of satellites
+        }
+        if (gps_get_ns() == 'S')
+        {
+            lat_sign = -1; //negative number
+        }
+        else
+        {
+            lat_sign = 1;
+        }    
+        if (gps_get_ew() == 'W')
+        {
+            long_sign = -1; //negative number
+        }
+        else
+        {
+            long_sign = 1;
+        }    
+#if (1)
+        PRINTF("gps_satellites        : %d\r\n", gps_satellites);
+        PRINTF("gps_get_latitude      : %f\r\n", (lat_sign * gps_get_latitude()));
+        PRINTF("gps_get_longitude     : %f\r\n", (long_sign * gps_get_longitude()));
+        PRINTF("gps_get_altitude      : %f meters\r\n", gps_get_altitude());
+        PRINTF("gps_get_speed         : %f knots\r\n", gps_get_speed());
+        PRINTF("gps_get_course        : %f degrees\r\n", gps_get_course());
+#endif
+        sprintf(SENSOR_DATA.GPS_Satellites, "%d", gps_satellites);
+        sprintf(SENSOR_DATA.GPS_Latitude, "%f", (lat_sign * gps_get_latitude()));
+        sprintf(SENSOR_DATA.GPS_Longitude, "%f", (long_sign * gps_get_longitude()));
+        sprintf(SENSOR_DATA.GPS_Altitude, "%f", gps_get_altitude());
+        sprintf(SENSOR_DATA.GPS_Speed, "%f", gps_get_speed());
+        sprintf(SENSOR_DATA.GPS_Course, "%f", gps_get_course());
+    } //bGPS_present
+} //Read_GPS()
+
+ 
+void sensors_init(void)
+{
+    Init_HTS221();
+    Init_Si7020();
+    Init_motion_sensor();
+    Init_GPS();
+} //sensors_init
+
+void read_sensors(void)
+{
+    Read_Battery_Volts();
+    Read_HTS221();
+    Read_Si7020();
+    Read_motion_sensor();
+    Read_GPS();
+} //read_sensors