Super Sentai Cat Smart Room Controller

0 1510 Medium

Smart room controller designed to introduce teens to programming and IoT. It was inspired by "Why's Poignant Guide to Ruby".

projectImage

Things used in this project

 

Hardware components

HARDWARE LIST
1 DFRobot Gravity: I2C BME280 Environmental Sensor
1 Arduino Ethernet Shield 2
1 Teensy 3.1
1 Rotary Encoder with Push-Button
1 SparkFun Pushbutton switch 12mm
1 ElectroPeak 0.96" OLED 64x128 Display Module
1 Adafruit NeoPixel Ring: WS2812 5050 RGB LED
1 Adafruit Flora RGB Neopixel LEDs- Pack of 4
1 Rotary potentiometer (generic)

Hand tools and fabrication machines

 

Laser cutter (generic)

 

3D Printer (generic)

Story

projectImage

The purpose of this project was to program a microprocessor controller to control different devices in the room.

 

I modeled my project after the first book that I read on programming called "why's (poignant) Guide to Ruby"; where it follows a cat through the process of learning Ruby.

projectImage

I wanted my project to be a project for a middle or high school student wanting to learn how to program and build smart devices.

 

I chose to use images from the book because of how artistic the book is. Ruby is an easy language to learn (but has limited application), while C++ is a difficult language wide a verity amount of applications.

 

Around the middle and high school age range is when life can get dark very quickly for teens. I wish that I was able to create a physical manifestation of my code when I was younger and going through a difficult time myself.

 

If my nephews can do some coding and can artistically express themselves then I believe that my project is successful.

 

The different items I created are used to teach my nephews (and teens) how to use tools and designs to gain control over their lives and the environment through the use of 3D printing

 

Laser cutting and microcontroller programming are valuable skills to learn and I believe that those skills will allow them to manifest their desires positively.

 

How does this concept work?

 

The encoder and potentiometer move electrically through different states/cases and "mental rooms". Some of these rooms are paired with cat images to demonstrate the concept of the programmer controlling the mental, physical, and emotional rooms around them. The NeoPixel color is blue to demonstrate the ability for teens to control their mental, spiritual, electrical methods of expression. I believe these concepts are important to develop during this point in their lives. Each switch case is labeled to allow them to easily understand basic programming and electrical concepts.

projectImage

Each concept is labeled in the code and on the LED to easily tie together difficult concepts.

 

The 3D print was designed to show how to connect the ideas that bring my nephew's interests to the physical world through a simple form of physical manifestation.

 

The cats were placed as a continuation of the Ruby concepts and implemented as bosses for conquering programming levels. I believe that this concept will help them to tie previous concepts in with new programming concepts.

projectImage

Schematics

 

Fritzing

The encoder is grounded by 2 capacitors to help reduce jitter

projectImage

Code

 

Smart Room Controller

C/C++

Use a microprocessor to control various devices in the room.

CODE
/*
 *  Project:      Smart Room Controller 
 *  Description:  Uses keypad/breadboard components to control different   
 *  objects in the room.  
 *             
 *  Authors:      Daniel Mills
 *  Date:         03-Mar-2022
 */


//Header Files
#include <SPI.h>                       //Library to use for the SPI modual
#include <Wire.h>                      //Imports the wire library for connecting 
#include <Adafruit_GFX.h>              //Ada graphics library
#include <Adafruit_SSD1306.h>          //Imports the library for the OLED display
#include <OneButton.h>                 //includes the OneButton library
#include <SPI.h>                       //includes the header for the serial bus
#include <Ethernet.h>                  //includes the Ethernet header for internet
#include <mac.h>                       //Includes the mac header 
#include <hue.h>                       //Includes the hue header for adjusting the hue
#include <colors.h>                    //includes the colors library
#include <Encoder.h>                   //includes the library for the encoder
#include <SD.h>                        //includes the SD card library
#include "wemo.h"                      //includes the wemo library
#include <Adafruit_NeoPixel.h>         //includes the NeoPixel library
#include <Adafruit_BME280.h>           //Calls library for Adafruit display

//Image Header Files - For doubleClick in Switch Cases
#include "kittyHeader.h"               //Includes the kitty image        
#include "Cat1.h"
#include "Cat2.h"
#include "Cat3.h"
#include "cat4.h"
#include "cat5.h"

