Skip to content
Snippets Groups Projects
Commit 4a312c4d authored by fxk8y's avatar fxk8y :spider:
Browse files

Merge branch 'master' of git.services.c3pb.de:fxk8y/CyanLight

parents b25a691f 5edeaec2
No related branches found
No related tags found
No related merge requests found
idf_component_register(
SRC_DIRS
"."
INCLUDE_DIRS
"."
REQUIRES
main
)
component_compile_options(-std=c++17)
#include "CyanLightControl.hpp" #include "CyanLightControl.hpp"
#include "qthing.h" #include <qthing>
#include "mqtt_common.h" #include <qthing/mqtt_common.hpp>
#include <string> #include <string>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <function>
#include <algorithm> #include <algorithm>
#include <functional>
#include "esp_err.h" #include "esp_err.h"
#include "esp_log.h" #include "esp_log.h"
#include "driver/ledc.h" #include "driver/ledc.h"
using namespace qthing;
const char *TAG = "CLC"; const char *TAG = "CLC";
const std::string delimiter = ":"; const std::string delimiter = ":";
float CyanLight::bytes2float(uint8_t *bytes) { static float bytes2float(const char *bytes) {
float f; float f;
memcpy(&f, bytes, sizeof(f)); memcpy(&f, bytes, sizeof(f));
...@@ -28,12 +30,12 @@ float CyanLight::bytes2float(uint8_t *bytes) { ...@@ -28,12 +30,12 @@ float CyanLight::bytes2float(uint8_t *bytes) {
} }
CyanLight::Controller::Controller(uint8_t networkChannel, uint8_t channelsConfigured) : networkChannel{networkChannel} { CyanLight::Controller::Controller(uint16_t baseChannel, uint8_t channelsConfigured) : baseChannel{baseChannel} {
this->channelsConfigured = std::max(channelsConfigured, CyanLight::MAX_CHANNELS); this->channelsConfigured = std::min(channelsConfigured, CyanLight::MAX_CHANNELS);
this->channels = new CyanLight::PWMChannel*[this->channelsConfigured]; this->channels = new CyanLight::PWMChannel*[this->channelsConfigured];
//this->channels = (CyanLight::PWMChannel **) malloc(this->channelsConfigured * sizeof(CyanLight::PWMChannel*));
this->timer_cfg.duty_resolution = (ledc_timer_bit_t)this->resolution; this->timer_cfg.duty_resolution = (ledc_timer_bit_t)this->resolution;
this->timer_cfg.freq_hz = this->frequency; this->timer_cfg.freq_hz = this->frequency;
...@@ -46,22 +48,32 @@ CyanLight::Controller::Controller(uint8_t networkChannel, uint8_t channelsConfig ...@@ -46,22 +48,32 @@ CyanLight::Controller::Controller(uint8_t networkChannel, uint8_t channelsConfig
ledc_fade_func_install(0); ledc_fade_func_install(0);
for (uint8_t i = 0; i < channelsConfigured; i++) { for (uint8_t i = 0; i < this->channelsConfigured; i++) {
this->channels[i] = new CyanLight::PWMChannel(i, CyanLight::channelGPIOS[i]); this->channels[i] = new CyanLight::PWMChannel(i, CyanLight::channelGPIOS[i]);
char topic[256]; char topic[32];
snprintf(topic, sizeof(topic), DEVICE_NAMESPACE "CyanLight/pwm/$%i", i); snprintf(topic, sizeof(topic), "pwm/$%i", i);
add_message_callback(topic, [&, i](std::string message) {
this->setChannel(i, strtof(message.c_str(), NULL));
// float f = strtof(message.c_str(), NULL); add_message_callback(this->genDeviceTopic(topic), [&, i](std::string message) {
// ESP_LOGW(TAG, "i: %d msg: %s f: %f", i, message.c_str(), f); this->setPWM(i, strtof(message.c_str(), NULL));
this->callPacketCallback();
}); });
} }
std::string header("fxCyanF");
uint8_t headerLength = header.length();
qthing::addUDPPacketCallback(header, [&, headerLength](udpPacket packet) {
this->handleUnicast(packet.payload + headerLength, packet.length - headerLength);
});
std::function<void(std::string)> setCh = [&](std::string message) {
long int ch = strtol(message.c_str(), NULL, 0);
this->setBaseChannel(ch);
};
std::function<void(std::string)> setFrq = [&](std::string message) { std::function<void(std::string)> setFrq = [&](std::string message) {
long int frq = strtol(message.c_str(), NULL, 0); long int frq = strtol(message.c_str(), NULL, 0);
this->setFrequency(frq); this->setFrequency(frq);
...@@ -95,6 +107,14 @@ CyanLight::Controller::Controller(uint8_t networkChannel, uint8_t channelsConfig ...@@ -95,6 +107,14 @@ CyanLight::Controller::Controller(uint8_t networkChannel, uint8_t channelsConfig
}; };
std::function<void(std::string)> getBCh = [&](std::string ignored) {
this->publishBaseChannel();
};
std::function<void(std::string)> getChs = [&](std::string ignored) {
this->publishChannelCount();
};
std::function<void(std::string)> getFrq = [&](std::string ignored) { std::function<void(std::string)> getFrq = [&](std::string ignored) {
this->publishFrequency(); this->publishFrequency();
}; };
...@@ -109,34 +129,82 @@ CyanLight::Controller::Controller(uint8_t networkChannel, uint8_t channelsConfig ...@@ -109,34 +129,82 @@ CyanLight::Controller::Controller(uint8_t networkChannel, uint8_t channelsConfig
// device-local setters // device-local setters
add_message_callback(DEVICE_NAMESPACE "CyanLight/frqres/set", setFrqRes); add_message_callback(this->genDeviceTopic("frqres/set"), setFrqRes);
add_message_callback(DEVICE_NAMESPACE "CyanLight/frequency/set", setFrq); add_message_callback(this->genDeviceTopic("channel/set"), setCh);
add_message_callback(DEVICE_NAMESPACE "CyanLight/resolution/set", setRes); add_message_callback(this->genDeviceTopic("frequency/set"), setFrq);
add_message_callback(this->genDeviceTopic("resolution/set"), setRes);
// device-lokal getters // device-lokal getters
add_message_callback(DEVICE_NAMESPACE "CyanLight/frequency/get", getFrq); add_message_callback(this->genDeviceTopic("frequency/get"), getFrq);
add_message_callback(DEVICE_NAMESPACE "CyanLight/resolution/get", getRes); add_message_callback(this->genDeviceTopic("resolution/get"), getRes);
add_message_callback(DEVICE_NAMESPACE "CyanLight/frqres/get", getFrqRes); add_message_callback(this->genDeviceTopic("frqres/get"), getFrqRes);
add_message_callback(this->genDeviceTopic("channel/get"), getBCh);
add_message_callback(this->genDeviceTopic("channelCnt/get"), getChs);
// global setters // global setters
add_message_callback("service/CyanLight/frqres/set", setFrqRes); add_message_callback(this->genServiceTopic("frqres/set"), setFrqRes);
add_message_callback("service/CyanLight/frequency/set", setFrq); add_message_callback(this->genServiceTopic("channel/set"), setCh);
add_message_callback("service/CyanLight/resolution/set", setRes); add_message_callback(this->genServiceTopic("frequency/set"), setFrq);
add_message_callback(this->genServiceTopic("resolution/set"), setRes);
// global getters // global getters
add_message_callback("service/CyanLight/frequency/get", getFrq); add_message_callback(this->genServiceTopic("frequency/get"), getFrq);
add_message_callback("service/CyanLight/resolution/get", getRes); add_message_callback(this->genServiceTopic("resolution/get"), getRes);
add_message_callback("service/CyanLight/frqres/get", getFrqRes); add_message_callback(this->genServiceTopic("frqres/get"), getFrqRes);
add_message_callback(this->genServiceTopic("channel/get"), getBCh);
add_message_callback(this->genServiceTopic("channelCnt/get"), getChs);
}
bool CyanLight::Controller::handleUnicast(const char *data, std::size_t length) {
std::size_t size = this->getChannelCount() * sizeof(float);
int32_t diff = length - size;
if (diff < 0) {
ESP_LOGE(TAG, "Invalid data length[ %i ]: Received ΔB = %i bytes too few", length, -diff);
return false;
}
for (uint8_t ch = 0; ch < this->getChannelCount(); ch++) {
float f = bytes2float(data + ch * sizeof(float));
this->setPWM(ch, f);
}
this->callPacketCallback();
return true;
}
bool CyanLight::Controller::handleBroadcast(const char *data, std::size_t length) {
std::size_t size = this->getChannelCount() * sizeof(float);
std::size_t offset = this->getBaseChannel() * sizeof(float);
int32_t diff = length - offset - size;
if (diff < 0) { // TODO: test thoroughly!
ESP_LOGE(TAG, "Invalid data length[ %i ]: Received ΔB = %i bytes too few", length, -diff);
return false;
}
for (uint8_t ch = 0; ch < this->getChannelCount(); ch++) {
float f = bytes2float(data + offset + ch * sizeof(float));
this->setPWM(ch, f);
}
this->callPacketCallback();
return true;
} }
void CyanLight::Controller::setChannel(uint8_t channel, float value) { void CyanLight::Controller::setPWM(uint8_t channel, float value) {
if (channel >= this->channelsConfigured) { if (channel >= this->channelsConfigured) {
ESP_LOGW(TAG, "ChannelID out of range: channel[ %i ]", channel); ESP_LOGW(TAG, "ChannelID out of range: channel[ %i ]", channel);
return; return;
} }
value = this->gammaCorrector(value);
if (value < 0.0f) value = 0.0f; if (value < 0.0f) value = 0.0f;
if (value > 1.0f) value = 1.0f; if (value > 1.0f) value = 1.0f;
...@@ -147,6 +215,11 @@ void CyanLight::Controller::setChannel(uint8_t channel, float value) { ...@@ -147,6 +215,11 @@ void CyanLight::Controller::setChannel(uint8_t channel, float value) {
} }
void CyanLight::Controller::setGammaCorrector(CyanLight::GammaCorrector gammaCorrector) {
this->gammaCorrector = gammaCorrector;
}
bool CyanLight::Controller::setFrqRes(uint32_t frq_hz, uint8_t res_bits) { bool CyanLight::Controller::setFrqRes(uint32_t frq_hz, uint8_t res_bits) {
this->timer_cfg.duty_resolution = (ledc_timer_bit_t)res_bits; this->timer_cfg.duty_resolution = (ledc_timer_bit_t)res_bits;
...@@ -167,6 +240,19 @@ bool CyanLight::Controller::setFrqRes(uint32_t frq_hz, uint8_t res_bits) { ...@@ -167,6 +240,19 @@ bool CyanLight::Controller::setFrqRes(uint32_t frq_hz, uint8_t res_bits) {
} }
} }
void CyanLight::Controller::setBaseChannel(uint16_t baseChannel) {
this->baseChannel = baseChannel;
}
uint16_t CyanLight::Controller::getBaseChannel() {
return this->baseChannel;
}
uint8_t CyanLight::Controller::getChannelCount() {
return this->channelsConfigured;
}
bool CyanLight::Controller::setFrequency(uint32_t frq_hz) { bool CyanLight::Controller::setFrequency(uint32_t frq_hz) {
return this->setFrqRes(frq_hz, this->resolution); return this->setFrqRes(frq_hz, this->resolution);
} }
...@@ -183,21 +269,52 @@ uint8_t CyanLight::Controller::getResolution() { ...@@ -183,21 +269,52 @@ uint8_t CyanLight::Controller::getResolution() {
return this->resolution; return this->resolution;
} }
void CyanLight::Controller::setPacketHandledCallback(CyanLight::PacketHandledCallback callback) {
this->packetCallback = callback;
}
std::string CyanLight::Controller::genDeviceTopic(const char *suffix) {
return std::string(DEVICE_NAMESPACE + "fxCyanF/") + std::string(suffix);
}
std::string CyanLight::Controller::genServiceTopic(const char *suffix) {
return std::string("service/fxCyanF/") + std::string(suffix);
}
void CyanLight::Controller::callPacketCallback() {
this->packetCallback();
}
void CyanLight::Controller::publishBaseChannel() {
char tmp[16];
snprintf(tmp, sizeof(tmp), "%i", this->getBaseChannel());
publish_message(DEVICE_NAMESPACE + "CyanLight/channel", tmp);
}
void CyanLight::Controller::publishChannelCount() {
char tmp[16];
snprintf(tmp, sizeof(tmp), "%i", this->getChannelCount());
publish_message(DEVICE_NAMESPACE + "CyanLight/channelCnt", tmp);
}
void CyanLight::Controller::publishFrequency() { void CyanLight::Controller::publishFrequency() {
char tmp[16]; char tmp[16];
snprintf(tmp, sizeof(tmp), "%i", this->getFrequency()); snprintf(tmp, sizeof(tmp), "%i", this->getFrequency());
publish_message(DEVICE_NAMESPACE "CyanLight/frequency", tmp); publish_message(DEVICE_NAMESPACE + "CyanLight/frequency", tmp);
} }
void CyanLight::Controller::publishResolution() { void CyanLight::Controller::publishResolution() {
char tmp[16]; char tmp[16];
snprintf(tmp, sizeof(tmp), "%i", this->getResolution()); snprintf(tmp, sizeof(tmp), "%i", this->getResolution());
publish_message(DEVICE_NAMESPACE "CyanLight/resolution", tmp); publish_message(DEVICE_NAMESPACE + "CyanLight/resolution", tmp);
} }
void CyanLight::Controller::publishFrqRes() { void CyanLight::Controller::publishFrqRes() {
char tmp[32]; char tmp[32];
snprintf(tmp, sizeof(tmp), "%i%s%i", this->getFrequency(), delimiter.c_str(), this->getResolution()); snprintf(tmp, sizeof(tmp), "%i%s%i", this->getFrequency(), delimiter.c_str(), this->getResolution());
publish_message(DEVICE_NAMESPACE "CyanLight/frqres", tmp); publish_message(DEVICE_NAMESPACE + "CyanLight/frqres", tmp);
} }
...@@ -15,11 +15,21 @@ namespace CyanLight { ...@@ -15,11 +15,21 @@ namespace CyanLight {
const uint8_t channelGPIOS[MAX_CHANNELS] = { 27, 16, 17, 18, 19, 21 }; const uint8_t channelGPIOS[MAX_CHANNELS] = { 27, 16, 17, 18, 19, 21 };
typedef std::function<void()> PacketHandledCallback;
typedef std::function<float(float)> GammaCorrector;
class Controller { class Controller {
public: public:
Controller(uint8_t networkChannel, uint8_t channelsConfigured = 1); Controller(uint16_t baseChannel, uint8_t channelsConfigured = 1);
void setPWM(uint8_t channel, float value);
void setGammaCorrector(GammaCorrector gammaCorrector);
void setChannel(uint8_t channel, float value); uint8_t getChannelCount();
void setBaseChannel(uint16_t baseChannel);
uint16_t getBaseChannel();
bool setFrequency(uint32_t frq_hz); bool setFrequency(uint32_t frq_hz);
uint32_t getFrequency(); uint32_t getFrequency();
...@@ -29,19 +39,34 @@ namespace CyanLight { ...@@ -29,19 +39,34 @@ namespace CyanLight {
bool setFrqRes(uint32_t frq_hz, uint8_t res_bits); bool setFrqRes(uint32_t frq_hz, uint8_t res_bits);
void setPacketHandledCallback(PacketHandledCallback callback);
bool handleUnicast(const char *data, std::size_t length);
bool handleBroadcast(const char *data, std::size_t length);
std::string genDeviceTopic(const char *suffix);
std::string genServiceTopic(const char *suffix);
void publishFrqRes(); void publishFrqRes();
void publishFrequency(); void publishFrequency();
void publishResolution(); void publishResolution();
void publishBaseChannel();
void publishChannelCount();
private: private:
uint32_t frequency = 1000; // Hz uint32_t frequency = 1000; // Hz
uint8_t resolution = 10; // bits uint8_t resolution = 10; // bits
uint8_t networkChannel; uint16_t baseChannel;
uint8_t channelsConfigured; uint8_t channelsConfigured;
PWMChannel **channels; PWMChannel **channels;
ledc_timer_config_t timer_cfg; ledc_timer_config_t timer_cfg;
GammaCorrector gammaCorrector = [](float f) { return f*f; };
PacketHandledCallback packetCallback = []() {};
void callPacketCallback();
}; };
float bytes2float(uint8_t *bytes);
} }
#pragma once #pragma once
#include <qthing.h> #include <qthing>
#include "driver/ledc.h" #include "driver/ledc.h"
......
#include <qthing.h> #include <qthing>
#include "esp_log.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "CyanLightControl.hpp" #include "CyanLightControl.hpp"
CyanLight::Controller ctrl(0, 3); CyanLight::Controller ctrl(0, 1);
void device_main() { void device_main() {
// ground the pin beneath our channel 0 IO // ground the pin beneath our channel 0 IO
uint8_t GND = 23; /* uint8_t GND = 23;
gpio_config_t conf; gpio_config_t conf;
...@@ -23,12 +28,31 @@ void device_main() { ...@@ -23,12 +28,31 @@ void device_main() {
conf.pull_down_en = GPIO_PULLDOWN_DISABLE; conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&conf); gpio_config(&conf);
gpio_set_level((gpio_num_t)GND, 0); gpio_set_level((gpio_num_t)GND, 0); */
qthing::Config cfg;
cfg.apply();
qthing::enable_wifi();
return;
auto delay = 10 / portTICK_PERIOD_MS;
float limit = 50.0f;
while (true) {
for (uint8_t i = 0; i < limit; i++) {
float pwm = i / limit;
ctrl.setPWM(0, pwm*pwm);
vTaskDelay(delay);
}
enable_wlan(); for (uint8_t i = 0; i < limit; i++) {
float pwm = i / limit;
ctrl.setPWM(0, 1.0f - pwm*pwm);
vTaskDelay(delay);
}
}
} }
#!/usr/bin/env nix-shell #!/usr/bin/env nix-shell
#!nix-shell -i python -p python3 #!nix-shell -i python -p python3
import time
import struct import struct
from socket import *
value = 13.37 port = 4213
hosts = ['192.168.0.115']
fps = 60
step = 1 / 32
s = socket(AF_INET, SOCK_DGRAM)
def f2b(f): def f2b(f):
return bytearray(struct.pack("f", f)) return bytearray(struct.pack("f", f))
ba = f2b(value) x = 0.0
print(ba) goUp = True
print([ "0x%02x" % b for b in ba ])
while True:
if goUp:
x += step
else:
x -= step
if x > 1:
x = 1.0
goUp = not goUp
if x < 0:
x = 0.0
goUp = not goUp
data = f2b(x)
for _ in range(5):
data += f2b(0.0)
data = bytearray(b'fxCyanF') + data
for host in hosts:
try:
s.sendto(data, (host, port))
except:
pass
time.sleep(1 / fps)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment