Skip to content

Instantly share code, notes, and snippets.

@tkroo
Last active December 22, 2024 19:20
Show Gist options
  • Save tkroo/e1934520536652a0ce8f176399ef1e79 to your computer and use it in GitHub Desktop.
Save tkroo/e1934520536652a0ce8f176399ef1e79 to your computer and use it in GitHub Desktop.
Thermometer with alarms

Thermometer with high and low set temperature alarms.

Parts:

  • ESP32c3
  • Max31855 thermocouple amplifier
  • K-type thermocouple
  • passive piezo buzzer
  • ssd1306 oled
  • buttons

Short press button on gpio 21 to cycle through menu
Button on gpio 20 increments, button on gpio 3 decrements

Long press button on gpio 21 to switch between Celsius and Fahrenheit

substitutions:
name: esphome-web-adcbb8
internal_name: ESPHOME_adcbb8
friendly_name: wax-thermometer
board_name: supermini esp32c3
comment: Max31855 and K type thermocouple probe
font_family: 'Ubuntu Mono'
font_size: '18'
line_height: '16'
col: '70'
esphome:
name: ${name}
friendly_name: ${friendly_name}
comment: "${board_name} - ${comment}"
min_version: 2024.6.0
name_add_mac_suffix: false
project:
name: esphome.web
version: dev
platformio_options:
board_build.flash_mode: dio
on_boot:
- priority: 800
then:
- output.turn_on: buzzer
# some MH-FMD passive buzzers with a PNP transistor will
# get warm and waste power unless you set the IO line to
# high, when not in use.
# https://www.avrfreaks.net/s/topic/a5C3l000000Ub6bEAC/t156425
esp32:
board: esp32-c3-devkitm-1
framework:
type: esp-idf
packages:
common: !include ../common/common.yaml
# web_server: !include ../common/web_server.yaml
wifi:
output_power: 13
i2c:
sda:
number: 10
scl:
number: 9
scan: true
id: bus_a
frequency: 400kHz
spi:
miso_pin:
number: 5
clk_pin:
number: 4
number:
- platform: template
id: "temp_high"
name: "Temp High"
mode: "box"
optimistic: true
min_value: 0
max_value: 300
initial_value: 85
restore_value: true
step: 0.1
unit_of_measurement: "°C"
device_class: "temperature"
- platform: template
id: "temp_low"
name: "Temp Low"
mode: "box"
optimistic: true
min_value: 0
max_value: 300
initial_value: 76.667
restore_value: true
step: 0.1
unit_of_measurement: "°C"
device_class: "temperature"
select:
- platform: template
name: "Display Unit"
id: display_unit
options:
- "°C"
- "°F"
initial_option: "°C"
optimistic: true
switch:
- platform: template
name: "Alarm Sound"
id: alarm_switch
restore_mode: RESTORE_DEFAULT_OFF
optimistic: true
sensor:
- platform: max31855
id: probe_temp
cs_pin:
number: 7
name: "Temperature"
reference_temperature:
name: "Reference Temp"
id: ref_temp
disabled_by_default: true
update_interval: 3s
- platform: copy
id: calibrated_temp
name: "Calibrated"
source_id: probe_temp
filters:
- calibrate_linear:
method: least_squares
datapoints:
- 4.3 -> 0.1
- 98.625 -> 100
# Map sensor values -> true values
# 100 °C is boiling point.
# Portland, OR at elevation 105FT and 29.99 barometric pressure boiling point is 99.97 °C
# calculated using: https://www.thermoworks.com/bpcalc/
on_value_range:
- above: !lambda "return id(temp_high).state;"
then:
- if:
condition:
- switch.is_on: alarm_switch
then:
- rtttl.play: "scale_up:d=16,o=5,b=160:c,d,e,f,g,a,b,c6,c,d,e,f,g,a,b,c6,c,d,e,f,g,a,b,c6"
- below: !lambda "return id(temp_low).state;"
then:
- if:
condition:
- switch.is_on: alarm_switch
then:
- rtttl.play: "scale_down:d=16,o=4,b=100:b,a#,g#,f#,e,d#,c#,c"
- platform: copy
id: sliding_average
name: "Sliding Window Moving Average"
source_id: calibrated_temp
filters:
- sliding_window_moving_average:
window_size: 10
send_every: 5
binary_sensor:
- platform: gpio
pin:
number: 3
mode:
input: true
pullup: true
id: decrease_temp
on_press:
then:
- script.execute:
id: adjust_settings_script
my_action: 'decrement'
- platform: gpio
pin:
number: 20
mode:
input: true
pullup: true
id: increase_temp
on_press:
then:
- script.execute:
id: adjust_settings_script
my_action: 'increment'
- platform: gpio
pin:
number: 21
inverted: true
mode:
input: true
pullup: true
id: cycle_button
# on_press:
# then:
# - script.execute: page_toggle_script
on_multi_click:
- timing:
- ON for at most 0.25s
- OFF for at least 0.1s
then:
- script.execute: page_toggle_script
- timing:
- ON for at least 1s
then:
- script.execute: CorF_script
button:
- platform: factory_reset
name: Restart with Factory Default Settings
font:
- file:
type: gfonts
family: ${font_family}
weight: 400
id: font1
size: ${font_size}
- file:
type: gfonts
family: ${font_family}
weight: 700
id: font1b
size: ${font_size}
graph:
# Show bare-minimum auto-ranged graph
- id: single_temperature_graph
duration: 5min
x_grid: 1min
# y_grid: 1.0
width: 128
height: 64
traces:
sensor: probe_temp
line_type: DOTTED
line_thickness: 3
display:
- platform: ssd1306_i2c
model: "SSD1306 128x64"
address: 0x3C
id: my_display
pages:
- id: page_main
<<: !include
file: ../common/thermostat_page_lines.yaml
vars:
bold_line: 1
- id: page_set_high
<<: !include
file: ../common/thermostat_page_lines.yaml
vars:
bold_line: 2
- id: page_set_low
<<: !include
file: ../common/thermostat_page_lines.yaml
vars:
bold_line: 3
- id: page_set_alarm
<<: !include
file: ../common/thermostat_page_lines.yaml
vars:
bold_line: 4
- id: page_graph
lambda: |-
const int l = ${line_height};
const auto d_unit = id(display_unit).state == "°F" ? "°F" : "°C";
const bool my_test = id(display_unit).state == "°F";
// it.print(0, 0, id(font1), "temp");
it.printf(0, 0, id(font1), " %3.0f %2s", my_test ? id(calibrated_temp).state * (9.0/5.0) + 32.0 : id(calibrated_temp).state, d_unit);
// Draw the graph at position [x=0,y=0]
it.graph(0, 0, id(single_temperature_graph));
# - id: page_calibrate
# lambda: |-
# const int l = ${line_height};
# const auto d_unit = id(display_unit).state == "°F" ? "°F" : "°C";
# const bool my_test = id(display_unit).state == "°F";
# it.print(0, 0, id(font1), "temp");
# it.printf(${col}, 0, id(font1), "%3.2f %2s", my_test ? id(probe_temp).state * (9.0/5.0) + 32.0 : id(probe_temp).state, d_unit);
# it.print(0, l, id(font1), "calib");
# it.printf(${col}, l, id(font1), "%3.2f %2s", my_test ? id(calibrated_temp).state * (9.0/5.0) + 32.0 : id(calibrated_temp).state, d_unit);
# it.print(0, l*2, id(font1), "ref");
# it.printf(${col}, l*2, id(font1), "%3.2f %2s", my_test ? id(ref_temp).state * (9.0/5.0) + 32.0 : id(ref_temp).state, d_unit);
output:
- platform: ledc
pin:
number: 2
id: buzzer
rtttl:
output: buzzer
gain: 0.5
on_finished_playback:
then:
- output.turn_on: buzzer
script:
- id: page_toggle_script
then:
- display.page.show_next: my_display
- component.update: my_display
- id: adjust_settings_script
parameters:
my_action: string
then:
- if:
condition:
display.is_displaying_page: page_main
then:
- if:
condition:
lambda: |-
return my_action == "increment";
then:
- rtttl.play: "startup:d=16,o=4,b=360:d6.,a.,f#.,d."
- if:
condition:
lambda: |-
return my_action == "decrement";
then:
- rtttl.play: "startup:d=16,o=4,b=360:16d6,8a,8f#,16d"
- logger.log:
format: "button: %s"
args: my_action
- if:
condition:
display.is_displaying_page: page_set_high
then:
- if:
condition:
lambda: |-
return my_action == "increment";
then:
- number.set:
id: temp_high
value: !lambda |-
if(id(display_unit).state == "°F") {
float f = id(temp_high).state * (9.0/5.0) + 32.0;
f = f + 1.0;
return (f - 32.0) * (5.0/9.0);
} else {
return id(temp_high).state + 1.0;
}
- if:
condition:
lambda: |-
return my_action == "decrement";
then:
- number.set:
id: temp_high
value: !lambda |-
if(id(display_unit).state == "°F") {
float f = id(temp_high).state * (9.0/5.0) + 32.0;
f = f - 1.0;
return (f - 32.0) * (5.0/9.0);
} else {
return id(temp_high).state - 1.0;
}
- if:
condition:
display.is_displaying_page: page_set_low
then:
- if:
condition:
lambda: |-
return my_action == "increment";
then:
- number.set:
id: temp_low
value: !lambda |-
float f = id(temp_low).state * (9.0/5.0) + 32.0;
f = f + 1.0;
return (f - 32.0) * (5.0/9.0);
- if:
condition:
lambda: |-
return my_action == "decrement";
then:
- number.set:
id: temp_low
value: !lambda |-
float f = id(temp_low).state * (9.0/5.0) + 32.0;
f = f - 1.0;
return (f - 32.0) * (5.0/9.0);
- if:
condition:
display.is_displaying_page: page_set_alarm
then:
- switch.toggle: alarm_switch
- id: CorF_script
then:
- select.next:
id: display_unit
cycle: true
lambda: |-
const int l = ${line_height};
const auto d_unit = id(display_unit).state == "°F" ? "°F" : "°C";
const bool my_test = id(display_unit).state == "°F";
it.print(0, 0, id(font1), "temp");
it.printf(${col}, 0, id(font1), "%3.0f %2s", my_test ? id(calibrated_temp).state * (9.0/5.0) + 32.0 : id(calibrated_temp).state, d_unit);
it.print(0, l, ${bold_line} == 2 ? id(font1b) : id(font1), "high");
it.printf(${col}, l, ${bold_line} == 2 ? id(font1b) : id(font1), "%3.0f %2s", my_test ? id(temp_high).state * (9.0/5.0) + 32.0 : id(temp_high).state, d_unit);
it.print(0, l*2, ${bold_line} == 3 ? id(font1b) : id(font1), "low");
it.printf(${col}, l*2, ${bold_line} == 3 ? id(font1b) : id(font1), "%3.0f %2s", my_test ? id(temp_low).state * (9.0/5.0) + 32.0 : id(temp_low).state, d_unit);
it.print(0, l*3, ${bold_line} == 4 ? id(font1b) : id(font1), "alarm");
it.printf(${col}, l*3, ${bold_line} == 4 ? id(font1b) : id(font1), "%s ", id(alarm_switch).state ? "ON" : "OFF");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment