Soothing RGB Lamp

by Muhammad Kemal Ardiansyah in Circuits > Microcontrollers

215 Views, 0 Favorites, 0 Comments

Soothing RGB Lamp

Soothing Night Lamp Demo

By:

Budicahya Rama Bagaskara (2440113433)

Muhammad Kemal Ardiansyah (2440105513)

Tagreg Wiryo Nursalim (2440081721)

Computer Engineering -- Binus University


Good day everyone, 

Welcome to our Instructable where we will slowly build a battery-powered soothing lamp device with the ability of connecting to WiFi and our own mobile application. The code for the mobile application is linked to this Instructable which will redirect to GitHub. For the lamp itself, We will be using PCB board to connect all of our electronic components to save space. But you may subsitute the PCB with breadboard although its bulky size may add difficulty and size while modeling for case of the lamp. 

While the design of the case in this Instructable is a square table lamp with the case built 3D-printed base and acrylic sheets, it is up to you to decide the shape of the lamp as long as the space required for storing the components taken into consideration.

That is all the brief information that overall conclude this article. Now we can continue to build the smart lamp with the help of instruction below.




Supplies

4.7k.jpg
jumper ftf.jpg
jumper.jpg
bahan.jpg
battery holder.jpg
button.jpg

Hardware

  • ESP32 DOIT DEVKIT
  • PCB Board
  • Male-to-Male Jumper Cable
  • Female-to-Female Jumper Cable
  • Battery Holder 3 slots for AA Battery
  • 1x 8 Pixels WS2812 (Neopixel)
  • 2x Push Button
  • 3x Terminal Block
  • 2x 4.7K resistor
  • 6x Male Pin Header
  • 1x Switch
  • 3x 1.5 V AA battery
  • 5x 3mm Screw
  • 1x 2mm Screw
  • Casing

Software

  • Visual Studio Code
  • Firebase Console (by Google)

Block Diagram & Flow Chart

SPE_Block Diagram.png
SPE_Flowchart.png

Here's our block diagram and flow chart

Schematic

Schematic_EasyEDA.jpg
Main Board_PCB_3D Model.jpg
Button_PCB_3D Model.jpg
Button_PCB_Bottom Layer.jpg
Main Board_PCB_Buttom Layer.jpg

Create Firestore Database

  1. First, search "firebase console" on your web browser, open the website backed by google then login.
  2. Then click add project, enter your project name then click continue, enable google analytics then continue, choose a default account then click continue.
  3. After you have seen the home page, go to "Authentication" on the left side then click "Get started".
  4. You will open the "Sign-in method" menu, then click "Email/password and enable "Email/password".
  5. Go to "Users" menu then Add user.
  6. Add your email and password. you need to remember the password. I suggest you to note it. Then click "Add User"
  7.  After that go to the "Firestore Database" menu on the left side, then click "Create database". Click test mode and click next.
  8. You will see the page of your firestore database. there will be collections, documents, and fields after you programmed it on Visual Studio Code.
  9. Go to project settings, you will see the information needed for this project.

For connecting the flutter to the firestore, you need to add JSON file. To get the JSON file follow the instruction below:

  1. On your home firebase console screen , there is a android icon ( under your firebase project name at the top of the page), then click it
  2. You will move to another screen that shows some points:
  • Register App
  • Download and then add config file
  • Add firebase SDK
  •  Next steps
  1. For the first point, register app, fill the android package name is a must and fill it with your application Id. You can find it on your VSCode in android/app/src/build.gradle. Then click register
  2. For the second point, download and then add config file, download the google-services.json and click next.
  3. Put the download file in android/app/src.
  4. Then for the third point, add firebase SDK, copy all the code to the android/app/src/build.gradle. Then. click next
  5. Then click continue to console


