Phylicia Bediako
Published © MIT

Midterm 1 Smart Room Controller (Snack Trax)

Snack Trax integrates a food scale, an environmental sensor, and smart lights to help you keep your pantry stocked and your foods fresh.

BeginnerWork in progress34
Midterm 1 Smart Room Controller (Snack Trax)

Things used in this project

Hardware components

Photon 2
Particle Photon 2
×1
SparkFun Load Cell Amplifier - HX711
SparkFun Load Cell Amplifier - HX711
×1
SparkFun Atmospheric Sensor Breakout - BME280
SparkFun Atmospheric Sensor Breakout - BME280
×1
0.96" OLED 64x128 Display Module
ElectroPeak 0.96" OLED 64x128 Display Module
×1
Rotary Encoder with Push-Button
Rotary Encoder with Push-Button
×1
Flora RGB Neopixel LEDs- Pack of 4
Adafruit Flora RGB Neopixel LEDs- Pack of 4
×1

Software apps and online services

Visual Studio Code Extension for Arduino
Microsoft Visual Studio Code Extension for Arduino
SolidWorks
Adobe Illustrator
Bambu Studio

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Miter saw
3D Printer (generic)
3D Printer (generic)
Laser cutter (generic)
Laser cutter (generic)

Story

Read more

Custom parts and enclosures

Midterm 1 Snack Trax 3D Tray

A tray to hold the breadboard in place inside the case

Midterm 1 Controller Case

Midterm 1 Scale Parts

Schematics

Midterm1 Fritzing

Midterm1 Drawn Schematic

Midterm1 Fritzing JPG

Code

Midterm 1 Smart Room Controller Code

C/C++
/* 
 * Project Midterm 1 Smart Room Controller
 * Author: Phylicia Bediako
 * Date: 07/08/2025
 * For comprehensive documentation and examples, please visit:
 * https://docs.particle.io/firmware/best-practices/firmware-template/
 */

// Include Particle Device OS APIs
#include "Particle.h"
#include "neopixel.h"
#include "Colors.h"
#include "Button.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#include "Adafruit_BME280.h"
#include "IoTTimer.h"
#include "Encoder.h"
#include "HX711.h"
#include "IoTClassroom_CNM.h"


SYSTEM_MODE(MANUAL);
//SYSTEM_MODE(SEMI_AUTOMATIC);

//Neopixel variables
const int PIXELCOUNT = 3;
int color;
const int TMPPIXEL = 0;
const int HUPIXEL = 1;
const int WTPIXEL= 2;

//Encoder variables
const int PINB=D8;
const int PINA=D9;
const int ENCSWITCH = D15;
const float MAXENCPOS = 96;
int encPos, lastEncPos;

//Load cell variables
const int DT=D5;
const int CLK = D4;
const int CALFACTOR = 790; //temporary cal factor
const int SAMPLES = 10;
float weight, lastWgtTime;

//BME variables
float tempC, tempF, humidRH;
bool status;
const char degree = 0xF8;
const int BME280=0x76;
const int OLEDSCRN=0x3c;

//WEMO variables
const int WEMO0=0;
const int WEMO1=1;
const int WEMO2=2;
const int WEMO3=3;
const int WEMO4=4;
const int WEMO5=5;
int WEMONUM;

//Hue light variables
const int BULB1=1;
const int BULB2=2;
const int BULB3=3;
const int BULB4=4;
const int BULB5=5;
const int BULB6=6;
int BULBNUM;
const int MAXBRITE=255;
const int SATURATION=255;
float brightness;


Adafruit_NeoPixel pixel (PIXELCOUNT, SPI1, WS2812B);
Adafruit_BME280 bme;
const int OLED_RESET = -1;
Adafruit_SSD1306 display(OLED_RESET);
Encoder encDial (PINA, PINB);
HX711 snackScale(DT,CLK);

/********************************************************
*********************************************************
*********************************************************/
void setup() {
  Wire.begin();
  Serial.begin(9600);
  waitFor(Serial.isConnected, 10000);

  status=bme.begin(BME280);
  if (status == false){
    Serial.printf("BME280 at address 0x%02x failed to start",BME280);
  }
  display.begin(SSD1306_SWITCHCAPVCC,OLEDSCRN);
  display.display();
  display.clearDisplay();
  
  snackScale.set_scale();
  delay(5000);
  snackScale.tare();
  snackScale.set_scale(CALFACTOR);
  
  WiFi.on();
  WiFi.clearCredentials();
  WiFi.setCredentials("IoTNetwork");
  
  WiFi.connect();
  while(WiFi.connecting()) {
    Serial.printf(".");
  }
  
  Serial.printf("\n\n");

  pixel.begin();
  pixel.setBrightness(35);
  pixel.show();

  //start the hue lights at a medium brightness level for when they first turn on (this can be adjusted below)
  brightness=50*(MAXBRITE/MAXENCPOS); 
  encDial.write(50);
}

