Project showcase
YADL: Yet Another Data Logger

YADL: Yet Another Data Logger

Use a Nano 33 IoT or a MKR WiFi 1010 to access a TI SensorTag to easily build a powerful BLE data logger.

  • 3,546 views
  • 1 comment
  • 14 respects

Components and supplies

Necessary tools and machines

About this project

Several years ago, I made a nice little data logger using the SensorTag from TI and an Arduino 101 to read and save the data using the BLE capabilities of the board. Alas, that board has gone away and I had been looking for a suitable replacement to take up the project once again.

Enter the MKR WiFi 1010 and Nano 33 IoT boards. Both of these boards have the needed BLE capability to connect to the SensorTag and much more.

In this project, I built a data logger capable of reading any of the sensors on the TI SensorTag, using just four components:

1) Either the MKR WiFi 1010 or the Nano 33 IoT board

2) SensorTag

3) SD card for saving the data

4) I2C OLED LCD module for viewing some of the data in real-time

Program Flow and Commentary:

Note the code and wiring is the same if you use either the Nano 33 IoT or the MKR WiFi 1010 in this project. Both boards have been tested in this project.

First the program checks that you have an SD card inserted and working. If not, an error will be displayed and execution goes no further. The project has been tested with the MicroSD click board as well as some generic SPI SD modules. If you do not want to use an SD card to save the data, you can eliminate that function by setting the value of a variable, see the program line,

byte SDswitch = 1; // SDswitch 1=ON (write to SD) or 0 (Do not write to SD)

By default, the option to write to the SD is on.

Next, the program will use the board's WiFi capability to contact a time server and get the current epoch. Note that you can correct this epoch for your time zone and DST setting (see program variable GMT). Once the epoch is obtained, the on-board real-time-clock (RTC) is set and used to keep the time.

Don't forget to add your own ssid and password for your internet connectivity, see program code lines:

char ssid[] = ""; // your network SSID (name)

char pass[] = ""; // your network password (use for WPA, or use as key for WEP)

The program requires that a time epoch is received so that accurate time stamping of the sensor reads can occur. After the time epoch has been received, WiFi is ended and BLE begins. Since both of the boards have WiFi and BLE capability, this is easily accomplished.

Next, the program scans for a SensorTag and attempts to connect when it is found. Note that there are different versions of the SensorTag CC2650 and some newer versions may not include the TMP007 sensor like mine does. In this case, you would simply need to comment out the program references to that sensor.

Currently, the program reads the following sensors:

HDC 1000 temperature F, HDC 1000 relative humidity %RH, BMP 280 temperature F, BMP 280 barometric pressure in hectoPascals, OPT3001 illumination in lux, TMP007 object temperature F and TMP007 temperature F

Data are saved on the SD card in comma-delimited format and consist of the following:

Epoch, day, month, year (last two digits), hours, minutes, seconds, followed by the sensor values in the order previously listed.

How frequently the sensors are read depends on a variable in the program - see the program line, long period = 600000L; The value (in milliseconds) defaults to every 10 minutes (plus sensor read time) but can be adjusted to whatever you like.

While the program is initializing, various progress messages will be displayed on the OLED LCD. Once the program has connected to the SensorTag and sensors are being read, some values will be continuously displayed and updated as follows.

From the top of the screen (see picture), Temperature and Humidity are displayed in a large font. Atmospheric pressure (AP) and Illumination (IL) are displayed on the next two lines. On the bottom line, the time (hh:mm) that the sensor was read is displayed on the left and on the right the current time is displayed (hh:min), which is updated every minute.

If you uncomment the program line,

//#define DEBUG // uncomment this line for serial monitor output

progress status when initializing and each sensor data line will be displayed on the serial monitor.

Libraries Used

The program uses a number of libraries (listed below with the versions used) that must be installed. These libraries do all of the real work.

  • WiFiNINA v1.40
  • ArduinoBLE v1.1.1
  • RTCZero 1.6.0
  • SD v1.2.3
  • U8g2 (U8x8) v2.26.14

The code was developed and tested using the Arduino IDE v1.8.10

This has been a fun and useful project. Both the Nano 33 IoT and MKR WiFi 1010, with their WiFi and BLE capabilities, along with the availability of some sophisticated libraries, made the project relatively easy to construct. Finally, only some of the available sensors on the SensorTag were used in the project, but it can be easily customized and expanded.

Code

YADL v1.0RArduino
Complete Program Code for the Data Logger
//*********************************************************************************
// YADL SensorTag Data Logger v1.0R [DrG]
//  ** This software is offered strictly as is with no guarantees or warranties. **
//  ** - USE IT AT YOUR OWN RISK!                                                **
//*********************************************************************************
//
// Uses:
// WiFiNINA v1.40
// RTCZero 1.6.0
// ArduinoBLE v1.1.1
// SD v1.2.3
// U8g2 (U8x8) v2.26.14
//
// Arduino IDE v1.8.10
//
// Hardware:
// MKR1010 / NANO 33 IOT (both have been tested)
// microSD card (both generics and MicroElectronika click board tested)
// HiLetgo 1.3" I2C monochrome OLED (others should work with modification)
//
// Note: Replace the code entries below with your network info:
// char ssid[] = "XXXXXXXXXX";
// char pass[] = "XXXXXXXXXX";
//
// Note: set GMT (with DST if needed) in code entry:
// const int GMT =XX
//
// #includes
#include <WiFiNINA.h>
#include <RTCZero.h>
#include <ArduinoBLE.h>
#include <SPI.h>
#include <SD.h>
#include <U8x8lib.h>
#include <avr/dtostrf.h> // needed for MKR1010

//#define DEBUG  // uncomment this line for serial monitor output

RTCZero rtc;
BLEDevice peripheral;
File SDF;

U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE);

// Global structure to hold the sensor data
struct DATA {
  float tem;     // HDC1000 temperature F
  float hum;     // HDC1000 relative humidity %RH
  float bptemp;  // BMP 280 die temperature F
  float bp;      // BMP 280 barometric pressure in hectoPascals (1 hPa = 100 Pa)
  float li;      // OPT3001 lux
  float temd;    // TMP007 die temperature F
  float temo;    // TMP007 object temperature F
};

typedef struct DATA DATA;
DATA SensorData;

// for SensorTag sensors
uint8_t sensorOn = 1;
uint8_t sensorOff = 0;

