Open spuder opened 2 years ago
Slow = 2.35 seconds
Values between 460 and 470 are pretty close
// RGB LED Circle Wave Breathing LED
int led_pins[1] = {6};
int jj = 0; // 0 = red, 1 = green, 2 = blue
float smoothness_pts = 463;//larger=slower change in brightness
void setup() {
pinMode(2, INPUT_PULLUP);
Serial.begin(9600);
for (int ii = 0;ii<sizeof(led_pins)/sizeof(int);ii++){
pinMode(led_pins[ii],OUTPUT);
}
}
void loop() {
for (int ii=0;ii<smoothness_pts;ii++){
float pwm_val = 255.0*sqrt(1.0 - pow(abs((2.0*(ii/smoothness_pts))-1.0),2.0));
analogWrite(led_pins[jj],int(pwm_val));
auto pin = digitalRead(2);
if (pin == LOW) {
ii=0;
}
delay(5);
// Serial.println(int(pwm_val));
}
}
Current breathing is blocking
https://avital.ca/notes/a-closer-look-at-apples-breathing-light
The gausian is very pleasing, however it is too subtile compared to the official mute me. The mute me has more of an urgency feeling to it
// RGB LED Gaussian Wave Breathing LED
int led_pins[1] = {6};
int jj = 1; // 0 = red, 1 = green, 2 = blue
float smoothness_pts = 500;//larger=slower change in brightness
float gamma = 0.14; // affects the width of peak (more or less darkness)
float beta = 0.5; // shifts the gaussian to be symmetric
void setup() {
Serial.begin(9600);
for (int ii = 0;ii<sizeof(led_pins)/sizeof(int);ii++){
pinMode(led_pins[ii],OUTPUT);
}
}
void loop() {
for (int ii=0;ii<smoothness_pts;ii++){
float pwm_val = 255.0*(exp(-(pow(((ii/smoothness_pts)-beta)/gamma,2.0))/2.0));
analogWrite(led_pins[jj],int(pwm_val));
delay(5);
Serial.println(int(pwm_val));
}
}
This is really really close.
https://thingpulse.com/breathing-leds-cracking-the-algorithm-behind-our-breathing-pattern
#include <math.h>
void setup() {
pinMode(6, OUTPUT);
Serial.begin(9600);
}
void loop() {
//1300 = slower
//1100 = faster
auto period = 1200.0;// 2000.0
float val = (exp(sin(millis()/period * PI)) - 0.368) * 108.0;
Serial.print(0);
Serial.print(" ");
Serial.print(val);
Serial.print(" ");
Serial.println(255);
analogWrite(6, val);
}
Here is how this library solves it. Note that it only runs the calculation once per millisecond
https://github.com/SethSenpai/singleLEDLibrary/blob/master/singleLEDLibrary.cpp#L108-L112
I tried to pregenerate all the values, but it takes too much memory.
float ledBrightnessTable[2400];
float table[2400];
auto now = millis();
auto counter = 0;
void setup() {
while (millis() < (now + 2400)) {
if (now != millis() ) {
auto period = 1200.0;// 2000.0
float val = (exp(sin(millis()/period * PI)) - 0.368) * 108.0;
table[counter] = val;
counter++;
now=millis();
}
}
Serial.begin(9600);
Serial.print("Generated table");
for (auto i = 0; i < sizeof(table); i++) {
Serial.print(table[i]);
Serial.print(',');
}
}
void loop() {
}
New problem. Whenever the values is ~ 127 the led turns off for a split second.
void Led::pulse(int period) {
if((last_refresh_time + 1) < millis() ){
last_refresh_time = millis();
// https://thingpulse.com/breathing-leds-cracking-the-algorithm-behind-our-breathing-pattern/
float min = 0.36787944;
float amplitude = 42.54590641;
uint8_t b = (exp(sin(millis()/(float)period*PI)) - min)* amplitude;
this->brightness = (byte)b * 255 / 100;
Serial.print(0);
Serial.print(" ");
Serial.print(255);
Serial.print(" ");
Serial.print(127);
Serial.print(" ");
Serial.println(this->brightness);
byte red_brightness = (mapRed(this->color) * this->brightness) / 100;
byte green_brightness = (mapGreen(this->color) * this->brightness) / 100;
byte blue_brightness = (mapBlue(this->color) * this->brightness) / 100;
invertAnalogWrite(this->red_pin, red_brightness);
invertAnalogWrite(this->green_pin, green_brightness);
invertAnalogWrite(this->blue_pin, blue_brightness);
}
}
Here is a dump of the data. I expect to see a 0 somwhere around 127, but I never see that. There doesn't appear to be any gaps that would explain why the LED turns off briefly in the pulse sequence. (Captured at 115200)
This does not clip
#include <math.h>
void setup() {
pinMode(6, OUTPUT);
Serial.begin(9600);
}
void loop() {
auto period = 1200.0;
float min = 0.381966011;
float amplitude = 42.0; //42.54590641;
uint8_t b = (exp(sin(millis()/(float)period*PI)) - min)* amplitude;
b = b * 255 / 100;
Serial.print(0);
Serial.print(" ");
Serial.print(255);
Serial.print(" ");
Serial.print(127);
Serial.print(" ");
Serial.println(b);
analogWrite(6, 255 - b);
}
Solved with constrain b = constrain(b * 255 / 100, 0, 255) ;
#include <math.h>
void setup() {
pinMode(6, OUTPUT);
Serial.begin(9600);
}
void loop() {
//1300 = slower
//1100 = faster
auto period = 1200.0;// 2000.0
float min = 0.25; // how curved the bell is (also has an impact on max altitude)
float amplitude = 42.0; //42.54590641;
uint8_t b = (exp(sin(millis()/(float)period*PI)) - min)* amplitude;
b = constrain(b * 255 / 100, 0, 255) ;
Serial.print(0);
Serial.print(" ");
Serial.print(255);
Serial.print(" ");
Serial.print(127);
Serial.print(" ");
Serial.println(b);
analogWrite(6, 255 - b);
}
I tried to create a histogram showing the function. Unfortunatly 30 frames per second just isn't that much data ( I really need data every 10/100 milliseconds).
Convert video to images
docker run -it --entrypoint /bin/bash --workdir /data -v $PWD:/data linuxserver/ffmpeg:version-4.4-cli
ffmpeg -i ../breathing-left.mp4 %04d.png
Convert images to histogram
docker run -it -v $PWD:/data --workdir /data --entrypoint /bin/bash dpokidov/imagemagick
# https://stackoverflow.com/a/51332870/1626687
function foobar {
echo "$1" && convert "$1" -colorspace HSI -channel b -separate +channel -scale 1x1 -format "%[fx:100*u]\n" info: >> /tmp/foobar.txt;
}
export -f foobar
rm /tmp/foobar.txt
find . -type f -name "*.png" -exec bash -c 'foobar "$1"' _ {} \;
cp /tmp/foobar.txt /data
Nevertheless it still appears to be a gaussian or a sinusoid function
https://makersportal.com/blog/2020/3/27/simple-breathing-led-in-arduino
This is pretty good. It never goes fully dark. However it is blocking which isn't ideal
// RGB LED Gaussian Wave Breathing LED
int led_pins[1] = {10};
int jj = 1; // 0 = red, 1 = green, 2 = blue
float smoothness_pts = 485;//larger=slower change in brightness
float gamma = 0.250; // affects the width of peak (more or less darkness)
float beta = 0.5; // shifts the gaussian to be symmetric
void setup() {
Serial.begin(9600);
for (int ii = 0;ii<sizeof(led_pins)/sizeof(int);ii++){
pinMode(led_pins[ii],OUTPUT);
}
}
void loop() {
for (int ii=0;ii<smoothness_pts;ii++){
float pwm_val = 255.0*(exp(-(pow(((ii/smoothness_pts)-beta)/gamma,2.0))/2.0));
analogWrite(led_pins[jj],255-int(pwm_val));
delay(5);
Serial.println(255-int(pwm_val));
}
}
More testing. I setup a 2400 second loop and discovered that the period isn't exactly 2400 seconds. After 20-30 loops the leds get out of sync. 🤔 The ramp up and ramp down might be different durations.
void setup() {
pinMode(6, OUTPUT);
}
void loop() {
analogWrite(6, 0);
delay(1200);
analogWrite(6, 255);
delay(1200);
}
Tried to visualize the graph with processing.
String[] data;
float[] newData;
int x = 100;
int counter = 0;
void setup() {
frameRate(24);
data = loadStrings("/Users/spencer.owen/Desktop/muteme-single-wave.txt");
newData = new float[data.length];
for (int i = 0; i < data.length; i++) {
print("data: ");
print(data[i]);
newData[i] = map(float(data[i]),42.0, 66.0, 0, 255);
print(" newData: ");
print(newData[i]);
println();
}
size(400, 400);
noStroke();
}
void draw() {
background(155);
ellipse(counter, newData[counter], 50, 50);
if (counter+1 >= newData.length) {
counter = 0;
}
else {
counter++;
}
}
I'm realizing that only capturing 30fps on the phone isn't enough data points
https://user-images.githubusercontent.com/242382/184798221-5c4b51ce-f0f5-49e9-8682-b0c6484ddc90.mov
I refilmed at 240 hertz (slow mo) on my iphone with locked exposure. I believe my phone was compensating exposure which would explain why the top of the wave looked slanted.
Running for ~12 seconds gave 2305 datapoints.
cd ~/Desktop/breathing
docker run -it --entrypoint /bin/bash --workdir /data -v $PWD:/data linuxserver/ffmpeg:version-4.4-cli
ffmpeg -i ./breathing-slowmo.mov %04d.png
docker run -it -v $PWD:/data --workdir /data --entrypoint /bin/bash dpokidov/imagemagick
# https://stackoverflow.com/a/51332870/1626687
function foobar {
echo "$1" && convert "$1" -colorspace HSI -channel b -separate +channel -scale 1x1 -format "%[fx:100*u]\n" info: >> /tmp/foobar.txt;
}
export -f foobar
rm /tmp/foobar.txt
find . -type f -name "*.png" -exec bash -c 'foobar "$1"' _ {} \;
cp /tmp/foobar.txt /data
I can see how it is clipping/flickering, but no idea why
float b = (exp(sin(millis()/(float)period*PI)) - 0.36787944)* 108.0;
Timed several more and found 10 pulses exactly equaled 23.5 seconds.
23.5 * 6 = 14.1 pulses per minute
T (period) = 2.35 seconds F = 1flash / 2.35 seconds = 0.42553191 HZ
Fixed studdering
byte red_brightness = (mapRed(this->color) * this->brightness) / 100;
Should be
byte red_brightness = (mapRed(this->color) * this->brightness) / 255;
Additional tests on the period
1175 should theoretically be exact, but feels fast
1175 = 23.4 seconds for 10 cycles
1180 = 23.6 seconds for 10 cycles
1190 = 23.8 seconds for 10 cycles
1200 = 23.8 seconds for 10 cycles
1202 = 23.84 seconds for 10 cycles
1210 = 24.3 seconds for 10 cycles
1204 = 23.9 seconds for 10 cycles
1205 = 23.93 seconds for 10 cycles
mute me = 23.9 seconds for 10 cycles
mute me = 23.9 seconds for 10 cycles
mute me = 23.9 seconds for 10 cycles
The current breathing light goes from 0% to 100%.
Change it so it matches the official mute me
https://makersportal.com/blog/2020/3/27/simple-breathing-led-in-arduino
May need to convert to the FastLED library since the RGBLed library can't limit min brightness