//Var for OLED Screen
const int SCREEN_WIDTH = 128;         //sets the screen width for the OLED
const int SCREEN_HEIGHT = 64;         //sets the screen height
const int OLED_RESET = 4;             
const int SCREEN_ADDRESS = 0x3C;      //Registeres the screen address to the SPI
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);  //defines the Adafruit screen

//Declaring vars for encoder
Encoder myEnc(3, 2);       //pins the encoder is connected to
int encoderOutput;         //var to hold encoder output
int encoderLastPosition;   //var to hold last encoder position
bool ONOFF;                //bool to turn on and off the hue



//OneButton Vars
OneButton button1(20, false);   //OneButton to pin 23 and sets value to false
bool buttonState = 0;
bool blinker = 0;               //Creates a bool var for buttonState and for blinker
int iButton = 0;                //Creates var to hold the button press outcome

//Creates the SD card object
File dataFile;
//Creates the vars for the SD Card
const int chipSelect = 4;     //Registers the SD card to pin 4
//int object;                 //Creates an int var for saving data from the SD Card
bool status;

//Wemo Vars
int wemoPorts = 0;  //starts the wemo ports on zero
byte thisbyte;      //used to get IP address
int i;

//NeoPixel Var
const int PIXELPIN = 17;
const int PIXELCOUNT = 16;
int currentNeoPixel;

//Vars for BME - Temp Sensor 
Adafruit_BME280 bme;  //Defines BME sensor data 
int temp;
int humid;
float tempF;   //set temperature variable
float pressPA; //sets pressure var
float humidRH; //sets RH var


Adafruit_NeoPixel pixel(PIXELCOUNT,PIXELPIN, NEO_GRB + NEO_KHZ800);  //declares the NeoPixel Object
 
//Ints for potent
int potentPin = 14;  //set potent to port 14
int lastPotentValue;  //creates int for last potent value
int potentMap;

void setup() {
  //##SetUp block for OneButton
  Serial.begin(9600);                  //Checks serial monitor
  button1.attachClick(click1);         //initialized button1 click1
  button1.attachDoubleClick(doubleClick1);
  button1.setClickTicks(700);
  button1.setPressTicks(2000);
  buttonState = false;
  blinker = false;

  //##Ethernet Start Up Block
  Ethernet.begin(mac);  //starts Ethernet
  delay(200);          //ensure Serial Monitor is up and running           
  printIP();       
  Serial.printf("LinkStatus: %i  \n",Ethernet.linkStatus());


  //##Setup Information for the OLED
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.printf("SSD1306 allocation failed");
    for(;;);   //looks for an end to the loop
  }

  display.display();         //Shows the Adafruit splah
  delay(100);               //delay
  display.clearDisplay();    //Clears the screen
  display.display();         //Only use this command when needed instead of repeating
  delay(100);


  //##Setup Block for the SD Card
  Serial.printf("Initializing SD card...");

  // initialize uSD card module CS to off
  pinMode(chipSelect,OUTPUT); 
  digitalWrite(chipSelect,HIGH);
  
  // see if the card is present and can be initialized:
  status = SD.begin(chipSelect);
  if (!status) {  // if status is false
    Serial.printf("Card failed, or not present\n");
  //while(true);  // pause the code indefinately   //commented out in case SD card isn't present
  }
  else {
    Serial.printf("card initialized.\n");
  }

  //##Set Up block for Wemo Ports - Print your local IP address
  Serial.print("My IP address: ");                           //Lets user know that the ip is being printed to the screen
  for (thisbyte = 0; thisbyte < 3; thisbyte++) {             //Start of loop for identifing the IP
    //print value of each byte of the IP address
    Serial.printf("%i.",Ethernet.localIP()[thisbyte]);       //Command for printing the Ethernet local IP
  }
  Serial.printf("%i\n",Ethernet.localIP()[thisbyte]);        //Prints the Final ethernet local ip to the monitor
  switchOFF(0);                                              //Turns off the wemo port 0

  //Setup Block for NeoPixels
  pixel.setBrightness(150);    //sets the brightness of the NeoPixels to 150
  pixel.begin();               //Sets the Neopixel and starts listening for commands

  //Sets the pinMode for the potent values
  pinMode(potentPin, INPUT);   // set pin modes
  Serial.begin(9600);          //listen to Serial monitor

  status = bme.begin(0x76); //looks for the temp sensor

  //NeoPixel Setup Block
  pixel.setBrightness(150);  //brightness value for the Neopixels display
  pixel.begin();             //sets Neopixels
  pixel.show();              //shows Neopixels
}



