SIBA 1A SMD fuse 6.1x2.6mm
Wemos-d1-mini
ROT-1A/B or KY-040 (it’s quite cheap and popular, sold with different names)
DSN-1504-3A buck converter
JST-XH 2 and 4 pin connectors (standard 2.5mm)
Thx, any recommendations on where to get them?
I got the PCB’s and built my version.
(Many thanks for building and sharing the layout !!!)
I had trouble with the GPIO picking up a reliable signal (don’t have the pins decoupled due to lack of caps).
Also, the state of the fan was not always clear/obvious to me. So I made a variation using the rotary encoder component instead.
here is my yaml
esphome:
name: "fornuftig"
esp8266:
board: d1_mini
# Enable logging
logger:
# Enable Home Assistant API
api:
ota:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Fornuftig-AP"
password: ""
captive_portal:
globals:
- id: fan_speed
type: int
restore_value: yes
initial_value: "0"
- id: speed_0
type: int
restore_value: yes
initial_value: "0"
- id: speed_1
type: int
restore_value: yes
initial_value: "33"
- id: speed_2
type: int
restore_value: yes
initial_value: "66"
- id: speed_3
type: int
restore_value: yes
initial_value: "100"
switch:
- platform: restart
name: "${friendly_name} Restart"
sensor:
- platform: template
name: "$friendly_name - Power consumption"
id: fonuftig_power_consumption
device_class: power
state_class: measurement
unit_of_measurement: "W"
accuracy_decimals: 1
update_interval: 30s
- platform: integration
name: "$friendly_name - Consumed Energy"
sensor: fonuftig_power_consumption
time_unit: "h"
device_class: energy
state_class: total_increasing
unit_of_measurement: "Wh"
restore: false
integration_method: left
- platform: pulse_counter
pin:
# Fan - FG
number: D2
mode: INPUT_PULLUP
unit_of_measurement: "RPM"
accuracy_decimals: 0
id: fan_tach
name: Fornuftig Fanspeed
filters:
- multiply: 0.1
- platform: template
id: set_fan_freq
filters:
- multiply: 3
- lambda: |-
if (id(fornuftig_fan).state){
if(x > 300) { // limit max frequency
return 300;
} else if(x < 36 && x > 3) { // limit low frequency
return 36;
} else if(x <= 3) {
return 0;
} else {
return x;
}
} else {
return 0;
}
on_value:
then:
- output.esp8266_pwm.set_frequency:
id: pwm_output
frequency: !lambda "return int(x);"
- logger.log: "on_value called"
- lambda: |-
// send current status to homeassistant
if(x) {
auto call = id(fornuftig_fan).turn_on();
call.set_speed(int(x)/3);
call.perform();
} else {
auto call = id(fornuftig_fan).turn_off();
call.perform();
}
- sensor.template.publish:
id: fonuftig_power_consumption
state: !lambda "return int(15*(id(my_rotary_encoder).state)/100);"
- platform: rotary_encoder
id: my_rotary_encoder
name: "Rotary Encoder"
min_value: 0
max_value: 100
publish_initial_value: true
restore_mode: ALWAYS_ZERO
pin_a: # clk
number: D5
inverted: true
mode:
input: true
pullup: true
pin_b: # DT
number: D6
inverted: true
mode:
input: true
pullup: true
on_clockwise:
then:
- light.turn_on:
id: status_led
- logger.log:
format: "rotary sensor state was: %.1f"
args: ["id(my_rotary_encoder).state"]
- lambda: |-
if (id(my_rotary_encoder).state < id(speed_1)) {
// set rotary to speed1, turn on fan
id(my_rotary_encoder).set_value(id(speed_1));
id(status_led).turn_on();
} else if (id(my_rotary_encoder).state < id(speed_2) && id(my_rotary_encoder).state > id(speed_0) && id(my_rotary_encoder).state < id(speed_3)) {
// set rotary to speed2
id(my_rotary_encoder).set_value(id(speed_2));
} else if (id(my_rotary_encoder).state < id(speed_3) && id(my_rotary_encoder).state > id(speed_1) && id(my_rotary_encoder).state > id(speed_0)) {
// set rotary to speed1
id(my_rotary_encoder).set_value(id(speed_3));
} else {
// do nothing;
}
# trigger set fan speed
- fan.turn_on:
id: fornuftig_fan
speed: !lambda "return int(id(my_rotary_encoder).state);"
- logger.log:
format: "rotary sensor state set to: %.1f"
args: ["id(my_rotary_encoder).state"]
on_anticlockwise:
then:
- logger.log:
format: "rotary sensor state was: %.1f"
args: ["id(my_rotary_encoder).state"]
- lambda: |-
if (id(my_rotary_encoder).state > id(speed_2)) {
// set rotary to speed2
id(my_rotary_encoder).set_value(id(speed_2));
} else if (id(my_rotary_encoder).state > id(speed_1) && id(my_rotary_encoder).state < id(speed_3) && id(my_rotary_encoder).state > id(speed_0)) {
// set rotary to speed1
id(my_rotary_encoder).set_value(id(speed_1));
} else if (id(my_rotary_encoder).state > id(speed_0) && id(my_rotary_encoder).state < id(speed_1)) {
// set rotary to speed0; turn fan & led off
id(my_rotary_encoder).set_value(id(speed_1));
} else {
// do nothing;
}
# trigger set fan speed
- fan.turn_on:
id: fornuftig_fan
speed: !lambda "return int(id(my_rotary_encoder).state);"
- logger.log:
format: "rotary sensor state set to: %.1f"
args: ["id(my_rotary_encoder).state"]
binary_sensor:
- platform: gpio
# SW
id: "knob_1"
pin:
number: D7
mode: INPUT_PULLUP
inverted: true
filters:
- delayed_on_off: 100ms
on_press:
then:
if:
condition:
and:
- fan.is_off: fornuftig_fan
- light.is_off: status_led
then:
- fan.turn_on:
id: fornuftig_fan
speed: !lambda "return id(speed_1);"
- light.turn_on:
id: status_led
- sensor.rotary_encoder.set_value:
id: my_rotary_encoder
value: !lambda 'return id(speed_1);'
else:
- fan.turn_off:
id: fornuftig_fan
- light.turn_off:
id: status_led
- logger.log: "SW - pin_c - Turned Off"
- sensor.rotary_encoder.set_value:
id: my_rotary_encoder
value: !lambda "return id(speed_0);"
- sensor.template.publish:
id: fonuftig_power_consumption
state: !lambda "return int(0);"
light:
- platform: binary
id: "status_led"
name: "status LED"
output: light_output
output:
- id: light_output
platform: gpio
pin: D3
- platform: esp8266_pwm
# Fan CLK
pin: D1
frequency: 150 Hz
id: pwm_output
min_power: 0.5
max_power: 0.5
zero_means_zero: true
fan:
- platform: speed
output: pwm_output
name: "Fornuftig Air Purifier"
id: "fornuftig_fan"
restore_mode: RESTORE_DEFAULT_OFF
on_speed_set:
- lambda: |-
if(id(fornuftig_fan).speed != id(fan_speed) && id(fornuftig_fan).state){
id(fan_speed) = id(fornuftig_fan).speed;
id(set_fan_freq).publish_state(id(fornuftig_fan).speed);
}
on_turn_on:
- lambda: |-
id(set_fan_freq).publish_state(id(fornuftig_fan).speed);
id(my_rotary_encoder).publish_state(id(fornuftig_fan).speed);
- light.turn_on:
id: status_led
on_turn_off:
- output.esp8266_pwm.set_frequency:
id: pwm_output
frequency: !lambda "return int(0);"
- lambda: |-
id(my_rotary_encoder).publish_state(id(speed_0));
- light.turn_off:
id: status_led
- logger.log: "Fan Turned Off!"
I modified the version from [0xDE4DBEEF], combined the very early version for the power calculations.
I also salvaged the LED from the original board and used it as a status light to show that the fan is actually turned on.
The functionality is as follows:
push the knob:
- switch fan on, speed lvl (1)
- switch fan off
set speed:
- Turn knob clockwise: increase speed lvl (1-2-3)
- Turn knob counter clockwise: decrease speed lvl (3-2-1)
It’s likely that the script can use some improvements - still an early adopter of HA.
Let me know if you have any suggestions or improvements.
buck converter:
rotary:
fuse (not sure if these are the right ones):
FYI, atm, its missing the sync of the LED and rotary state, if you control the device through HA
that should be taken care of, for now… updated the fan block in the code above.
IKEA has a new air purifier called Uppatvind. I created a project for it. You can check the details if you are interested in.
IKEA Uppatvind Air Purifier - Share your Projects! - Home Assistant Community (home-assistant.io)
Thank you for sharing PCB and template, it works really great !
@horvathgergo has released a new v2 board that now comes with a rotary switch. Great work !
The project currently uses micropython to integrate w/ HA. I’m still learning esphome and don’t know much about micropython. Would the esphome yaml file by @mcruiser work with this latest board? It’s unclear in my mind if the v2 board is using the approach that mcruiser used to add the rotary switch.
@horvathgergo Great stuff! Used your Gerber file to order and assemble the board. Can’t wait!
The position of that voltage regulator is a bit weird but i’m pretty sure that the manufacturer will fix it in the review phase.
Very sharp of you! Didn’t even notice before, even after triple checking. And yes, JLCPB corrected it perfectly.
Hi! Did you already check whether you could get this to work with esphome?
Got it to work more easily then I thought. I just changed the GPIO PIN’s from one of the previously shared esphome configuration… @horvathgergo I can turn on and turn off the purifier and select the fan speed from 0 to 100%. Als the knob is working correctly. Could you please check if the below will not negatively affect the used parts of your v2 board?
output:
- platform: esp8266_pwm
pin: 5
frequency: 150 Hz
id: pwm_output
min_power: 0.5
max_power: 0.5
zero_means_zero: true
fan:
- platform: speed
output: pwm_output
name: "Fornuftig Air Purifier"
id: "fornuftig_fan"
restore_mode: RESTORE_DEFAULT_OFF
on_speed_set:
lambda: |-
if(id(fornuftig_fan).speed != id(fan_speed) && id(fornuftig_fan).state){
id(fan_speed) = id(fornuftig_fan).speed;
id(set_fan_freq).publish_state(id(fornuftig_fan).speed);
}
on_turn_on:
lambda: |-
id(set_fan_freq).publish_state(id(fornuftig_fan).speed);
on_turn_off:
- output.esp8266_pwm.set_frequency:
id: pwm_output
frequency: !lambda 'return int(0);'
- logger.log: "Fan Turned Off!"
sensor:
- platform: pulse_counter
pin:
number: 4
mode: INPUT_PULLUP
unit_of_measurement: 'RPM'
accuracy_decimals: 0
id: fan_tach
name: Fornuftig Fanspeed
filters:
- multiply: 0.1
- platform: template
id: set_fan_freq
filters:
- multiply: 3
- lambda: |-
if (id(fornuftig_fan).state){
if(x > 300) { // limit max frequency
return 300;
} else if(x < 36 && x > 3) { // limit low frequency
return 36;
} else if(x <= 3) {
return 0;
} else {
return x;
}
} else {
return 0;
}
on_value:
then:
- output.esp8266_pwm.set_frequency:
id: pwm_output
frequency: !lambda 'return int(x);'
- logger.log: "on_value called"
- lambda: |-
// send current status to homeassistant
if(x) {
auto call = id(fornuftig_fan).turn_on();
call.set_speed(int(x)/3);
call.perform();
} else {
auto call = id(fornuftig_fan).turn_off();
call.perform();
}
binary_sensor:
- platform: template
id: 'knob_0'
filters:
- delayed_on_off: 300ms
lambda: |-
if (!id(knob_1).state && !id(knob_2).state && !id(knob_3).state) {
return true;
} else {
return false;
}
on_press:
lambda: |-
if(!id(speed_0)){
auto call = id(fornuftig_fan).turn_off();
call.perform();
}else{
auto call = id(fornuftig_fan).turn_on();
call.set_speed(id(speed_0));
call.perform();
}
- platform: gpio
id: 'knob_1'
pin:
number: 13
mode: INPUT_PULLUP
inverted: true
filters:
- delayed_on_off: 100ms
on_press:
then:
- fan.turn_on:
id: fornuftig_fan
speed: !lambda 'return id(speed_1);'
- platform: gpio
id: 'knob_2'
pin:
number: 12
mode: INPUT_PULLUP
inverted: true
filters:
- delayed_on_off: 100ms
on_press:
then:
- fan.turn_on:
id: fornuftig_fan
speed: !lambda 'return id(speed_2);'
- platform: gpio
id: 'knob_3'
pin:
number: 14
mode: INPUT_PULLUP
inverted: true
filters:
- delayed_on_off: 100ms
on_press:
then:
- fan.turn_on:
id: fornuftig_fan
speed: !lambda 'return id(speed_3);'
globals:
- id: fan_speed
type: int
restore_value: yes
initial_value: '0'
- id: speed_0
type: int
restore_value: yes
initial_value: '0'
- id: speed_1
type: int
restore_value: yes
initial_value: '33'
- id: speed_2
type: int
restore_value: yes
initial_value: '66'
- id: speed_3
type: int
restore_value: yes
initial_value: '100'
How could I get the v2-board ready to use? Is there any manufacuturer worldwide who can I sent the files to and get an assembled and soldered board from?
Yes. There are many of them. I used this one as it was the easiest (and relatively cheap): https://jlcpcb.com/
คุณช่วยแชร์วิธีสั่งได้ไหมครับ ขอบคุคณครับ
Can you please translate that to English, thank you.
Hi,
Looking to order this from JLC too. Is there a way I can only get one board printer and assembled?
Anyone know if it’s possible to source a replacement board? I managed to brick mine while trying to power a wifi relay from the 5v on the board, probably shorted something (noob)
Edit: to specify – I meant the original Förnuftig board