ESP32 Environment Monitoring Project

Want to create a little device that is just loaded with Sensor about the current environment. I'm have always loved the Cyberpunk style so I figured I would do something like that. This page is to document my progress as I build it.

Some Styling Ideas

Current Progress

Still working on a breadboard getting all the sensors loaded and basic coded to work with each other. The display is basic just to show what is currently being read by the sensor. Final idea would be an avatar giving you quick idea of state of the environment by animations with maybe some basic stuff laid out around it.

/*************************************************** 
This is a example sketch demonstrating graphic drawing
capabilities of the SSD1351 library for the 1.5"
and 1.27" 16-bit Color OLEDs with SSD1351 driver chip

Pick one up today in the adafruit shop!
------> http://www.adafruit.com/products/1431
------> http://www.adafruit.com/products/1673

If you're using a 1.27" OLED, change SCREEN_HEIGHT to 96 instead of 128.

These displays use SPI to communicate, 4 or 5 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!

Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution

The Adafruit GFX Graphics core library is also required
https://github.com/adafruit/Adafruit-GFX-Library
Be sure to install it!
****************************************************/

#define DEBUG_OUTPUT true

// Screen dimensions
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 128 // Change this to 96 for 1.27" OLED.

//GPIO for Screen
#define SCLK_PIN 18
#define MOSI_PIN 23
#define DC_PIN 27
#define CS_PIN 2
#define RST_PIN 4

//GPIO for DHT11
//#define DHTPIN 32 // Digital pin connected to the DHT sensor
//#define DHTTYPE DHT11 // DHT 11 Sensor Type

//GPIO for CJMCU-680 (Temperature, Humidity, Pressure, Gas Sensor)
//I2C Pin
#define CJMCU_SCL 22
#define CJMCU_SDA 21
#define SEALEVELPRESSURE_HPA (1015.00)

//GPIO for SD Card SAME SPI as screen
#define SD_CS 5 // SD card select pin


//GPIO for TEMT6000 Ambient Light Sensor
#define LIGHTSENSORPIN 36 //Ambient light sensor reading
#define POWER 4 /* Power to raise the ratio of read-value to largest read-value.
* Higher is less likely to be on during the day,
* but too high and it may not turn on once it's night.*/

//GPIO for Buttons
#define BUT_ONE 34

// Color definitions
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF

//Libraries to be included
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1351.h>
#include <SdFat.h> // SD card & FAT filesystem library
#include <Adafruit_SPIFlash.h> // SPI / QSPI flash library
#include <Adafruit_ImageReader.h> // Image-reading functions
#include <Adafruit_Sensor.h>
#include "Adafruit_BME680.h"
#include <Adafruit_ADXL345_U.h>
#include <SPI.h>
#include <Wire.h>

//Screen over SPI for speed
Adafruit_SSD1351 tft = Adafruit_SSD1351(SCREEN_WIDTH, SCREEN_HEIGHT, &SPI, CS_PIN, DC_PIN, RST_PIN);
Adafruit_BME680 bme; // I2C
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(4242);//I2C


int screen_state = 1; //Running
//0 if off
int no_input_count = 0; //Track how long since the last input in Tick Count


//SD Card Setup
SdFat SD; // SD card filesystem
Adafruit_ImageReader reader(SD); // Image-reader object, pass in SD filesys

Adafruit_Image img; // An image loaded into RAM
int32_t width = 0, // BMP image dimensions
height = 0;

//TEMT6000 Variables
uint16_t _max = 0; //Holds the largest brightness reading so far.
float ratio = 0; //Stores the ratio of current brightness to largest recorded brightness.

//Buttons Variables
// variables will change:
int buttonState_One = 0; // variable for reading the pushbutton status

//Variables for the Program as a Whole
uint32_t Tick_Count = 0;

void setup(void) {
//Serial Port Setup ################################################################
Serial.begin(115200);
Serial.println("Serial Started");

//Screen Start ################################################################
Serial.println("Screen Init");
tft.begin();
// You can optionally rotate the display by running the line below.
// Note that a value of 0 means no rotation, 1 means 90 clockwise,
// 2 means 180 degrees clockwise, and 3 means 270 degrees clockwise.
tft.setRotation(3);
lcdTestPattern();
//tft.fillScreen(BLACK);

//BME Sensor ###################################################################
if (!bme.begin()) {
Serial.println(F("Could not find a valid BME680 sensor, check wiring!"));
while (1);
}
// Set up oversampling and filter initialization
bme.setTemperatureOversampling(BME680_OS_8X);
bme.setHumidityOversampling(BME680_OS_2X);
bme.setPressureOversampling(BME680_OS_4X);
bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme.setGasHeater(320, 150); // 320*C for 150 ms

//SD CARD Setup
// The Adafruit_ImageReader constructor call (above, before setup())
// accepts an uninitialized SdFat or FatFileSystem object. This MUST
// BE INITIALIZED before using any of the image reader functions!
Serial.print(F("Initializing filesystem..."));
// SD card is pretty straightforward, a single call...
if(!SD.begin(SD_CS, SD_SCK_MHZ(10))) { // Breakouts require 10 MHz limit due to longer wires
Serial.println(F("SD begin() failed"));
for(;;); // Fatal error, do not continue
}
Serial.println(F("OK!"));

//TEMP6000 Setup
pinMode(LIGHTSENSORPIN, INPUT);

ImageReturnCode stat; // Status from image-reading functions
Serial.println(F("Loading bmp to canvas..."));
stat = reader.loadBMP("/whisper.bmp", img);

//Buttons Setup
pinMode(BUT_ONE, INPUT);

//Accelerometer Setup
Serial.println("Accelerometer Test"); Serial.println("");
/* Initialise the sensor */
if(!accel.begin())
{
/* There was a problem detecting the ADXL345 ... check your connections */
Serial.println("Ooops, no ADXL345 detected ... Check your wiring!");
while(1);
}
/* Set the range to whatever is appropriate for your project */
accel.setRange(ADXL345_RANGE_16_G);
// accel.setRange(ADXL345_RANGE_8_G);
// accel.setRange(ADXL345_RANGE_4_G);
// accel.setRange(ADXL345_RANGE_2_G);

delay(5000);
Serial.println("Setup Done");
}