// lcd vars
char degree[] = {0xb0, 0x00};
char percent[] = {0x25, 0x00};
char p_buffer[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// clock change catcher
int lastmin;

// for RTC
unsigned long epoch;
int numberOfTries = 0, maxTries = 6;

int status = WL_IDLE_STATUS;  // WiFiNINA use

// Common user-changeable switches
const int GMT = -4; //change this to adapt it to your time zone
byte SDswitch = 1;  // SDswitch 1=ON (write to SD) or 0 (Do not write to SD)
char fname[] = "STDATA.txt"; // data log file Name

//period defines the length of time between measurements in milliseconds
// note that this does not includes delays for sensor reads (~8.7 sec)
long period = 600000L; // 10 minutes
//long period = 5000L;  // 5 sec for testing

char ssid[] = "";   // your network SSID (name)
char pass[] = "";   // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0;             // your network key Index number (needed only for WEP)

// SensorTag characteristic definitions
// BMP280
BLECharacteristic BPConCharacteristic;
BLECharacteristic BPValCharacteristic;
// OPT3001
BLECharacteristic OPTConCharacteristic;
BLECharacteristic OPTValCharacteristic;
// TMP007
BLECharacteristic IRTConCharacteristic;
BLECharacteristic IRTValCharacteristic;
// HDC1000
BLECharacteristic HUMConCharacteristic;
BLECharacteristic HUMValCharacteristic;
//------------------------------------------------------------------

void setup() {
  u8x8.begin(); // start lcd driver, will clear display
  u8x8.setFont(u8x8_font_amstrad_cpc_extended_f); // 8 X 8 font
  u8x8.drawString(0, 0, "YADL starting...");
#ifdef DEBUG
  Serial.begin(9600);
  delay(5000);  // delay for user to open the serial monitor
  Serial.println("YADL MKR1010/NANO IOT 33 SensorTag Data Logger");
  Serial.println();
#endif
  // check the SD card
  if (SDswitch == 1) {
    if (!SD.begin(4)) {
      u8x8.drawString(0, 2 , "No SD card!");
      u8x8.drawString(0, 3 , "Terminal Error!");
#ifdef DEBUG
      Serial.println("SD Card initialization failed!");
#endif
      while (1);
    }
    else {
      u8x8.drawString(0, 2 , "SD card found. ");
      delay(2000);    // to let user know
      u8x8.drawString(0, 2 , "               ");
#ifdef DEBUG
      Serial.println("SD Card found");
#endif
    }
  }
  else {
    u8x8.drawString(0, 2 , "No SD card");
    u8x8.drawString(0, 3 , "option");
    delay(2000);    // to let user know
    u8x8.drawString(0, 2 , "          ");
    u8x8.drawString(0, 3 , "      ");
#ifdef DEBUG
    Serial.println("No SD Card option");
#endif
  }
  delay(2000);    // to let user know about sd card
  // check if the WiFi module works
  if (WiFi.status() == WL_NO_SHIELD) {
#ifdef DEBUG
    Serial.println("WiFi shield not present");
#endif
    u8x8.drawString(0, 1 , "NO WiFi!");
    u8x8.drawString(0, 2 , "Terminal Error!");
    // don't continue:
    while (true);
  }

  // attempt to connect to WiFi network:
  u8x8.drawString(0, 2 , "Connecting.....");
  while ( status != WL_CONNECTED) {
#ifdef DEBUG
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
#endif
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);
    delay(10000); // wait 10 seconds for connection:
  }
  u8x8.drawString(0, 3 , "Connected!     ");
#ifdef DEBUG
  printWiFiStatus(); // you're connected now, so print out the status:
#endif
  rtc.begin();
  do {
    epoch = WiFi.getTime();
    numberOfTries++;
  }
  while ((epoch == 0) && (numberOfTries < maxTries));

  if (numberOfTries == maxTries) {
    u8x8.drawString(0, 4 , "NTP Unreachable");
    u8x8.drawString(0, 5 , "TERMINAL ERROR!");
#ifdef DEBUG
    Serial.print("NTP unreachable!!");
#endif
    while (1);
  }
  else {
    u8x8.drawString(0, 4 , "Got NTP Epoch  ");
    epoch = epoch + (GMT * 3600UL); // adjust offset for TZ/DST
    rtc.setEpoch(epoch);
#ifdef DEBUG
    Serial.print("Epoch received: ");
    Serial.print(epoch);
    Serial.print(" ");
    printP02D(rtc.getHours());
    Serial.print(":");
    printP02D(rtc.getMinutes());
    Serial.print(":");
    printP02D(rtc.getSeconds());
    Serial.print(" ");
    Serial.print(rtc.getDay());
    Serial.print("/");
    Serial.print(rtc.getMonth());
    Serial.print("/");
    Serial.print(rtc.getYear());
    Serial.println();
#endif
    WiFi.end();
    delay(15000);
    u8x8.drawString(0, 5 , "WiFi Ended     ");
    u8x8.drawString(0, 6 , "Starting BLE   ");
    delay(2000); // to see it on the screen
#ifdef DEBUG
    Serial.println("WiFi.end executed");
#endif
  }
  // Try to initialize BLE
  if (!BLE.begin()) {
    Serial.println("Terminal Error: Could not start BLE!");
    u8x8.clear();
    u8x8.drawString(0, 0 , "BLE Start Fail");
    u8x8.drawString(0, 1 , "Terminal Error!");
    while (1);
  }
  BLE.scan();
  u8x8.clear();
  u8x8.drawString(0, 0 , "Scanning......");
}
//------------------------------------------------------------------
void loop() {
  long lastMillis = 0;  // for period test
  long nowMillis = 0;   // for period test

  // check if a peripheral has been discovered
  peripheral = BLE.available();
  if (peripheral) {
    // discovered a peripheral, print out address and local name
#ifdef DEBUG
    Serial.print("Found ");
    Serial.print(peripheral.address());
    Serial.print(" '");
    Serial.print(peripheral.localName());
    Serial.println("' ");
#endif
    if (peripheral.localName() == "CC2650 SensorTag") {
      BLE.stopScan(); // stop scanning
      // connect to the peripheral
      u8x8.drawString(0, 1 , "Connecting....");
#ifdef DEBUG
      Serial.print("Connecting to SensorTag ...");
#endif
      if (peripheral.connect()) {
        u8x8.drawString(0, 2 , "Connected.....");
#ifdef DEBUG
        Serial.println("Connected...");
#endif
        do_discovery(peripheral);
        // Note: we do not subscribe to any services because we are not using notify
        u8x8.clear();
        print_screenT();
#ifdef DEBUG
        Serial.println("Reading sensors...");
        Serial.println();
#endif
      }
      else {
        u8x8.drawString(0, 2 , "Scanning......");
#ifdef DEBUG
        Serial.println(" scanning");
#endif
      }
    }
    // main while connected loop
    while (peripheral.connected()) {
      read_BP(peripheral);
      read_OPT(peripheral);
      read_IRT(peripheral);
      read_HUM(peripheral);
      if (SDswitch) write_SDdata(); // write data to sd card
      // screen for debug no print here as well
#ifdef DEBUG
      print_data();
#endif
      print_screenValues();
      printclockD(1); // Update sensor clock
      printclockD(2); // update current clock
      lastmin = rtc.getMinutes();
      lastMillis = millis();
      // stay here until the period is up
      // update current time here
      while ( ( (nowMillis = millis()) - lastMillis) <= period) {
        // need to update the clock here
        if (lastmin != rtc.getMinutes()) {
          lastmin = rtc.getMinutes();
          printclockD(2); // update current clock
        }
      }
    }
    // peripheral disconnected, start scanning again
    u8x8.clear();
    u8x8.drawString(0, 2 , "Scanning......");
#ifdef DEBUG
    Serial.println(" - rescan...");
#endif
    BLE.scan();
  }
}
//------------------------------------------------------------------