/********************************************************
*********************************************************
*********************************************************/
void loop() {
  tempC=bme.readTemperature();
  tempF=(tempC*1.8) +32; //convert temperature reading to Celsius
  humidRH=bme.readHumidity();

  //Create a time buffer between weight readings without using a "delay"
  if((millis()-lastWgtTime) > 3000){
    weight = snackScale.get_units(SAMPLES);
    if (weight < 0){
      weight = 0;
    }
    lastWgtTime=millis();
  }

  //Establish encoder dial related variables so turning the dial changes the brightness of the hue lights
  brightness=encPos*(MAXBRITE/MAXENCPOS);
  encPos= encDial.read();
  if(encDial.read()>95){
    encDial.write(encPos=95);
  }
  
  if(encDial.read()<0){
    encDial.write(encPos=0);
  }
  
  if(encPos != lastEncPos){
    Serial.printf("Encoder position %i\n",encPos);
    lastEncPos = encPos; 
  }

  //Programming the OLED screen to show the environment and weight readings
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.clearDisplay();
  display.printf("Temperature: %0.1f %cF\nHumidity: %0.1f RH\nWeight: %0.1f g\n",tempF, degree, humidRH, weight);
  Serial.printf("Temperature: %0.1f %cF\nHumidity: %0.1f RH\nWeight: %0.1f g\n",tempF, degree, humidRH, weight);
  display.display();
  
  //Program the neopixels, hue lights, and wemo outlets so they depict temperature, humidity, and weight status

  ////Temperature: Establish a range of acceptable temperatures, turn neopixel green if within range and turn off ODD numbered wemos
  if ((tempF > 73.0) && (tempF < 75.0)){ 
    pixel.setPixelColor(TMPPIXEL,green);
    pixel.show();
    for(WEMONUM=0; WEMONUM < 6; WEMONUM++){
      if(WEMONUM%2 ==1){
        wemoWrite(WEMONUM,LOW);
      }
    }
  }
  //Turn neopixel red if out of range and turn on odd numbered wemos
  else{
    pixel.setPixelColor(TMPPIXEL,red);
    pixel.show();
    for(WEMONUM=0; WEMONUM < 6; WEMONUM++){
      if(WEMONUM%2 ==1){
        wemoWrite(WEMONUM, HIGH);
      }
    }
  }

////Humidity: Establish a range of acceptable humidity values, turn neopixel green if within range and turn off EVEN numbered wemos
  if ((humidRH > 64.8) && (humidRH < 66.0)){
    pixel.setPixelColor(HUPIXEL,green);
    pixel.show();
    
    for(WEMONUM=0; WEMONUM < 6; WEMONUM++){
      if(WEMONUM%2 ==0){
        wemoWrite(WEMONUM, LOW);
      }
    }
  }

else{
    pixel.setPixelColor(HUPIXEL,red);
    pixel.show();
    for(WEMONUM=0; WEMONUM < 6; WEMONUM++){
      if(WEMONUM%2 ==0){
      wemoWrite(WEMONUM, HIGH);
      }
    }
  }

////Weight: Set a minimum weight for the scale, change the neopixel and hue lights green or red based on the weight of remaining snacks
  if(weight < 200){
    pixel.setPixelColor(WTPIXEL,red);
    pixel.show();
    for (BULBNUM=1; BULBNUM<7; BULBNUM++){
      setHue(BULBNUM,TRUE,HueRed,brightness,SATURATION);
      delay(200);
    }
  }
  
    else {
    pixel.setPixelColor(WTPIXEL,green);
    pixel.show();
    for (BULBNUM=1; BULBNUM<7; BULBNUM++){
      brightness=encPos*(MAXBRITE/MAXENCPOS);
      setHue(BULBNUM,TRUE,HueGreen,brightness,SATURATION);
      delay(200);
    }
  }
}

Credits

Phylicia Bediako
3 projects • 5 followers

Comments