void loop() {
// Tell BME680 to begin measurement.
unsigned long endTime = bme.beginReading();
if (endTime == 0) {
Serial.println(F("Failed to begin reading :("));
return;
}

if (!bme.endReading()) {
Serial.println(F("Failed to complete reading :("));
return;
}

float BME_Temperature = bme.temperature;
float BME_Humidity = bme.humidity;
float BME_Pressure = bme.pressure / 100.0;
float BME_Gas = bme.gas_resistance / 1000.0;
float BME_Altitude = bme.readAltitude(SEALEVELPRESSURE_HPA);

//Convert to F
float BME_Temp_F = (BME_Temperature * 9/5) + 32;
//Convert Meter to Feet
float BME_Altitude_Feet = BME_Altitude * 3.28084;

float reading = analogRead(LIGHTSENSORPIN); //Read light level
if (reading > _max) _max = reading;

/* Get a new sensor event for Acceleriometer*/
sensors_event_t event;
accel.getEvent(&event);

if (DEBUG_OUTPUT)
{
Serial.print(F("Tick Count: "));
Serial.println(Tick_Count);

Serial.print(F("Input Count: "));
Serial.println(no_input_count);

Serial.print(F("Screen State: "));
Serial.println(screen_state);

Serial.print(F("Light Level: "));
Serial.print(reading);
Serial.print(F(" - Percent: "));
Serial.print(reading/4096 * 100);
Serial.println(F("%"));


Serial.print(F("Temperature = "));
Serial.print(BME_Temp_F);
Serial.println(F(" *F"));

Serial.print(F("Pressure = "));
Serial.print(BME_Pressure);
Serial.println(F(" hPa"));

Serial.print(F("Humidity = "));
Serial.print(BME_Humidity);
Serial.println(F(" %"));

Serial.print(F("Gas = "));
Serial.print(BME_Gas);
Serial.println(F(" KOhms"));

Serial.print(F("Approx. Altitude = "));
Serial.print(BME_Altitude_Feet);
Serial.println(F(" Ft"));

//If Button Pressed Do (Will need event base looping now so catch button presses)
buttonState_One = digitalRead(BUT_ONE);

// check if the pushbutton is pressed. If it is, the buttonState is HIGH:
if (buttonState_One == HIGH)
{
Serial.println(F("Button Pressed"));
screen_state = 1;
no_input_count=0;
}
if (buttonState_One == LOW)
{
Serial.println(F("Button OPEN"));
}


/* Display the results (acceleration is measured in m/s^2) */
Serial.print("X: "); Serial.print(event.acceleration.x); Serial.print(" ");
Serial.print("Y: "); Serial.print(event.acceleration.y); Serial.print(" ");
Serial.print("Z: "); Serial.print(event.acceleration.z); Serial.print(" ");Serial.println("m/s^2 ");


// END OF SERIAL PRINTING FOR DEBUGGING
Serial.println(F("################################################"));
Serial.println();
}

//Check screen power state
tft.enableDisplay(screen_state);

//Print to the screen
if (screen_state == 1)
{
tft.fillRect(0,0,tft.width(),tft.height(),BLACK); //Clear Screen
img.draw(tft,(tft.width() - img.width()) / 2,tft.height() - img.height()); //Draw Background Image

tft.setCursor(0, 5);
tft.setTextColor(WHITE);
tft.setTextSize(1);

tft.print("Humidity: ");
tft.print(BME_Humidity);
tft.println("%");

tft.print("Temperature: ");
tft.print(BME_Temp_F);
tft.println("F");

tft.print("Pressure: ");
tft.print(BME_Pressure);
tft.println("hPa");

tft.print("Altitude: ");
tft.print(BME_Altitude_Feet);
tft.println("Ft");

tft.print("Gas: ");
tft.print(BME_Gas);
tft.println("kOhms");

tft.print("Light: ");
tft.print(reading/4096 * 100);
tft.println("%");

tft.print("X: "); tft.print(event.acceleration.x);tft.println("m/s^2 ");
tft.print("Y: "); tft.print(event.acceleration.y);tft.println("m/s^2 ");
tft.print("Z: "); tft.print(event.acceleration.z);tft.println("m/s^2 ");

}

//set Delay on Loop
Tick_Count++;
no_input_count++;
if (no_input_count > 10)
{
//Turn off Screen
screen_state = 0;
}


if (Tick_Count == UINT32_MAX)
{
Tick_Count = 0;
}
delay(2000);
}


/**************************************************************************/
/*!
@brief Renders a simple test pattern on the screen
*/
/**************************************************************************/
void lcdTestPattern(void)
{
static const uint16_t PROGMEM colors[] =
{ RED, YELLOW, GREEN, CYAN, BLUE, MAGENTA, BLACK, WHITE };

for(uint8_t c=0; c<8; c++) {
tft.fillRect(0, tft.height() * c / 8, tft.width(), tft.height() / 8,
pgm_read_word(&colors[c]));
}
}