├── images
├── side.jpeg
├── tilt.jpeg
├── buttons.jpeg
├── display.jpeg
├── fusion1.jpg
├── fusion2.jpg
├── standalone-display.jpeg
└── integrated-display-back.jpeg
├── 3d_print
├── ESP32-2432S028
│ ├── mount.3mf
│ ├── inner-body.stl
│ ├── outer-body.stl
│ ├── table-mount.stl
│ ├── locking-body.stl
│ └── touch-display.f3d
└── ILI9341-only-display
│ ├── mount.3mf
│ ├── inner-body.stl
│ ├── outer-body.stl
│ ├── locking-body.stl
│ ├── table-mount.stl
│ └── touch-display.f3d
├── LICENSE
├── README.md
└── esphome
├── ili9341-with-external-esp.yml
└── esp32-2432s028.yml
/images/side.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/images/side.jpeg
--------------------------------------------------------------------------------
/images/tilt.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/images/tilt.jpeg
--------------------------------------------------------------------------------
/images/buttons.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/images/buttons.jpeg
--------------------------------------------------------------------------------
/images/display.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/images/display.jpeg
--------------------------------------------------------------------------------
/images/fusion1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/images/fusion1.jpg
--------------------------------------------------------------------------------
/images/fusion2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/images/fusion2.jpg
--------------------------------------------------------------------------------
/images/standalone-display.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/images/standalone-display.jpeg
--------------------------------------------------------------------------------
/3d_print/ESP32-2432S028/mount.3mf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/3d_print/ESP32-2432S028/mount.3mf
--------------------------------------------------------------------------------
/images/integrated-display-back.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/images/integrated-display-back.jpeg
--------------------------------------------------------------------------------
/3d_print/ESP32-2432S028/inner-body.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/3d_print/ESP32-2432S028/inner-body.stl
--------------------------------------------------------------------------------
/3d_print/ESP32-2432S028/outer-body.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/3d_print/ESP32-2432S028/outer-body.stl
--------------------------------------------------------------------------------
/3d_print/ESP32-2432S028/table-mount.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/3d_print/ESP32-2432S028/table-mount.stl
--------------------------------------------------------------------------------
/3d_print/ILI9341-only-display/mount.3mf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/3d_print/ILI9341-only-display/mount.3mf
--------------------------------------------------------------------------------
/3d_print/ESP32-2432S028/locking-body.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/3d_print/ESP32-2432S028/locking-body.stl
--------------------------------------------------------------------------------
/3d_print/ESP32-2432S028/touch-display.f3d:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/3d_print/ESP32-2432S028/touch-display.f3d
--------------------------------------------------------------------------------
/3d_print/ILI9341-only-display/inner-body.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/3d_print/ILI9341-only-display/inner-body.stl
--------------------------------------------------------------------------------
/3d_print/ILI9341-only-display/outer-body.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/3d_print/ILI9341-only-display/outer-body.stl
--------------------------------------------------------------------------------
/3d_print/ILI9341-only-display/locking-body.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/3d_print/ILI9341-only-display/locking-body.stl
--------------------------------------------------------------------------------
/3d_print/ILI9341-only-display/table-mount.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/3d_print/ILI9341-only-display/table-mount.stl
--------------------------------------------------------------------------------
/3d_print/ILI9341-only-display/touch-display.f3d:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akuehlewind/ESPHome-touch-display-mount/HEAD/3d_print/ILI9341-only-display/touch-display.f3d
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Adrian Kuehlewind
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 2.8" ILI9341 Touch Display Enclosure for ESP32 with Home Assistant Integration
2 |
3 | This project provides ESPHome config files and 3D-printable files for an enclosure designed to house a 2.8" ILI9341 touch display. Compatible with standalone 2.8" ILI9341 touch display connected to an ESP32 board or using the ESP32-2432S028 (integrating an ESP32 Wroom module and ILI9341 display on one board), this setup serves as an external, touchscreen-enabled display for your Home Assistant environment.
4 |
5 | The enclosure is designed for mounting beneath a desk and includes a swivel joint, allowing for easy adjustments to the viewing angle while keeping all cables neatly concealed for a clean, streamlined look. The touchscreen offers multiple customizable buttons linked to Home Assistant automations, making it easy to control functions such as lighting—and you’re free to create additional buttons as desired.
6 |
7 | The joint supports an adjustable tilt of +/- 35 degrees up and down. The display’s positioning tension can be fine-tuned by adjusting the screw tightness. The screw hole is deliberately sized slightly smaller than the M4 screw, requiring a bit of force during insertion to ensure a secure hold that prevents any looseness.
8 |
9 | Simply print the files and flash the firmware to the ESP using ESPHome, connect it to Home Assistant.
10 |
11 | The images show the version with an ESP32-2432S028. I recommend using the ESP32-2432S028 as its just the power cable you need to pass-thru into the case and everything else is hidden. Down below you'll find some images of the ILI9341 display with an external ESP32.
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | ### Features:
21 |
22 | - Customizable 3D-printed enclosure with hidden cable design for a clean setup
23 | - Swivel joint for adjustable viewing angles
24 | - Supports 2.8" ILI9341 touch display, compatible with ESP32 or ESP32-2432S028 (includes ESP32 Wroom and display on one board)
25 | - Connect to Home Assistant compatibility to control automations such as lighting
26 | - Using [ESPHome](https://www.esphome.io) which makes it easy to code and modify for your needs
27 |
28 |
29 | ## Parts needed
30 |
31 | | Part | Price | Comment |
32 | |------------------------------|---------|-------------------------------------------------|
33 | | ESP32-2432S028 | 15$ | 2.8" ILI9341 with integrated ESP |
34 | | 2x M4 3.5x16 screw (flathead)| 0.10$ | Screws to connect the mount case with the base |
35 |
36 | or
37 |
38 | | Part | Price | Comment |
39 | |------------------------------|---------|-------------------------------------------------|
40 | | 2.8" ILI9341 | 8$ | 2.8" ILI9341 |
41 | | ESP32 Wroom 32D | 8$ | or some ESP32 |
42 | | 2x M4 3.5x16 screw (flathead)| 0.10$ | Screws to connect the mount case with the base |
43 |
44 |
45 |
46 | ## How
47 | - Flash your ESP32 via ESPHome and use the yaml from the esphome folder. Adjust to your needs
48 | - Print the parts.
49 | - Use two M4 screws to attach the display case and the base
50 | - *Connect all pins (when using a solo display with an ESP32)
51 |
52 | ## HomeAssistant
53 | - Create some automations in home assistant using the exposed buttons as trigger.
54 |
55 |
56 |
57 | # Options
58 |
59 | ## a) Using an ESP32-2432S028
60 | Simply connect to USB and flash the ESP using the yml file in the esphome directory. If your board is designed different check the pin layout
61 |
62 | | ESP32-2432S028 | PIN | Comment |
63 | |------------------------------|--------------|------------------------------------------------------|
64 | | LCD | | |
65 | | clk_pin | GPIO14 | SPI LCD Clock |
66 | | mosi_pin | GPIO13 | SPI LCD MOSI (sometimes also labeled as SDI) |
67 | | miso_pin | GPIO12 | SPI LCD MISO (sometimes also labeled as SDO |
68 | | cs_pin | GPIO15 | Display CS |
69 | | dc_pin | GPIO2 | Display DC |
70 | | Touchscreen | | |
71 | | clk_pin | GPIO25 | SPI Touchscreen Clock |
72 | | mosi_pin | GPIO32 | SPI Touchscreen MOSI (sometimes also labeled as DIN) |
73 | | miso_pin | GPIO39 | SPI Touchscreen MISO (sometimes also labeled as DO) |
74 | | cs_pin | GPIO33 | Touchscreen CS |
75 | | interrupt_pin | GPIO36 | Touchscreen Interrupt |
76 | | LED | | |
77 | | ledc | GPIO21 | Backlight LED display |
78 | | ledc | GPIO4 | Onboard LED (not used for this project) |
79 |
80 | To power the device, use the VIN and GND pins from the JST port (typically provided with a JST-to-pin cable). Pass the cable through the case using the hole in the center which runs through the swivel joint and connect these ends to the 5V and GND wires of an old USB cable
81 |
82 |
83 | ## b) Using an 2.8" ILI9141 standalone connected to an ESP32 Wroom 32D
84 | Follow the connection diagram, which shows how to put everything together.
85 |
86 | | ILI9341 | PIN ESP32 | Comment |
87 | |------------------------------|--------------|------------------------------------------------------|
88 | | GND | GND | Ground |
89 | | VCC | 3.3V | Power |
90 | | LCD | | |
91 | | SCK | GPIO18 | SPI LCD Clock |
92 | | SDI (MOSI) | GPIO23 | SPI LCD MOSI (sometimes also labeled as SDI) |
93 | | SDOK (MISO) | GPIO19 | SPI LCD MISO (sometimes also labeled as SDO |
94 | | CS | GPIO27 | Display CS |
95 | | D/C | GPIO26 | Display DC |
96 | | RESET | GPI16 | Display Reset |
97 | | Touchscreen | | |
98 | | T_CLK | GPIO25 | SPI Touchscreen Clock |
99 | | T_DO | GPIO35 | SPI Touchscreen MOSI (sometimes also labeled as DIN) |
100 | | T_DIN | GPIO32 | SPI Touchscreen MISO (sometimes also labeled as DO) |
101 | | T_CS | GPIO33 | Touchscreen CS |
102 | | T_IRQ | GPIO34 | Touchscreen Interrupt |
103 | | LED | | |
104 | | ledc | GPIO4 | Backlight LED display |
105 |
106 |
107 | ## 3D Print
108 | See folder print to get all SLT files and the fusion360 file. Feel free to adjust and remix
109 |
110 | # DISCLAIMER
111 | This project is to be considered as a work in progress.
112 | Feel free to adjust and remix the 3D cover to your needs. Its designed in Fusion 360, see fusion 360 file in the 3d_print folder.
113 |
114 | # Other
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | # More projects
123 | Looking for more cool projects using this display? Check out the [LoctekMotion Touch Display GitHub repository](https://github.com/3DJupp/LoctekMotion-TouchDisplay)! This awesome project takes the same 2.8" ILI9341 touchscreen setup and repurposes it—not for lights, but for controlling a height-adjustable Flexispot desk with ease. If you're into smart home automation and custom builds, it's definitely worth a look!
124 |
125 |
126 |
--------------------------------------------------------------------------------
/esphome/ili9341-with-external-esp.yml:
--------------------------------------------------------------------------------
1 | esphome:
2 | name: smartdisplay
3 | friendly_name: SmartDisplay
4 |
5 | esp32:
6 | board: esp32dev
7 | framework:
8 | type: arduino
9 |
10 | logger:
11 |
12 | api:
13 | encryption:
14 | key: "XXXXXXXXXXXXXXXXXXXXX/LI="
15 |
16 | ota:
17 | - platform: esphome
18 | password: "XXXXXXXXXXXXXXXXXXXXX"
19 |
20 | wifi:
21 | ssid: !secret wifi_ssid
22 | password: !secret wifi_password
23 | ap:
24 | ssid: "Smartdisplay Fallback Hotspot"
25 | password: "XXXXXXXXXXXXXXXXXXXXX"
26 |
27 | captive_portal:
28 |
29 | time:
30 | - platform: homeassistant
31 | id: homeassistant_time
32 | on_time:
33 | - seconds: 0
34 | minutes: /1 # Update every minute
35 | then:
36 | - component.update: my_display
37 |
38 | font:
39 |
40 | # gfonts://family[@weight]
41 | - file: "gfonts://Roboto@500"
42 | id: big_text
43 | size: 60
44 | bpp: 4
45 | glyphs:
46 | ['&', '@', '!', ',', '.', '?', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
47 | '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
48 | 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
49 | 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
50 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
51 | 'u', 'v', 'w', 'x', 'y', 'z','å', 'Ä', 'ä', 'Ö', 'ö', 'Ü', 'ü', '/']
52 |
53 | # gfonts://family[@weight]
54 | - file: "gfonts://Roboto@300"
55 | id: notice_text
56 | size: 16
57 | bpp: 4
58 | glyphs:
59 | ['&', '@', '!', ',', '.', '?', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
60 | '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
61 | 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
62 | 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
63 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
64 | 'u', 'v', 'w', 'x', 'y', 'z','å', 'Ä', 'ä', 'Ö', 'ö', 'Ü', 'ü', '/']
65 |
66 | # gfonts://family[@weight]
67 | - file: "gfonts://Roboto@400"
68 | id: label
69 | size: 13
70 | bpp: 4
71 | glyphs:
72 | ['&', '@', '!', ',', '.', '?', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
73 | '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
74 | 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
75 | 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
76 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
77 | 'u', 'v', 'w', 'x', 'y', 'z','å', 'Ä', 'ä', 'Ö', 'ö', 'Ü', 'ü', '/']
78 |
79 | - file: 'fonts/materialdesignicons-webfont.ttf'
80 | id: materialdesign_icons
81 | size: 35
82 | glyphs: ["\U000F095F", "\U000F0769", "\U000F08DD","\U000F1051"] # Get them from https://github.com/Templarian/MaterialDesign-Webfont/blob/master/fonts/materialdesignicons-webfont.ttf and copy to a folder /fonts
83 |
84 |
85 | color:
86 | - id: ACTIVE
87 | hex: "FEC600"
88 | - id: INACTIVE
89 | hex: "808080"
90 |
91 | # If you want a background image for your screen you can uncomment the next lines. Keep in mind rendering images slows down refresh of the display
92 | #image:
93 | # - file: "images/my_background_image.jpg"
94 | # id: background
95 | # type: RGB565
96 |
97 | sensor:
98 | - platform: wifi_signal
99 | name: "Wifi Signal"
100 | update_interval: 600s
101 | - platform: uptime
102 | name: "Uptime"
103 | id: uptime_s
104 | update_interval: 15s
105 |
106 | binary_sensor:
107 | # Adjust touchscreen touch areas if needed
108 | # To trigger button actions use HA and just create an automation that uses the exposed buttons as triggers on change
109 | - platform: status
110 | name: "Node Status"
111 | id: system_status
112 | - platform: touchscreen
113 | name: Button 1 # exposes a button to HA
114 | x_min: 190
115 | x_max: 268
116 | y_min: 18
117 | y_max: 63
118 | - platform: touchscreen
119 | name: Button 2 # exposes a button to HA
120 | x_min: 190
121 | x_max: 268
122 | y_min: 72
123 | y_max: 115
124 | - platform: touchscreen
125 | name: Button 3 # exposes a button to HA
126 | x_min: 190
127 | x_max: 268
128 | y_min: 126
129 | y_max: 169
130 | - platform: touchscreen
131 | name: Button 4 # exposes a button to HA
132 | x_min: 190
133 | x_max: 268
134 | y_min: 178
135 | y_max: 224
136 |
137 | text_sensor:
138 | - platform: template
139 | name: "Uptime (formatted)"
140 | lambda: |-
141 | uint32_t dur = id(uptime_s).state;
142 | int dys = 0;
143 | int hrs = 0;
144 | int mnts = 0;
145 | if (dur > 86399) {
146 | dys = trunc(dur / 86400);
147 | dur = dur - (dys * 86400);
148 | }
149 | if (dur > 3599) {
150 | hrs = trunc(dur / 3600);
151 | dur = dur - (hrs * 3600);
152 | }
153 | if (dur > 59) {
154 | mnts = trunc(dur / 60);
155 | dur = dur - (mnts * 60);
156 | }
157 | char buffer[17];
158 | sprintf(buffer, "%ud %02uh %02um %02us", dys, hrs, mnts, dur);
159 | return {buffer};
160 | icon: mdi:clock-start
161 | update_interval: 60s
162 |
163 | # Import HA entities
164 | # Adjust to your needs. I used the entities to change the background color of the button depending on the state
165 | # There is for sure a better solution but feel free to adjust.
166 | - platform: homeassistant
167 | id: button1
168 | entity_id: light.buero_schreibtisch
169 | on_value:
170 | then:
171 | - component.update: my_display
172 | - platform: homeassistant
173 | id: button2
174 | entity_id: light.buro_decke
175 | on_value:
176 | then:
177 | - component.update: my_display
178 | - platform: homeassistant
179 | id: button3
180 | entity_id: light.buero_kugel
181 | on_value:
182 | then:
183 | - component.update: my_display
184 | - platform: homeassistant
185 | id: button4
186 | entity_id: light.buro_schreibtisch_ambient_2
187 | on_value:
188 | then:
189 | - component.update: my_display
190 |
191 | switch:
192 | - platform: restart
193 | name: "Restart"
194 |
195 | spi:
196 | - id: lcd
197 | clk_pin: 18
198 | mosi_pin: 23
199 | miso_pin: 19
200 | - id: my_touchscreen
201 | clk_pin: 25
202 | mosi_pin: 32
203 | miso_pin: 35
204 |
205 | output:
206 | - platform: ledc
207 | pin: 4
208 | id: gpio_backlight_pwm
209 |
210 | light:
211 | - platform: monochromatic
212 | output: gpio_backlight_pwm
213 | name: "Power Display Backlight"
214 | id: back_light
215 | restore_mode: ALWAYS_ON
216 |
217 | touchscreen:
218 | platform: xpt2046
219 | spi_id: my_touchscreen
220 | cs_pin: 33
221 | interrupt_pin: 34
222 | update_interval: 50ms
223 | threshold: 400
224 | on_touch:
225 | - lambda: |-
226 | ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
227 | touch.x,
228 | touch.y,
229 | touch.x_raw,
230 | touch.y_raw
231 | );
232 | calibration:
233 | x_min: 280
234 | x_max: 3860
235 | y_min: 340
236 | y_max: 3860
237 | transform:
238 | mirror_x: true
239 | mirror_y: false
240 |
241 | display:
242 | - platform: ili9xxx
243 | id: my_display
244 | spi_id: lcd
245 | model: ILI9341
246 | color_palette: 8BIT
247 | cs_pin: 27
248 | dc_pin: 26
249 | reset_pin: 16
250 | rotation: 90
251 | invert_colors: false
252 | update_interval: never
253 |
254 | # # If you want to use a background image uncomment these lines.
255 | #color_palette: IMAGE_ADAPTIVE
256 | #color_palette_images:
257 | # - "images/smartpanel/smartpanel_bg.jpg"
258 |
259 | pages:
260 | - id: lockscreen_page
261 | lambda: |-
262 | std::map day_map
263 | {
264 | {2, "Montag"},
265 | {3, "Dienstag"},
266 | {4, "Mittwoch"},
267 | {5, "Donnerstag"},
268 | {6, "Freitag"},
269 | {7, "Samstag"},
270 | {1, "Sonntag"}
271 | };
272 |
273 | std::map month_map
274 | {
275 | {1, "Januar"},
276 | {2, "Februar"},
277 | {3, "März"},
278 | {4, "April"},
279 | {5, "Mai"},
280 | {6, "Juni"},
281 | {7, "Juli"},
282 | {8, "August"},
283 | {9, "September"},
284 | {10, "Oktober"},
285 | {11, "November"},
286 | {12, "Dezember"}
287 | };
288 |
289 | //it.image(0, 0, id(background));
290 |
291 |
292 | auto white = Color(255, 255, 255);
293 |
294 | int b_pos_y = 170;
295 |
296 | // Button 1
297 | int b1_pos_x = (it.get_width()/2) - 105;
298 | if (id(button1).state == "off") {
299 | it.filled_circle(b1_pos_x, b_pos_y, 30, id(INACTIVE));
300 | } else {
301 | it.filled_circle(b1_pos_x, b_pos_y, 30, white);
302 | }
303 | it.printf(b1_pos_x, b_pos_y, id(materialdesign_icons),id(ACTIVE), TextAlign::CENTER, "\U000F095F");
304 | it.printf(b1_pos_x, b_pos_y + 45, id(label),id(Color::WHITE), TextAlign::CENTER, "Tisch");
305 |
306 | // Button 2
307 | int b2_pos_x = (it.get_width()/2) - 35;
308 | if (id(button2).state == "off") {
309 | it.filled_circle(b2_pos_x, b_pos_y, 30, id(INACTIVE));
310 | } else {
311 | it.filled_circle(b2_pos_x, b_pos_y, 30, white);
312 | }
313 | it.printf(b2_pos_x, b_pos_y, id(materialdesign_icons),id(ACTIVE), TextAlign::CENTER, "\U000F0769");
314 | it.printf(b2_pos_x, b_pos_y + 45, id(label),id(Color::WHITE), TextAlign::CENTER, "Decke");
315 |
316 | // Button 3
317 | int b3_pos_x = (it.get_width()/2) + 35;
318 | if (id(button3).state == "off") {
319 | it.filled_circle(b3_pos_x, b_pos_y, 30, id(INACTIVE));
320 | } else {
321 | it.filled_circle(b3_pos_x, b_pos_y, 30, white);
322 | }
323 | it.printf(b3_pos_x, b_pos_y, id(materialdesign_icons),id(ACTIVE), TextAlign::CENTER, "\U000F08DD");
324 | it.printf(b3_pos_x, b_pos_y + 45, id(label),id(Color::WHITE), TextAlign::CENTER, "Kugel");
325 |
326 | // Button 4
327 | int b4_pos_x = (it.get_width()/2) + 105;
328 | if (id(button4).state == "off") {
329 | it.filled_circle(b4_pos_x, b_pos_y, 30, id(INACTIVE));
330 | } else {
331 | it.filled_circle(b4_pos_x, b_pos_y, 30, white);
332 | }
333 | it.printf(b4_pos_x, b_pos_y, id(materialdesign_icons),id(ACTIVE), TextAlign::CENTER, "\U000F1051");
334 | it.printf(b4_pos_x, b_pos_y + 45, id(label),id(Color::WHITE), TextAlign::CENTER, "Ambient");
335 |
336 |
337 | // Date Time
338 | std::string day_of_week = day_map[id(homeassistant_time).now().day_of_week];
339 | int day_of_month = id(homeassistant_time).now().day_of_month;
340 | std::string month = month_map[id(homeassistant_time).now().month];
341 | it.printf(it.get_width()/2, it.get_height()/2 - 90, id(notice_text),id(Color::WHITE), TextAlign::CENTER, "%s, %d. %s", day_of_week.c_str(),day_of_month,month.c_str());
342 | it.strftime(it.get_width()/2, it.get_height()/2 - 45, id(big_text),id(Color::WHITE), TextAlign::CENTER, "%H:%M", id(homeassistant_time).now());
343 |
344 |
--------------------------------------------------------------------------------
/esphome/esp32-2432s028.yml:
--------------------------------------------------------------------------------
1 | esphome:
2 | name: smartdisplay
3 | friendly_name: SmartDisplay
4 |
5 | esp32:
6 | board: esp32dev
7 | framework:
8 | type: arduino
9 |
10 | logger:
11 |
12 | api:
13 | encryption:
14 | key: "XXXXXXXXXXXXXXXXXXXXX"
15 |
16 | ota:
17 | - platform: esphome
18 | password: "XXXXXXXXXXXXXXXXXXXXX"
19 |
20 | wifi:
21 | ssid: !secret wifi_ssid
22 | password: !secret wifi_password
23 | ap:
24 | ssid: "Smartdisplay Fallback Hotspot"
25 | password: "XXXXXXXXXXXXXXXXXXXXX"
26 |
27 | captive_portal:
28 |
29 | time:
30 | - platform: homeassistant
31 | id: homeassistant_time
32 | on_time:
33 | - seconds: 0
34 | minutes: /1 # Update every minute
35 | then:
36 | - component.update: my_display
37 |
38 | font:
39 |
40 | # gfonts://family[@weight]
41 | - file: "gfonts://Roboto@500"
42 | id: big_text
43 | size: 60
44 | bpp: 4
45 | glyphs:
46 | ['&', '@', '!', ',', '.', '?', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
47 | '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
48 | 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
49 | 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
50 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
51 | 'u', 'v', 'w', 'x', 'y', 'z','å', 'Ä', 'ä', 'Ö', 'ö', 'Ü', 'ü', '/']
52 |
53 | # gfonts://family[@weight]
54 | - file: "gfonts://Roboto@300"
55 | id: notice_text
56 | size: 16
57 | bpp: 4
58 | glyphs:
59 | ['&', '@', '!', ',', '.', '?', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
60 | '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
61 | 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
62 | 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
63 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
64 | 'u', 'v', 'w', 'x', 'y', 'z','å', 'Ä', 'ä', 'Ö', 'ö', 'Ü', 'ü', '/']
65 |
66 | # gfonts://family[@weight]
67 | - file: "gfonts://Roboto@400"
68 | id: label
69 | size: 13
70 | bpp: 4
71 | glyphs:
72 | ['&', '@', '!', ',', '.', '?', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
73 | '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
74 | 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
75 | 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
76 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
77 | 'u', 'v', 'w', 'x', 'y', 'z','å', 'Ä', 'ä', 'Ö', 'ö', 'Ü', 'ü', '/']
78 |
79 | - file: 'fonts/materialdesignicons-webfont.ttf' # Get them from https://github.com/Templarian/MaterialDesign-Webfont/blob/master/fonts/materialdesignicons-webfont.ttf and copy to a folder /fonts
80 | id: materialdesign_icons
81 | size: 35
82 | glyphs: ["\U000F095F", "\U000F0769", "\U000F08DD","\U000F1051"]
83 |
84 | # If you want a background image for your screen you can uncomment the next lines. Keep in mind rendering images slows down refresh of the display
85 | #image:
86 | # - file: "images/my_background_image.jpg"
87 | # id: background
88 | # type: RGB565
89 |
90 | color:
91 | - id: ACTIVE
92 | hex: "FEC600"
93 | - id: INACTIVE
94 | hex: "808080"
95 |
96 | sensor:
97 | - platform: wifi_signal
98 | name: "Wifi Signal"
99 | update_interval: 600s
100 | - platform: uptime
101 | name: "Uptime"
102 | id: uptime_s
103 | update_interval: 15s
104 |
105 | binary_sensor:
106 | # Adjust touchscreen touch areas if needed
107 | # To trigger button actions use HA and just create an automation that uses the exposed buttons as triggers on change
108 | - platform: status
109 | name: "Node Status"
110 | id: system_status
111 | - platform: touchscreen
112 | name: Button 1 # exposes a button to HA
113 | x_min: 53
114 | x_max: 131
115 | y_min: 18
116 | y_max: 63
117 | - platform: touchscreen
118 | name: Button 2 # exposes a button to HA
119 | x_min: 53
120 | x_max: 131
121 | y_min: 72
122 | y_max: 115
123 | - platform: touchscreen
124 | name: Button 3 # exposes a button to HA
125 | x_min: 53
126 | x_max: 131
127 | y_min: 126
128 | y_max: 169
129 | - platform: touchscreen
130 | name: Button 4 # exposes a button to HA
131 | x_min: 53
132 | x_max: 131
133 | y_min: 178
134 | y_max: 224
135 |
136 | text_sensor:
137 | - platform: template
138 | name: "Uptime (formatted)"
139 | lambda: |-
140 | uint32_t dur = id(uptime_s).state;
141 | int dys = 0;
142 | int hrs = 0;
143 | int mnts = 0;
144 | if (dur > 86399) {
145 | dys = trunc(dur / 86400);
146 | dur = dur - (dys * 86400);
147 | }
148 | if (dur > 3599) {
149 | hrs = trunc(dur / 3600);
150 | dur = dur - (hrs * 3600);
151 | }
152 | if (dur > 59) {
153 | mnts = trunc(dur / 60);
154 | dur = dur - (mnts * 60);
155 | }
156 | char buffer[17];
157 | sprintf(buffer, "%ud %02uh %02um %02us", dys, hrs, mnts, dur);
158 | return {buffer};
159 | icon: mdi:clock-start
160 | update_interval: 60s
161 |
162 | # Import HA entities
163 | # Adjust to your needs. I used the entities to change the background color of the button depending on the state
164 | # There is for sure a better solution but feel free to adjust.
165 | - platform: homeassistant
166 | id: button1
167 | entity_id: light.buero_schreibtisch
168 | on_value:
169 | then:
170 | - component.update: my_display
171 | - platform: homeassistant
172 | id: button2
173 | entity_id: light.buro_decke
174 | on_value:
175 | then:
176 | - component.update: my_display
177 | - platform: homeassistant
178 | id: button3
179 | entity_id: light.buero_kugel
180 | on_value:
181 | then:
182 | - component.update: my_display
183 | - platform: homeassistant
184 | id: button4
185 | entity_id: light.buro_schreibtisch_ambient_2
186 | on_value:
187 | then:
188 | - component.update: my_display
189 |
190 | switch:
191 | - platform: restart
192 | name: "Restart"
193 |
194 | spi:
195 | - id: lcd
196 | clk_pin: GPIO14
197 | mosi_pin: GPIO13
198 | miso_pin: GPIO12
199 | - id: my_touchscreen
200 | clk_pin: GPIO25
201 | mosi_pin: GPIO32
202 | miso_pin: GPIO39
203 |
204 | output:
205 | - platform: ledc
206 | pin: GPIO21
207 | id: gpio_backlight_pwm
208 | - platform: ledc
209 | id: output_red
210 | pin: GPIO4
211 | inverted: true
212 | - platform: ledc
213 | id: output_green
214 | pin: GPIO16
215 | inverted: true
216 | - platform: ledc
217 | id: output_blue
218 | pin: GPIO17
219 | inverted: true
220 |
221 | light:
222 | - platform: monochromatic
223 | output: gpio_backlight_pwm
224 | name: "Power Display Backlight"
225 | id: back_light
226 | restore_mode: ALWAYS_ON
227 | - platform: rgb
228 | name: LED
229 | red: output_red
230 | id: led
231 | green: output_green
232 | blue: output_blue
233 | restore_mode: ALWAYS_OFF
234 |
235 | touchscreen:
236 | platform: xpt2046
237 | spi_id: my_touchscreen
238 | cs_pin: 33
239 | interrupt_pin: 36
240 | update_interval: 50ms
241 | threshold: 400
242 | on_touch:
243 | - lambda: |-
244 | ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
245 | touch.x,
246 | touch.y,
247 | touch.x_raw,
248 | touch.y_raw
249 | );
250 | calibration:
251 | x_min: 280
252 | x_max: 3860
253 | y_min: 340
254 | y_max: 3860
255 | transform:
256 | mirror_x: true
257 | mirror_y: false
258 |
259 | display:
260 | - platform: ili9xxx
261 | id: my_display
262 | spi_id: lcd
263 | model: ILI9341
264 | color_palette: 8BIT
265 | cs_pin: 15
266 | dc_pin: 2
267 | #reset_pin: 16
268 | rotation: 90
269 | invert_colors: false
270 | update_interval: never
271 |
272 | # # If you want to use a background image uncomment these lines.
273 | #color_palette: IMAGE_ADAPTIVE
274 | #color_palette_images:
275 | # - "images/smartpanel/smartpanel_bg.jpg"
276 |
277 | pages:
278 | - id: lockscreen_page
279 | lambda: |-
280 | std::map day_map
281 | {
282 | {2, "Montag"},
283 | {3, "Dienstag"},
284 | {4, "Mittwoch"},
285 | {5, "Donnerstag"},
286 | {6, "Freitag"},
287 | {7, "Samstag"},
288 | {1, "Sonntag"}
289 | };
290 |
291 | std::map month_map
292 | {
293 | {1, "Januar"},
294 | {2, "Februar"},
295 | {3, "März"},
296 | {4, "April"},
297 | {5, "Mai"},
298 | {6, "Juni"},
299 | {7, "Juli"},
300 | {8, "August"},
301 | {9, "September"},
302 | {10, "Oktober"},
303 | {11, "November"},
304 | {12, "Dezember"}
305 | };
306 |
307 | //it.image(0, 0, id(background));
308 |
309 |
310 | auto white = Color(255, 255, 255);
311 |
312 | int b_pos_y = 170;
313 |
314 | // Button 1
315 | int b1_pos_x = (it.get_width()/2) - 105;
316 | if (id(button1).state == "off") {
317 | it.filled_circle(b1_pos_x, b_pos_y, 30, id(INACTIVE));
318 | } else {
319 | it.filled_circle(b1_pos_x, b_pos_y, 30, white);
320 | }
321 | it.printf(b1_pos_x, b_pos_y, id(materialdesign_icons),id(ACTIVE), TextAlign::CENTER, "\U000F095F");
322 | it.printf(b1_pos_x, b_pos_y + 45, id(label),id(Color::WHITE), TextAlign::CENTER, "Tisch");
323 |
324 | // Button 2
325 | int b2_pos_x = (it.get_width()/2) - 35;
326 | if (id(button2).state == "off") {
327 | it.filled_circle(b2_pos_x, b_pos_y, 30, id(INACTIVE));
328 | } else {
329 | it.filled_circle(b2_pos_x, b_pos_y, 30, white);
330 | }
331 | it.printf(b2_pos_x, b_pos_y, id(materialdesign_icons),id(ACTIVE), TextAlign::CENTER, "\U000F0769");
332 | it.printf(b2_pos_x, b_pos_y + 45, id(label),id(Color::WHITE), TextAlign::CENTER, "Decke");
333 |
334 | // Button 3
335 | int b3_pos_x = (it.get_width()/2) + 35;
336 | if (id(button3).state == "off") {
337 | it.filled_circle(b3_pos_x, b_pos_y, 30, id(INACTIVE));
338 | } else {
339 | it.filled_circle(b3_pos_x, b_pos_y, 30, white);
340 | }
341 | it.printf(b3_pos_x, b_pos_y, id(materialdesign_icons),id(ACTIVE), TextAlign::CENTER, "\U000F08DD");
342 | it.printf(b3_pos_x, b_pos_y + 45, id(label),id(Color::WHITE), TextAlign::CENTER, "Kugel");
343 |
344 | // Button 4
345 | int b4_pos_x = (it.get_width()/2) + 105;
346 | if (id(button4).state == "off") {
347 | it.filled_circle(b4_pos_x, b_pos_y, 30, id(INACTIVE));
348 | } else {
349 | it.filled_circle(b4_pos_x, b_pos_y, 30, white);
350 | }
351 | it.printf(b4_pos_x, b_pos_y, id(materialdesign_icons),id(ACTIVE), TextAlign::CENTER, "\U000F1051");
352 | it.printf(b4_pos_x, b_pos_y + 45, id(label),id(Color::WHITE), TextAlign::CENTER, "Ambient");
353 |
354 |
355 | // Date Time
356 | std::string day_of_week = day_map[id(homeassistant_time).now().day_of_week];
357 | int day_of_month = id(homeassistant_time).now().day_of_month;
358 | std::string month = month_map[id(homeassistant_time).now().month];
359 | it.printf(it.get_width()/2, it.get_height()/2 - 90, id(notice_text),id(Color::WHITE), TextAlign::CENTER, "%s, %d. %s", day_of_week.c_str(),day_of_month,month.c_str());
360 | it.strftime(it.get_width()/2, it.get_height()/2 - 45, id(big_text),id(Color::WHITE), TextAlign::CENTER, "%H:%M", id(homeassistant_time).now());
361 |
362 |
--------------------------------------------------------------------------------