//////////////////////////////
/////////////////////////////
void loop() {
  
  button1.tick();
  Serial.printf("Button State %i \n", buttonState); 
  encoderOutput = myEnc.read();

  
 int potentValue = analogRead(potentPin);                         // read potentPin and divide by 255 to give 5 possible readings
 potentMap = map(potentValue, 0, 1023, 0, 4);
 //Serial.printf("Potent Map %i \n, Potent Values %i \n", potentMap, potentValue);
 Serial.printf("Potent Map %i \n", potentMap);
  //sp-Space or serial.print output testing
  if(potentMap != lastPotentValue){  //Start of Switch loop
    buttonState = false;
    blinker = false;
    lastPotentValue = potentMap;
  }
    // enter switch case
    switch(potentMap)
    {
      case 0:
          
         
         if (buttonState) {
            pixel.fill(blue, i, 16);
            pixel.show();
            Serial.printf("Case 0 Button Check %i \n", buttonState); 
         }
         else {
            Serial.printf("Case 0 Button Check Else Stat %i \n", buttonState);
            pixel.clear(); 
            pixel.show();
         }
        
          if(blinker){
            Cat1display();
          }
          else {
            
            display.clearDisplay();      //clears the display 
            display.setCursor(0,0);             // Start at top-left corner
            display.setTextColor(SSD1306_WHITE);
            display.printf("Squire, you are in: \n Case0-NeoPixels < \n Case1 \n Case2 \n Case3 \n Case4 \n ");   //Outputs Switch Case
            display.display(); 
          }
        
        break;
      case 1:
          encoderOutput = myEnc.read();
          if (encoderOutput != encoderLastPosition) {
                Serial.println(encoderOutput);
                buttonState = false;
                encoderLastPosition = encoderOutput;
                  if (encoderOutput > 4) {
                    myEnc.write(4);
                    display.clearDisplay();      //clears the display 
                    display.setTextColor(SSD1306_WHITE);
                    display.setCursor(0,0);             // Start at top-left corner
                    display.printf("Encoder %i, ", encoderOutput);   //Outputs Switch Case
                    display.display();
                  }
                  if (encoderOutput < 0) {
                    myEnc.write(0);
                  }
           }
          if (buttonState) {
             setHue(encoderOutput,true,HueBlue,255,255);  //lights the bulb the color blue
              Serial.printf("Case 1 Button Check %i \n", buttonState);    
          }  
          else {
            Serial.printf("Case 1 Button Check Else Stat %i \n", buttonState);
            setHue(encoderOutput,false,HueBlue,0,0);  //lights the bulb the color blue
          }

          if(blinker){
            catDisplay2();
          }
          else {
            //display.setTextSize(1);                                // Draw 2X-scale text (too large for screen)
            display.clearDisplay();      //clears the display 
            display.setTextColor(SSD1306_WHITE);
            display.setCursor(0,0);             // Start at top-left corner
            display.printf("Squire you are in: \n Case0 \n Case1-HueLights < \n Case2 \n Case3 \n Case4 ");   //Outputs Switch Case
            display.display();
            
          }

          Serial.printf("Encoder Value %i \n", encoderOutput);

        break;
     //Start of case 2
      case 2:
            encoderOutput = myEnc.read();
            if (encoderOutput != encoderLastPosition) {
                Serial.println(encoderOutput);
                buttonState = false;
                encoderLastPosition = encoderOutput;
                  if (encoderOutput > 5) {
                    myEnc.write(5);
                  }
                  if (encoderOutput < 0) {
                    myEnc.write(0);
                  }
            }
          
            if (buttonState){
                switchON(encoderOutput);
                Serial.printf("Wemo is on \n");
            }  
            else{
                switchOFF(encoderOutput);
            }
          
          if(blinker){
            catDisplay3();
          }
          else {
            //display.setTextSize(1);                                // Draw 2X-scale text (too large for screen)
            display.clearDisplay();      //clears the display 
            display.setTextColor(SSD1306_WHITE);
            display.setCursor(0,0);             // Start at top-left corner
            display.printf("Squire you are in: \n Case0 \n Case1 \n Case2-Wemo< \n Case3 \n Case4 ");   //Outputs Switch Case
            display.display();

            }

            
          
         
          Serial.println("Switch Case 2");
        break;
      //Start of case 3
      case 3:
           display.clearDisplay();      //clears the display 
           if (buttonState) {
            tempF = (bme.readTemperature()*1.8)+32;         //converting F to C
            pressPA = bme.readPressure()*0.00030;           //converting pascal pressure to inches of mercury
            humidRH = bme.readHumidity();                   //read humidity

            Serial.printf("temp %.02f \n", tempF);          //Serial.print temp
            Serial.printf("pressPA %.02f \n", pressPA);     //serial.print pressure
            Serial.printf("humid %.02f \n", humidRH);       //Serial print humidity

            display.setTextSize(1);                          // Sets 1:1 pixel scale
            display.setTextColor(SSD1306_WHITE);             // Draws black text on white (inverse text)
            display.setCursor(0,0);                          // Start at top-left corner
            display.printf("Temp: %.02f%cF  \n Press: %.02f \n RH: %.02f%cF \n", tempF, 248, pressPA, humidRH, 248);   //outputs sensor values
            display.display();
  
            //#Calls the write to datafile and read from file
            writeToSD(tempF, pressPA, humidRH);    //Calls the writeToSD function and writes these vars to the file
            Serial.printf("Case 3 Button Check %i \n", buttonState); 
              
            if (tempF > 70){
                switchON(1);
                switchON(3);
                Serial.printf("Wemo is on \n");
            }  
            else{
                switchOFF(1);
                switchOFF(3);
            }                         
            }
            else {
                Serial.printf("Case 3 Button Check Else Stat %i \n", buttonState);
            }

            if(blinker){
                catDisplay4();
            }
            else {
                
                display.clearDisplay();             //clears the display
                display.setTextColor(SSD1306_WHITE); //sets the display color to white
                display.setCursor(0,0);             // Start at top-left corner
                display.printf("Squire, you are in: \n Case0 \n Case1 \n Case2 \n Case3-FanandTemp < \n Case4 \n ");   //Outputs Switch Case
                display.display();   //shows the display
            }
           

          Serial.println("Switch Case 3");
        break;
      //Start of Case 4
      case 4:

          if (buttonState) {
            setHue(1,true,random(0,60000),255,255);  //lights the bulb the color random
            setHue(2,true,random(0,60000),255,255);  //lights the bulb the color random
            setHue(3,true,random(0,60000),255,255);  //lights the bulb the color random
            setHue(4,true,random(0,60000),255,255);  //lights the bulb the color random
            setHue(5,true,random(0,60000),255,255);  //lights the bulb the color random
            Serial.printf("Case 4 Button Check %i \n", buttonState);    
          }  
          else {
            Serial.printf("Case 1 Button Check Else Stat %i \n", buttonState);
            setHue(1,false,random(0,60000),0,0);  //lights the bulb the color random
            setHue(2,false,random(0,60000),0,0);  //lights the bulb the color random
            setHue(3,false,random(0,60000),0,0);  //lights the bulb the color random
            setHue(4,false,random(0,60000),0,0);  //lights the bulb the color random
            setHue(5,false,random(0,60000),0,0);  //lights the bulb the color random
            Serial.printf("Case 4 Button Check %i \n", buttonState);    
          }
           
           if(blinker){
            catDisplay5();
           }
           else {
            display.clearDisplay();             //clears the display
            display.setTextColor(SSD1306_WHITE);
            display.setCursor(0,0);             // Start at top-left corner
            display.printf("Squire, you are in: \n Case0 \n Case1 \n Case2 \n Case3 \n Case4-HueLightsRandom< \n ");   //Outputs Switch Case
            display.display();
           }


          Serial.println("Switch Case 4");
        break;
      default:

          display.clearDisplay();             //clears the display
          display.setTextColor(SSD1306_WHITE);
          display.setCursor(0,0);             // Start at top-left corner
          display.printf("Error");            //Outputs Switch Case
          display.display();
          delay(2000);                        //delays the clear display for 2 seconds

          
          Serial.println("error!");
        break;

    lastPotentValue = potentMap;
  }
}

