EDIT: verzia 2
NOVINKY:
- pridany jumper pre prechod do deep sleep alebo zakázany
- úprava kódu a optimalizácia
- lepšie sledovanie napájania
- dlhšia výdrž na batérií
- pridaná funkcia global pre zapamatanie si poslednej hodnoty
Hardware:
- RadiationD-v1.1 (CAJOE)
- Gaiger tube J321 (kompatibilné s M4011)
- ESP8266 Wemos D1 mini Pro
- oled displej 128x32
- Batéria 3.7V 1500mAh
(Ďalšie súčiastky, nabijačka s boost modulom, odpory pre led, tranzistor a snimanie nabitia batérie, tranzistor 2N2222 na snimanie pripojenia nabijačky, led, kable, jumpere, dupont kable, skrutky)
Software:
Yaml kód:
Kód: Vybrat vše
substitutions:
friendly_name: ESP-Dozimeter
device_name: esp-dozimeter
device_description: "Dozimeter Modul RadiationD-v1.1 (CAJOE) Tube J321 on battery 1500mAh"
created_by: "StudioTiiM 2022"
battery_adc: A0 # monitoring pin battery voltage
reset_pin: D0 # reset after deep sleep connected whit resitor to RST pin
scl_pin: D1 # scl pin connected to oled display
sda_pin: D2 # sda pin connected to oled display
status_pin: D4 # on board led
sleep_pin: D5 # control deepsleep whit jumper
power_pin: D6 # monitoring usb power
# charge_pin: D7 # connected to charger module to charger pin (blinking when charging battery)
geiger_pin: D7 # connected to module to VIN pin
reset_oled: D8 # reset pin oled display not connected
#-------------------------------------------
# ESP main settings
#-------------------------------------------
esphome:
name: esp-dozimeter
on_boot:
priority: -150.0
then:
- component.update: text_charge
- text_sensor.template.publish:
id: text_status
state: "Online"
- lambda: id(power_status).publish_state(true);
on_shutdown:
priority: -500.0
then:
- logger.log:
format: "go to offline"
level: warn
- text_sensor.template.publish:
id: text_status
state: "Offline"
#-------------------------------------------
# ESP Main settings
#-------------------------------------------
esp8266:
board: d1_mini_pro
logger:
mqtt:
broker: '192.168.31.212'
username: !secret mqtt_username
password: !secret mqtt_password
discovery: true
# discovery_retain: false
# wach online status message
birth_message:
topic: $device_name/topic
payload: online
shutdown_message:
topic: $device_name/topic
payload: offline
# Ota prevent sleep
on_message:
- topic: $device_name/ota_mode_on
payload: 'ON'
then:
- deep_sleep.prevent: deep_sleep_component
- logger.log: "OTA ON"
- topic: $device_name/ota_mode_off
payload: 'OFF'
then:
- logger.log: "OTA OFF"
- deep_sleep.allow: deep_sleep_component
ota:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: HIGH
manual_ip:
# Set this to the IP of the ESP
static_ip: 192.168.31.89
# Set this to the IP address of the router. Often ends with .1
gateway: 192.168.31.1
# The subnet of the network. 255.255.255.0 works for most home networks.
subnet: 255.255.255.0
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "$device_name Hotspot"
password: "passdozimeter"
captive_portal:
#-------------------------------------------
# I2C component
#-------------------------------------------
i2c:
sda: "$sda_pin"
scl: "$scl_pin"
scan: false
#-------------------------------------------
# DEEP SLEEP
#-------------------------------------------
deep_sleep:
id: deep_sleep_component
sleep_duration: 30min
run_duration: 2min
#-------------------------------------------
# GLOBALS
#-------------------------------------------
globals:
#-------------------------------------------
# send first data geiger after boot in cache
#-------------------------------------------
- id: gaiger_global
type: float
restore_value: yes
initial_value: '0.000'
#-------------------------------------------
# Status led (boot/error/wifi conneted) - on board blue led
#-------------------------------------------
status_led:
pin:
number: "$status_pin"
inverted: true
#-------------------------------------------
# SENSORS
#-------------------------------------------
sensor:
#-------------------------------------------
# Wifi signal component
#-------------------------------------------
- platform: wifi_signal
name: "${friendly_name} Signal"
id: "signal"
update_interval: 60s
#-------------------------------------------
# Battery sensor - ADC whit divider units % battery 1500mAh 3.7V
#-------------------------------------------
- platform: adc
pin: "$battery_adc"
name: "${friendly_name} Battery"
accuracy_decimals: 0
unit_of_measurement: "%"
update_interval: 60s
filters:
# - multiply: 16.557 #low 2.9V #full 4.27
- calibrate_linear:
- 0.175 -> 0
- 0.257 -> 100
- filter_out: 0.0
# No value lower than 0
- lambda: 'return max((float)0.0, x);'
# No value greater than 100
- lambda: 'return min((float)100.0, x);'
icon: mdi:battery
id: batlevel
device_class: battery
entity_category: diagnostic
#-------------------------------------------
# Duty cycle sensor - charger blinking desolder led on charger board
#-------------------------------------------
# - platform: duty_cycle
# pin: "$charge_pin"
# name: "${friendly_name} Charge"
#-------------------------------------------
# Pulse counter - Geiger module
# Correct value:
# >0.81 >100cpm alert
# calculate tube m4011, mounted J321
# Gamma Sensitivity Ra226 (cps/mR/hr) 27 (SBM-20 tube = 29)
# Gamma Sensitivity Co60 (cps/mR/hr) 22
# (27+22)/2=24.5
# Multiply counts per second by 60 to get CPM: 24.5 x 60 = 1470 CPM
# absorption rate to be use is 8.77 which I understand is for air
# So finally the 1530 CPM is divided by the absorption rate of 8.77 to get ratio between CPM and uSv: 1470 / 8.77 = 167.617
# inverse 0.00596598639455782312925170068027
# https://sites.google.com/site/diygeigercounter/technical/gm-tubes-supported (method 2)
#-------------------------------------------
- platform: pulse_counter
pin: "$geiger_pin"
id: geigercounter
internal: true
name: "${friendly_name} Radiation_Raw"
# update_interval: 60s
unit_of_measurement: 'µSv/h'
accuracy_decimals: 3
count_mode:
rising_edge: DISABLE
falling_edge: INCREMENT
icon: mdi:radioactive
filters:
# - multiply: 0.00812037037037 old
- multiply: 0.005966
on_value:
then:
- globals.set:
id: gaiger_global
value: !lambda 'return x;'
- component.update: radiation_temp
- if:
condition:
sensor.in_range:
id: geigercounter
above: 0.3
then:
- light.turn_on: oledlight
- logger.log:
format: "RADIATION DETECTED!!! ... SLEEP PREVENT"
level: warn
- deep_sleep.prevent: deep_sleep_component
else:
- if:
condition:
binary_sensor.is_on: sleep_mode
then:
- light.turn_off: oledlight
- logger.log:
format: "RADIATION OK ... SLEEP AFTER 8s"
level: warn
- text_sensor.template.publish:
id: text_status
state: "Offline"
- delay: 8sec
- deep_sleep.enter: deep_sleep_component
- if:
condition:
binary_sensor.is_off: sleep_mode
then:
- light.turn_off: oledlight
- logger.log:
format: "RADIATION OK, SLEEP MODE IS OFF ... SLEEP PREVENT"
level: warn
- deep_sleep.prevent: deep_sleep_component
- platform: template
name: "${friendly_name} Radiation"
id: radiation_temp
lambda: |-
return id(gaiger_global);
accuracy_decimals: 3
unit_of_measurement: 'µSv/h'
# update_interval: 10s
#-------------------------------------------
# BINARY SENSORS
#-------------------------------------------
binary_sensor:
#-------------------------------------------
# Sleep control whit jumper
#-------------------------------------------
- platform: gpio
pin:
number: D5
mode: INPUT_PULLUP
inverted: true
name: "${friendly_name} Sleep mode"
id: sleep_mode
icon: "mdi:power-sleep"
#-------------------------------------------
# Power info - connect to led yellow on charger module
#-------------------------------------------
- platform: gpio
pin:
number: "$power_pin"
name: "${friendly_name} Power status"
id: power_status
icon: "mdi:usb-port"
on_state:
if:
condition:
binary_sensor.is_on: power_status
then:
- light.turn_on: oledlight
- if:
condition:
sensor.in_range:
id: batlevel
above: 98
then:
- lambda: id(text_charge).publish_state("Full");
- logger.log:
format: "FULL BATTERY"
level: info
else:
- lambda: id(text_charge).publish_state("Charging");
- logger.log:
format: "CHARGING BATTERY"
level: info
else:
- light.turn_off: oledlight
- if:
condition:
sensor.in_range:
id: batlevel
above: 20
then:
- lambda: id(text_charge).publish_state("Discharge");
- logger.log:
format: "DISCHARGE BATTERY"
level: info
else:
- lambda: id(text_charge).publish_state("Low");
- logger.log:
format: "LOW BATTERY"
level: info
#-------------------------------------------
# TEXT SENSORS
#-------------------------------------------
text_sensor:
#-------------------------------------------
# Charger text sensor
#-------------------------------------------
- platform: template
name: "${friendly_name} Battery mode"
icon: mdi:power-plug
id: text_charge
lambda: |-
return {"Loading"};
update_interval: never
#-------------------------------------------
# Sleep text sensor
#-------------------------------------------
- platform: template
id: text_status
name: "${friendly_name} Connected status"
lambda: |-
return {"Loading"};
icon: "mdi:web"
update_interval: never
#-------------------------------------------
# TIME COMPONENT
#-------------------------------------------
time:
- platform: sntp
id: esptime
timezone: "Europe/Bratislava"
#-------------------------------------------
# LIGHT - OLED brightness control
#-------------------------------------------
light:
- platform: monochromatic
name: "${friendly_name} Brightness"
output: oled_bc
id: oledlight
icon: "mdi:monitor"
#-------------------------------------------
# OUTPUT - OLED brightness control
#-------------------------------------------
output:
- platform: template
id: oled_bc
type: float
write_action:
then:
- lambda: id(oled).set_contrast(state);
min_power: 0.1
max_power: 1.0
display:
- platform: ssd1306_i2c
model: "SSD1306 128x32"
id: oled
reset_pin: D8
address: 0x3C
# invert: true
rotation: 180°
lambda: |-
//--------------------------------------------------------------------------
// START PAGE
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// GEIGER
//--------------------------------------------------------------------------
it.printf(0, 32, id(font2), TextAlign::BASELINE_LEFT, "%.3f", id(radiation_temp).state);
it.print(55, 30, id(font3), TextAlign::BASELINE_LEFT, "µSv/h");
//--------------------------------------------------------------------------
// RADIATION ICON AND ALERT (ALERT BLINKING ICON)
//--------------------------------------------------------------------------
if (id(geigercounter).state >= 0.8) {
static int i = 0;
i++;
if ((i % 2) == 0)
it.printf(90, 30, id(font5), TextAlign::BASELINE_LEFT, "\U000F043C");
else
it.printf(90, 30, id(font5), TextAlign::BASELINE_LEFT, "\U000F11CF");
}
else {
it.printf(90, 30, id(font5), TextAlign::BASELINE_LEFT, "\U000F043C");
}
//--------------------------------------------------------------------------
// WIFI SIGNAL SENSOR - mdi print
//--------------------------------------------------------------------------
int signal_wifi = int(id(signal).state); //convert state to int
if (signal_wifi < 0 && signal_wifi >= -50) {
it.printf(62, 0, id(font4), TextAlign::TOP_CENTER, "\U000F08BE");
}
if (signal_wifi < -50 && signal_wifi >= -70) {
it.printf(62, 0, id(font4), TextAlign::TOP_CENTER, "\U000F08BD");
}
if (signal_wifi < -70 && signal_wifi >= -100) {
it.printf(62, 0, id(font4), TextAlign::TOP_CENTER, "\U000F08BC");
}
else {
it.printf(62, 0, id(font4), TextAlign::TOP_CENTER, "\U000F08BF");
}
//--------------------------------------------------------------------------
// BATTERY SENSOR - mdi print
//--------------------------------------------------------------------------
float bat_level = (id(batlevel).state); //convert batlevel state to float
//>100%
if (bat_level >= 95) {
it.printf(73, -1, id(fontbattery), TextAlign::TOP_CENTER, "\U000F0079");
}
//>75%
else if (bat_level < 95 && bat_level >= 75) {
it.printf(72, 0, id(fontbattery), TextAlign::TOP_CENTER, "\U000F0081");
}
//>50%
else if (bat_level < 75 && bat_level >= 50) {
it.printf(72, -1, id(fontbattery), TextAlign::TOP_CENTER, "\U000F007F");
}
//>25%
else if (bat_level < 50 && bat_level >= 25) {
it.printf(72, -1, id(fontbattery), TextAlign::TOP_CENTER, "\U000F007B");
}
//>0%
else {
it.printf(72, -1, id(fontbattery), TextAlign::TOP_CENTER, "\U000F10CD");
}
//--------------------------------------------------------------------------
// BATTERY CHARGE SENSOR - mdi print
//--------------------------------------------------------------------------
if (id(power_status).state) {
if (bat_level >= 95) {
it.printf(81, 0, id(fontbattery), TextAlign::TOP_CENTER, "\U000F0241");
} else {
static int y = 0;
y++;
if ((y % 2) == 0)
it.printf(81, 0, id(fontbattery), TextAlign::TOP_CENTER, "\U000F0241");
}
}
//--------------------------------------------------------------------------
// CONNECTED STATUS SENSOR - mdi print
//--------------------------------------------------------------------------
if(id(text_status).state == "Offline") {
it.printf(85, -1, id(fontbattery), TextAlign::TOP_CENTER, "\U000F0904");
}
font:
- file: 'font_rg.ttf'
id: font1
size: 28
- file: 'font_rg.ttf'
id: font2
size: 38
- file: 'arial.ttf'
id: font3
size: 13
glyphs:
['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/','º','µ','³']
- file: 'materialdesignicons-webfont.ttf'
id: font4
size: 14
glyphs: [
"\U000F08BF", # no wifi
"\U000F08BC", # wifi low
"\U000F08BD", # mwifi middle
"\U000F08BE",# wifi high
]
- file: 'materialdesignicons-webfont.ttf'
id: fontbattery
size: 14
glyphs: [
"\U000F0079", # full
"\U000F0081", # 75
"\U000F007F", # 50
"\U000F007B", # 25
"\U000F10CD", # empty
"\U000F0241", # flash
"\U000F0904", # sleep
]
- file: 'materialdesignicons-webfont.ttf'
id: font5
size: 38
glyphs: [
"\U000F043C",# radioactive
"\U000F11CF",# alert
]
- Počitanie counterov
- Zobrazenie hodnoty radioaktivity uSv/h
(prepočet podľa typu trubice je napísaný v yaml kóde tak ako som postupoval ja a poďla Slvenského hydrometeorologického inštítútu https://www.shmu.sk/sk/?page=1&id=radio ... n_id=11813 pre Bratislavu mi dáva spárvne hodnoty, na obrázku nižšie vidíte až poslednú hodinu po zmene kódu a úpravy multiplieru začal dávať porovnateľné hodnoty. - 100% chod na batérií s deep sleep, napájanie plánujem z malého 5v solárneho panelu a poďla toho nastavím dĺžku uspania
EDIT: dlžka uspania je na 30minút solarny panel je umiestnený dosť na zlom mieste.
- Detekcia pripojenia nabíjania
- Wifi a batéria stav na dipleji
- nad hodnotu 0,8uSv/h začne ikona radioaktivity blikať. (limitná hodnota pre bezpečnú radiaktivbitu vo vzduchu)
Finálna verzia: Spodné jumpere ovládaju zapnutie a vipnutie napájania z batérie / akustický signál / deep sleep mod