ESP32 Code

  1. Open your visual studio code, and create a new project in PlatformIo with ESP32 DOIT DEVKIT as a board.
  2. Don't forget to add libraries from PlatformIo. In this project, we need 3 library which is Adafruit Neopixel from Adafruit and Firebase from mobitz.
  3. Copy the code below and paste it into your main.cpp file. Don't forget to input your Firestore Database API key, Project ID, user email, user password (you can get it in your project settings), SSID, and Password of your internet/WiFi. Then upload it.
  4. You can control the Neopixel through your Firestore Database by changing the value on the fields section
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#include <WiFi.h>
#include <DNSServer.h>
#include <Firebase_ESP_Client.h>
#include <addons/TokenHelper.h>


#define PIN 15
#define NUMPIXELS 8
#define MODEBUTTON 12
#define POWERBUTTON 13
#define OFFLINEMODE 0
#define ONLINEMODE 1


#define SSID "<Your SSID>"
#define PASSWORD "<Your SSID>"


#define API_KEY "<Your API Key>"
#define FIREBASE_PROJECT_ID "<Your Project ID>"
#define USER_EMAIL "<Your User Email>"
#define USER_PASSWORD "<Your User Password"


Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);


int state = 0;
int rain_state = 0;
int pressed = 0;
int rain_pressed = 0;
int currentMode = ONLINEMODE;


FirebaseAuth auth;
FirebaseConfig config;
FirebaseData fbdo;


unsigned long dataMillis = 0;
int count = 0;


bool taskcomplete = false;


String getDevice()
{ // get ESP32 unique ID
  char name[23];
  uint64_t chipid = ESP.getEfuseMac(); // The chip ID is essentially its MAC address(length: 6 bytes).
  snprintf(name, 23, "%04X%08X", (uint16_t)(chipid >> 32), (uint32_t)chipid);


  return name;
}


String devicename = getDevice(); // accomodate esp32 ID into a variable


String documentPath = "Neopixel/" + devicename;


int getData(String field)
{


  if (Firebase.Firestore.getDocument(&fbdo, FIREBASE_PROJECT_ID, "", documentPath.c_str(), field.c_str()))
    ;
  // Serial.printf("ok\n%s\n\n", fbdo.payload().c_str());
  else
    Serial.println(fbdo.errorReason());


  FirebaseJson payload;
  payload.setJsonData(fbdo.payload().c_str());


  // Get the data from FirebaseJson object
  FirebaseJsonData jsonData;
  payload.get(jsonData, "fields/" + field + "/integerValue", true);
  int value = jsonData.intValue;
  return value;
}


bool getBoolean(String field)
{


  if (Firebase.Firestore.getDocument(&fbdo, FIREBASE_PROJECT_ID, "", documentPath.c_str(), field.c_str()))
    ;
  // Serial.printf("ok\n%s\n\n", fbdo.payload().c_str());
  else
    Serial.println(fbdo.errorReason());


  FirebaseJson payload;
  payload.setJsonData(fbdo.payload().c_str());


  // Get the data from FirebaseJson object
  FirebaseJsonData jsonData;
  payload.get(jsonData, "fields/" + field + "/booleanValue", true);
  bool value = jsonData.boolValue;
  return value;
}


void setup()
{
  Serial.begin(115200);
  pixels.begin();
  Serial.println(devicename);
  WiFi.begin(SSID, PASSWORD);
  pinMode(MODEBUTTON, INPUT);
  pinMode(POWERBUTTON, INPUT);
  pinMode(PIN, OUTPUT);


  Serial.printf("Firebase Client v%s\n\n", FIREBASE_CLIENT_VERSION);


  /* Assign the api key (required) */
  config.api_key = API_KEY;


  /* Assign the user sign in credentials */
  auth.user.email = USER_EMAIL;
  auth.user.password = USER_PASSWORD;


  /* Assign the callback function for the long running token generation task */
  config.token_status_callback = tokenStatusCallback; // see addons/TokenHelper.h


  fbdo.setResponseSize(2048);


  Firebase.begin(&config, &auth);


  Firebase.reconnectWiFi(true);
}