/////////////////////////////////////////////
////////////////////////////////////////////
//Void Functions block

//Creates the void functions for OneButton
void click1() {                                     //when input is received (button is pressed)..
    buttonState = !buttonState;                     //when button is pressed it changes the bool value of the buttonState
    Serial.printf("Single button press \n");        //can do the same thing as  different data types on the same line                          
}
void doubleClick1() {                       //looks for the doubleclick function
  blinker = !blinker;                       //if current state is not equal to previous state
  Serial.printf("Double button press \n");  //outputs that a doublepress occured to the screen
}

//#Void Function for Starting of the printIP function
void printIP() {
  Serial.printf("My IP address: ");                     //outputs this to the screen if the connection was successful
  for (byte thisByte = 0; thisByte < 3; thisByte++) {
    Serial.printf("%i.",Ethernet.localIP()[thisByte]);  //prints IP to serial monitor
  }
  Serial.printf("%i\n",Ethernet.localIP()[3]);          //prints IP to serial monitor
}

void writeToSD(float tempF, float pressPA, float humidRH ) {     //looks to write these variable to the SD card
  dataFile = SD.open("datalog.csv", FILE_WRITE);                 //file the information was written to
  // if the file is available, write to it:
  if (dataFile) {         
    dataFile.printf("Temp: %.02f%cF  \n Press: %.02f \n RH: %.02f%c \n", tempF, 248, pressPA, humidRH, 248);    //writes these var to the SD card
    dataFile.close();                                                                                           //closes the SD when done
    Serial.printf("Temp: %.02f%cF  \n Press: %.02f \n RH: %.02f%c \n", tempF, 248, pressPA, humidRH, 248);      //prints these values to the screen
  }  
  else {
    Serial.printf("error opening datalog.csv \n");    //returns this message if information couldn't be read from the card
  }
  return;  //exits function
} 