// BLE SensorTag routines
void do_discovery(BLEDevice peripheral) {
  // discover the peripheral's attributes that we want
  // barometric
#ifdef DEBUG
  Serial.print("Discovering attributes for Barometric Pressure service ...");
#endif
  if (peripheral.discoverService("f000aa40-0451-4000-b000-000000000000")) {
#ifdef DEBUG
    Serial.println("discovered");
#endif
    BPConCharacteristic = peripheral.characteristic("f000aa42-0451-4000-b000-000000000000");
    BPValCharacteristic = peripheral.characteristic("f000aa41-0451-4000-b000-000000000000");
  }
  else  {
#ifdef DEBUG
    Serial.println("ERROR: Barometric Pressure service discovery failed.");
#endif
    peripheral.disconnect();
    return;
  }
  // discover the peripheral's attributes that we want
  // optical sensor
#ifdef DEBUG
  Serial.print("Discovering attributes for Luxometer service ...");
#endif
  if (peripheral.discoverService("f000aa70-0451-4000-b000-000000000000")) {
#ifdef DEBUG
    Serial.println("discovered");
#endif
    OPTConCharacteristic = peripheral.characteristic("f000aa72-0451-4000-b000-000000000000");
    OPTValCharacteristic = peripheral.characteristic("f000aa71-0451-4000-b000-000000000000");
  }
  else  {
#ifdef DEBUG
    Serial.println("Error: Luxometer service discovery failed.");
#endif
    peripheral.disconnect();
    return;
  }
  // IR
#ifdef DEBUG
  Serial.print("Discovering attributes for Infrared service ...");
#endif
  if (peripheral.discoverService("f000aa00-0451-4000-b000-000000000000")) {
#ifdef DEBUG
    Serial.println("discovered");
#endif
    IRTConCharacteristic = peripheral.characteristic("f000aa02-0451-4000-b000-000000000000");
    IRTValCharacteristic = peripheral.characteristic("f000aa01-0451-4000-b000-000000000000");
  }
  else  {
#ifdef DEBUG
    Serial.println("Error: Infrared service discovery failed.");
#endif
    peripheral.disconnect();
    return;
  }
  // humidity
#ifdef DEBUG
  Serial.print("Discovering attributes for Humidity service ...");
#endif
  if (peripheral.discoverService("f000aa20-0451-4000-b000-000000000000")) {
#ifdef DEBUG
    Serial.println("discovered");
#endif
    HUMConCharacteristic = peripheral.characteristic("f000aa22-0451-4000-b000-000000000000");
    HUMValCharacteristic = peripheral.characteristic("f000aa21-0451-4000-b000-000000000000");
  }
  else  {
#ifdef DEBUG
    Serial.println("Error: Humidity service discovery failed.");
#endif
    peripheral.disconnect();
    return;
  }
}

// Sensor reads
void read_BP(BLEDevice peripheral) {
  uint8_t holdvalues[6];

  if (peripheral.connected()) {
    // wake up the sensor
    BPConCharacteristic.writeValue(sensorOn);
    delay(1200); // wait for the sensor to do a read
    BPValCharacteristic.readValue(holdvalues, 6);
    unsigned long rawbptemp = (holdvalues[2] * 65536) + (holdvalues[1] * 256) + holdvalues[0];
    unsigned int rawbp = (holdvalues[5] * 65536) + (holdvalues[4] * 256) + holdvalues[3];
    // sleep sensor
    BPConCharacteristic.writeValue(sensorOff);
    // calculate temperature and pressure final values
    float bptemp = ((double)rawbptemp / 100.0);
    bptemp = ((bptemp * 9.0) / 5.0) + 32.0; // convert to F - comment out to leave at C
    float bp = ((double)rawbp / 100.0);
    // save into the structure
    SensorData.bp = bp;
    SensorData.bptemp = bptemp;
  }
  else {
#ifdef DEBUG
    Serial.println(" *not connected* ");
#endif
  }
}

void read_OPT(BLEDevice peripheral) {
  // in this version the characteristic's value is read directly
  // into rawlux and then processed. No array is used.

  uint16_t rawlux;

  if (peripheral.connected()) {
    // wake up the sensor
    OPTConCharacteristic.writeValue(sensorOn);
    delay(1200); // wait for the sensor to do a read
    OPTValCharacteristic.readValue(rawlux);
    OPTConCharacteristic.writeValue(sensorOff); // sleep sensor
    // calculate lux final value
    unsigned int m = rawlux & 0x0FFF;
    unsigned int e = (rawlux & 0xF000) >> 12;
    float lux = (m * (0.01 * pow(2.0, e)));
    // save into the structure
    SensorData.li = lux;
  }
  else {
#ifdef DEBUG
    Serial.println(" *not connected* ");
#endif
  }
}

void read_IRT(BLEDevice peripheral) {
  uint8_t holdvalues[4];

  if (peripheral.connected()) {
    // wake up the sensor
    IRTConCharacteristic.writeValue((uint8_t) 0x01);
    delay(1200); // wait for the sensor to do a read
    IRTValCharacteristic.readValue(holdvalues, 4);
    unsigned int rawobj = (holdvalues[0]) + (holdvalues[1] * 256);
    unsigned int rawamb = (holdvalues[2]) + (holdvalues[3] * 256);
    IRTConCharacteristic.writeValue(sensorOff); // sleep sensor
    // calculate final temperature values
    const float SCALE_LSB = 0.03125;
    int it = (int)( rawobj >> 2);
    float IRTo = ( (float)it) * SCALE_LSB;
    IRTo = ( (IRTo * 9.0) / 5.0 ) + 32.0; // convert to F - comment out to leave at C
    it = (int)(rawamb >> 2);
    float IRTa = (float)it * SCALE_LSB;
    IRTa = ( (IRTa * 9.0) / 5.0) + 32.0; // convert to F - comment out to leave at C
    // save into the structure
    SensorData.temd = IRTa;
    SensorData.temo = IRTo;
  }
  else {
#ifdef DEBUG
    Serial.println(" *not connected* ");
#endif
  }
}

void read_HUM(BLEDevice peripheral) {
  uint8_t holdvalues[4]; // hold the characteristic's bytes

  if (peripheral.connected()) {
    // wake up sensor
    HUMConCharacteristic.writeValue(sensorOn);
    delay(1200); // wait for the sensor to do a read
    HUMValCharacteristic.readValue(holdvalues, 4);
    HUMConCharacteristic.writeValue(sensorOff); // sleep sensor
    unsigned int rawtem = (holdvalues[0]) + (holdvalues[1] * 256);
    unsigned int rawhum = (holdvalues[2]) + (holdvalues[3] * 256);
    // calculate final temperature and relative humidity values
    float temp = (rawtem / 65536.0) * 165.0 - 40.0;
    temp = ((temp * 9.0) / 5.0) + 32.0; // convert to F - comment out to leave at C
    float hum = ((double)rawhum / 65536.0) * 100.0;
    // save into the structure
    SensorData.tem = temp;
    SensorData.hum = hum;
  }
  else {
#ifdef DEBUG
    Serial.println(" *not connected* ");
#endif
  }
}

