Created
January 7, 2021 13:58
-
-
Save vtalpaert/ad76461b1cb46f9c7d0757e9f27335ea to your computer and use it in GitHub Desktop.
Add a DancePad input to your home automation, helps creating cool lighting effects for parties
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Add your Wii Dance Dance Revolution Dance Pad Controller as an input to your home automation. | |
https://en.wikipedia.org/wiki/Dance_pad | |
For those that want an interactive RGB lights scenery, configure your homeassistant (hass.io) | |
to change your dancefloor's lighting according to the MQTT signals. Now you are doing your | |
own light show ! | |
Hardware: | |
Each dance pad input is a weight sensible resistor. I replaced the original circuit board | |
with an Arduino Nano 33 IoT. The eight signals are read from the analog pins with internal | |
pullups, and the middle connection is connected to the ground. The signal quality is directly | |
dependant on the quality of your electric connections. | |
Configuration: | |
You should adapt the hardcoded configuration from the DancePad::begin method. | |
The parameters are : | |
- a threshold for considering an analog value as high or low | |
- a "p" value between 0 and 1, used in the equation : new_value = p * read_value + (1-p) * previous_value | |
- an activeHigh parameter to invert the logic such as high analog value is read as inactive | |
- a delta value to create an hysteresis effect in order to avoid spamming on/off messages at the threshold | |
PubSubClient library: https://github.com/knolleary/pubsubclient MIT License | |
WiFiNINA library: https://github.com/arduino-libraries/WiFiNINA | |
Tested with Arduino Nano 33 IoT | |
*/ | |
#include <WiFiNINA.h> | |
#include <PubSubClient.h> | |
char ssid[] = "MY_NETWORK_NAME"; | |
char pass[] = "WIFI_PASSWORD"; | |
char host[] = "192.168.mqtt.host"; | |
char clientid[] = "arduino-iot"; | |
char username[] = "mqttUser"; | |
char password[] = "mqttPassword"; | |
String pubBaseTopic = "dancepad/"; | |
long pubRate = 50; // [ms] | |
bool serialPlot = true; // set to true to silence all debug prints | |
WiFiClient wifiClient; | |
PubSubClient client(wifiClient); | |
long lastMsg = 0; | |
void setup_wifi() | |
{ | |
delay(10); | |
if (!serialPlot) { | |
Serial.println(); | |
Serial.print("Connecting to "); | |
Serial.println(ssid); | |
} | |
WiFi.begin(ssid, pass); | |
while (WiFi.status() != WL_CONNECTED) | |
{ | |
delay(500); | |
if (!serialPlot) { | |
Serial.print("."); | |
} | |
} | |
randomSeed(micros()); | |
if (!serialPlot) { | |
Serial.println(""); | |
Serial.println("WiFi connected"); | |
Serial.println("IP address: "); | |
Serial.println(WiFi.localIP()); | |
} | |
} | |
void reconnect() | |
{ | |
// Loop until we're reconnected | |
while (!client.connected()) | |
{ | |
if (!serialPlot) { | |
Serial.print("Attempting MQTT connection..."); | |
} | |
// Create a random client ID | |
String clientId = "ArduinoClient-"; | |
clientId += String(random(0xffff), HEX); | |
// Attempt to connect | |
if (client.connect(clientId.c_str(), username, password)) | |
{ | |
if (!serialPlot) { | |
Serial.println("connected"); | |
} | |
} else { | |
if (!serialPlot) { | |
Serial.print("failed, rc="); | |
Serial.print(client.state()); | |
Serial.println(" try again in 5 seconds"); | |
} | |
// Wait 5 seconds before retrying | |
delay(5000); | |
} | |
} | |
} | |
void publish(String topic, int isPressed) { | |
// publish single char | |
char payLoad[1]; | |
itoa(isPressed, payLoad, 10); | |
String fullTopic = pubBaseTopic + topic; | |
client.publish(fullTopic.c_str(), payLoad); | |
} | |
class SlowMovingAnalogValue { | |
// some analog pin might not work with pullups | |
private: | |
uint8_t pin_; | |
float threshold_; | |
float p_; | |
bool activeHigh_ = false; | |
bool state = false; | |
float delta_; | |
public: | |
float value_; | |
void begin(uint8_t pin, float threshold, float p, bool activeHigh = false, float delta = 1) { | |
pin_ = pin; | |
threshold_ = threshold; | |
p_ = p; | |
activeHigh_ = activeHigh; | |
delta_ = delta; | |
pinMode(pin_, INPUT_PULLUP); | |
}; | |
uint8_t read() { | |
// keep in mind the pull-up means the pushbutton's logic is inverted | |
uint8_t val = analogRead(pin_); | |
value_ = p_ * (float) val + (1 - p_) * value_; | |
if (value_ > threshold_ + delta_) { | |
state = activeHigh_; | |
} else if (value_ < threshold_ - delta_) { | |
state = !activeHigh_; | |
} | |
return val; | |
}; | |
bool isPressed() { | |
return state; | |
} | |
}; | |
struct ArrowsState { | |
bool up; | |
bool down; | |
bool left; | |
bool right; | |
}; | |
struct ControlsState { | |
bool a; | |
bool b; | |
bool plus; | |
bool minus; | |
}; | |
class DancePad { | |
public: | |
SlowMovingAnalogValue plus_; | |
SlowMovingAnalogValue a_; | |
SlowMovingAnalogValue right_; | |
SlowMovingAnalogValue up_; | |
SlowMovingAnalogValue down_; | |
SlowMovingAnalogValue left_; | |
SlowMovingAnalogValue b_; | |
SlowMovingAnalogValue minus_; | |
ArrowsState arrows; | |
ControlsState controls; | |
void begin() { | |
plus_.begin(A7, 30, 0.05); | |
a_.begin(A6, 5, 0.05); | |
right_.begin(A5, 160, 0.05); | |
up_.begin(A4, 165, 0.05, true); | |
down_.begin(A3, 55, 0.05); | |
left_.begin(A2, 20, 0.05); | |
b_.begin(A1, 20, 0.05); | |
minus_.begin(A0, 30, 0.05); | |
} | |
void read() { | |
plus_.read(); | |
a_.read(); | |
right_.read(); | |
up_.read(); | |
down_.read(); | |
left_.read(); | |
b_.read(); | |
minus_.read(); | |
} | |
void updateArrows() { | |
bool updated = false; | |
bool up = up_.isPressed(); | |
if (up != arrows.up) { | |
updated = true; | |
arrows.up = up; | |
publish("up", up); | |
} | |
bool down = down_.isPressed(); | |
if (down != arrows.down) { | |
updated = true; | |
arrows.down = down; | |
publish("down", down); | |
} | |
bool left = left_.isPressed(); | |
if (left != arrows.left) { | |
updated = true; | |
arrows.left = left; | |
publish("left", left); | |
} | |
bool right = right_.isPressed(); | |
if (right != arrows.right) { | |
updated = true; | |
arrows.right = right; | |
publish("right", right); | |
} | |
if (updated) { | |
char payLoad[4]; | |
itoa(up, &payLoad[0], 10); | |
itoa(down, &payLoad[1], 10); | |
itoa(left, &payLoad[2], 10); | |
itoa(right, &payLoad[3], 10); | |
String fullTopic = pubBaseTopic + "arrows"; | |
client.publish(fullTopic.c_str(), payLoad); | |
} | |
} | |
bool updateControls() { | |
bool updated = false; | |
bool a = a_.isPressed(); | |
if (a != controls.a) { | |
updated = true; | |
controls.a = a; | |
publish("a", a); | |
} | |
bool b = b_.isPressed(); | |
if (b != controls.b) { | |
updated = true; | |
controls.b = b; | |
publish("b", b); | |
} | |
bool plus = plus_.isPressed(); | |
if (plus != controls.plus) { | |
updated = true; | |
controls.plus = plus; | |
publish("plus", plus); | |
} | |
bool minus = minus_.isPressed(); | |
if (minus != controls.minus) { | |
updated = true; | |
controls.minus = minus; | |
publish("minus", minus); | |
} | |
return updated; | |
} | |
} | |
dance = DancePad(); | |
void setup() | |
{ | |
Serial.begin(115200); | |
dance.begin(); | |
setup_wifi(); | |
client.setServer(host, 1883); | |
} | |
void loop() | |
{ | |
if (!client.connected()) | |
{ | |
reconnect(); | |
} | |
client.loop(); | |
long now = millis(); | |
// note that reading in the loop() is time dependant, | |
// so the p parameter is adapted to this tempo | |
dance.read(); | |
if (now - lastMsg > pubRate) | |
{ | |
lastMsg = now; | |
dance.updateArrows(); | |
dance.updateControls(); | |
} | |
if (serialPlot) { | |
Serial.print(" v = "); | |
// Change here to visualize another input | |
Serial.println(dance.up_.value_); | |
} | |
delay(2); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice !!