Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
7c31b15de2 | |||
01575e2937 | |||
f9a92bceca | |||
fbcac7c884 | |||
a4f8106009 | |||
f5ea07c90a | |||
469af4820e | |||
2a5f61101a | |||
a7857a3e45 |
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@ -33,7 +33,12 @@
|
|||||||
"tuple": "cpp",
|
"tuple": "cpp",
|
||||||
"type_traits": "cpp",
|
"type_traits": "cpp",
|
||||||
"utility": "cpp",
|
"utility": "cpp",
|
||||||
"typeinfo": "cpp"
|
"typeinfo": "cpp",
|
||||||
|
"complex": "cpp",
|
||||||
|
"cstring": "cpp",
|
||||||
|
"ctime": "cpp",
|
||||||
|
"iomanip": "cpp",
|
||||||
|
"iostream": "cpp"
|
||||||
},
|
},
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
}
|
}
|
20
README.md
20
README.md
@ -8,17 +8,15 @@ An esp8266 powered **IKEA Tvärfot** with a Neopixel ring and a proximity sensor
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Writing a configuration
|
## LED strip
|
||||||
|
|
||||||
### LED strip
|
|
||||||
A NeoPixel strip with 7 diods is used for the light and connects to **D8**. This could really be any DIO. For this change the variable ***ledPin*** in ***main.cpp***.
|
A NeoPixel strip with 7 diods is used for the light and connects to **D8**. This could really be any DIO. For this change the variable ***ledPin*** in ***main.cpp***.
|
||||||
To power the LED strip connect it to the 5V supply. The power output on the ESP8266 may not be enough if connected to the *raw* pin when powering from the USB port. The recommendation is to power it in parallel to the ESP by e.g. supplying the ESP with power on the raw pin while using the same for powering the LED.
|
To power the LED strip connect it to the 5V supply. The power output on the ESP8266 may not be enough if connected to the *raw* pin when powering from the USB port. The recommendation is to power it in parallel to the ESP by e.g. supplying the ESP with power on the raw pin while using the same for powering the LED.
|
||||||
|
|
||||||
### Distance Sensor
|
## Distance Sensor
|
||||||
To measure the distance an IR based sensor from Sharp (2Y0A21) is used. This sensor is connected to the analog input **A0**. This is the only analog input on the ESP8266.
|
To measure the distance an IR based sensor from Sharp (2Y0A21) is used. This sensor is connected to the analog input **A0**. This is the only analog input on the ESP8266.
|
||||||
To power the Sharp sensor connect it to any free 3.3V pin on the ESP.
|
To power the Sharp sensor connect it to any free 3.3V pin on the ESP.
|
||||||
|
|
||||||
### Configuration
|
## Writing a configuration
|
||||||
Open a Serial Monitor at baudrate 9600 and paste the following json configuration adapted to your needs.
|
Open a Serial Monitor at baudrate 9600 and paste the following json configuration adapted to your needs.
|
||||||
Before sending press the on board button "flash" or connect a button to D0. You will now have 10s to send the configuration. The onboard LED will light up as long as the board reads Serial input. If successful the new configuration should be shown in the terminal.
|
Before sending press the on board button "flash" or connect a button to D0. You will now have 10s to send the configuration. The onboard LED will light up as long as the board reads Serial input. If successful the new configuration should be shown in the terminal.
|
||||||
|
|
||||||
@ -32,6 +30,7 @@ It is also possible to send the same json configuration on mqtt using topic *lig
|
|||||||
"mqttuser": "mymqtt_user",
|
"mqttuser": "mymqtt_user",
|
||||||
"mqttpass": "7j%!fs#",
|
"mqttpass": "7j%!fs#",
|
||||||
"mqttport": "1883",
|
"mqttport": "1883",
|
||||||
|
"guestureEnabled" : "true"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -57,6 +56,15 @@ An identical json package is both sent and received to set and get the status of
|
|||||||
### Configuration topic
|
### Configuration topic
|
||||||
See *Writing a configuration*. This can also be done using mqtt at topic ***light/bedlamp/relay/0/config***
|
See *Writing a configuration*. This can also be done using mqtt at topic ***light/bedlamp/relay/0/config***
|
||||||
|
|
||||||
|
### Debug topic
|
||||||
|
Meant for debugging there is a topic ***light/bedlamp/debug***. This topic will output:
|
||||||
|
* The configuration of the lamp at boot
|
||||||
|
* Uptime (s) is sent repeatedly at every *state* update (default each 120s)
|
||||||
|
* All incoming topics and payloads
|
||||||
|
|
||||||
|
### Guesture Enable topic
|
||||||
|
Topic to activate or deactivate guestures for controlling the lamp ***light/bedlamp/guestureenabled/set***. Payload **1** or **0**.
|
||||||
|
|
||||||
## Home Assistant
|
## Home Assistant
|
||||||
Adjust the name and topics to suite your configuration
|
Adjust the name and topics to suite your configuration
|
||||||
```
|
```
|
||||||
@ -66,6 +74,8 @@ Adjust the name and topics to suite your configuration
|
|||||||
state_topic: light/bedlamp/relay/0
|
state_topic: light/bedlamp/relay/0
|
||||||
command_topic: light/bedlamp/relay/0/set
|
command_topic: light/bedlamp/relay/0/set
|
||||||
availability_topic: light/bedlamp/status
|
availability_topic: light/bedlamp/status
|
||||||
|
payload_available: 1
|
||||||
|
payload_not_available: 0
|
||||||
brightness: true
|
brightness: true
|
||||||
rgb: true
|
rgb: true
|
||||||
```
|
```
|
@ -13,6 +13,7 @@ public:
|
|||||||
char mqttUser[10];
|
char mqttUser[10];
|
||||||
char mqttPass[20];
|
char mqttPass[20];
|
||||||
int mqttPort;
|
int mqttPort;
|
||||||
|
bool gestureEnabled;
|
||||||
uint8_t brightness;
|
uint8_t brightness;
|
||||||
std::array<uint8_t, 3> color;
|
std::array<uint8_t, 3> color;
|
||||||
} Data;
|
} Data;
|
||||||
@ -23,6 +24,8 @@ public:
|
|||||||
ConfigTopic,
|
ConfigTopic,
|
||||||
AvailabilityTopic,
|
AvailabilityTopic,
|
||||||
DebugTopic,
|
DebugTopic,
|
||||||
|
InGuestureEnabled,
|
||||||
|
OutGuestureEnabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
static Config& Instance();
|
static Config& Instance();
|
||||||
|
@ -13,9 +13,16 @@ platform = espressif8266
|
|||||||
board = esp12e
|
board = esp12e
|
||||||
framework = arduino
|
framework = arduino
|
||||||
|
|
||||||
[env:esp8266 OTA]
|
[env:esp8266 OTA Right]
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
board = esp12e
|
board = esp12e
|
||||||
framework = arduino
|
framework = arduino
|
||||||
upload_protocol = espota
|
upload_protocol = espota
|
||||||
upload_port = bedlamp-philip
|
upload_port = bedlamp-right
|
||||||
|
|
||||||
|
[env:esp8266 OTA Left]
|
||||||
|
platform = espressif8266
|
||||||
|
board = esp12e
|
||||||
|
framework = arduino
|
||||||
|
upload_protocol = espota
|
||||||
|
upload_port = bedlamp-left
|
@ -65,13 +65,21 @@ String Config::getMqttTopic(MqttTopic topic)
|
|||||||
return String("light/" + String(data.hostname) + "/relay/0/set");
|
return String("light/" + String(data.hostname) + "/relay/0/set");
|
||||||
break;
|
break;
|
||||||
case (MqttTopic::ConfigTopic):
|
case (MqttTopic::ConfigTopic):
|
||||||
return String("light/" + String(data.hostname) + "/relay/0/config");
|
return String("light/" + String(data.hostname) + "/config");
|
||||||
break;
|
break;
|
||||||
case (MqttTopic::AvailabilityTopic):
|
case (MqttTopic::AvailabilityTopic):
|
||||||
return String("light/" + String(data.hostname) + "/status");
|
return String("light/" + String(data.hostname) + "/status");
|
||||||
break;
|
break;
|
||||||
case (MqttTopic::DebugTopic):
|
case (MqttTopic::DebugTopic):
|
||||||
return String("light/" + String(data.hostname) + "/debug");
|
return String("light/" + String(data.hostname) + "/debug");
|
||||||
|
break;
|
||||||
|
case (MqttTopic::InGuestureEnabled):
|
||||||
|
return String("light/" + String(data.hostname) +
|
||||||
|
"/guestureenabled/set");
|
||||||
|
break;
|
||||||
|
case (MqttTopic::OutGuestureEnabled):
|
||||||
|
return String("light/" + String(data.hostname) + "/guestureenabled");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
56
src/main.cpp
56
src/main.cpp
@ -22,6 +22,8 @@ const int sensorPin = A0;
|
|||||||
const int ledPin = D8;
|
const int ledPin = D8;
|
||||||
const int ledCount = 7;
|
const int ledCount = 7;
|
||||||
|
|
||||||
|
const int publishInterval = 120;
|
||||||
|
|
||||||
Config& config = Config::Instance();
|
Config& config = Config::Instance();
|
||||||
|
|
||||||
EasyButton button(buttonPin);
|
EasyButton button(buttonPin);
|
||||||
@ -53,6 +55,7 @@ void setConfig(String jsonConfig)
|
|||||||
String mqttUser = obj["mqttuser"];
|
String mqttUser = obj["mqttuser"];
|
||||||
String mqttPass = obj["mqttpass"];
|
String mqttPass = obj["mqttpass"];
|
||||||
int mqttPort = obj["mqttport"];
|
int mqttPort = obj["mqttport"];
|
||||||
|
bool gestureEnabled = obj["gestureEnabled"];
|
||||||
Serial.printf("Parsed hostname: %s, ssid: %s, pass: %s\n",
|
Serial.printf("Parsed hostname: %s, ssid: %s, pass: %s\n",
|
||||||
hostname.c_str(),
|
hostname.c_str(),
|
||||||
ssid.c_str(),
|
ssid.c_str(),
|
||||||
@ -72,6 +75,8 @@ void setConfig(String jsonConfig)
|
|||||||
sizeof(config.data.mqttPass));
|
sizeof(config.data.mqttPass));
|
||||||
config.data.mqttPort = mqttPort;
|
config.data.mqttPort = mqttPort;
|
||||||
|
|
||||||
|
config.data.gestureEnabled = gestureEnabled;
|
||||||
|
|
||||||
config.write();
|
config.write();
|
||||||
|
|
||||||
Serial.println("rebooting...");
|
Serial.println("rebooting...");
|
||||||
@ -81,7 +86,7 @@ void setConfig(String jsonConfig)
|
|||||||
|
|
||||||
String configToString()
|
String configToString()
|
||||||
{
|
{
|
||||||
DynamicJsonDocument doc(512);
|
DynamicJsonDocument doc(1024);
|
||||||
auto jsonConfig = doc["config"];
|
auto jsonConfig = doc["config"];
|
||||||
jsonConfig["hostname"] = config.data.hostname;
|
jsonConfig["hostname"] = config.data.hostname;
|
||||||
jsonConfig["ssid"] = config.data.ssid;
|
jsonConfig["ssid"] = config.data.ssid;
|
||||||
@ -90,6 +95,7 @@ String configToString()
|
|||||||
jsonConfig["mqttuser"] = config.data.mqttUser;
|
jsonConfig["mqttuser"] = config.data.mqttUser;
|
||||||
jsonConfig["mqttpass"] = config.data.mqttPass;
|
jsonConfig["mqttpass"] = config.data.mqttPass;
|
||||||
jsonConfig["mqttport"] = config.data.mqttPort;
|
jsonConfig["mqttport"] = config.data.mqttPort;
|
||||||
|
jsonConfig["guestureenabled"] = config.data.gestureEnabled;
|
||||||
jsonConfig["brightness"] = config.data.brightness;
|
jsonConfig["brightness"] = config.data.brightness;
|
||||||
JsonArray color = jsonConfig.createNestedArray("color");
|
JsonArray color = jsonConfig.createNestedArray("color");
|
||||||
color.add(config.data.color.at(0));
|
color.add(config.data.color.at(0));
|
||||||
@ -138,7 +144,15 @@ void mqttPublishState()
|
|||||||
myLed.get().c_str());
|
myLed.get().c_str());
|
||||||
mqttClient.publish(
|
mqttClient.publish(
|
||||||
config.getMqttTopic(Config::MqttTopic::AvailabilityTopic).c_str(),
|
config.getMqttTopic(Config::MqttTopic::AvailabilityTopic).c_str(),
|
||||||
"online");
|
"1");
|
||||||
|
mqttClient.publish(
|
||||||
|
config.getMqttTopic(Config::MqttTopic::OutGuestureEnabled).c_str(),
|
||||||
|
String(config.data.gestureEnabled).c_str());
|
||||||
|
|
||||||
|
auto uptime = millis() / 1000;
|
||||||
|
mqttClient.publish(
|
||||||
|
config.getMqttTopic(Config::MqttTopic::DebugTopic).c_str(),
|
||||||
|
String("{ \"uptime\":" + String(uptime) + "}").c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +171,9 @@ void mqttConnect()
|
|||||||
config.getMqttTopic(Config::MqttTopic::InTopic).c_str());
|
config.getMqttTopic(Config::MqttTopic::InTopic).c_str());
|
||||||
mqttClient.subscribe(
|
mqttClient.subscribe(
|
||||||
config.getMqttTopic(Config::MqttTopic::ConfigTopic).c_str());
|
config.getMqttTopic(Config::MqttTopic::ConfigTopic).c_str());
|
||||||
|
mqttClient.subscribe(
|
||||||
|
config.getMqttTopic(Config::MqttTopic::InGuestureEnabled)
|
||||||
|
.c_str());
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
Serial.printf("failed, rc=%d\n", mqttClient.state());
|
Serial.printf("failed, rc=%d\n", mqttClient.state());
|
||||||
@ -166,18 +183,31 @@ void mqttConnect()
|
|||||||
|
|
||||||
void mqttCallback(char* topic, byte* payload, unsigned int length)
|
void mqttCallback(char* topic, byte* payload, unsigned int length)
|
||||||
{
|
{
|
||||||
String msg(reinterpret_cast<char const*>(payload));
|
const String sTopic(topic);
|
||||||
|
String msg;
|
||||||
|
msg.concat(reinterpret_cast<char*>(payload), length);
|
||||||
Serial.printf("Message arrived [%s] \n", topic);
|
Serial.printf("Message arrived [%s] \n", topic);
|
||||||
Serial.println(msg.c_str());
|
Serial.println(msg.c_str());
|
||||||
|
|
||||||
if (String(topic) == config.getMqttTopic(Config::InTopic))
|
if (sTopic == config.getMqttTopic(Config::InTopic))
|
||||||
{
|
{
|
||||||
myLed.set(msg);
|
myLed.set(msg);
|
||||||
mqttPublishState();
|
mqttPublishState();
|
||||||
} else if (String(topic) == config.getMqttTopic(Config::ConfigTopic))
|
} else if (sTopic == config.getMqttTopic(Config::ConfigTopic))
|
||||||
{
|
{
|
||||||
setConfig(msg);
|
setConfig(msg);
|
||||||
|
} else if (sTopic == config.getMqttTopic(Config::InGuestureEnabled))
|
||||||
|
{
|
||||||
|
config.data.gestureEnabled = (msg == "1");
|
||||||
|
config.write();
|
||||||
|
mqttClient.publish(
|
||||||
|
config.getMqttTopic(Config::MqttTopic::OutGuestureEnabled).c_str(),
|
||||||
|
String(config.data.gestureEnabled).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mqttClient.publish(
|
||||||
|
config.getMqttTopic(Config::MqttTopic::DebugTopic).c_str(),
|
||||||
|
String("{\"received\": { \"" + sTopic + "\": " + msg + " } }").c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void offCallback()
|
void offCallback()
|
||||||
@ -232,7 +262,7 @@ void setup()
|
|||||||
ArduinoOTA.setHostname(config.data.hostname);
|
ArduinoOTA.setHostname(config.data.hostname);
|
||||||
ArduinoOTA.begin();
|
ArduinoOTA.begin();
|
||||||
|
|
||||||
tickerPublishStatus.attach(120, mqttPublishState);
|
tickerPublishStatus.attach(publishInterval, mqttPublishState);
|
||||||
mqttConnect();
|
mqttConnect();
|
||||||
|
|
||||||
mqttClient.publish(
|
mqttClient.publish(
|
||||||
@ -259,7 +289,7 @@ SensorData getDist()
|
|||||||
// https://global.sharp/products/device/lineup/data/pdf/datasheet/gp2y0a21yk_e.pdf
|
// https://global.sharp/products/device/lineup/data/pdf/datasheet/gp2y0a21yk_e.pdf
|
||||||
const int maxVal = 780; // 60 mm
|
const int maxVal = 780; // 60 mm
|
||||||
const int minVal = 173; // 800 mm
|
const int minVal = 173; // 800 mm
|
||||||
const int maxValidDist = 610;
|
const int maxValidDist = 480;
|
||||||
const int minValidDist = 70;
|
const int minValidDist = 70;
|
||||||
|
|
||||||
long dist = map(analogRead(sensorPin), maxVal, minVal, 60, 800);
|
long dist = map(analogRead(sensorPin), maxVal, minVal, 60, 800);
|
||||||
@ -269,7 +299,7 @@ SensorData getDist()
|
|||||||
float normalizedDistance =
|
float normalizedDistance =
|
||||||
(static_cast<float>(dist) - static_cast<float>(minValidDist)) /
|
(static_cast<float>(dist) - static_cast<float>(minValidDist)) /
|
||||||
static_cast<float>(maxValidDist - minValidDist);
|
static_cast<float>(maxValidDist - minValidDist);
|
||||||
// Serial.printf("Norm dist: %f\n", normalizedDistance);
|
// Serial.printf("Dist: %d, normalized: %f\n", dist, normalizedDistance);
|
||||||
|
|
||||||
return {.distance = static_cast<float>(dist),
|
return {.distance = static_cast<float>(dist),
|
||||||
.distanceNormalized = normalizedDistance,
|
.distanceNormalized = normalizedDistance,
|
||||||
@ -286,7 +316,7 @@ void evalDist(SensorData data)
|
|||||||
|
|
||||||
static float lastDist = data.distance;
|
static float lastDist = data.distance;
|
||||||
|
|
||||||
const float stillThreshold = 20.0f; // Distance in mm
|
const float stillThreshold = 15.0f; // Distance in mm
|
||||||
const float releaseThreshold =
|
const float releaseThreshold =
|
||||||
70.0f; // If value increases more than this it is considered a release
|
70.0f; // If value increases more than this it is considered a release
|
||||||
|
|
||||||
@ -351,9 +381,11 @@ void evalDist(SensorData data)
|
|||||||
|
|
||||||
void slowLoop()
|
void slowLoop()
|
||||||
{
|
{
|
||||||
SensorData data = getDist();
|
if (config.data.gestureEnabled)
|
||||||
|
{
|
||||||
evalDist(data);
|
SensorData data = getDist();
|
||||||
|
evalDist(data);
|
||||||
|
}
|
||||||
|
|
||||||
myLed.run();
|
myLed.run();
|
||||||
|
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const float minBrightness = 0.1;
|
const float minBrightness = 0.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
MyLed::MyLed(int ledPin, int ledCount, bool effects)
|
MyLed::MyLed(int ledPin, int ledCount, bool effects)
|
||||||
@ -56,7 +58,7 @@ void MyLed::toggle()
|
|||||||
|
|
||||||
void MyLed::set(String jsonState)
|
void MyLed::set(String jsonState)
|
||||||
{
|
{
|
||||||
DynamicJsonDocument doc(1024);
|
DynamicJsonDocument doc(2048);
|
||||||
deserializeJson(doc, jsonState);
|
deserializeJson(doc, jsonState);
|
||||||
JsonObject obj = doc.as<JsonObject>();
|
JsonObject obj = doc.as<JsonObject>();
|
||||||
Serial.println(jsonState.c_str());
|
Serial.println(jsonState.c_str());
|
||||||
@ -107,7 +109,7 @@ MyLed::State MyLed::getState()
|
|||||||
|
|
||||||
void MyLed::setBrightness(float brightness)
|
void MyLed::setBrightness(float brightness)
|
||||||
{
|
{
|
||||||
brightness = max(minBrightness, brightness);
|
brightness = min(max(minBrightness, brightness), 1.0f);
|
||||||
|
|
||||||
if (_useEffects)
|
if (_useEffects)
|
||||||
{
|
{
|
||||||
@ -151,8 +153,13 @@ void MyLed::registerOffCallback(CallbackFunc callback)
|
|||||||
|
|
||||||
void MyLed::apply()
|
void MyLed::apply()
|
||||||
{
|
{
|
||||||
_strip.fill(_strip.Color(static_cast<uint8_t>(_color.at(0) * _brightness),
|
uint8_t a = 254;
|
||||||
static_cast<uint8_t>(_color.at(1) * _brightness),
|
_strip.fill(
|
||||||
static_cast<uint8_t>(_color.at(2) * _brightness)));
|
_strip.Color(min(static_cast<uint8_t>(_color.at(0) * _brightness),
|
||||||
|
std::numeric_limits<uint8_t>::max()),
|
||||||
|
min(static_cast<uint8_t>(_color.at(1) * _brightness),
|
||||||
|
std::numeric_limits<uint8_t>::max()),
|
||||||
|
min(static_cast<uint8_t>(_color.at(2) * _brightness),
|
||||||
|
std::numeric_limits<uint8_t>::max())));
|
||||||
_strip.show();
|
_strip.show();
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user