void loop()
{
  if (digitalRead(MODEBUTTON) == HIGH && rain_state == LOW)
  {
    rain_pressed++;
    rain_state = HIGH;
  }
  else if (digitalRead(POWERBUTTON) == LOW && state == HIGH)
  {
    rain_state = LOW;
  }


  if (digitalRead(POWERBUTTON) == HIGH && state == LOW)
  {
    pressed++;
    state = HIGH;
  }
  else if (digitalRead(POWERBUTTON) == LOW && state == HIGH)
  {
    state = LOW;
  }


  rain_state = digitalRead(MODEBUTTON);
  if (rain_pressed == 0 && rain_state == LOW)
  {
    currentMode = ONLINEMODE;
  }
  else if (rain_pressed == 1)
  {
    printf("switch to online mode\n");
    // digitalWrite(PIN, HIGH);
    currentMode = OFFLINEMODE;
  }
  else if (rain_pressed == 2)
  {
    printf("rain_button pressed 2x\n");
    digitalRead(state);
    // rain_state = 0;
    pressed = 1;
    rain_pressed = 0;
    currentMode = ONLINEMODE;
  }


  if (currentMode == OFFLINEMODE)
  {
    digitalRead(state);
    if (pressed == 0)
    {
      printf("button pressed 0x\n");
      digitalWrite(PIN, HIGH);
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(255, 255, 255));
        pixels.show();
        delay(25);
      }
    }
    else if (pressed == 1)
    {
      printf("button pressed 1x\n");
      digitalWrite(PIN, HIGH);
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(255, 0, 0));
        pixels.show();
        delay(25);
      }
    }
    else if (pressed == 2)
    {
      printf("button pressed 2x\n");
      digitalWrite(PIN, HIGH);
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(255, 0, 191));
        pixels.show();
        delay(25);
      }
    }
    else if (pressed == 3)
    {
      printf("button pressed 3x\n");
      digitalWrite(PIN, HIGH);
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(255, 255, 0));
        pixels.show();
        delay(25);
      }
    }
    else if (pressed == 4)
    {
      printf("button pressed 4x\n");
      digitalWrite(PIN, HIGH);
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(0, 255, 0));
        pixels.show();
        delay(25);
      }
    }
    else if (pressed == 5)
    {
      printf("button pressed 5x\n");
      digitalWrite(PIN, HIGH);
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(0, 0, 255));
        pixels.show();
        delay(25);
      }
    }
    else if (pressed == 6)
    {
      printf("button pressed 6x\n");
      digitalWrite(PIN, HIGH);
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(255, 0, 0));
        pixels.show();
        delay(20);
      }
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(255, 128, 0));
        pixels.show();
        delay(20);
      }
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(255, 255, 0));
        pixels.show();
        delay(20);
      }
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(128, 255, 0));
        pixels.show();
        delay(20);
      }
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(0, 255, 0));
        pixels.show();
        delay(20);
      }
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(0, 255, 255));
        pixels.show();
        delay(20);
      }
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(0, 0, 255));
        pixels.show();
        delay(20);
      }
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(127, 0, 255));
        pixels.show();
        delay(20);
      }
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(255, 0, 255));
        pixels.show();
        delay(20);
      }
      for (int i = 0; i <= NUMPIXELS; i++)
      {
        pixels.setPixelColor(i, pixels.Color(255, 0, 127));
        pixels.show();
        delay(20);
      }
    }
    else if (pressed == 8)
    {
      printf("button pressed 8x\n");
      digitalWrite(PIN, HIGH);
      pressed = 0;
    }
  }


  else if (currentMode == ONLINEMODE)
  {
    if (Firebase.ready() && (millis() - dataMillis > 1000 || dataMillis == 0))
    {
      dataMillis = millis();


      // For the usage of FirebaseJson, see examples/FirebaseJson/BasicUsage/Create.ino
      FirebaseJson content;


      // aa is the collection id, bb is the document id.


      // If the document path contains space e.g. "a b c/d e f"
      // It should encode the space as %20 then the path will be "a%20b%20c/d%20e%20f"


      if (!taskcomplete)
      {
        taskcomplete = true;


        content.clear();
        content.set("fields/Power/booleanValue", String(1).c_str());
        content.set("fields/Red/integerValue", String(255).c_str());
        content.set("fields/Green/integerValue", String(255).c_str());
        content.set("fields/Blue/integerValue", String(255).c_str());
        content.set("fields/Rainbow/booleanValue", String(1).c_str());


        Serial.print("Create a document... ");


        if (Firebase.Firestore.createDocument(&fbdo, FIREBASE_PROJECT_ID, "" /* databaseId can be (default) or empty*/, documentPath.c_str(), content.raw()))
          Serial.printf("ok\n%s\n\n", fbdo.payload().c_str());
        else
          Serial.println(fbdo.errorReason());
      }


      content;
      content.clear();


      bool powervalue = getBoolean("Power");
      int redvalue = getData("Red");
      int greenvalue = getData("Green");
      int bluevalue = getData("Blue");


      Serial.print("Power: ");
      Serial.println(powervalue);
      Serial.print("Red: ");
      Serial.println(redvalue);
      Serial.print("Green: ");
      Serial.println(greenvalue);
      Serial.print("Blue: ");
      Serial.println(bluevalue);


      for (int i = 0; i <= NUMPIXELS; i++)
      {
        if (powervalue)
        {
          bool rainbow = getBoolean("Rainbow");


          Serial.print("Rainbow: ");
          Serial.println(rainbow);
          if (rainbow)
          {


            for (int i = 0; i <= NUMPIXELS; i++)
            {
              pixels.setPixelColor(i, pixels.Color(255, 0, 0));
              pixels.show();
              delay(20);
            }
            for (int i = 0; i <= NUMPIXELS; i++)
            {
              pixels.setPixelColor(i, pixels.Color(255, 128, 0));
              pixels.show();
              delay(20);
            }
            for (int i = 0; i <= NUMPIXELS; i++)
            {
              pixels.setPixelColor(i, pixels.Color(255, 255, 0));
              pixels.show();
              delay(20);
            }
            for (int i = 0; i <= NUMPIXELS; i++)
            {
              pixels.setPixelColor(i, pixels.Color(128, 255, 0));
              pixels.show();
              delay(20);
            }
            for (int i = 0; i <= NUMPIXELS; i++)
            {
              pixels.setPixelColor(i, pixels.Color(0, 255, 0));
              pixels.show();
              delay(20);
            }
            for (int i = 0; i <= NUMPIXELS; i++)
            {
              pixels.setPixelColor(i, pixels.Color(0, 255, 255));
              pixels.show();
              delay(20);
            }
            for (int i = 0; i <= NUMPIXELS; i++)
            {
              pixels.setPixelColor(i, pixels.Color(0, 0, 255));
              pixels.show();
              delay(20);
            }
            for (int i = 0; i <= NUMPIXELS; i++)
            {
              pixels.setPixelColor(i, pixels.Color(127, 0, 255));
              pixels.show();
              delay(20);
            }
            for (int i = 0; i <= NUMPIXELS; i++)
            {
              pixels.setPixelColor(i, pixels.Color(255, 0, 255));
              pixels.show();
              delay(20);
            }
            for (int i = 0; i <= NUMPIXELS; i++)
            {
              pixels.setPixelColor(i, pixels.Color(255, 0, 127));
              pixels.show();
              delay(20);
            }
          }
          else
          {
            pixels.setPixelColor(i, pixels.Color(redvalue, greenvalue, bluevalue));
            pixels.show();
            delay(20);
          }
        }
        else
        {
          pixels.setPixelColor(i, pixels.Color(0, 0, 0));
          pixels.show();
          delay(20);
        }
      }
    }
  }
}

 

