Hey guys,

today I’d like to show you how I modded an ordinary lamp into a smart (IoT) one.

Foreword

I’m a huge Rocketbeans TV Fan. Rocketbeans is a big 24/7 Stream Show focused on gaming. Germans may know the hosts from “Giga” or “Game One”.

As they started selling a USB Lamp with their logo and some RGB-LED I thought about making it smart to display the current type of show running. At the moment there are three types of shows available: “Live”(=Red), “Premiere”(=Blue), “Playback”(=White). The unmodified lamp is able to switch colors on a button press.

via Rocketbeans Forum

Requirements

The Lamp

I bought one of them. As soon as it arrived I started to disassemble the lamp to check the circuit board. To my surprise, it’s straight forward.

The (smart) Microcontroller

Leroy told me about the ESP32 which is a very small Microcontroller with built-in Wifi and Bluetooth. This one seems to be a perfect match for the smart part of the lamp.

Loading





You can buy one via eBay

The Schedule of Rocketbeans TV

After viewing and manually scraping their Website I was able to find a Link which returns the current and four further shows as JSON object. (The following image only shows the current show also referred as Array[0]).

Now we gathered all requirements and start tinkering.

Reverse Engineering the Lamp Circuit

We started to measure some circuits and figured out how the embedded controller works. The controller has eight pins: GND, 5v, a Touch Button on the Case and three colors. The remaining ones may be used for infrared.

Next we removed the embedded microcontroller to attach the ESP32 controller.

Writing the Microcontroller Code

Since I never wrote any kind of ESP32 Code I decided to invest some time to read Tutorials/Blogs about “ESP32 Wifi”, “Arduino JSON” and “Arduino HTTP Client”.

After several compile errors and reading through manuals on how to use the above libraries I wrote the following code.

ESP32 Code (Version 1) #include <WiFi.h> #include <HTTPClient.h> #include <ArduinoJson.h> #define __product__ "Beanduino: Dynamic Color Lamp" #define __model__ "DyCoLa" #define __version__ "1000DEV" #define __author__ "Marvyn Zalewski <[email protected]>" #define __copyright__ "(c) 2015 The Senso Team" #define __license__ "All rights reserved." const char* ssid = "WIFI"; const char* password = "WIFIPASSWORD"; const char* rbTvUri = "https://www.rocketbeans.tv/?next5Shows=true"; int status = WL_IDLE_STATUS; void setup() { Serial.begin(115200); Serial.println(__product__); Serial.print(__model__); Serial.println(__version__); Serial.println(__author__); Serial.println((String)__copyright__ + ", " + (String)__license__ + "

"); while ( status != WL_CONNECTED) { Serial.println("Beanduino - Attempting to connect to Wifi network, SSID: " + String(ssid)); status = WiFi.begin(ssid, password); delay(10000); } Serial.println("Beanduino - Connected to network"); } String stateToColor(String state){ const size_t showStateJsonBufferSize = JSON_OBJECT_SIZE(3); DynamicJsonBuffer showStateJsonBuffer(showStateJsonBufferSize); JsonObject& showStates = showStateJsonBuffer.createObject(); showStates["live"] = "red"; showStates["new"] = "blue"; showStates["playback"] = "white"; return showStates[state]; } void setLampState(String state){ String showState = state; String showColor = stateToColor(state); Serial.println(__product__ ";" + showState + ";" + showColor); } void loop() { if ((WiFi.status() == WL_CONNECTED)) { HTTPClient http; http.begin(rbTvUri); int httpCode = http.GET(); if (httpCode > 0) { const size_t bufferSize = JSON_ARRAY_SIZE(5) + 5*JSON_OBJECT_SIZE(14) + 2970; DynamicJsonBuffer jsonBuffer(bufferSize); String payload = http.getString(); JsonArray& root = jsonBuffer.parseArray(payload); if (!root.success()){ Serial.println("PARSING FAILED"); Serial.println(payload); } JsonArray& root_ = root; JsonObject& root_0 = root_[0]; int isLive = root_0["isLive"]; int isNew = root_0["isNew"]; if (isLive == 1) { setLampState("live"); } else { if (isNew == 1) { setLampState("new"); } else { setLampState("playback"); } } } else { Serial.println("Error on HTTP request"); } http.end(); } delay(10000); }

Running the code

Beanduino: Dynamic Color Lamp

DyCoLa1000DEV

Marvyn Zalewski <[email protected]>

(c) 2015 The Senso Team, All rights reserved. Beanduino – Attempting to connect to Wifi network, SSID: WIFI

Beanduino – Attempting to connect to Wifi network, SSID: WIFI

Beanduino – Connected to network

