MAR10
For X-mas, Cara got me this sweet Paladone Mario Build-A-Level Desk Light. Each block has an embedded LED and can be moved around It. The unit power in via a barrel jack and has a non-momentary switch on the side to turn the LEDs all on or off. Pretty cool, but I think we can make it cooler…
I pulled it apart and found a simple circuit board inside and a LOT of room for a power supply, Arduino, relays, or whatever I wanted! Yay!
I wanted to keep it looking as stock as possible, including keeping the 5v barrel jack and switch, so someone who recognized the device wouldn’t be easily able to tell the difference.
Modification of the non-momentary (locking) switch was pretty easy, just pull it apart and remove the retention pin. Boom! Now it’s a momentary (non-locking) push button that can easily be connected to a GPIO for control.
Arduino Code
/*******************************************************************************
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▄▄▄▄▒▒▒▒▒▒▒ ▒▒
▒▒▒▒▒▒▄▀▀▓▓▓▀█▒▒▒▒▒▒ March 10th 2022 - yo at joeltron dot com ▒▒
▒▒▒▒▄▀▓▓▄██████▄▒▒▒▒ _______ _______ _______ __ _______ ▒▒
▒▒▒▄█▄█▀░░▄░▄░█▀▒▒▒▒ ( )( ___ )( ____ )/ \ ( __ ) ▒▒
▒▒▄▀░██▄░░▀░▀░▀▄▒▒▒▒ | () () || ( ) || ( )|\/) ) | ( ) | ▒▒
▒▒▀▄░░▀░▄█▄▄░░▄█▄▒▒▒ | || || || (___) || (____)| | | | | / | ▒▒
▒▒▒▒▀█▄▄░░▀▀▀█▀▒▒▒▒▒ | |(_)| || ___ || __) | | | (/ /) | ▒▒
▒▒▒▄▀▓▓▓▀██▀▀█▄▀▀▄▒▒ | | | || ( ) || (\ ( | | | / | | ▒▒
▒▒█▓▓▄▀▀▀▄█▄▓▓▀█░█▒▒ | ) ( || ) ( || ) \ \____) (_| (__) | ▒▒
▒▒▀▄█░░░░░█▀▀▄▄▀█▒▒▒ |/ \||/ \||/ \__/\____/(_______) ▒▒
▒▒▒▄▀▀▄▄▄██▄▄█▀▓▓█▒▒ ▒▒
▒▒█▀▓█████████▓▓▓█▒▒ Paladone Super Mario Bros Build-A-Level Light ▒▒
▒▒█▓▓██▀▀▀▒▒▒▀▄▄█▀▒▒ Making dumb things less dumb ▒▒
▒▒▒▀▀▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
******************************************************************************/
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <EEPROM.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <ESP_Mail_Client.h>
#include <ArduinoOTA.h>
/*****************************************************************************/
// settings
// gpio
int buttonPin = 2;
int relayCount = 4;
int relayPins[] = {5, 4, 12, 14};
// wifi
const char* ssid = "XXXXXXXXXXXXX";
const char* password = "XXXXXXXXXXXXX";
// ota
#define SENSORNAME "mar10_esp"
#define OTApassword "XXXXXXXXXXXXX"
int OTAport = 8266;
// email
#define IMAP_HOST "smtp.joeltron.com"
#define IMAP_FOLDER "INBOX"
#define IMAP_PORT 993
#define AUTHOR_EMAIL "yo@joeltron.com"
#define AUTHOR_PASSWORD "XXXXXXXXXXXXX"
// timezone
int timeOffset = 28800; // GMT +8
/*****************************************************************************/
// Cycle time
int tickLength = 100; // milisecond delay between cycles (1000=1sec)
int tickCount = 999; // always trigger first time
// mode
int mode = 0; //0=normal 1=hour 2=email 3=random
// max number can display
int maxBinary = relayCount * relayCount - 1;
// Hour
int currentHour = 0;
int hourDelay = 100;
// Email
int currentEmails = 0;
int emailDelay = 100;
void printSelectedMailboxInfo(SelectedFolderInfo sFolder);
IMAPSession imap;
ESP_Mail_Session session;
IMAP_Config config;
// random lights
int randDelay = 12;
// count
int counter = 0;
int countDelay = 20;
// Buttons
bool buttonState = false;
// Relays
bool relayStates[] = {true, true, true, true};
// Blink between modes
int modeBlink = 4; // 0 for no blink to indicate mode
int modeBlinkLength = 250;
// Time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
/*****************************************************************************/
void setup() {
// start serial
Serial.begin(9600);
// eeprom
EEPROM.begin(512);
// button input
pinMode(buttonPin, INPUT_PULLUP);
// relay outputs
for (int i = 0; i < relayCount; i++) {
pinMode(relayPins[i], OUTPUT_OPEN_DRAIN);
digitalWrite(relayPins[i], HIGH);
}
// network
connectWiFi();
// ota update
ArduinoOTA.setPort(OTAport);
ArduinoOTA.setHostname(SENSORNAME);
ArduinoOTA.setPassword((const char *)OTApassword);
ArduinoOTA.onStart([]() {
Serial.println("Start");
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
// time
timeClient.begin();
timeClient.setTimeOffset(timeOffset);
timeClient.begin();
// email
session.server.host_name = IMAP_HOST;
session.server.port = IMAP_PORT;
session.login.email = AUTHOR_EMAIL;
session.login.password = AUTHOR_PASSWORD;
session.network_connection_handler = connectWiFi;
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
loadMode();
}
void connectWiFi()
{
Serial.println("Booting");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("Connection Failed! Rebooting...");
delay(5000);
ESP.restart();
}
}
void loadMode()
// read from eeprom
{
mode = char(EEPROM.read(0));
if (mode > 3) mode = 0;
Serial.println("Loading eeprom");
Serial.println(mode);
}
void saveMode()
{
EEPROM.write(0, mode);
EEPROM.commit();
Serial.println("Saving eeprom");
Serial.println(mode);
tickCount = 0; // reset timer
}
void readButton()
// read the state of the button and switch modes
{
buttonState = !digitalRead(buttonPin);
//Serial.println(buttonState);
// inc mode
if (buttonState) {
mode++;
if (mode > 3) mode = 0;
Serial.print("Mode: ");
Serial.println(mode);
saveMode();
// turn off all relays
for (int i = 0; i < relayCount; i++)
relayStates[i] = false;
// blink to show what mode you are in
for (int j = 0; j < modeBlink; j++) {
// just this mode on
relayStates[mode] = true;
setRelays();
delay(modeBlinkLength);
relayStates[mode] = false;
setRelays();
delay(modeBlinkLength);
}
}
/*
for (int j = 0; j < modeBlink; j++) {
for (int i = 0; i < relayCount; i++) {
if (i == mode)
relayStates[i] = true;
else
relayStates[i] = false;
}
for (int i = 0; i < relayCount; i++) {
relayStates[i] = false;
setRelays();
delay(modeBlinkLength);
setRelays();
delay(modeBlinkLength);
}
}
}
*/
}
void setRelays()
// turn on/off relays according to relayStates array
{
// Serial.println("Setting Relays");
for (int i = 0; i < relayCount; i++) {
/*Serial.print("LED");
Serial.print(i);
Serial.print(": ");
Serial.println(relayStates[i]);*/
if (!relayStates[i]) // Normally Closed logic
digitalWrite(relayPins[i], LOW);
else
digitalWrite(relayPins[i], HIGH);
}
}
void intToBinaryRelays(int input)
{
// dont go over max
if (input > maxBinary)
input = maxBinary;
Serial.println("");
for (byte i = 0; i < relayCount; i++) {
byte state = bitRead(input, i);
relayStates[i] = state;
Serial.print(state);
}
}
void modeCount()
// counts up to max
{
if (tickCount > countDelay) {
tickCount = 0;
intToBinaryRelays(counter);
counter++;
if (counter > maxBinary)
counter = 0;
} else {
tickCount++;
}
}
void modeAllOn()
// turn all on
{
for (int i = 0; i < relayCount; i++)
relayStates[i] = true;
}
void modeHour()
// shows hour in binary
{
// only delay after first run
if (tickCount > hourDelay) {
tickCount = 0;
timeClient.update();
currentHour = timeClient.getHours();
// 24hrs is for cool kids only
if (currentHour > 12)
currentHour = currentHour - 12;
Serial.println(currentHour);
Serial.println("");
intToBinaryRelays(currentHour);
} else {
tickCount++;
}
}
void modeEmails()
// displays email count in binary
{
if (tickCount > emailDelay) {
tickCount = 0;
if (!imap.connect(&session, &config))
return;
if (!imap.selectFolder(F(IMAP_FOLDER)))
return;
currentEmails = imap.selectedFolder().msgCount();
intToBinaryRelays(currentEmails);
Serial.println(currentEmails);
} else {
tickCount++;
}
}
void modeTest()
// swaps relays on/off
{
if (tickCount > randDelay) {
tickCount = 0;
for (int i = 0; i < relayCount; i++) {
relayStates[i] = !relayStates[i];
}
} else {
tickCount++;
}
}
void modeRun()
// climbs up through relays
{
if (tickCount > randDelay) {
tickCount = 0;
for (int i = 0; i < relayCount; i++) {
relayStates[i] = (counter == i);
}
counter++;
if (counter > 3)
counter = 0;
} else {
tickCount++;
}
}
void modeRandom()
// randomly sets on/off
{
if (tickCount > randDelay) {
tickCount = 0;
for (int i = 0; i < relayCount; i++) {
relayStates[i] = random(0, 2);
}
} else {
tickCount++;
}
}
void loop() {
ArduinoOTA.handle();
// now do stuff
readButton();
switch (mode) {
case 0:
modeAllOn();
break;
case 1:
modeHour();
break;
case 2:
modeEmails();
break;
case 3:
//modeRandom();
modeCount();
//modeRun();
//modeTest();
break;
}
// trigger relays
setRelays();
// waitin
delay(tickLength);
}
/******************************************************************************
le end. yo at joeltron dot com
******************************************************************************/