Flutter Code

flutter_1.jpg
flutter_2.jpg
flutter_3.jpg
flutter_4.jpg
flutter_5.jpg
flutter_6.jpg
flutter_7.jpg
flutter_8.jpg

The reason why we need a mobile application is to create a ‘remote’ for the lamp so the user is able to use it without any physical interaction. The mobile application needs to fulfill certain objectives which are to hold multiple unique lamp devices, changing the color variable’s value, enabling ‘rainbow’ mode and turning on or off the lamp from the application. Furthermore it adds more flexibility for the user in using this lamp.

First of all, we need to start with downloading ‘google-services.json’ provided by Firestore and insert it into the src folder inside the android/app directory in order for Firestore able to establish a connection with our mobile application. Then we can start building our own mobile application by creating a rough sketch of User Interface (UI) and User Experience (UX) for the application. Once the sketch is capable of achieving our objectives, we can now start to plan the application’s logic and which types of data will be communicated between Firestore and our application. In this instructables, we will be using Flutter with dart programming language and SQLite for in-application data storage.

With picture number 1 as the reference, our mobile application will start executing ‘main.dart’ first which contains packages and files ready to be booted up and displayed for the application. Once an user open the app, the application will display a home screen UI file named ‘home_screen.dart’ where it give options for the user to add room and device from the circular plus button in the bottom of screen, directing the user into a created room with device listed inside and configuring personal application setting in the right side of top app bar in the shape of gear which leads another screen.