// Print serial and screen and write SD routines
void print_data() {
  // Print the data to the serial moniter
  // NOTE: the time vars could be slightly different then the SD card
  // since they are two different routines but the Serial prints are
  // mainly for debugging

  String separator = ", ";
  // Data Line as follow (with comma separator):
  // epoch day, month, year, hours, minutes, seconds, HDC1000 temp, HDC1000 hum,
  // BMP280 pressure, BMP280 tem, OPT3001 light (lux), TMP007 temp, TMP0007 object temp
#ifdef DEBUG
  Serial.print(rtc.getEpoch());
  Serial.print(separator);
  Serial.print(rtc.getDay());
  Serial.print(separator);
  Serial.print(rtc.getMonth());
  Serial.print(separator);
  Serial.print(rtc.getYear());
  Serial.print(separator);
  printP02D(rtc.getHours());
  Serial.print(separator);
  printP02D(rtc.getMinutes());
  Serial.print(separator);
  printP02D(rtc.getSeconds());
  Serial.print(separator);
  Serial.print(SensorData.tem);
  Serial.print(separator);
  Serial.print(SensorData.hum);
  Serial.print(separator);
  Serial.print(SensorData.bp);
  Serial.print(separator);
  Serial.print(SensorData.bptemp);
  Serial.print(separator);
  Serial.print(SensorData.li);
  Serial.print(separator);
  Serial.print(SensorData.temo);
  Serial.print(separator);
  Serial.print(SensorData.temd);
  Serial.println();
  // end of data line
#endif
}

void write_SDdata() {
  // Write the data to the SD card
  String separator = ", ";

  // Data Line as follow (with comma separator):
  // epoch day, month, year, hours, minutes, seconds, HDC1000 temp, HDC1000 hum,
  // BMP280 pressure, BMP280 tem, OPT3001 light (lux), TMP007 temp, TMP0007 object temp
  // open the file
  SDF = SD.open(fname, FILE_WRITE);
  if (!SDF) {
    // terminal error if we can't open the SD File (we already initialized)
    u8x8.clearDisplay();
    u8x8.drawString(0, 2 , "SD Card    ");
    u8x8.drawString(0, 3 , "Terminal Error!");
#ifdef DEBUG
    Serial.println("SD card write failure!");
#endif
    while (1);
  }
  else {
    // write the separator-delimited data line
    // comment out what you don't want e.g.,
    // epoch, day,mon,year,hour,min,sec, HDC tem, HDC hum, AP, BMP tem, Illum, TMP obj Tem, TMP tem
    SDF.print(rtc.getEpoch());
    SDF.print(separator);
    SDF.print(rtc.getDay());
    SDF.print(separator);
    SDF.print(rtc.getMonth());
    SDF.print(separator);
    SDF.print(rtc.getYear());
    SDF.print(separator);
    SDF.print(rtc.getHours());
    SDF.print(separator);
    SDF.print(rtc.getMinutes());
    SDF.print(separator);
    SDF.print(rtc.getSeconds());
    SDF.print(separator);
    SDF.print(SensorData.tem);
    SDF.print(separator);
    SDF.print(SensorData.hum);
    SDF.print(separator);
    SDF.print(SensorData.bp);
    SDF.print(separator);
    SDF.print(SensorData.bptemp);
    SDF.print(separator);
    SDF.print(SensorData.li);
    SDF.print(separator);
    SDF.print(SensorData.temo);
    SDF.print(separator);
    SDF.print(SensorData.temd);
    SDF.println();  // Windows cr/lf
    SDF.close();
  }
}

void print_screenT() {
  // print the LCD template
  u8x8.setFont(u8x8_font_px437wyse700a_2x2_f);   // large for Tem/Hum
  u8x8.drawString(0, 0, "T:");
  u8x8.drawUTF8(14, 0, degree);
  u8x8.drawString(0, 2, "H:");
  u8x8.drawUTF8(14, 2, percent);
  // back to smaller font for tyhe rest
  u8x8.setFont(u8x8_font_amstrad_cpc_extended_f);
  u8x8.drawString(0, 5, "AP:");
  //u8x8.drawString(0, 5, "BP:    0123 hPa");
  u8x8.setCursor(10, 5);
  u8x8.print(" hPa");
  u8x8.drawString(0, 6, "IL:");
  u8x8.setCursor(10, 6);
  u8x8.print(" lux");
  // alternative times current left
  u8x8.drawString(0, 7, "00:00");
  u8x8.drawString(11, 7, "00:00");
}

void print_screenValues() {
  float Dtem, Dhum, Dap, Dli;
  // call this *after* sensor structure has been updated
  // first update the logged time? need small font
  u8x8.setFont(u8x8_font_px437wyse700a_2x2_f);   // large for Tem/Hum
  // temperature
  // sensor error check NOTE: read values will be printed to screen and SD
  Dtem = SensorData.tem;
  if (Dtem > 999.9) Dtem = 999.9;
  if (Dtem < -99.9) Dtem = -99.9;
  dtostrf(Dtem, 5, 1, p_buffer); // convert to 5 chars 1 after decimal
  u8x8.setCursor(4, 0);
  u8x8.print(p_buffer);
  //u8x8.drawUTF8(14, 0, degree); degree sign has been done in
  // humidity
  // sensor error check
  Dhum = SensorData.hum;
  if (Dhum > 100.0) Dhum = 100.0;
  if (Dhum < 0.0) Dhum = 0.0;
  dtostrf(Dhum, 5, 1, p_buffer); // convert to 5 chars 1 after decimal
  u8x8.setCursor(4, 2);
  u8x8.print(p_buffer);
  //u8x8.drawUTF8(14, 2, percent);  already done in template print
  // back to smaller font for tyhe rest
  u8x8.setFont(u8x8_font_amstrad_cpc_extended_f);
  // barometric pressure
  // sensor error check
  Dap = SensorData.bp;
  if (Dap < 750.0) Dap = 000.0;
  if (Dap > 1200.0) Dap = 9999.9;
  dtostrf(Dap, 7, 1, p_buffer); // convert to 7 chars 1 after decimal
  u8x8.setCursor(3, 5);
  u8x8.print(p_buffer);
  // Illuminance
  // sensor error check
  Dli = SensorData.li;
  if (Dli > 99999.9) Dli = 99999.9;
  if (Dli < 0.0) Dli = 0;
  dtostrf(Dli, 7, 1, p_buffer); // convert to 7 chars 1 after decimal
  u8x8.setCursor(3, 6);
  u8x8.print(p_buffer);
}