//##Start of the read from SD card function
void readFromSD(){
  // re-open the file for reading:
  dataFile = SD.open("datalog.csv");   //opens the datalog.csv file
  if (dataFile) {
    Serial.printf("datalog.csv: \n");  //if it was able to open that file it will output those values to the serial monitor
  // read from the file until there's nothing else in it:
  while (dataFile.available()) {
      Serial.write(dataFile.read());   //will continue to read from card as long as data is present
  } 
  dataFile.close();   //closes the data file when it is finished reading 
  } 
  else { 
    Serial.printf("error opening datalog.csv \n");   //will output this message if there was a problem reading from the file.
  }
  return;  //exits function
}




//////////////////////////////////////////////
/////////////////////////////////////////////
//Images Block
void Cat1display(void) {
  int centerV =  (display.height()-64)/2;     //(display.height()-128)/2;
  int centerH =  (display.width()-128)/2;     //(display.width()-64)/2;
  display.clearDisplay();
  display.drawBitmap(centerH, centerV, Cat1, 128, 64, 1);
  display.display();
  delay(2000);
}

void catDisplay2(void) {
  int centerV =  (display.height()-64)/2;     //(display.height()-128)/2;
  int centerH =  (display.width()-128)/2;     //(display.width()-64)/2;
  display.clearDisplay();
  display.drawBitmap(centerH, centerV, Cat2, 128, 64, 1);
  display.display();
  delay(2000);
}

void catDisplay3(void) {
  int centerV =  (display.height()-64)/2;     //(display.height()-128)/2;
  int centerH =  (display.width()-128)/2;     //(display.width()-64)/2;
  display.clearDisplay();
  display.drawBitmap(centerH, centerV, Cat3, 128, 64, 1);
  display.display();
  delay(2000);
}

void catDisplay4(void) {
  int centerV =  (display.height()-64)/2;     //(display.height()-128)/2;
  int centerH =  (display.width()-128)/2;     //(display.width()-64)/2;
  display.clearDisplay();
  display.drawBitmap(centerH, centerV, Cat4, 128, 64, 1);
  display.display();
  delay(2000);
}

void catDisplay5(void) {
  int centerV =  (display.height()-64)/2;   //(display.height()-128)/2;
  int centerH =  (display.width()-128)/2;     //(display.width()-64)/2;
  display.clearDisplay();
  display.drawBitmap(centerH, centerV, Cat5, 128, 64, 1);
  display.display();
  delay(2000);
}

The article was first published in hackster, March 11 2022

cr: https://www.hackster.io/daniel-mills/super-sentai-cat-smart-room-controller-2a79da

author: Daniel Mills

License
All Rights
Reserved
licensBg
0