The Logic and design template for ‘home_screen.dart’ is supported by ‘sqlite.dart’,home provider folder, repository folder and model folder. The file ‘sqlite.dart’ will build an in-application database by creating two table data which are for specification of room and user’s lamp devices. The home provider and other similar provider files are used for checking the presence of a file to avoid copying a similar file and alerting to a file regarding update or change committed to a certain variable or class. The repository folder gives abilities to a file to retrieve and update databases from ‘sqlite.dart’ or Firestore.

If the user pressed a circular button to add a new device and room in the picture number 2, the user will be greeted by a pop up filled with three text fields which are the room name, device name and device identification (ID). Once the user pressed ‘Add’ text, the application will scan the device ID in the database or Firestore to avoid the same copy being added. When the scan finds no same ID identified, the application will add the room and device into the home screen, ready to use.

In the picture number 3, when the user chose and clicked a created room in the application, the user will be redirected to another screen filled with a list of lamp devices associated with the chosen room. The trash button on the right side of the lamp, and room in previous screen, acted as delete button which removed the selected lamp or room from the databases thus removing it entirely from the application.

With picture number 4 as the reference, when the user chooses and presses a lamp device, the user is able to turn the power on or off, manipulate the color of the lamp with the ‘Change Color’ button and turn the Rainbow mode on or off. The values of these option will be updated to the Firestore which will be relayed to the chosen lamp device

In the picture number 5, when the user interacts with the ‘Change Color’ button, the user is greeted with three sliders each representing color and the combination of three colors will result in a new color. These three values of color will be sent and updated to Firestore.

In the picture number 6, if the user presses the gear button to personally configure their application, the application offers two features which are changing the application’s language and theme setting.

From the picture number 7, with the help of easy_localization library, the application is now able to contain more than one language for its interface. In this application, we have made 3 different languages and made English as default language in the first-time launching of the application.

Here is the link of easy_localization: easy_localization | Flutter Package (pub.dev) (You can find all of dependency used in the flutter community and check the guide on how to use it in this website too)

In the picture number 8, inside the ‘THEME’ button, there are 2 buttons which each represent a developed theme for the application. Currently the theme only offers light mode, which is a default mode, and dark mode. The dark mode theme will replace all the white background into near-black color once the user pressed the Dark Mode.

And that is all of our brief explanation regarding our mobile application. Overall it have 6 different screens and uses lots of dependency to support all the logics behind the application


GitHub link for flutter code

3D Desain

You can use our design by downloading the attached document or making your own design. We also use cube milky acrylic for the top.

Downloads

Evaluation

The downside of this system are the light sometimes flickering because we use AA battery and there is some delay when we change the color or rainbow mode from the mobile application in this case flutter.

For the next development we suggest you to do some upgrades to get better result. First, we suggest you to change the power supply. Change the power supply with 5V adapter. Second, we suggest you to use Wi-Fi Manager library, so you can change your internet connection easily because in this tutorial you need to upload another code if you want to change the SSID and the Password. Third, you can use pull-down resistor that ESP32 already has inside by define it in the code. Our last suggestion is to change the shape of the casing to better or even cuter design