Engineering 100-950

Sensor Integration Code Manual

Code manual for Lab3_SensorIntegration.ino, published in the University of Michigan ENGR100-950 Electronics for Atmospheric and Space Measurements’ public Arduino example library.

Contents


Introduction

Lab3_SensorIntegration.ino is a comprehensive Arduino program designed for ENGR100-950. This code integrates both digital and analog sensors to gather environmental and motion data in real-time. The primary use case is for high-altitude weather balloon projects, where sensor accuracy, data redundancy, and proper integration are critical.

In order to run this code, make sure you install the “Adafruit BME680 Library” by Adafruit in the Arduino IDE Library.

Key Features:

This manual provides a detailed breakdown of each code section, explaining its functionality, purpose, modifiable components, and usage examples. It also includes guidance on configuring placeholders (?) in the code with values specific for your setup.

Important: Ensure all ? placeholders in the code are replaced with specific values relevant to your setup. These include pin numbers, calibration constants, and resistor values.


Code Breakdown

Global Configuration and Libraries

This section includes the necessary libraries, pin assignments, and initialization of key components. It ensures the program can interface with the sensors and peripherals.

#include <SPI.h>
#include <SD.h>
#include <Adafruit_BME680.h>

#define BME_SCK   6
#define BME_MISO  7
#define BME_MOSI  8
#define BME_CS    9
#define SD_CS     10

Adafruit_BME680 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);

Description:

Modifiable Components:


Pin Assignments and Constants

const int vDivPin           = A?; // Voltage divider pin
const int tmpPin            = A?; // TMP36 pin
const int xAccelPin         = A?; // x-axis accelerometer pin
const int yAccelPin         = A?; // y-axis accelerometer pin
const int zAccelPin         = A?; // z-axis accelerometer pin

const float R1              = ???; // Ohms, resistance between Vin and Vout
const float R2              = ???; // Ohms, resistance between Vout and GND

const float tmpSlope        = ?;   // TMP36 slope (from calibration curve)
const float tmpIntercept    = ?;   // TMP36 intercept (from calibration curve)

const float xAccelSlope     = ?;   // X-axis accelerometer slope
const float xAccelIntercept = ?;   // X-axis accelerometer intercept
const float yAccelSlope     = ?;   // Y-axis accelerometer slope
const float yAccelIntercept = ?;   // Y-axis accelerometer intercept
const float zAccelSlope     = ?;   // Z-axis accelerometer slope
const float zAccelIntercept = ?;   // Z-axis accelerometer intercept

bool SerialPrint            = ???; // Set to true for serial monitor output, false to disable

Description:

Modifiable Components:

To determine the values for tmpSlope and tmpIntercept, perform a linear calibration of the TMP36 sensor against known temperature references. Similarly, calibrate each axis of the accelerometer by recording output values for -1g and 1g.


Setup Function

void setup() {
  if (SerialPrint) {
    Serial.begin(9600);
    while (!Serial) {}
  }

  pinMode(SD_CS, OUTPUT);
  if (!SD.begin(SD_CS)) {
    if (SerialPrint) {
      Serial.println(F("SD initialization failed!"));
    }
    while (true) {}
  }

  int fileIndex = 1;
  while (true) {
    snprintf(dataFileName, sizeof(dataFileName), "datalog%d.csv", fileIndex);
    if (!SD.exists(dataFileName)) {
      break;
    }
    fileIndex++;
  }

  File dataFile = SD.open(dataFileName, FILE_WRITE);
  if (dataFile) {
    dataFile.println(F("Time (ms),Voltage (V),TMP36 (C),BME Temp (C),Pressure (Pa),Humidity (%),xAccel (g),yAccel (g),zAccel(g)"));
    dataFile.close();
  }

  if (!bme.begin()) {
    if (SerialPrint) {
      Serial.println(F("BME680 initialization failed!"));
    }
    while (true) {}
  }

  bme.setTemperatureOversampling(BME680_OS_2X);
  bme.setHumidityOversampling(BME680_OS_2X);
  bme.setPressureOversampling(BME680_OS_4X);
  bme.setIIRFilterSize(BME680_FILTER_SIZE_1);
  bme.setGasHeater(0, 0);
}

Description

Modifiable Components:

If the BME680 fails to initialize, double-check your wiring and power supply connections.


Loop Function

void loop() {
  static unsigned long startTime = 0;
  unsigned long currentTime = millis();
  bool bmeOk = bme.performReading();

  // Placeholder values for BME680
  float bme_temperature = NAN;
  float humidity        = NAN;
  float pressure        = NAN;

  if (!bmeOk) {
    if (SerialPrint) {
      Serial.println(F("**BME680 Read Failed**"));
    }
    delay(10);
  } else {
    bme_temperature  = bme.temperature; // °C
    humidity         = bme.humidity;    // %
    pressure         = bme.pressure;    // Pa
  }

  float vDivVal      = analogRead(vDivPin);
  float vDivVoltage  = (vDivVal * 5.0 / 1023.0) * ((R1 + R2) / R2);

  float tmpV         = analogRead(tmpPin) * 5.0 / 1023.0;
  float tmp          = (tmpSlope * tmpV) + tmpIntercept;

  float xAccelV      = analogRead(xAccelPin) * 5.0 / 1023.0;
  float xAccel       = (xAccelSlope * xAccelV) + xAccelIntercept;

  float yAccelV      = analogRead(yAccelPin) * 5.0 / 1023.0;
  float yAccel       = (yAccelSlope * yAccelV) + yAccelIntercept;

  float zAccelV      = analogRead(zAccelPin) * 5.0 / 1023.0;
  float zAccel       = (zAccelSlope * zAccelV) + zAccelIntercept;

  String dataString;
  dataString += String(millis());
  dataString += ",";
  dataString += String(vDivVoltage);
  dataString += ",";
  dataString += String(tmp);
  dataString += ",";
  dataString += String(bme_temperature);
  dataString += ",";
  dataString += String(pressure);
  dataString += ",";
  dataString += String(humidity);
  dataString += ",";
  dataString += String(xAccel);
  dataString += ",";
  dataString += String(yAccel);
  dataString += ",";
  dataString += String(zAccel);

  File dataFile = SD.open(dataFileName, FILE_WRITE);
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
  }

  if (SerialPrint) {
    Serial.println(dataString);
  }

  unsigned long elapsed = millis() - currentTime;
  unsigned long delayTime = 1000 - elapsed % 1000;
  delay(delayTime);
}

Description:

Modifiable Components:


Additional Tips