Skip to content

Commit 2aa5796

Browse files
committed
IKEA Frekvens LED spotlight "side-project"
1 parent 0c43c57 commit 2aa5796

3 files changed

Lines changed: 354 additions & 0 deletions

File tree

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# 💡 IKEA Frekvens LED spotlight
2+
3+
> Article number 504.203.67
4+
5+
## 📈 Schematics
6+
7+
### PCB schema
8+
9+
```text
10+
┌──────────────────────────────────┐
11+
│ ┌───────────────────┬────────┼─ Microphone
12+
0 V DC ─┼ DC- │ ┌───────┐ │ ┌─┐ │
13+
+4 V DC ─┼ DC+ │ │┌────┐ │ │ ┤ ├ │
14+
│ │ U2 ││ │ │ │ ┤ ├ │
15+
│ │ ┌┴┴┴┴┐ │ │ U3 └─┤ ├ │
16+
│ │ └┬┬┬┬┘ │ │ LM358 ┤ ├ │
17+
│ └────┘ │ │ └─┘ │
18+
Enable ─┼────────┬───────┤ └─────┬───────┬─┼─ Button
19+
│ │ ┌─┐ │ ┌─┐ │ ┌─┐ │ │
20+
│ │ ┤ ├ │ ┤ ├ │ ┤ ├ │ │
21+
│ │ ┤ ├ │ ┤ ├ └──┤ ├──┘ │
22+
│ └──┤ ├ └──┤ ├ K3 └─┘ │
23+
│ U6 ┤ ├ U7 ┤ ├ │
24+
│ └─┘ └─┘ MIC- │
25+
│ LED+ LED- MIC+ ┼─ Signal
26+
└───────┼──────────────────────────┘
27+
LED ─────────┘
28+
```
29+
30+
### Microphone schema
31+
32+
```text
33+
┌─────┐
34+
│ VCC ├─ Signal
35+
│ GND ├─ 0 V DC
36+
└─────┘
37+
```
38+
39+
### LED diode schema
40+
41+
```text
42+
┌────────┐
43+
│ Anode ├─ +4 V DC
44+
│ Catode ├─ LED
45+
└────────┘
46+
```
47+
48+
### Power supply schema
49+
50+
```text
51+
┌──────┐
52+
100-240 V AC ─┤ L + ├─ +4 V DC
53+
Neutral ─┤ N - ├─ 0 V DC
54+
└──────┘
55+
```
56+
57+
### ESP32 schema
58+
59+
```text
60+
┌────────────────┐
61+
│ VIN ├─ +4 V DC
62+
│ GND ├─ 0 V DC
63+
│ │
64+
│ PWM output ├─ Enable
65+
│ │
66+
│ Digital input ├─ Button
67+
│ │
68+
│ Analog input ├─ Microphone
69+
└────────────────┘
70+
```
71+
72+
## 🚀 Getting started
73+
74+
Remove the `U2` chip, then connect each component to the ESP32 using wires.
75+
76+
Refer to the [PCB schema](#pcb-schema) above for the internal connections of each component on the board — this allows you to solder the wire to any convenient point on the PCB.
77+
78+
## 🔧 Configuration
79+
80+
### Power and ground
81+
82+
> To prevent backfeeding, never connect the ESP32 to USB while the 4 V power supply is connected — even if it is unplugged from the mains.
83+
84+
### Enable
85+
86+
Use any *PWM output* pin.
87+
88+
> Avoid strapping pins as this pin may be floating.
89+
90+
### Button
91+
92+
Use any *digital input* pin, preferably those that are also RTC-capable, to allow deep sleep wake-up functionality.
93+
94+
> Avoid strapping pins as this pin is pulled *HIGH* using a resistor and *LOW* when pressed.
95+
96+
### Microphone
97+
98+
Use any *analog input* pin.
99+
100+
> Avoid strapping pins as this pin is biased.
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
# Source: https://github.com/VIPnytt/Frekvens/tree/main/extra/ESPHome/IKEA-Frekvens-LED-spotlight
2+
3+
esphome:
4+
name: frekvens-spotlight
5+
friendly_name: "Frekvens spotlight"
6+
comment: "IKEA Frekvens LED spotlight"
7+
min_version: "2025.7.0"
8+
9+
esp32:
10+
board: seeed_xiao_esp32c3 # Replace with the board you are using
11+
12+
substitutions:
13+
PIN_LED: 3 # PWM output to LED drivers (U6/U7)
14+
PIN_MIC: 4 # Analog input from microphone (U3)
15+
PIN_SW1: 5 # Digital input from button (K3)
16+
17+
api:
18+
# Optional: enable encryption if desired
19+
#encryption:
20+
#key: !secret api_key
21+
# Optional: disable automatic reboot on API disconnect
22+
#reboot_timeout: 0s
23+
24+
# Optional: enables configuration via captive portal during setup
25+
#captive_portal:
26+
27+
logger: # Enables logging to aid in debugging
28+
level: INFO # Set desired log level
29+
30+
ota:
31+
- platform: esphome
32+
# Optional: secure OTA with a password
33+
#password: !secret ota_password
34+
35+
web_server: # Optional: enable a basic web interface
36+
37+
wifi:
38+
ssid: !secret wifi_ssid # Wi-Fi network name
39+
password: !secret wifi_password # Wi-Fi password
40+
# Optional: configure fallback access point
41+
#ap:
42+
#password: !secret ap_password
43+
44+
binary_sensor:
45+
- platform: gpio
46+
id: sw1
47+
name: "Button"
48+
icon: "mdi:power"
49+
disabled_by_default: True # Hidden from Home Assistant UI
50+
internal: True # Optional
51+
pin:
52+
number: ${PIN_SW1}
53+
allow_other_uses: true
54+
filters:
55+
- invert
56+
on_click:
57+
- max_length: 499ms # Short press: toggle light
58+
then:
59+
- light.toggle: led
60+
# Optional: trigger event in Home Assistant
61+
#- homeassistant.event:
62+
#event: esphome.button_short_press
63+
on_multi_click:
64+
- timing:
65+
- ON for at least 500ms # Long press: enter dimming mode
66+
then:
67+
# Optional: trigger event in Home Assistant
68+
#- homeassistant.event:
69+
#event: esphome.button_long_press
70+
- while:
71+
condition:
72+
binary_sensor.is_on: sw1
73+
then:
74+
- if:
75+
condition:
76+
lambda: !lambda 'return id(dim_up);'
77+
then:
78+
- light.dim_relative:
79+
id: led
80+
relative_brightness: 1%
81+
transition_length: 0s
82+
else:
83+
- light.dim_relative:
84+
id: led
85+
relative_brightness: -1%
86+
transition_length: 0s
87+
- delay: 20ms
88+
- globals.set:
89+
id: dim_up
90+
value: !lambda 'return !id(dim_up);'
91+
92+
button:
93+
- platform: template
94+
name: "Power off"
95+
icon: "mdi:power"
96+
entity_category: config
97+
on_press:
98+
then:
99+
- deep_sleep.enter # Enter deep sleep to save power
100+
101+
- platform: restart
102+
id: reboot
103+
name: "Reboot"
104+
disabled_by_default: True # Hidden from UI unless enabled
105+
106+
deep_sleep:
107+
wakeup_pin:
108+
number: ${PIN_SW1}
109+
inverted: True
110+
allow_other_uses: True
111+
112+
globals:
113+
- type: bool
114+
id: dim_up
115+
restore_value: True # Remembers dimming direction between restarts
116+
117+
- type: float
118+
id: mic_peak
119+
restore_value: False # Temporary peak value, resets every interval
120+
initial_value: '0'
121+
122+
- type: int
123+
id: mic_last
124+
restore_value: True # Stores last microphone reading for diff calc
125+
126+
- type: int
127+
id: mic_threshold
128+
initial_value: '512'
129+
restore_value: True # Threshold for triggering sound response
130+
131+
interval:
132+
- interval: 60s
133+
then:
134+
- sensor.template.publish:
135+
id: mic_level
136+
state: !lambda 'return id(mic_peak);'
137+
- lambda: !lambda |-
138+
id(mic_peak) = 0;
139+
140+
light:
141+
- platform: monochromatic
142+
id: led
143+
name: None
144+
icon: "mdi:spotlight"
145+
restore_mode: RESTORE_DEFAULT_OFF
146+
output: led_pwm
147+
effects: # Optional light effects
148+
- flicker:
149+
- pulse:
150+
- random:
151+
152+
number:
153+
- platform: template
154+
id: threshold
155+
name: "Threshold"
156+
icon: "mdi:microphone"
157+
entity_category: config
158+
disabled_by_default: True
159+
mode: box
160+
step: 1
161+
max_value: 4095
162+
min_value: 1
163+
lambda: !lambda 'return id(mic_threshold);'
164+
set_action:
165+
- lambda: |-
166+
id(mic_threshold) = (int)x;
167+
id(threshold).publish_state(id(mic_threshold));
168+
169+
output:
170+
- platform: ledc
171+
id: led_pwm
172+
pin: ${PIN_LED}
173+
frequency: ${80000000 / 65536 | int} # Max resolution (12-bit)
174+
min_power: ${1 / 255}
175+
zero_means_zero: True # Ensures 0% = fully off
176+
177+
script:
178+
- id: on_sound
179+
then: # Response to detected sound event
180+
- light.toggle:
181+
id: led
182+
transition_length: 750ms
183+
- delay: 750ms
184+
- light.toggle:
185+
id: led
186+
transition_length: 750ms
187+
- delay: 750ms
188+
189+
sensor:
190+
- platform: adc
191+
id: mic_raw
192+
pin: ${PIN_MIC}
193+
raw: True
194+
attenuation: 12db
195+
samples: 10
196+
update_interval: 10ms
197+
unit_of_measurement: ""
198+
accuracy_decimals: 0
199+
filters:
200+
- max:
201+
send_every: 10
202+
window_size: 10
203+
- lambda: !lambda |-
204+
if (id(mic).state)
205+
{
206+
int diff = abs((int)x - id(mic_last));
207+
id(mic_last) = (int)x;
208+
return diff;
209+
} else {
210+
return 0;
211+
}
212+
- delta: 1.0
213+
on_value:
214+
then:
215+
- if:
216+
condition:
217+
lambda: !lambda 'return x >= (int)id(threshold).state;'
218+
then:
219+
- script.execute: on_sound
220+
- lambda: !lambda |-
221+
if (x > id(mic_peak)) {
222+
id(mic_peak) = x;
223+
}
224+
225+
- platform: internal_temperature
226+
name: "Internal temperature"
227+
disabled_by_default: True
228+
229+
- platform: template
230+
id: mic_level
231+
name: "Sound level"
232+
icon: "mdi:microphone"
233+
entity_category: diagnostic
234+
state_class: measurement
235+
accuracy_decimals: 0
236+
update_interval: never
237+
lambda: !lambda |-
238+
return id(mic_peak);
239+
240+
- platform: wifi_signal
241+
name: "Wi-Fi signal"
242+
243+
switch:
244+
- platform: template
245+
id: mic
246+
name: "Microphone"
247+
icon: "mdi:microphone"
248+
entity_category: config
249+
restore_mode: RESTORE_DEFAULT_OFF
250+
optimistic: True

extra/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
> Optional tools, scripts, and resources that serve as non-essential, supplemental, or auxiliary content.
44
5+
## 🏠 ESPHome
6+
7+
Side-projects, primarily for other devices in the [IKEA Frekvens](https://github.com/VIPnytt/Frekvens/wiki/IKEA-Frekvens) and [IKEA Obegränsad](https://github.com/VIPnytt/Frekvens/wiki/IKEA-Obegransad) collections.
8+
59
## 🐍 Python
610

711
Utilities that are not part of the core functionality but may be useful to advanced users or contributors.

0 commit comments

Comments
 (0)