Beanduino: Dynamic Color Lamp;live;red

Beanduino: Dynamic Color Lamp;live;red

Putting it all together

Now we know everything to set up the smart lamp. At first, we’re soldering all needed wires and stick them into the battery compartment.

As you can see there are six wires coming from the Lamp Circuit. (VCC which is 5V, GND, directly from the Button and all three colors.)

Due to space issues, we soldered the wires directly onto the ESP32.

Wire Map:

ESP32 VIN -> Lamp Circuit VCC

ESP32 GND -> Lamp Circuit GND

ESP32 D14 -> Lamp Circuit LED Green (the left resistor R5)

ESP32 D22 -> Lamp Case Button (without any function at the moment)

ESP32 D26 -> Lamp Circuit LED Blue (the middle resistor R6)

ESP32 D33 -> Lamp Circuit LED Red (the right resistor R7)

After a nearly whole code refactoring, I finished it and moved it to Github.

Beanlamp Code #include <Arduino.h> #include <WiFi.h> #include <HTTPClient.h> #include <ArduinoJson.h> /* * Coniguration ** CONNECTION_INDICATOR - blinking led during startup when defined */ #define CONNECTION_INDICATOR #define LED_RED GPIO_NUM_14 #define LED_GREEN GPIO_NUM_27 #define LED_BLUE GPIO_NUM_26 #define WIFI_SSID "WLAN" #define WIFI_PASSWORD "WLAN_PASSWORD" #define ROCKETBEANS_API "https://www.rocketbeans.tv/?next5Shows=true" /* * Information */ #define __product__ "Beanlight" #define __version__ "1000DEV" #define __author__ "Marvyn Zalewski <[email protected]>" #define __copyright__ "(c) 2018 KeyboardInterrupt.org" /* * Global Variables */ int status = WL_IDLE_STATUS; /* * Functions */ void setLampToRed() { digitalWrite(LED_RED, HIGH); digitalWrite(LED_GREEN, LOW); digitalWrite(LED_BLUE, LOW); } void setLampToBlue() { digitalWrite(LED_BLUE, HIGH); digitalWrite(LED_GREEN, LOW); digitalWrite(LED_RED, LOW); } void setLampToGreen() { digitalWrite(LED_GREEN, HIGH); digitalWrite(LED_BLUE, LOW); digitalWrite(LED_RED, LOW); } void setLampToWhite() { digitalWrite(LED_GREEN, HIGH); digitalWrite(LED_BLUE, HIGH); digitalWrite(LED_RED, HIGH); } void setLampToBlack() { digitalWrite(LED_GREEN, LOW); digitalWrite(LED_BLUE, LOW); digitalWrite(LED_RED, LOW); } /* * Init */ void setup() { Serial.begin(115200); Serial.println(__product__); Serial.println(__version__); Serial.println(__author__); Serial.println((String)__copyright__ + "

"); pinMode(LED_RED, OUTPUT); pinMode(LED_BLUE, OUTPUT); pinMode(LED_GREEN, OUTPUT); while (status != WL_CONNECTED) { Serial.println((String)__product__ + " attempting to connect to Wifi network, WIFI_SSID: " + (String)WIFI_SSID); status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD); #ifdef CONNECTION_INDICATOR setLampToRed(); delay(2000); setLampToBlue(); delay(2000); setLampToGreen(); delay(2000); #else delay(6000); #endif }; #ifdef CONNECTION_INDICATOR for (int i = 0; i <= 3; i++) { setLampToGreen(); delay(250); setLampToBlack(); delay(250); }; #endif Serial.println((String)__product__ + " connected to network"); } void loop() { if ((WiFi.status() == WL_CONNECTED)) { HTTPClient http; http.begin(ROCKETBEANS_API); int httpCode = http.GET(); if (httpCode > 0) { const size_t bufferSize = JSON_ARRAY_SIZE(5) + 5 * JSON_OBJECT_SIZE(14) + 2970; DynamicJsonBuffer jsonBuffer(bufferSize); String payload = http.getString(); JsonArray &root = jsonBuffer.parseArray(payload); if (!root.success()) { Serial.println((String)__product__ + " parsing failed."); Serial.println(payload); } /* When current show is live */ if (root[0]["isLive"] == 1) setLampToRed(); /* When current show is new */ else if (root[0]["isNew"] == 1) setLampToBlue(); /* When current show is playback */ else setLampToWhite(); } else { Serial.println((String)__product__ + " error on HTTP request"); } http.end(); } delay(60000); }

Result

After working nearly ten hours on the project I would say it was a lot of fun.

If you have any questions or improvements, let me know in the comments. 🙂