void printclockD(byte side) {
  // print the HH:SS of the current clock on the right or left side of the LCD
  // must be in a small font! (could do this as a switch case)
int hourT,minT;

  switch (side) {
    case 1: //left side
      u8x8.setCursor(0, 7);
      hourT = rtc.getHours();
      if (hourT < 10) { // pad hours <10
        u8x8.drawString(0, 7 , "0");
        u8x8.setCursor(1, 7);
        u8x8.print(hourT);
      }
      else {
        u8x8.print(hourT);
      }
      // note the ':' is from the template
      u8x8.setCursor(3, 7);
      minT = rtc.getMinutes();
      if (minT < 10) { // pad seconds <10
        u8x8.drawString(3, 7 , "0");
        u8x8.setCursor(4, 7);
        u8x8.print(minT);
      }
      else {
        u8x8.print(minT);
      }
      break;
    case 2: // right side
      u8x8.setCursor(11, 7);
      hourT = rtc.getHours();
      if (hourT < 10) { // pad hours <10
        u8x8.drawString(11, 7 , "0");
        u8x8.setCursor(12, 7);
        u8x8.print(hourT);
      }
      else {
        u8x8.print(hourT);
      }
      // note the ':' is from the template
      u8x8.setCursor(14, 7);
      minT = rtc.getMinutes();
      if (minT < 10) { // pad secondss <10
        u8x8.drawString(14, 7 , "0");
        u8x8.setCursor(15, 7);
        u8x8.print(minT);
      }
      else {
        u8x8.print(minT);
      }
      break;
    default:  // can add other options
      // statements
      break;
  }
}

