LilyGO TTGO-T-Display
https://www.tindie.com/products/iotdev/ ... r-counter/
volitelně Lipol o rozměru 803160 nebo menší.
Krabička
Odkaz na základní kód ze kterého vycházím na iot-devices.com.ua
Návod:
1) Registrace na api.safecast.org (zkopírovat si api key)
2) Modul geigeru - připojit napájení na nějaký 3V a GND na TTGO modulu. Připojit OUT geigeru na GPIO27 TTGO modulu.
3) Připojit Li-pol do konektoru TTGO modulu
4) Vložit do krabičky - ttgo modul dříve než zarážku displeje
ESPHome - geiger.yaml (pracovní verze)
Kód: Vybrat vše
# font_ikony:
# Preview https://pictogrammers.github.io/@mdi/font/7.0.96/
# Download: https://cdnjs.com/libraries/MaterialDesign-Webfont/7.0.96
# Board: https://github.com/Xinyuan-LilyGO/TTGO-T-Display
# Sensor: https://www.tindie.com/products/iotdev/ggreg20_v3-ionizing-radiation-geiger-counter/ + SBM-20 Geiger tube
# Wiring: GPIO27 to Geiger module output, 3V & GND from TTGO-T-Display to Geiger. Don't power Geiger directly from Li-on, no enough charging current from TTGO-T-Display to recharge li-pol.
# Box: https://www.printables.com/model/330675
# Li-pol (optional) - max. size 803160 to fit in the box
substitutions:
device_name: geiger
esphome:
name: $device_name
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# deassert_rts_dtr: true
# baud_rate: 0
level: error
# Enable Home Assistant API
api:
encryption:
key: "qi52lSM8jUZ748mLKUJyjKBGkg2cdEUjsfUh6o4KRDQ="
ota:
password: "67cf31efcde5fb212ca4d45adc43dbd6"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Geiger Fallback Hotspot"
password: "kk9pPltibq5Q"
captive_portal:
### CUSTOM!!
spi:
clk_pin: GPIO18
mosi_pin: GPIO19
time:
- platform: homeassistant
id: esptime
switch:
- platform: gpio
pin: GPIO4
name: "${device_name} Backlight"
id: backlight
binary_sensor:
- platform: status
name: "${device_name} Node Status"
id: system_status
- platform: template
device_class: safety
name: "${device_name} Radiation Warning"
# # This doesn't necessarily represent a "dangerous" count, but one that is abnormally high
lambda: |-
if (id(cpm).state > 100) {
// High Count.
return true;
} else {
// Normal Count.
return false;
}
- platform: template
name: "${device_name} pulse detect"
internal: true
# update_interval: 0ms
id: pulse_detect
on_press:
then:
- lambda:
ESP_LOGE("pulse_detect","pulse detected");
- component.update: mydisplay
on_release:
then:
# - delay: 50ms
- delay: 5ms
- lambda:
ESP_LOGE("pulse_detect","pulse cleared");
- component.update: mydisplay
# lambda: |-
# if (id(pulse_input).state) {
# }
text_sensor:
- platform: template
name: Uptime Human Readable
id: uptime_human
icon: mdi:clock-start
internal: true
# Here we calc and include to the firmware a power and dose values of ionizing radiation as sensor outputs
sensor:
- platform: uptime
name: ${device_name} uptime
id: uptime_sensor
update_interval: 60s
on_raw_value:
then:
- text_sensor.template.publish:
id: uptime_human
state: !lambda |-
int seconds = round(id(uptime_sensor).raw_state);
int days = seconds / (24 * 3600);
seconds = seconds % (24 * 3600);
int hours = seconds / 3600;
seconds = seconds % 3600;
int minutes = seconds / 60;
seconds = seconds % 60;
return (
(days ? to_string(days) + "d " : "") +
(hours ? to_string(hours) + "h " : "") +
(minutes ? to_string(minutes) + "m " : "") +
(to_string(seconds) + "s")
).c_str();
- platform: pulse_counter
pin: GPIO27
unit_of_measurement: 'μSv/h'
name: '${device_name} dávkový příkon'
count_mode:
rising_edge: DISABLE
falling_edge: INCREMENT
update_interval: 60s
accuracy_decimals: 3
id: my_dose_meter
filters:
- sliding_window_moving_average: # 5-minutes moving average (MA5) here
window_size: 5
send_every: 5
- multiply: 0.0057 # SBM20 tube conversion factor of pulses into mkSv/Hour
- platform: pulse_counter
name: internal_pulse
pin: GPIO27
internal: true
update_interval: 0ms
filters:
# LogE because we want it to stand out on the console while testing
- lambda: |-
static int num_zeros = 0;
if (x > 0) {
// reset
num_zeros = 0;
// Indicate that a non 0 measurement was taken and dump to console
// This is where a beeper would be fired off if desired...
id(pulse_detect).publish_state(1);
// ESP_LOGE("filterLambda", "raw is %f", x );
} else {
num_zeros++;
}
// Dont spam console
/*if (num_zeros % 1000 == 0) {
ESP_LOGI("filterLambda", "num_zeros is %d", num_zeros );
}*/
return x;
- platform: pulse_counter
pin: GPIO27
unit_of_measurement: 'CPM'
name: '${device_name} částic/min'
id: cpm
count_mode:
rising_edge: DISABLE
falling_edge: INCREMENT
update_interval: 30s
- platform: integration
name: "${device_name} Celková dávka záření"
id: dose
unit_of_measurement: "μSv"
sensor: my_dose_meter # link entity id to the pulse_counter values above
icon: "mdi:radioactive"
accuracy_decimals: 5
time_unit: min # integrate values every next minute
filters:
# obtained dose. Converting from mkSv/hour into mkSv/minute: [mkSv/h / 60] OR [mkSv/h * 0.0166666667].
# if my_dose_meter in CPM, then [0.0054 / 60 minutes] = 0.00009; so CPM * 0.00009 = dose every next minute, mkSv.
- multiply: 0.0166666667
# - platform: pulse_counter
# pin: GPIO36
# name: "esp-geiger01 geiger counter CPM"
# id: "geiger_counter"
# update_interval: 60s
# unit_of_measurement: 'CPM'
# on_raw_value:
# - sensor.template.publish:
# id: radiation_level
# state: !lambda 'return x *0.0081;'
# # # this was what I got for my data sheet and it matched reasonably well with the background data that I have. Many people are using other values.
# - platform: template
# name: "esp-geiger01 Radiation Level"
# id: "radiation_level"
# unit_of_measurement: 'µSv/h'
# update_interval: 60s
# icon: mdi:radioactive
# accuracy_decimals: 5
###############################################################################################
# sensor:
- platform: wifi_signal
id: wifisignal
name: "WiFi Signal Sensor"
update_interval: 1s # default 60s, neni nejlepší nápad nechat internal:false a nízký update interval - zbytečný provoz mqtt
internal: true
on_value:
then:
- component.update: mydisplay
- platform: adc
pin: 34
attenuation: 11db
name: ${device_name} VBatt
id: vcc
update_interval: 10s
filters:
- multiply: 2.0 # The voltage divider requires us to multiply by 2 (100k+100k)
device_class: voltage
accuracy_decimals: 2
unit_of_measurement: "V"
on_value:
- component.update: bat_pct
- component.update: mydisplay
- platform: template
name: "${device_name} procent baterie"
id: bat_pct
update_interval: never
accuracy_decimals: 0
unit_of_measurement: "%"
device_class: battery
lambda: return id(vcc).state ;
filters:
- calibrate_polynomial:
degree: 3
datapoints:
- 0.00 -> 0.0
- 2.97 -> 0.0 #umře
# - 3.15 -> 0.0 #nestartuje občas spolehlivě
# - 3.20 -> 0.0
- 3.28 -> 10.0
- 3.33 -> 20.0
- 3.40 -> 30.0
- 3.48 -> 40.0
- 3.57 -> 50.0
- 3.65 -> 60.0
- 3.72 -> 70.0
- 3.80 -> 80.0
- 3.88 -> 90.0
- 4.15 -> 95.0
- 4.20 -> 100.0
- lambda: |-
if (x <= 100) {
return x;
} else {
return 100;
}
graph:
# Show bare-minimum auto-ranged graph
- id: dose_graph
duration: 50min
x_grid: 1min
y_grid: 10
width: 152
height: 60
# min_value: 20
# max_value: 60
traces:
- sensor: cpm
line_type: SOLID
line_thickness: 2
color: color_green
font:
- file: "fonts/arial.ttf"
glyphs: "!%()+,-/_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyzμčáůýř"
id: pismo
size: 14
- file: "fonts/arial.ttf"
glyphs: "!%()+,-/_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyzμčáůýř"
id: pismo40
size: 40
- file: "fonts/arial.ttf"
glyphs: "!%()+,-/_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyzμčáůýř"
id: pismo26
size: 26
- file: "fonts/arial.ttf"
glyphs: "!%()+,-/_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyzμčáůýř"
id: pismo20
size: 20
- file: "fonts/materialdesignicons-webfont.ttf"
id: radioactive
size: 60
glyphs: [
'', # F043C mdi-radioactive
'' # F185E mdi-radioactive-circle-outline
]
- file: "fonts/materialdesignicons-webfont.ttf"
id: ikony
size: 16
glyphs: [
# Wifi
'', # F092F mdi-wifi-strength-outline
'', # F091F mdi-wifi-strength-1
'', # F0922 mdi-wifi-strength-2
'', # F0925 mdi-wifi-strength-3
'', # F0928 mdi-wifi-strength-4
# Battery
'', # F10CD mdi-battery-alert-variant-outline
'', # F007A mdi-battery-10
'', # F007B mdi-battery-20
'', # F007C mdi-battery-30
'', # F007D mdi-battery-40
'', # F007E mdi-battery-50
'', # F007F mdi-battery-60
'', # F0080 mdi-battery-70
'', # F0081 mdi-battery-80
'', # F0082 mdi-battery-90
'', # F0079 mdi-battery 100
'', # F089F mdi-battery-charging-outline
'', # F089C mdi-battery-charging-10
'', # F0086 mdi-battery-charging-20
'', # F0087 mdi-battery-charging-30
'', # F0088 mdi-battery-charging-40
'', # F089D mdi-battery-charging-50
'', # F0089 mdi-battery-charging-60
'', # F089E mdi-battery-charging-70
'', # F008A mdi-battery-charging-80
'', # F008B mdi-battery-charging-90
'', # F0085 mdi-battery-charging-100
# Button
'', # mdi-gesture-tap-button
'', # mdi-gesture-tap-hold
'', # F0199 mdi-counter
'', # F0E9F mdi-electric-switch
'', # F10D9 mdi-electric-switch-closed
'', # F044A mdi-record
'', # F0EC2 mdi-record-circle
'', # F0EC3 mdi-record-circle-outline
'', # F043D mdi-radiobox-blank
'', # F043E mdi-radiobox-marked
# MQTT connection
'', # F0318 mdi-lan-connect
'', # F0319 mdi-lan-disconnect
# OTA
'', # F137E mdi-auto-download
'', # F01DA mdi-download
# Spanek
'', # F04B2 mdi-sleep
'', # F04B3 mdi-sleep-off
]
- file: 'fonts/slkscr.ttf'
id: font_slkscr_8
size: 8
glyphs: "!%()+,-/_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyzμčáůýř"
- file: 'fonts/BebasNeue-Regular.ttf'
id: font_bebas_48
size: 48
- file: 'fonts/BebasNeue-Regular.ttf'
id: font_bebas_56
size: 56
- file: 'fonts/arial.ttf'
id: font_arial_12
size: 12
color:
- id: color_red
red: 1
green: 0
blue: 0
- id: color_green
red: 0
green: 1
blue: 0
- id: color_blue
red: 0
green: 0
blue: 1
- id: color_orange
red: 1
green: 0.6
blue: 0
- id: color_teal_blue
red: 0
green: 0.5
blue: 0.45
- id: color_white
red: 1
green: 1
blue: 1
# TODO brightness
display:
- platform: st7789v
model: TTGO TDisplay 135x240
backlight_pin: GPIO4
cs_pin: GPIO5
dc_pin: GPIO16
reset_pin: GPIO23
rotation: 90°
#brightness: 0.5
id: mydisplay
update_interval: never
lambda: |-
int x, y, zmod;
//radiactivity icon
//x=240/2, y = 134/2;
//x=56, y = 56;
x=240, y = 135;
//it.print(x, y, id(radioactive), id(color_green), TextAlign::CENTER, "");
//it.printf(x, y, id(radioactive), id(pulse_input).state ? id(color_red) : id(color_green), TextAlign::CENTER, "%s", "" );
it.printf(x, y, id(radioactive), id(pulse_detect).state ? id(color_red) : id(color_green), TextAlign::BOTTOM_RIGHT, "%s", "" );
//zobrazena castice, vynuluj
if (id(pulse_detect)) {id(pulse_detect).publish_state(0);}
if (id(vcc).has_state()) {
it.printf(24, 4, id(pismo), id(color_teal_blue), "%.2f VBat (%.2f %%)", id(vcc).state, id(bat_pct).state);
}
//it.print(212, 4, id(font_slkscr_8), id(color_teal_blue), "MICRO");
//it.print(190, 120, id(font_slkscr_8), id(color_teal_blue), "TV CAMA");
//graf
it.graph(10, 20, id(dose_graph), color_orange);
x=240, y=4;
it.printf(x,y, id(pismo40), id(color_white), TextAlign::TOP_RIGHT, "%.0lf", id(cpm).state);
x=240, y=46;
it.print(x,y, id(pismo26), id(color_white), TextAlign::TOP_RIGHT, "cpm");
x=4, y=80;
//Ma5:
it.printf(x,y, id(pismo26), id(color_white), "%.5f μSv/h", id(my_dose_meter).state);
x=4, y=110;
//it.printf(x,y, id(pismo), id(color_white), "%.3f μSv", id(dose).state);
x=4, y=115;
it.printf(x,y, id(pismo20), id(color_white), "uptime: %s", (id(uptime_human).state).c_str());
//it.print(x,y, id(pismo), id(color_white), (id(uptime_human).state).c_str());
// WiFi Signal Strenght
if(id(wifisignal).has_state()) {
// it.printf(0,10, id(pismo), " %.0f db", id(wifisignal).state);
x = 0, y = 0;
if (id(wifisignal).state >= -50) {
//Excellent
it.print(x, y, id(ikony), TextAlign::TOP_LEFT, "");
// ESP_LOGI("WiFi", "Exellent");
} else if (id(wifisignal).state >= -60) {
//Good
it.print(x, y, id(ikony), TextAlign::TOP_LEFT, "");
// ESP_LOGI("WiFi", "Good");
} else if (id(wifisignal).state >= -67) {
//Fair
it.print(x, y, id(ikony), TextAlign::TOP_LEFT, "");
// ESP_LOGI("WiFi", "Fair");
} else if (id(wifisignal).state >= -70) {
//Weak
it.print(x, y, id(ikony), TextAlign::TOP_LEFT, "");
// ESP_LOGI("WiFi", "Weak");
} else {
//Unlikely working signal
it.print(x, y, id(ikony), TextAlign::TOP_LEFT, "");
// ESP_LOGI("WiFi", "Unlikely");
}
}
# // ted oddelit linkou a vykreslit horni stavovy radek #####################################################################
# it.line(0, 16, 63, 16);
# /* Battery Voltage Discharging */
# if(id(bat_pct).has_state() && !(id(${device_name}_usb_zapojeno).state) ) {
# x = 63, y = 0;
# if (id(bat_pct).state >= 97) {
# // 100 %
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 90) {
# // 90 %
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 80) {
# // 80%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 70) {
# // 70%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 60) {
# // 60%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 50) {
# // 50%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 40) {
# // 40%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 30) {
# // 30%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 20) {
# // 20%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 10) {
# // 10%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else {
# // 0%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# }
# }
# /* Battery Voltage Charging */
# if(id(bat_pct).has_state() && (id(${device_name}_usb_zapojeno).state)) {
# x = 63, y = 0;
# if (id(bat_pct).state >= 97) {
# // 100 %
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 90) {
# // 90 %
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 80) {
# // 80%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 70) {
# // 70%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 60) {
# // 60%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 50) {
# // 50%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 40) {
# // 40%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 30) {
# // 30%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 20) {
# // 20%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else if (id(bat_pct).state >= 10) {
# // 10%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# } else {
# // 0%
# it.print(x, y, id(ikony), TextAlign::TOP_RIGHT, "");
# }
# }
do secrets.yaml:
Kód: Vybrat vše
safecastapi_key: API_Key_získaný_na_stránce_https://api.safecast.org/
Kód: Vybrat vše
input_text:
safecastapi_key:
initial: !secret safecastapi_key
mode: password
rest_command:
#Safecast
#Device_id: aneb typ zařízení https://api.safecast.org/en-US/devices?page=10
send_radiation_safecast_cpm:
url: https://api.safecast.org/measurements.json?api_key={{states("input_text.safecastapi_key")}}
content_type: "application/json; charset=utf-8"
method: POST
payload: >-
{"longitude": "{{('%0.4f'|format(state_attr('zone.home', 'longitude')))}}","latitude": "{{('%0.6f'|format(state_attr('zone.home', 'latitude')))}}","device_id": "256","value": "{{states("sensor.geiger_castic_min")}}","unit": "cpm","captured_at":"{{ (now().isoformat())}}"}
send_radiation_safecast_usv:
url: https://api.safecast.org/measurements.json?api_key={{states("input_text.safecastapi_key")}}
content_type: "application/json; charset=utf-8"
method: POST
payload: >-
{"longitude": "{{('%0.4f'|format(state_attr('zone.home', 'longitude')))}}","latitude": "{{('%0.6f'|format(state_attr('zone.home', 'latitude')))}}","device_id": "256","value": "{{states("sensor.geiger_davkovy_prikon")}}","unit": "usv","captured_at":"{{ (now().isoformat())}}"}
kód automatizace: (po restartu)
Kód: Vybrat vše
alias: CRON-safecast
description: radiation
trigger:
- platform: time_pattern
minutes: /1
condition:
- condition: and
conditions:
- condition: numeric_state
entity_id: sensor.geiger_castic_min
above: 10
- condition: numeric_state
entity_id: sensor.geiger_uptime
above: 60
action:
- service: rest_command.send_radiation_safecast_cpm
data: {}
- service: rest_command.send_radiation_safecast_usv
data: {}
mode: single