void printWiFiStatus() {
  // note: this will only be called if DEBUG is defines
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

void printP02D(int number) {
  if (number < 10) Serial.print('0');
  Serial.print(number);
}

// End of code
YADL_V1_0RArduino
Complete program code for the data logger
//*********************************************************************************
// YADL SensorTag Data Logger v1.0R [DrG]
//  ** This software is offered strictly as is with no guarantees or warranties. **
//  ** - USE IT AT YOUR OWN RISK!                                                **
//*********************************************************************************
//
// Uses:
// WiFiNINA v1.40
// RTCZero 1.6.0
// ArduinoBLE v1.1.1
// SD v1.2.3
// U8g2 (U8x8) v2.26.14
//
// Arduino IDE v1.8.10
//
// Hardware:
// MKR1010 / NANO 33 IOT (both have been tested)
// microSD card (both generics and MicroElectronika click board tested)
// HiLetgo 1.3" I2C monochrome OLED (others should work with modification)
//
// Note: Replace the code entries below with your network info:
// char ssid[] = "XXXXXXXXXX";
// char pass[] = "XXXXXXXXXX";
//
// Note: set GMT (with DST if needed) in code entry:
// const int GMT =XX
//
// #includes
#include <WiFiNINA.h>
#include <RTCZero.h>
#include <ArduinoBLE.h>
#include <SPI.h>
#include <SD.h>
#include <U8x8lib.h>
#include <avr/dtostrf.h> // needed for MKR1010

//#define DEBUG  // uncomment this line for serial monitor output

RTCZero rtc;
BLEDevice peripheral;
File SDF;

U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(U8X8_PIN_NONE);

// Global structure to hold the sensor data
struct DATA {
  float tem;     // HDC1000 temperature F
  float hum;     // HDC1000 relative humidity %RH
  float bptemp;  // BMP 280 die temperature F
  float bp;      // BMP 280 barometric pressure in hectoPascals (1 hPa = 100 Pa)
  float li;      // OPT3001 lux
  float temd;    // TMP007 die temperature F
  float temo;    // TMP007 object temperature F
};

typedef struct DATA DATA;
DATA SensorData;

// for SensorTag sensors
uint8_t sensorOn = 1;
uint8_t sensorOff = 0;

// lcd vars
char degree[] = {0xb0, 0x00};
char percent[] = {0x25, 0x00};
char p_buffer[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// clock change catcher
int lastmin;

// for RTC
unsigned long epoch;
int numberOfTries = 0, maxTries = 6;

int status = WL_IDLE_STATUS;  // WiFiNINA use

// Common user-changeable switches
const int GMT = -2; //change this to adapt it to your time zone
byte SDswitch = 1;  // SDswitch 1=ON (write to SD) or 0 (Do not write to SD)
char fname[] = "STDATA.txt"; // data log file Name

//period defines the length of time between measurements in milliseconds
// note that this does not includes delays for sensor reads (~8.7 sec)
long period = 600000L; // 10 minutes
//long period = 5000L;  // 5 sec for testing

char ssid[] = "";   // your network SSID (name)
char pass[] = "";   // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0;             // your network key Index number (needed only for WEP)

// SensorTag characteristic definitions
// BMP280
BLECharacteristic BPConCharacteristic;
BLECharacteristic BPValCharacteristic;
// OPT3001
BLECharacteristic OPTConCharacteristic;
BLECharacteristic OPTValCharacteristic;
// TMP007
BLECharacteristic IRTConCharacteristic;
BLECharacteristic IRTValCharacteristic;
// HDC1000
BLECharacteristic HUMConCharacteristic;
BLECharacteristic HUMValCharacteristic;
//------------------------------------------------------------------

void setup() {
  u8x8.begin(); // start lcd driver, will clear display
  u8x8.setFont(u8x8_font_amstrad_cpc_extended_f); // 8 X 8 font
  u8x8.drawString(0, 0, "YADL starting...");
#ifdef DEBUG
  Serial.begin(9600);
  delay(5000);  // delay for user to open the serial monitor
  Serial.println("YADL MKR1010/NANO IOT 33 SensorTag Data Logger");
  Serial.println();
#endif
  // check the SD card
  if (SDswitch == 1) {
    if (!SD.begin(4)) {
      u8x8.drawString(0, 2 , "No SD card!");
      u8x8.drawString(0, 3 , "Terminal Error!");
#ifdef DEBUG
      Serial.println("SD Card initialization failed!");
#endif
      while (1);
    }
    else {
      u8x8.drawString(0, 2 , "SD card found. ");
      delay(2000);    // to let user know
      u8x8.drawString(0, 2 , "               ");
#ifdef DEBUG
      Serial.println("SD Card found");
#endif
    }
  }
  else {
    u8x8.drawString(0, 2 , "No SD card");
    u8x8.drawString(0, 3 , "option");
    delay(2000);    // to let user know
    u8x8.drawString(0, 2 , "          ");
    u8x8.drawString(0, 3 , "      ");
#ifdef DEBUG
    Serial.println("No SD Card option");
#endif
  }
  delay(2000);    // to let user know about sd card
  // check if the WiFi module works
  if (WiFi.status() == WL_NO_SHIELD) {
#ifdef DEBUG
    Serial.println("WiFi shield not present");
#endif
    u8x8.drawString(0, 1 , "NO WiFi!");
    u8x8.drawString(0, 2 , "Terminal Error!");
    // don't continue:
    while (true);
  }

  // attempt to connect to WiFi network:
  u8x8.drawString(0, 2 , "Connecting.....");
  while ( status != WL_CONNECTED) {
#ifdef DEBUG
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
#endif
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);
    delay(10000); // wait 10 seconds for connection:
  }
  u8x8.drawString(0, 3 , "Connected!     ");
#ifdef DEBUG
  printWiFiStatus(); // you're connected now, so print out the status:
#endif
  rtc.begin();
  do {
    epoch = WiFi.getTime();
    numberOfTries++;
  }
  while ((epoch == 0) && (numberOfTries < maxTries));

  if (numberOfTries == maxTries) {
    u8x8.drawString(0, 4 , "NTP Unreachable");
    u8x8.drawString(0, 5 , "TERMINAL ERROR!");
#ifdef DEBUG
    Serial.print("NTP unreachable!!");
#endif
    while (1);
  }
  else {
    u8x8.drawString(0, 4 , "Got NTP Epoch  ");
    epoch = epoch + (GMT * 3600UL); // adjust offset for TZ/DST
    rtc.setEpoch(epoch);
#ifdef DEBUG
    Serial.print("Epoch received: ");
    Serial.print(epoch);
    Serial.print(" ");
    printP02D(rtc.getHours());
    Serial.print(":");
    printP02D(rtc.getMinutes());
    Serial.print(":");
    printP02D(rtc.getSeconds());
    Serial.print(" ");
    Serial.print(rtc.getDay());
    Serial.print("/");
    Serial.print(rtc.getMonth());
    Serial.print("/");
    Serial.print(rtc.getYear());
    Serial.println();
#endif
    WiFi.end();
    delay(15000);
    u8x8.drawString(0, 5 , "WiFi Ended     ");
    u8x8.drawString(0, 6 , "Starting BLE   ");
    delay(2000); // to see it on the screen
#ifdef DEBUG
    Serial.println("WiFi.end executed");
#endif
  }
  // Try to initialize BLE
  if (!BLE.begin()) {
    Serial.println("Terminal Error: Could not start BLE!");
    u8x8.clear();
    u8x8.drawString(0, 0 , "BLE Start Fail");
    u8x8.drawString(0, 1 , "Terminal Error!");
    while (1);
  }
  BLE.scan();
  u8x8.clear();
  u8x8.drawString(0, 0 , "Scanning......");
}
//------------------------------------------------------------------
void loop() {
  long lastMillis = 0;  // for period test
  long nowMillis = 0;   // for period test

  // check if a peripheral has been discovered
  peripheral = BLE.available();
  if (peripheral) {
    // discovered a peripheral, print out address and local name
#ifdef DEBUG
    Serial.print("Found ");
    Serial.print(peripheral.address());
    Serial.print(" '");
    Serial.print(peripheral.localName());
    Serial.println("' ");
#endif
    if (peripheral.localName() == "CC2650 SensorTag") {
      BLE.stopScan(); // stop scanning
      // connect to the peripheral
      u8x8.drawString(0, 1 , "Connecting....");
#ifdef DEBUG
      Serial.print("Connecting to SensorTag ...");
#endif
      if (peripheral.connect()) {
        u8x8.drawString(0, 2 , "Connected.....");
#ifdef DEBUG
        Serial.println("Connected...");
#endif
        do_discovery(peripheral);
        // Note: we do not subscribe to any services because we are not using notify
        u8x8.clear();
        print_screenT();
#ifdef DEBUG
        Serial.println("Reading sensors...");
        Serial.println();
#endif
      }
      else {
        u8x8.drawString(0, 2 , "Scanning......");
#ifdef DEBUG
        Serial.println(" scanning");
#endif
      }
    }
    // main while connected loop
    while (peripheral.connected()) {
      read_BP(peripheral);
      read_OPT(peripheral);
      read_IRT(peripheral);
      read_HUM(peripheral);
      if (SDswitch) write_SDdata(); // write data to sd card
      // screen for debug no print here as well
#ifdef DEBUG
      print_data();
#endif
      print_screenValues();
      printclockD(1); // Update sensor clock
      printclockD(2); // update current clock
      lastmin = rtc.getMinutes();
      lastMillis = millis();
      // stay here until the period is up
      // update current time here
      while ( ( (nowMillis = millis()) - lastMillis) <= period) {
        // need to update the clock here
        if (lastmin != rtc.getMinutes()) {
          lastmin = rtc.getMinutes();
          printclockD(2); // update current clock
        }
      }
    }
    // peripheral disconnected, start scanning again
    u8x8.clear();
    u8x8.drawString(0, 2 , "Scanning......");
#ifdef DEBUG
    Serial.println(" - rescan...");
#endif
    BLE.scan();
  }
}
//------------------------------------------------------------------

// BLE SensorTag routines
void do_discovery(BLEDevice peripheral) {
  // discover the peripheral's attributes that we want
  // barometric
#ifdef DEBUG
  Serial.print("Discovering attributes for Barometric Pressure service ...");
#endif
  if (peripheral.discoverService("f000aa40-0451-4000-b000-000000000000")) {
#ifdef DEBUG
    Serial.println("discovered");
#endif
    BPConCharacteristic = peripheral.characteristic("f000aa42-0451-4000-b000-000000000000");
    BPValCharacteristic = peripheral.characteristic("f000aa41-0451-4000-b000-000000000000");
  }
  else  {
#ifdef DEBUG
    Serial.println("ERROR: Barometric Pressure service discovery failed.");
#endif
    peripheral.disconnect();
    return;
  }
  // discover the peripheral's attributes that we want
  // optical sensor
#ifdef DEBUG
  Serial.print("Discovering attributes for Luxometer service ...");
#endif
  if (peripheral.discoverService("f000aa70-0451-4000-b000-000000000000")) {
#ifdef DEBUG
    Serial.println("discovered");
#endif
    OPTConCharacteristic = peripheral.characteristic("f000aa72-0451-4000-b000-000000000000");
    OPTValCharacteristic = peripheral.characteristic("f000aa71-0451-4000-b000-000000000000");
  }
  else  {
#ifdef DEBUG
    Serial.println("Error: Luxometer service discovery failed.");
#endif
    peripheral.disconnect();
    return;
  }
  // IR
#ifdef DEBUG
  Serial.print("Discovering attributes for Infrared service ...");
#endif
  if (peripheral.discoverService("f000aa00-0451-4000-b000-000000000000")) {
#ifdef DEBUG
    Serial.println("discovered");
#endif
    IRTConCharacteristic = peripheral.characteristic("f000aa02-0451-4000-b000-000000000000");
    IRTValCharacteristic = peripheral.characteristic("f000aa01-0451-4000-b000-000000000000");
  }
  else  {
#ifdef DEBUG
    Serial.println("Error: Infrared service discovery failed.");
#endif
    peripheral.disconnect();
    return;
  }
  // humidity
#ifdef DEBUG
  Serial.print("Discovering attributes for Humidity service ...");
#endif
  if (peripheral.discoverService("f000aa20-0451-4000-b000-000000000000")) {
#ifdef DEBUG
    Serial.println("discovered");
#endif
    HUMConCharacteristic = peripheral.characteristic("f000aa22-0451-4000-b000-000000000000");
    HUMValCharacteristic = peripheral.characteristic("f000aa21-0451-4000-b000-000000000000");
  }
  else  {
#ifdef DEBUG
    Serial.println("Error: Humidity service discovery failed.");
#endif
    peripheral.disconnect();
    return;
  }
}

// Sensor reads
void read_BP(BLEDevice peripheral) {
  uint8_t holdvalues[6];

  if (peripheral.connected()) {
    // wake up the sensor
    BPConCharacteristic.writeValue(sensorOn);
    delay(1200); // wait for the sensor to do a read
    BPValCharacteristic.readValue(holdvalues, 6);
    unsigned long rawbptemp = (holdvalues[2] * 65536) + (holdvalues[1] * 256) + holdvalues[0];
    unsigned int rawbp = (holdvalues[5] * 65536) + (holdvalues[4] * 256) + holdvalues[3];
    // sleep sensor
    BPConCharacteristic.writeValue(sensorOff);
    // calculate temperature and pressure final values
    float bptemp = ((double)rawbptemp / 100.0);
    bptemp = ((bptemp * 9.0) / 5.0) + 32.0; // convert to F - comment out to leave at C
    float bp = ((double)rawbp / 100.0);
    // save into the structure
    SensorData.bp = bp;
    SensorData.bptemp = bptemp;
  }
  else {
#ifdef DEBUG
    Serial.println(" *not connected* ");
#endif
  }
}

void read_OPT(BLEDevice peripheral) {
  // for this sensor the characteristic's value is read directly
  // into rawlux and then processed. No array is used.

  uint16_t rawlux;

  if (peripheral.connected()) {
    // wake up the sensor
    OPTConCharacteristic.writeValue(sensorOn);
    delay(1200); // wait for the sensor to do a read
    OPTValCharacteristic.readValue(rawlux);
    OPTConCharacteristic.writeValue(sensorOff); // sleep sensor
    // calculate lux final value
    unsigned int m = rawlux & 0x0FFF;
    unsigned int e = (rawlux & 0xF000) >> 12;
    float lux = (m * (0.01 * pow(2.0, e)));
    // save into the structure
    SensorData.li = lux;
  }
  else {
#ifdef DEBUG
    Serial.println(" *not connected* ");
#endif
  }
}

void read_IRT(BLEDevice peripheral) {
  uint8_t holdvalues[4];

  if (peripheral.connected()) {
    // wake up the sensor
    IRTConCharacteristic.writeValue((uint8_t) 0x01);
    delay(1200); // wait for the sensor to do a read
    IRTValCharacteristic.readValue(holdvalues, 4);
    unsigned int rawobj = (holdvalues[0]) + (holdvalues[1] * 256);
    unsigned int rawamb = (holdvalues[2]) + (holdvalues[3] * 256);
    IRTConCharacteristic.writeValue(sensorOff); // sleep sensor
    // calculate final temperature values
    const float SCALE_LSB = 0.03125;
    int it = (int)( rawobj >> 2);
    float IRTo = ( (float)it) * SCALE_LSB;
    IRTo = ( (IRTo * 9.0) / 5.0 ) + 32.0; // convert to F - comment out to leave at C
    it = (int)(rawamb >> 2);
    float IRTa = (float)it * SCALE_LSB;
    IRTa = ( (IRTa * 9.0) / 5.0) + 32.0; // convert to F - comment out to leave at C
    // save into the structure
    SensorData.temd = IRTa;
    SensorData.temo = IRTo;
  }
  else {
#ifdef DEBUG
    Serial.println(" *not connected* ");
#endif
  }
}

void read_HUM(BLEDevice peripheral) {
  uint8_t holdvalues[4]; // hold the characteristic's bytes

  if (peripheral.connected()) {
    // wake up sensor
    HUMConCharacteristic.writeValue(sensorOn);
    delay(1200); // wait for the sensor to do a read
    HUMValCharacteristic.readValue(holdvalues, 4);
    HUMConCharacteristic.writeValue(sensorOff); // sleep sensor
    unsigned int rawtem = (holdvalues[0]) + (holdvalues[1] * 256);
    unsigned int rawhum = (holdvalues[2]) + (holdvalues[3] * 256);
    // calculate final temperature and relative humidity values
    float temp = (rawtem / 65536.0) * 165.0 - 40.0;
    temp = ((temp * 9.0) / 5.0) + 32.0; // convert to F - comment out to leave at C
    float hum = ((double)rawhum / 65536.0) * 100.0;
    // save into the structure
    SensorData.tem = temp;
    SensorData.hum = hum;
  }
  else {
#ifdef DEBUG
    Serial.println(" *not connected* ");
#endif
  }
}

// Print serial and screen and write SD routines
void print_data() {
  // Print the data to the serial moniter
  // NOTE: the time vars could be slightly different than the SD card
  // since they are two different routines but the Serial prints are
  // mainly for debugging

  String separator = ", ";
  // Data Line as follow (with comma separator):
  // epoch day, month, year, hours, minutes, seconds, HDC1000 temp, HDC1000 hum,
  // BMP280 pressure, BMP280 tem, OPT3001 light (lux), TMP007 object temp, TMP007 temp
#ifdef DEBUG
  Serial.print(rtc.getEpoch());
  Serial.print(separator);
  Serial.print(rtc.getDay());
  Serial.print(separator);
  Serial.print(rtc.getMonth());
  Serial.print(separator);
  Serial.print(rtc.getYear());
  Serial.print(separator);
  printP02D(rtc.getHours());
  Serial.print(separator);
  printP02D(rtc.getMinutes());
  Serial.print(separator);
  printP02D(rtc.getSeconds());
  Serial.print(separator);
  Serial.print(SensorData.tem);
  Serial.print(separator);
  Serial.print(SensorData.hum);
  Serial.print(separator);
  Serial.print(SensorData.bp);
  Serial.print(separator);
  Serial.print(SensorData.bptemp);
  Serial.print(separator);
  Serial.print(SensorData.li);
  Serial.print(separator);
  Serial.print(SensorData.temo);
  Serial.print(separator);
  Serial.print(SensorData.temd);
  Serial.println();
  // end of data line
#endif
}

void write_SDdata() {
  // Write the data to the SD card
  String separator = ", ";

  // Data Line as follow (with comma separator):
  // epoch day, month, year, hours, minutes, seconds, HDC1000 temp, HDC1000 hum,
  // BMP280 pressure, BMP280 tem, OPT3001 light (lux), TMP007 temp, TMP0007 object temp
  // open the file
  SDF = SD.open(fname, FILE_WRITE);
  if (!SDF) {
    // terminal error if we can't open the SD File (we already initialized)
    u8x8.clearDisplay();
    u8x8.drawString(0, 2 , "SD Card    ");
    u8x8.drawString(0, 3 , "Terminal Error!");
#ifdef DEBUG
    Serial.println("SD card write failure!");
#endif
    while (1);
  }
  else {
    // write the separator-delimited data line
    // comment out what you don't want e.g.,
    // epoch, day,mon,year,hour,min,sec, HDC tem, HDC hum, AP, BMP tem, Illum, TMP obj Tem, TMP tem
    SDF.print(rtc.getEpoch());
    SDF.print(separator);
    SDF.print(rtc.getDay());
    SDF.print(separator);
    SDF.print(rtc.getMonth());
    SDF.print(separator);
    SDF.print(rtc.getYear());
    SDF.print(separator);
    SDF.print(rtc.getHours());
    SDF.print(separator);
    SDF.print(rtc.getMinutes());
    SDF.print(separator);
    SDF.print(rtc.getSeconds());
    SDF.print(separator);
    SDF.print(SensorData.tem);
    SDF.print(separator);
    SDF.print(SensorData.hum);
    SDF.print(separator);
    SDF.print(SensorData.bp);
    SDF.print(separator);
    SDF.print(SensorData.bptemp);
    SDF.print(separator);
    SDF.print(SensorData.li);
    SDF.print(separator);
    SDF.print(SensorData.temo);
    SDF.print(separator);
    SDF.print(SensorData.temd);
    SDF.println();  // Windows cr/lf
    SDF.close();
  }
}

void print_screenT() {
  // print the LCD template
  u8x8.setFont(u8x8_font_px437wyse700a_2x2_f);   // large for Tem/Hum
  u8x8.drawString(0, 0, "T:");
  u8x8.drawUTF8(14, 0, degree);
  u8x8.drawString(0, 2, "H:");
  u8x8.drawUTF8(14, 2, percent);
  // back to smaller font for tyhe rest
  u8x8.setFont(u8x8_font_amstrad_cpc_extended_f);
  u8x8.drawString(0, 5, "AP:");
  //u8x8.drawString(0, 5, "BP:    0123 hPa");
  u8x8.setCursor(10, 5);
  u8x8.print(" hPa");
  u8x8.drawString(0, 6, "IL:");
  u8x8.setCursor(10, 6);
  u8x8.print(" lux");
  // alternative times current left
  u8x8.drawString(0, 7, "00:00");
  u8x8.drawString(11, 7, "00:00");
}

void print_screenValues() {
  float Dtem, Dhum, Dap, Dli;
  // call this *after* sensor structure has been updated
  // first update the logged time? need small font
  u8x8.setFont(u8x8_font_px437wyse700a_2x2_f);   // large for Tem/Hum
  // temperature
  // sensor error check NOTE: read values will be printed to screen and SD
  Dtem = SensorData.tem;
  if (Dtem > 999.9) Dtem = 999.9;
  if (Dtem < -99.9) Dtem = -99.9;
  dtostrf(Dtem, 5, 1, p_buffer); // convert to 5 chars 1 after decimal
  u8x8.setCursor(4, 0);
  u8x8.print(p_buffer);
  //u8x8.drawUTF8(14, 0, degree); degree sign has been done in
  // humidity
  // sensor error check
  Dhum = SensorData.hum;
  if (Dhum > 100.0) Dhum = 100.0;
  if (Dhum < 0.0) Dhum = 0.0;
  dtostrf(Dhum, 5, 1, p_buffer); // convert to 5 chars 1 after decimal
  u8x8.setCursor(4, 2);
  u8x8.print(p_buffer);
  //u8x8.drawUTF8(14, 2, percent);  already done in template print
  // back to smaller font for tyhe rest
  u8x8.setFont(u8x8_font_amstrad_cpc_extended_f);
  // barometric pressure
  // sensor error check
  Dap = SensorData.bp;
  if (Dap < 750.0) Dap = 000.0;
  if (Dap > 1200.0) Dap = 9999.9;
  dtostrf(Dap, 7, 1, p_buffer); // convert to 7 chars 1 after decimal
  u8x8.setCursor(3, 5);
  u8x8.print(p_buffer);
  // Illuminance
  // sensor error check
  Dli = SensorData.li;
  if (Dli > 99999.9) Dli = 99999.9;
  if (Dli < 0.0) Dli = 0;
  dtostrf(Dli, 7, 1, p_buffer); // convert to 7 chars 1 after decimal
  u8x8.setCursor(3, 6);
  u8x8.print(p_buffer);
}

void printclockD(byte side) {
  // print the HH:SS of the current clock on the right or left side of the LCD
  // must be in a small font! (could do this as a switch case)
int hourT,minT;

  switch (side) {
    case 1: //left side
      u8x8.setCursor(0, 7);
      hourT = rtc.getHours();
      if (hourT < 10) { // pad hours <10
        u8x8.drawString(0, 7 , "0");
        u8x8.setCursor(1, 7);
        u8x8.print(hourT);
      }
      else {
        u8x8.print(hourT);
      }
      // note the ':' is from the template
      u8x8.setCursor(3, 7);
      minT = rtc.getMinutes();
      if (minT < 10) { // pad seconds <10
        u8x8.drawString(3, 7 , "0");
        u8x8.setCursor(4, 7);
        u8x8.print(minT);
      }
      else {
        u8x8.print(minT);
      }
      break;
    case 2: // right side
      u8x8.setCursor(11, 7);
      hourT = rtc.getHours();
      if (hourT < 10) { // pad hours <10
        u8x8.drawString(11, 7 , "0");
        u8x8.setCursor(12, 7);
        u8x8.print(hourT);
      }
      else {
        u8x8.print(hourT);
      }
      // note the ':' is from the template
      u8x8.setCursor(14, 7);
      minT = rtc.getMinutes();
      if (minT < 10) { // pad secondss <10
        u8x8.drawString(14, 7 , "0");
        u8x8.setCursor(15, 7);
        u8x8.print(minT);
      }
      else {
        u8x8.print(minT);
      }
      break;
    default:  // can add other options
      // statements
      break;
  }
}

void printWiFiStatus() {
  // note: this will only be called if DEBUG is defines
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

void printP02D(int number) {
  if (number < 10) Serial.print('0');
  Serial.print(number);
}

// End of code

Schematics

Wiring Diagram
Use these connections for either the NANO 33 IOT or MKR 1010 board to the SPI SD card module and the OLED LCD display.
Mkrdlconnections 8rklq59iem

Comments

Similar projects you might like

Temperature and Humidity Data Logger

Project tutorial by Wimpie van den Berg

  • 30,080 views
  • 2 comments
  • 27 respects

MKR Zero Weather Data Logger

Project tutorial by Arduino_Genuino

  • 28,902 views
  • 18 comments
  • 59 respects

Rube Goldberg Weather Station with Internet Data Storage

Project in progress by randtekk

  • 9,407 views
  • 7 comments
  • 48 respects

Temperature and Humidity Logger Per Hour

Project tutorial by lefteris1993

  • 2,616 views
  • 0 comments
  • 13 respects

How to Build an Arduino Energy Monitor and Data Logger

Project tutorial by Sridhar Rajagopal

  • 9,287 views
  • 2 comments
  • 22 respects

Temperature and Humidity Logger (Using Arduino)

Project showcase by lmsousa

  • 12,188 views
  • 8 comments
  • 38 respects
Add projectSign up / Login