├── .gitignore ├── LICENSE.txt ├── README.fr.md ├── README.md ├── docs ├── MAX8211-MAX8212.pdf ├── Weather_Sensor_Assembly_Updated.pdf ├── ina219.pdf └── ina3221.pdf ├── images ├── boitier.jpg ├── boitier_alimentation.jpg ├── grafana.png └── station.jpg ├── libraries └── scripts │ └── flashing.yaml ├── network.yaml ├── schematics ├── Elektor - 191148-1.pdf ├── Elektor - 191148-2 Schematic v1.1.pdf └── Elektor - 191148-3 Schematic v1.0.pdf ├── secrets.yaml ├── wall_pipe_support.scad └── weatherstation.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | /.esphome/ 2 | **/ 3 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Charles Rincheval 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.fr.md: -------------------------------------------------------------------------------- 1 | # ESPHome Weather Station 2 | 3 | Read this in other language: [English](README.md) 4 | 5 | ![Photo globale de la station météo](images/station.jpg) 6 | 7 | ## Introduction 8 | 9 | La partie électronique de cette station météo est basée sur le modèle décrit dans un article du magazine Elektor publié en mai 2020 intitulé [Station météo en réseau ouvert V.2](https://www.elektormagazine.fr/magazine/elektor-148/58640) (une évolution de ESP32 Weather Station décrit dans un article du même magazine en janvier 2019). 10 | 11 | L'article détaille la création d'une station météo basé sur un ensemble de capteurs référencé WH-SP-WS02 ([Datasheet](docs/Weather_Sensor_Assembly_Updated.pdf)) dont l'électronique d'origine est retirée pour être remplacée par une carte fille capteurs relayant les informations à une carte mère architecturée autour d'un ESP32 (ESP32 Pico Kit). 12 | 13 | Un micrologiciel OpenSource [GitHub - ElektorLabs/191148-RemakeWeatherStation: RemakeWeatherStation](https://github.com/ElektorLabs/191148-RemakeWeatherStation) est disponible et permet de faire fonctionner le tout. Malheureusement, je n'ai pas trouvé mon bonheur avec, il n'est pas encore assez abouti et souffre de quelques lacunes qui le rendent très difficilement utilisable tel quel. 14 | 15 | J'ai donc décidé d'utiliser ESPHome comme remplacement du programme d'origine afin de simplifier le développement de fonctionnalité mais surtout étendre grandement ses capacités. 16 | 17 | La carte détaillée dans l'article d'Elektor se limite finalement à un convertisseur de tension et une adaptation de tension 5V/3V pour l'ESP32. 18 | 19 | Il est donc assez simple de recréer cette station météo indépendamment du PCB d'Elektor. Pour les connexions, basez-vous sur les données inclues dans le fichier YAML. 20 | 21 | Avec l'aimable [autorisation d'Elektor](https://www.elektormagazine.com/labs/remake-elektor-weather-station#comment-70057), les schémas de principe sont disponibles ici : [Schémas de principe](schematics/). 22 | 23 | ![Photo du boitier](images/boitier.jpg) 24 | 25 | ### Inspirations 26 | 27 | * [Station météo en réseau ouvert V.2 | Elektor Magazine](https://www.elektormagazine.fr/magazine/elektor-148/58640) 28 | * [Remake Elektor weather station | Elektor Magazine](https://www.elektormagazine.com/labs/remake-elektor-weather-station) 29 | * [GitHub - mkuoppa/esphomeweatherstation: ESPHome based weatherstation station](https://github.com/mkuoppa/esphomeweatherstation) 30 | * [ESP8266 Weather Station - with Wind and Rain Sensors | Tysonpower.de](https://tysonpower.de/blog/esp8266-weather-station) 31 | 32 | ## Caractéristiques 33 | 34 | * Mesure de la température / humidité relative / pression atmosphérique 35 | * Vitesse / Direction du vent 36 | * Précipitations journalière / par minute 37 | * Luminosité ambiante 38 | * Tension d'entrée 39 | * Panneau solaire: 40 | * Tension 41 | * Courant 42 | * Puissance 43 | * Puissance accumulée journalière 44 | * Led de statut RGB à base de WS2812 45 | * Toutes les fonctions utilisables avec ESPHome 46 | * MQTT 47 | * OTA (Over The Air updates) 48 | * [La liste est longue](https://esphome.io/) 49 | 50 | Note: Sur la photo globale de la station météo, on aperçoit au sommet un boitier, il s'agit d'un module de détection de [la pluie indépendant](https://github.com/hugokernel/esphome-rain-detector). 51 | 52 | ## Installation 53 | 54 | Afin d'installer le firmware sur l'ESP32, je vous invite à suivre la démarche décrite sur le site d'ESPHome: [Getting Started with ESPHome](https://esphome.io/guides/getting_started_command_line.html) 55 | 56 | ## Électronique 57 | 58 | La carte électronique d'Elektor est à utiliser tel quel ou, au vu de sa simplicité, à reproduire sur une platine d'essai. 59 | 60 | La publication d'Elektor est erronée, en effet, ils utilisent les GPIO 34 (vitesse du vent) et 38 (mesure de précipitation) directement sans résistance de tirage (`pullup`), 61 | de plus, ces entrées / sorties n'intégrent pas de résistance de tirage, **il faut donc ajouter une résistance de tirage (~10kOhms) sur GPIO34 et GPIO38**. 62 | 63 | ## Mécanique 64 | 65 | Pour le mat, j'ai utilisé un tube de PVC renforcé en métal que vous pouvez trouver dans tout magasin de bricolage au rayon plomberie. Afin de fixer ce dernier sur un mûr, j'ai modélisé sur OpenSCAD [une pièce](wall_pipe_support.scad) que j'ai par la suite imprimé en PETG. 66 | 67 | On aperçoit ici la pièce imprimée à côté du boitier contenant le régulateur de charger pour panneau solaire: 68 | 69 | ![Photo du boitier d'alimentation](images/boitier_alimentation.jpg) 70 | 71 | ## Alimentation 72 | 73 | Un des défauts que je reproche à la carte originale d'Elektor, c'est d'avoir lié la technologie de la source de courant à la carte mère (en l'occurence la batterie au plomb), en effet, un [MAX8212](docs/MAX8211-MAX8212.pdf) utilisé avec quelques composants périphériques permet de couper l'alimentation lorsque celle-ci passe en dessous d'un seuil défini par la valeur de 3 résistances. Ce seuil à été choisi afin de protéger une batterie au plomb. 74 | 75 | Dans la mesure ou une station météo est censé rester allumée en permanence, je ne comprend pas vraiment le choix ci-dessus car: 76 | 77 | * On utilise un panneau solaire afin d'assurer la charge de la batterie mais dans ce cas, nous sommes obligé d'utiliser un régulateur de charge qui assure également la protection de la batterie et donc le circuit de protection intégré est redondant et peut même poser problême. 78 | * On branche la station sur une source de courant illimitée (secteur via un régulateur) et dans ce cas, le circuit de protection ne sert à rien. 79 | 80 | Dans les 2 cas mentionnés ci-dessus, on introduit un lien fort sur la carte mère avec la technologie de la batterie, ce qui devrait être fait, AMHA, sur une carte / module indépendant. 81 | 82 | Pour ma part, j'ai choisi d'alimenter ma station météo via un [panneau solaire de 30W](https://www.amazon.fr/gp/product/B07MZKLS4Z/ref=as_li_tl?ie=UTF8&camp=1642&creative=6746&creativeASIN=B07MZKLS4Z&linkCode=as2&tag=digita049-21&linkId=f280ba939aba379ee4586d3211f88c44) et un [contrôleur de charge relativement basique](https://www.amazon.fr/gp/product/B07K57WZVP/ref=as_li_tl?ie=UTF8&camp=1642&creative=6746&creativeASIN=B07K57WZVP&linkCode=as2&tag=digita049-21&linkId=d3af4f9616f8d0f0eea5031ee318a9b9). 83 | 84 | Si vous utilisez la carte mère d'Elektor avec un module de régulation de charge indépendant, n'oubliez pas d'abaisser le seuil de coupure du MAX8212. 85 | 86 | En sortie du panneau solaire, le circuit [INA219](docs/ina219.pdf) permet de mesurer la puissance générée par ce dernier. J'ai d'ailleurs prévu de le remplacer par un [INA3221](docs/ina3221.pdf) afin de pouvoir mesurer également la consommation totale de la station météo et m'aider à affiner la consommation globale (un RFLink avec [OpenMQTTGateway](https://github.com/1technophile/OpenMQTTGateway) et un second kit ESP32 est branché sur la station). 87 | 88 | J'utilise une batterie au plomb de 7A récupérée d'un onduleur. 89 | 90 | Pour dimensionner le tout, je vous recommande l'outil [BatteryStuff Tools](https://www.batterystuff.com/kb/tools/solar-calculator.html) qui est très pratique. 91 | 92 | Actuellement, je ne peux pas dire avoir réussi à rendre ma station météo totalement indépendante énergétiquement parlant à cause d'une mauvaise exposition de mon panneau solaire et d'une consommation trop forte d'un module annexe (module de détection de pluie). 93 | 94 | ## Explications 95 | 96 | ### Mesure de température / humidité / pression atmosphérique 97 | 98 | Ces 3 grandeurs sont mesurées par un capteur Bosch BME280 et sa configuration dans ESPHome est la suivante: 99 | 100 | ```yaml 101 | - platform: bme280 102 | address: 0x76 103 | update_interval: 60s 104 | iir_filter: 16x 105 | temperature: 106 | name: "${friendly_name} temperature" 107 | oversampling: 16x 108 | humidity: 109 | name: "${friendly_name} humidity" 110 | oversampling: 16x 111 | pressure: 112 | name: "${friendly_name} pressure" 113 | oversampling: 16x 114 | ``` 115 | 116 | Le capteur est à mettre à l'intérieur du boitier contenant l'électronique d'origine des capteurs. 117 | 118 | Initialement, j'avais également inclu un capteur AM2320 afin de comparer les valeurs des capteurs avec la configuration suivante: 119 | 120 | ```yaml 121 | - platform: am2320 122 | setup_priority: -100 123 | temperature: 124 | id: am2320_temperature 125 | name: "${friendly_name} AM2320 temperature" 126 | humidity: 127 | id: am2320_humidity 128 | name: "${friendly_name} AM2320 humidity" 129 | update_interval: 60s 130 | ``` 131 | 132 | Les températures et humidités des capteurs étaient moyennées avant d'être envoyé à Home Assistant (voir ci-dessous), il était bien entendu possible d'accéder aux données de chaque capteurs. 133 | 134 | ```yaml 135 | - platform: template 136 | name: "${friendly_name} temperature" 137 | icon: "mdi:thermometer" 138 | unit_of_measurement: "°C" 139 | lambda: |- 140 | return ( 141 | id(bme280_temperature).state 142 | + 143 | id(am2320_temperature).state 144 | ) / 2; 145 | 146 | - platform: template 147 | name: "${friendly_name} humidity" 148 | icon: "mdi:water-percent" 149 | unit_of_measurement: "%" 150 | lambda: |- 151 | return ( 152 | id(bme280_humidity).state 153 | + 154 | id(am2320_humidity).state 155 | ) / 2; 156 | ``` 157 | 158 | En conclution de mes tests, le capteur BME280 est plus fiable et plus précis que le capteur AM2320. 159 | 160 | ### Mesures relatives au vent 161 | 162 | #### Vitesse 163 | 164 | La capteur de mesure de vitesse du vent est relié à l'entrée générale 34 et la plateforme ESPHome `pulse_meter` est maintenant utilisée pour réaliser la mesure. 165 | 166 | Vous devez ajouter une résistance de tirage vers VCC sur la broche GPIO45 (il n'y a pas de résistance de tirage interne sur cette GPIO). 167 | 168 | ##### Calcul de la vitesse 169 | 170 | 1. En premier, comptez le nombre d'impulsion de votre capteur par tour (`$number_of_pulses_by_revolution`) 171 | 2. Ensuite, nous avons besoin de la circonférence de l'anémomètre, pour cela, mesurez le rayon de l'anémomètre en mètre. 172 | \ 173 | Dans l'exemple suivant, le rayon est de 9cm. 174 | ``` 175 | circumference_in_meter = $radius * 2 * π 176 | circumference_in_meter = 0.09 * 2 * 3.14 177 | circumference_in_meter = 0.565486678 178 | ``` 179 | 3. Maintenant, nous avons le nombre de rotations par seconde en comptant le nombre d'impulsions pour une rotation 180 | \ 181 | `rotations_per_sec = pulses / $number_of_pulses_by_revolution / 60` 182 | 4. Enfin, multiplions la circonférence et la rotation par seconde pour obtenir la vitesse du vente. 183 | Note: 1.18 est un facteur de calibration pour compenser la friction mécanique des éléments de la girouette (vous pouvez l'ajuster). 184 | ``` 185 | meter_per_second = 1.18 * circumference_in_meter * $rotations_per_sec 186 | meter_per_second = 1.18 * circumference_in_meter * 1 / $number_of_pulses_by_revolution / 60 187 | meter_per_second = 1.18 * 0.565486678 / 2 / 60 188 | meter_per_second = 0.005560619 189 | ``` 190 | 191 | Formule initiale de https://github.com/mkuoppa/esphomeweatherstation/issues/2#issuecomment-812686624 192 | 193 | Pour plus d'information à propos des calculs, voir https://github.com/hugokernel/esphome-weather-station/issues/6 194 | 195 | ```yaml 196 | - platform: pulse_meter 197 | pin: 198 | number: GPIO34 199 | mode: INPUT 200 | id: wind_speed 201 | unit_of_measurement: 'm/s' 202 | name: "${friendly_name} wind speed" 203 | icon: 'mdi:weather-windy' 204 | internal_filter: 13us 205 | timeout: 5s 206 | filters: 207 | - multiply: 0.005560619 208 | - sliding_window_moving_average: 209 | window_size: 5 210 | send_every: 5 211 | ``` 212 | 213 | #### Direction 214 | 215 | La direction du vent est faite dans le capteur à l'aide d'aimant (switch reed) qui commute des résistances. Selon la valeur finale, on en déduit la direction. 216 | 217 | Un exemple de la configuration ESPHome: 218 | 219 | ```yaml 220 | - platform: resistance 221 | sensor: source_sensor 222 | id: resistance_sensor 223 | configuration: DOWNSTREAM 224 | resistor: 10kOhm 225 | internal: true 226 | name: Resistance Sensor 227 | reference_voltage: 3.9V 228 | accuracy_decimals: 1 229 | filters: 230 | - median: 231 | window_size: 7 232 | send_every: 4 233 | send_first_at: 3 234 | on_value: 235 | - if: 236 | condition: 237 | sensor.in_range: 238 | id: resistance_sensor 239 | above: 15000 240 | below: 15500 241 | then: 242 | - text_sensor.template.publish: 243 | id: wind_dir_card 244 | state: "N" 245 | - sensor.template.publish: 246 | id: wind_heading 247 | state: 0.0 248 | [...] 249 | ``` 250 | 251 | #### Pluie 252 | 253 | La mesure des précipitations est réalisée par un système de balancier composé de 2 coupelles, l'eau ruisselle dans l'entonnoir du capteur et rempli la coupelle haute, une fois cette dernière remplie, elle bascule par gravité. Ce mouvement est détecté par un capteur magnétique (reed switch) et une impulsion est générée. 254 | La documentation du capteur indique que chaque impulsion correspond à 0.2794mm de précipitation. 255 | 256 | ```yaml 257 | - platform: pulse_counter 258 | pin: 259 | # Don't forget to add a pulling resistor, see README 260 | number: GPIO38 261 | mode: INPUT 262 | unit_of_measurement: 'mm' 263 | name: "${friendly_name} rain gauge" 264 | icon: 'mdi:weather-rainy' 265 | id: rain_gauge 266 | internal: true 267 | count_mode: 268 | rising_edge: DISABLE 269 | falling_edge: INCREMENT 270 | internal_filter: 13us 271 | update_interval: 60s 272 | filters: 273 | # Each 0.011" (0.2794mm) of rain causes one momentary contact closure 274 | - multiply: 0.2794 275 | accuracy_decimals: 4 276 | ``` 277 | 278 | Afin d'avoir des informations plus pertinentes, on convertie ces mesures en précipitations par minute et on calcul le cumul journalier. 279 | 280 | ```yaml 281 | - platform: integration 282 | name: "${friendly_name} rainfall per min" 283 | id: rain_per_min 284 | time_unit: min 285 | unit_of_measurement: 'mm' 286 | icon: 'mdi:weather-rainy' 287 | sensor: rain_gauge 288 | 289 | - platform: total_daily_energy 290 | name: "${friendly_name} total daily rain" 291 | power_id: rain_gauge 292 | unit_of_measurement: 'mm' 293 | icon: 'mdi:weather-rainy' 294 | # x60 To convert to aggregated rain amount 295 | filters: 296 | - multiply: 60 297 | ``` 298 | 299 | #### Luminosité 300 | 301 | Pensez à positionner la capteur de luminosité le plus haut possible sur votre station météo afin qu'il ne subisse pas l'ombre du mat ou d'une partie de la station météo. 302 | 303 | ```yaml 304 | - platform: tsl2561 305 | id: lux_meter 306 | name: "${friendly_name} ambient Light" 307 | address: 0x39 308 | update_interval: 5s 309 | integration_time: 14ms 310 | gain: 1x 311 | ``` 312 | 313 | ## Fichiers 314 | 315 | * [weatherstation.yaml](weatherstation.yaml): Le fichier de configuration ESPHome 316 | * [network.yaml](network.yaml): Les informations de votre réseau 317 | * [secrets.yaml](secrets.yaml): Les informations secrètes relatives à votre réseau 318 | * [wall_pipe_support.scad](wall_pipe_support.scad): Le fichier OpenSCAD pour le support de mat 319 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESPHome Weather Station 2 | 3 | Read this in other language: [French](README.fr.md) 4 | 5 | ![Main photo of the weather station](images/station.jpg) 6 | 7 | ## Introduction 8 | 9 | The electronic part of this weather station is based on the model described in an article in Elektor magazine published in May 2020 entitled [Remake Elektor weather station](https://www.elektormagazine.com/labs/remake-elektor-weather-station) (an evolution of ESP32 Weather Station described in an article in the same magazine in January 2019). 10 | 11 | The article details the creation of a weather station based on a set of sensors referenced WH-SP-WS02 ([Datasheet](docs/Weather_Sensor_Assembly_Updated.pdf)) whose original electronics are removed to be replaced by a sensor daughter board relaying the information to a motherboard built around an ESP32 (ESP32 Pico Kit). 12 | 13 | An OpenSource firmware [GitHub - ElektorLabs/191148-RemakeWeatherStation: RemakeWeatherStation](https://github.com/ElektorLabs/191148-RemakeWeatherStation) is available to run the whole system. Unfortunately, I did not find my happiness with it, it is not yet complete enough and suffers from some shortcomings that make it very difficult to use it as is. 14 | 15 | I therefore decided to use ESPHome as a replacement for the original program in order to simplify the development of functionality but above all to greatly extend its capabilities. 16 | 17 | The board detailed in Elektor's article is finally limited to a voltage converter and a 5V/3V voltage adaptation for the ESP32. 18 | 19 | It is therefore quite simple to recreate this weather station independently of Elektor's PCB. For the connections, please use the data included in the YAML file. 20 | 21 | With the kind [permission of Elektor](https://www.elektormagazine.com/labs/remake-elektor-weather-station#comment-70057), the schematic diagrams are available here: [Schematics](schematics/). 22 | 23 | ![Case photo](images/boitier.jpg) 24 | 25 | ### Inspirations 26 | 27 | * [Remake Elektor weather station | Elektor Magazine](https://www.elektormagazine.com/labs/remake-elektor-weather-station) 28 | * [GitHub - mkuoppa/esphomeweatherstation: ESPHome based weatherstation station](https://github.com/mkuoppa/esphomeweatherstation) 29 | * [ESP8266 Weather Station - with Wind and Rain Sensors | Tysonpower.de](https://tysonpower.de/blog/esp8266-weather-station) 30 | 31 | ## Features 32 | 33 | * Measurement of temperature / relative humidity / atmospheric pressure 34 | * Wind speed / Direction 35 | * Precipitation daily / per minute 36 | * Ambient brightness 37 | * Input voltage 38 | * Solar panel: 39 | * Voltage 40 | * Current 41 | * Power 42 | * Daily accumulated power 43 | * RGB status led with WS2812 44 | * All ESPHome features 45 | * MQTT 46 | * OTA (Over The Air updates) 47 | * [The list is long](https://esphome.io/) 48 | 49 | Note: On the main picture of the weather station, there is a box on top, it is an [independent rain detection module](https://github.com/hugokernel/esphome-rain-detector). 50 | 51 | ## Installation 52 | 53 | In order to install the firmware on the ESP32, I invite you to follow the procedure described on the ESPHome website: [Getting Started with ESPHome](https://esphome.io/guides/getting_started_command_line.html) 54 | 55 | ## Electronics 56 | 57 | The Elektor electronic board is to be used as is or, in view of its simplicity, to be reproduced on a test board. 58 | 59 | The Elektor publication is wrong, in fact, they use GPIO 34 (wind speed) and 38 (precipitation measurement) directly without a pullup resistor, 60 | Moreover, these inputs / outputs do not integrate a pull-up resistor, **so it is necessary to add a pullup resistor (~10kOhms) on GPIO34 and GPIO38**. 61 | 62 | ## Mechanical 63 | 64 | For the mast, I used a metal reinforced PVC tube that you can find in any DIY store in the plumbing department. In order to fix it on a wall, I modeled it on OpenSCAD [one piece](wall_pipe_support.scad) that I then printed in PETG. 65 | 66 | Here is the printed piece next to the box containing the solar panel charger controller: 67 | 68 | ![Photo of the power supply box](images/boitier_alimentation.jpg) 69 | 70 | ## Powering 71 | 72 | One of the flaws that I reproach to the original Elektor board is to have linked the technology of the power source to the motherboard (in this case the lead battery), indeed, a [MAX8212](docs/MAX8211-MAX8212.pdf) used with some peripheral components allows to cut the power supply when it goes below a threshold defined by the value of 3 resistors. This threshold has been chosen to protect a lead battery. 73 | 74 | Since a weather station is supposed to stay on all the time, I don't really understand the above choice because: 75 | 76 | * We use a solar panel to charge the battery but in this case we are obliged to use a charge regulator which also protects the battery and therefore the integrated protection circuit is redundant and can even cause problems. 77 | * The station is connected to an unlimited power source (domestic power via a regulator) and in this case the protection circuit is useless. 78 | 79 | In the 2 cases mentioned above, a strong link is inserted on the motherboard with the battery technology, which should be done, imho, on an independent card / module. 80 | 81 | For my part, I chose to power my weather station via a [30W solar panel](https://www.amazon.fr/gp/product/B07MZKLS4Z/ref=as_li_tl?ie=UTF8&camp=1642&creative=6746&creativeASIN=B07MZKLS4Z&linkCode=as2&tag=digita049-21&linkId=f280ba939aba379ee4586d3211f88c44) and a [relatively basic charge controller](https://www.amazon.fr/gp/product/B07K57WZVP/ref=as_li_tl?ie=UTF8&camp=1642&creative=6746&creativeASIN=B07K57WZVP&linkCode=as2&tag=digita049-21&linkId=d3af4f9616f8d0f0eea5031ee318a9b9). 82 | 83 | If you are using Elektor's motherboard with an independent load controller module, do not forget to lower the cut-off threshold of the MAX8212. 84 | 85 | At the output of the solar panel, the [INA219](docs/ina219.pdf) circuit allows the power generated by the solar panel to be measured. I plan to replace it with [INA3221](docs/ina3221.pdf) in order to also measure the total consumption of the weather station and help me to refine the global consumption (a RFLink with [OpenMQTTGateway](https://github.com/1technophile/OpenMQTTGateway) and a second ESP32 kit is connected to the station). 86 | 87 | I use a 7A lead battery recovered from an old inverter. 88 | 89 | To size the whole thing, I recommend the [BatteryStuff Tools](https://www.batterystuff.com/kb/tools/solar-calculator.html) tool which is very handy. 90 | 91 | Currently, I can't say that I have succeeded in making my weather station totally energy independent because of a bad exposure of my solar panel and a too high consumption of an additional module (rain detection module). 92 | 93 | ## Explanations 94 | 95 | ### Measurement of temperature / humidity / atmospheric pressure 96 | 97 | These 3 quantities are measured by a Bosch BME280 sensor and its configuration in ESPHome is as follows: 98 | 99 | ```yaml 100 | - platform: bme280 101 | address: 0x76 102 | update_interval: 60s 103 | iir_filter: 16x 104 | temperature: 105 | name: "${friendly_name} temperature" 106 | oversampling: 16x 107 | humidity: 108 | name: "${friendly_name} humidity" 109 | oversampling: 16x 110 | pressure: 111 | name: "${friendly_name} pressure" 112 | oversampling: 16x 113 | ``` 114 | 115 | The sensor is to be put inside the box containing the original sensor electronics. 116 | 117 | Initially, I also included an AM2320 sensor to compare the sensor values with the following configuration: 118 | 119 | ```yaml 120 | - platform: am2320 121 | setup_priority: -100 122 | temperature: 123 | id: am2320_temperature 124 | name: "${friendly_name} AM2320 temperature" 125 | humidity: 126 | id: am2320_humidity 127 | name: "${friendly_name} AM2320 humidity" 128 | update_interval: 60s 129 | ``` 130 | 131 | The temperatures and humidities of the sensors were averaged before being sent to Home Assistant (see below), it was of course possible to access the data of each sensor. 132 | 133 | ```yaml 134 | - platform: template 135 | name: "${friendly_name} temperature" 136 | icon: "mdi:thermometer" 137 | unit_of_measurement: "°C" 138 | lambda: |- 139 | return ( 140 | id(bme280_temperature).state 141 | + 142 | id(am2320_temperature).state 143 | ) / 2; 144 | 145 | - platform: template 146 | name: "${friendly_name} humidity" 147 | icon: "mdi:water-percent" 148 | unit_of_measurement: "%" 149 | lambda: |- 150 | return ( 151 | id(bme280_humidity).state 152 | + 153 | id(am2320_humidity).state 154 | ) / 2; 155 | ``` 156 | 157 | At the end of my tests, the BME280 sensor is more reliable and more accurate than the AM2320 sensor. 158 | 159 | ### Wind measurements 160 | 161 | #### Speed 162 | 163 | The wind speed sensor is connected to general input 34 and the ESPHome `pulse_meter` platform is now used to perform the measurement. 164 | 165 | You need to add a pulling resistor on the GPIO34 (there is no internal pullup on this GPIO). 166 | 167 | ##### Speed calculation 168 | 169 | 1. First, check the number of pulses by revolution (`$number_of_pulses_by_revolution`) 170 | 2. Then, we need the circumference of the anemomenter, to do that, measure the radius of the anemomenter in meter. 171 | \ 172 | In the example below, the radius is 9cm. 173 | ``` 174 | circumference_in_meter = $radius * 2 * π 175 | circumference_in_meter = 0.09 * 2 * 3.14 176 | circumference_in_meter = 0.565486678 177 | ``` 178 | 3. Now, we can found the number of rotations per seconds by counting the number of pulses for one rotation 179 | \ 180 | `rotations_per_sec = pulses / $number_of_pulses_by_revolution / 60` 181 | 4. Next, we multiply circumference and rotation par second to have the wind speed. 182 | \ 183 | Note: 1.18 is a calibration factor to compensate the friction (you can adjust it). 184 | ``` 185 | meter_per_second = 1.18 * circumference_in_meter * $rotations_per_sec 186 | meter_per_second = 1.18 * circumference_in_meter * 1 / $number_of_pulses_by_revolution / 60 187 | meter_per_second = 1.18 * 0.565486678 / 2 / 60 188 | meter_per_second = 0.005560619 189 | ``` 190 | 191 | Initial formula from https://github.com/mkuoppa/esphomeweatherstation/issues/2#issuecomment-812686624 192 | 193 | For more information about calculation, see https://github.com/hugokernel/esphome-weather-station/issues/6 194 | 195 | ```yaml 196 | - platform: pulse_meter 197 | pin: 198 | number: GPIO34 199 | mode: INPUT 200 | id: wind_speed 201 | unit_of_measurement: 'm/s' 202 | name: "${friendly_name} wind speed" 203 | icon: 'mdi:weather-windy' 204 | internal_filter: 13us 205 | timeout: 5s 206 | filters: 207 | - multiply: 0.005560619 208 | - sliding_window_moving_average: 209 | window_size: 5 210 | send_every: 5 211 | ``` 212 | 213 | #### Direction 214 | 215 | The wind direction is made in the sensor by means of magnets (switch reed) that switch resistors. Depending on the final value, the direction is deduced. 216 | 217 | An example of the ESPHome configuration: 218 | 219 | ```yaml 220 | - platform: resistance 221 | sensor: source_sensor 222 | id: resistance_sensor 223 | configuration: DOWNSTREAM 224 | resistor: 10kOhm 225 | internal: true 226 | name: Resistance Sensor 227 | reference_voltage: 3.9V 228 | accuracy_decimals: 1 229 | filters: 230 | - median: 231 | window_size: 7 232 | send_every: 4 233 | send_first_at: 3 234 | on_value: 235 | - if: 236 | condition: 237 | sensor.in_range: 238 | id: resistance_sensor 239 | above: 15000 240 | below: 15500 241 | then: 242 | - text_sensor.template.publish: 243 | id: wind_dir_card 244 | state: "N" 245 | - sensor.template.publish: 246 | id: wind_heading 247 | state: 0.0 248 | [...] 249 | ``` 250 | 251 | #### Rain 252 | 253 | The measurement of precipitation is carried out by a system of pendulum composed of 2 cups, the water runs in the funnel of the sensor and fills the high cup, once the latter is filled, it tips by gravity. This movement is detected by a magnetic sensor (reed switch) and an impulse is generated. 254 | The sensor documentation indicates that each pulse corresponds to 0.2794mm of precipitation. 255 | 256 | ```yaml 257 | - platform: pulse_counter 258 | pin: 259 | # Don't forget to add a pulling resistor, see README 260 | number: GPIO38 261 | mode: INPUT 262 | unit_of_measurement: 'mm' 263 | name: "${friendly_name} rain gauge" 264 | icon: 'mdi:weather-rainy' 265 | id: rain_gauge 266 | internal: true 267 | count_mode: 268 | rising_edge: DISABLE 269 | falling_edge: INCREMENT 270 | internal_filter: 13us 271 | update_interval: 60s 272 | filters: 273 | # Each 0.011" (0.2794mm) of rain causes one momentary contact closure 274 | - multiply: 0.2794 275 | accuracy_decimals: 4 276 | ``` 277 | 278 | In order to have more relevant information, these measurements are converted into precipitation per minute and the daily total is calculated. 279 | 280 | ```yaml 281 | - platform: integration 282 | name: "${friendly_name} rainfall per min" 283 | id: rain_per_min 284 | time_unit: min 285 | unit_of_measurement: 'mm' 286 | icon: 'mdi:weather-rainy' 287 | sensor: rain_gauge 288 | 289 | - platform: total_daily_energy 290 | name: "${friendly_name} total daily rain" 291 | power_id: rain_gauge 292 | unit_of_measurement: 'mm' 293 | icon: 'mdi:weather-rainy' 294 | # x60 To convert to aggregated rain amount 295 | filters: 296 | - multiply: 60 297 | ``` 298 | 299 | #### Brightness 300 | 301 | Remember to position the brightness sensor as high as possible on your weather station so that it is not shaded by the mast or any part of the weather station. 302 | 303 | ```yaml 304 | - platform: tsl2561 305 | id: lux_meter 306 | name: "${friendly_name} ambient Light" 307 | address: 0x39 308 | update_interval: 5s 309 | integration_time: 14ms 310 | gain: 1x 311 | ``` 312 | 313 | ## Files 314 | 315 | * [weatherstation.yaml](weatherstation.yaml): The ESPHome configuration file 316 | * [network.yaml](network.yaml): Your network information 317 | * [secrets.yaml](secrets.yaml): The secret information about your network. 318 | * [wall_pipe_support.scad](wall_pipe_support.scad): The OpenSCAD file for mat support. 319 | -------------------------------------------------------------------------------- /docs/MAX8211-MAX8212.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokernel/esphome-weather-station/e0c9a6dfa0e8e85e1fe4e65421df9992924dbb95/docs/MAX8211-MAX8212.pdf -------------------------------------------------------------------------------- /docs/Weather_Sensor_Assembly_Updated.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokernel/esphome-weather-station/e0c9a6dfa0e8e85e1fe4e65421df9992924dbb95/docs/Weather_Sensor_Assembly_Updated.pdf -------------------------------------------------------------------------------- /docs/ina219.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokernel/esphome-weather-station/e0c9a6dfa0e8e85e1fe4e65421df9992924dbb95/docs/ina219.pdf -------------------------------------------------------------------------------- /docs/ina3221.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokernel/esphome-weather-station/e0c9a6dfa0e8e85e1fe4e65421df9992924dbb95/docs/ina3221.pdf -------------------------------------------------------------------------------- /images/boitier.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokernel/esphome-weather-station/e0c9a6dfa0e8e85e1fe4e65421df9992924dbb95/images/boitier.jpg -------------------------------------------------------------------------------- /images/boitier_alimentation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokernel/esphome-weather-station/e0c9a6dfa0e8e85e1fe4e65421df9992924dbb95/images/boitier_alimentation.jpg -------------------------------------------------------------------------------- /images/grafana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokernel/esphome-weather-station/e0c9a6dfa0e8e85e1fe4e65421df9992924dbb95/images/grafana.png -------------------------------------------------------------------------------- /images/station.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokernel/esphome-weather-station/e0c9a6dfa0e8e85e1fe4e65421df9992924dbb95/images/station.jpg -------------------------------------------------------------------------------- /libraries/scripts/flashing.yaml: -------------------------------------------------------------------------------- 1 | - id: flashing 2 | # It's too complicated to create this effect with 3 | # the ESPHome’s light effects system 4 | mode: single 5 | then: 6 | - while: 7 | condition: 8 | lambda: "return true;" 9 | then: 10 | - light.turn_on: 11 | id: status_light 12 | transition_length: 100ms 13 | brightness: 100% 14 | red: 0% 15 | green: 100% 16 | blue: 0% 17 | - delay: 100ms 18 | - light.turn_off: 19 | id: status_light 20 | transition_length: 100ms 21 | - delay: 10s 22 | -------------------------------------------------------------------------------- /network.yaml: -------------------------------------------------------------------------------- 1 | wifi: 2 | ssid: !secret wifi_ssid 3 | password: !secret wifi_password 4 | 5 | # Enable fallback hotspot (captive portal) in case wifi connection fails 6 | ap: 7 | ssid: "${name} Fallback Hotspot" 8 | password: !secret fallback_hotspot_password 9 | -------------------------------------------------------------------------------- /schematics/Elektor - 191148-1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokernel/esphome-weather-station/e0c9a6dfa0e8e85e1fe4e65421df9992924dbb95/schematics/Elektor - 191148-1.pdf -------------------------------------------------------------------------------- /schematics/Elektor - 191148-2 Schematic v1.1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokernel/esphome-weather-station/e0c9a6dfa0e8e85e1fe4e65421df9992924dbb95/schematics/Elektor - 191148-2 Schematic v1.1.pdf -------------------------------------------------------------------------------- /schematics/Elektor - 191148-3 Schematic v1.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokernel/esphome-weather-station/e0c9a6dfa0e8e85e1fe4e65421df9992924dbb95/schematics/Elektor - 191148-3 Schematic v1.0.pdf -------------------------------------------------------------------------------- /secrets.yaml: -------------------------------------------------------------------------------- 1 | wifi_ssid: "YOUR_SSID" 2 | wifi_password: "YOUR_WIFI_PASSWORD" 3 | 4 | fallback_hotspot_password: "YOUR_FALLBACK_WIFI_PASSWORD" 5 | 6 | # OTA / API 7 | password: "API_PASSWORD" 8 | 9 | latitude: 0.0 10 | longitude: 0.0 11 | -------------------------------------------------------------------------------- /wall_pipe_support.scad: -------------------------------------------------------------------------------- 1 | $fn = 60; 2 | 3 | LENGTH = 180; 4 | WIDTH = 150; 5 | THICKNESS = 5; 6 | 7 | OBLONG_DIAMETER = 5.5; 8 | OBLONG_LENGTH = 15; 9 | 10 | TUBE_DIAMETER = 26.9; 11 | 12 | BOLT_DIAMETER = 4.5; 13 | BOLT_HEAD_DIAMETER = 9; 14 | NUT_DIAMETER = 8.25; 15 | NUT_HEIGHT = 6; 16 | 17 | module roundedBox(w,h,d,f){ 18 | difference(){ 19 | cube(size=[w, h, d], center=true); 20 | translate([-w/2,h/2,0]) cube(w/(f/2),true); 21 | translate([w/2,h/2,0]) cube(w/(f/2),true); 22 | translate([-w/2,-h/2,0]) cube(w/(f/2),true); 23 | translate([w/2,-h/2,0]) cube(w/(f/2),true); 24 | } 25 | translate([-w/2+w/f,h/2-w/f,-d/2]) cylinder(d,w/f, w/f); 26 | translate([w/2-w/f,h/2-w/f,-d/2]) cylinder(d,w/f, w/f); 27 | translate([-w/2+w/f,-h/2+w/f,-d/2]) cylinder(d,w/f, w/f); 28 | translate([w/2-w/f,-h/2+w/f,-d/2]) cylinder(d,w/f, w/f); 29 | } 30 | 31 | module oblong(diameter, length, thickness) { 32 | translate([-length / 2, 0, 0]) { 33 | hull() { 34 | cylinder(d=diameter, h=thickness); 35 | translate([length, 0, 0]) { 36 | cylinder(d=diameter, h=thickness); 37 | } 38 | } 39 | } 40 | } 41 | 42 | module nut(diameter, height) { 43 | cylinder(d=diameter, h=height, $fn=6); 44 | //%cylinder(d=7.8, h=height, $fn=40); 45 | } 46 | 47 | module tube_holder(diameter, length) { 48 | difference() { 49 | hull() { 50 | cube(size=[diameter * 3, length, 1], center=true); 51 | translate([0, length / 2, diameter]) { 52 | rotate([90, 0, 0]) { 53 | cylinder(d=diameter * 2, h=length); 54 | } 55 | } 56 | } 57 | 58 | translate([0, length, diameter]) { 59 | rotate([90, 0, 0]) { 60 | cylinder(d=diameter, h=length * 2); 61 | } 62 | } 63 | 64 | translate([0, 0, diameter * 2 - 3]) { 65 | cylinder(d=BOLT_HEAD_DIAMETER, h=diameter); 66 | } 67 | cylinder(d=BOLT_DIAMETER, h=diameter * 2); 68 | } 69 | } 70 | 71 | module main() { 72 | item_position = LENGTH / 2 - 35; 73 | 74 | difference() { 75 | union() { 76 | difference() { 77 | roundedBox(LENGTH, WIDTH, THICKNESS, 10); 78 | 79 | for (position = [ 80 | [item_position, WIDTH / 2 - 15, 0], 81 | [-item_position, WIDTH / 2 - 15, 0], 82 | [item_position, -(WIDTH / 2 - 15), 0], 83 | [-item_position, -(WIDTH / 2 - 15), 0], 84 | ]) { 85 | translate(position) { 86 | translate([0, 0, -THICKNESS]) { 87 | oblong(OBLONG_DIAMETER, OBLONG_LENGTH, THICKNESS * 2); 88 | } 89 | } 90 | } 91 | 92 | roundedBox(LENGTH / 3.5, WIDTH / 1.8, THICKNESS * 2, 10); 93 | } 94 | 95 | for (x=[item_position, -item_position]) { 96 | translate([x, 0, 0]) { 97 | rotate([0, 0, 90]) { 98 | tube_holder(TUBE_DIAMETER, 30); 99 | } 100 | } 101 | } 102 | } 103 | 104 | for (x=[item_position, -item_position]) { 105 | translate([x, 0, -THICKNESS / 2 - .1]) { 106 | nut(NUT_DIAMETER, NUT_HEIGHT); 107 | } 108 | } 109 | } 110 | } 111 | 112 | // Test 113 | intersection() { translate([0, 0, 27]) cube(size=[43, 50, 35], center=true); tube_holder(TUBE_DIAMETER, 30); } 114 | tube_holder(TUBE_DIAMETER, 30); 115 | oblong(OBLONG_DIAMETER, OBLONG_LENGTH, THICKNESS); 116 | difference() { cylinder(d=15, h=10); translate([0, 0, -.1]) nut(NUT_DIAMETER, NUT_HEIGHT); cylinder(d=BOLT_DIAMETER, h=100); } 117 | 118 | !main(); 119 | 120 | -------------------------------------------------------------------------------- /weatherstation.yaml: -------------------------------------------------------------------------------- 1 | .script_flashing: !include &flashing libraries/scripts/flashing.yaml 2 | 3 | substitutions: 4 | name: weatherstation 5 | friendly_name: "Weather Station" 6 | 7 | esphome: 8 | name: $name 9 | platform: ESP32 10 | board: pico32 11 | on_boot: 12 | priority: -10 13 | then: 14 | - script.execute: flashing 15 | 16 | <<: !include network.yaml 17 | 18 | captive_portal: 19 | 20 | ota: 21 | password: !secret password 22 | 23 | api: 24 | password: !secret password 25 | 26 | sun: 27 | latitude: !secret latitude 28 | longitude: !secret longitude 29 | 30 | logger: 31 | 32 | i2c: 33 | frequency: 100kHz 34 | sda: GPIO26 35 | scl: GPIO25 36 | 37 | script: 38 | - <<: *flashing 39 | 40 | sensor: 41 | - platform: pulse_meter 42 | pin: 43 | # Don't forget to add a pulling resistor, see README 44 | number: GPIO34 45 | mode: INPUT 46 | id: wind_speed 47 | unit_of_measurement: 'm/s' 48 | name: "${friendly_name} wind speed" 49 | icon: 'mdi:weather-windy' 50 | internal_filter: 13us 51 | timeout: 5s 52 | filters: 53 | - multiply: 0.005560619 54 | - sliding_window_moving_average: 55 | window_size: 20 56 | send_every: 20 57 | 58 | - platform: copy 59 | name: '${friendly_name} wind speed average' 60 | icon: 'mdi:weather-windy' 61 | id: wind_speed_avg 62 | source_id: wind_speed 63 | unit_of_measurement: 'm/s' 64 | filters: 65 | - throttle_average: 5s 66 | 67 | - platform: copy 68 | name: '${friendly_name} wind speed (km/h)' 69 | id: wind_speed_kmh 70 | source_id: wind_speed 71 | unit_of_measurement: 'km/h' 72 | icon: 'mdi:weather-windy' 73 | filters: 74 | - multiply: 3.6 75 | 76 | - platform: copy 77 | name: '${friendly_name} wind speed average (km/h)' 78 | icon: 'mdi:weather-windy' 79 | id: wind_speed_kmh_avg 80 | source_id: wind_speed_avg 81 | unit_of_measurement: 'km/h' 82 | filters: 83 | - multiply: 3.6 84 | on_value: 85 | lambda: |- 86 | if (x < 1) { 87 | id(wind_scale_code).publish_state("0"); 88 | id(wind_scale).publish_state("Calm"); 89 | } else if (x >= 1 && x < 6) { 90 | id(wind_scale_code).publish_state("1"); 91 | id(wind_scale).publish_state("Light Air"); 92 | } else if (x >= 6 && x < 12) { 93 | id(wind_scale_code).publish_state("2"); 94 | id(wind_scale).publish_state("Light Breeze"); 95 | } else if (x >= 12 && x < 20) { 96 | id(wind_scale_code).publish_state("3"); 97 | id(wind_scale).publish_state("Gentle Breeze"); 98 | } else if (x >= 20 && x < 29) { 99 | id(wind_scale_code).publish_state("4"); 100 | id(wind_scale).publish_state("Moderate Breeze"); 101 | } else if (x >= 29 && x < 39) { 102 | id(wind_scale_code).publish_state("5"); 103 | id(wind_scale).publish_state("Fresh Breeze"); 104 | } else if (x >= 39 && x < 50) { 105 | id(wind_scale_code).publish_state("6"); 106 | id(wind_scale).publish_state("Strong Breeze"); 107 | } else if (x >= 50 && x < 62) { 108 | id(wind_scale_code).publish_state("7"); 109 | id(wind_scale).publish_state("Near Gale"); 110 | } else if (x >= 62 && x < 75) { 111 | id(wind_scale_code).publish_state("8"); 112 | id(wind_scale).publish_state("Gale"); 113 | } else if (x >= 75 && x < 89) { 114 | id(wind_scale_code).publish_state("9"); 115 | id(wind_scale).publish_state("Severe Gale"); 116 | } else if (x >= 89 && x < 103) { 117 | id(wind_scale_code).publish_state("10"); 118 | id(wind_scale).publish_state("Storm"); 119 | } else if (x >= 103 && x < 118) { 120 | id(wind_scale_code).publish_state("11"); 121 | id(wind_scale).publish_state("Violent Storm"); 122 | } else if (x >= 118) { 123 | id(wind_scale_code).publish_state("12"); 124 | id(wind_scale).publish_state("Hurricane Force"); 125 | } else { 126 | ESP_LOGD("main", "It shouldn't happen (wind_speed_kmh_avg: %f)", x); 127 | } 128 | 129 | - platform: pulse_counter 130 | pin: 131 | # Don't forget to add a pulling resistor, see README 132 | number: GPIO38 133 | mode: INPUT 134 | unit_of_measurement: 'mm' 135 | name: "${friendly_name} rain gauge" 136 | icon: 'mdi:weather-rainy' 137 | id: rain_gauge 138 | internal: true 139 | count_mode: 140 | rising_edge: DISABLE 141 | falling_edge: INCREMENT 142 | internal_filter: 13us 143 | update_interval: 60s 144 | filters: 145 | # Each 0.011" (0.2794mm) of rain causes one momentary contact closure 146 | - multiply: 0.2794 147 | accuracy_decimals: 4 148 | 149 | - platform: integration 150 | name: "${friendly_name} rainfall per min" 151 | id: rain_per_min 152 | time_unit: min 153 | unit_of_measurement: 'mm' 154 | icon: 'mdi:weather-rainy' 155 | sensor: rain_gauge 156 | 157 | - platform: total_daily_energy 158 | name: "${friendly_name} total daily rain" 159 | power_id: rain_gauge 160 | unit_of_measurement: 'mm' 161 | icon: 'mdi:weather-rainy' 162 | # x60 To convert to aggregated rain amount 163 | filters: 164 | - multiply: 60 165 | 166 | - platform: bme280 167 | address: 0x76 168 | update_interval: 60s 169 | iir_filter: 16x 170 | temperature: 171 | name: "${friendly_name} temperature" 172 | id: bme280_temperature 173 | oversampling: 16x 174 | humidity: 175 | name: "${friendly_name} humidity" 176 | id: bme280_humidity 177 | oversampling: 16x 178 | pressure: 179 | name: "${friendly_name} pressure" 180 | id: bme280_pressure 181 | oversampling: 16x 182 | 183 | - platform: tsl2561 184 | name: "${friendly_name} ambient Light" 185 | address: 0x39 186 | update_interval: 60s 187 | integration_time: 14ms 188 | gain: 1x 189 | 190 | - platform: adc 191 | pin: GPIO35 192 | name: "${friendly_name} input voltage" 193 | icon: mdi:car-battery 194 | attenuation: 11db 195 | accuracy_decimals: 2 196 | filters: 197 | - calibrate_linear: 198 | - 3.24 -> 12.01 199 | - 2.80 -> 10.78 200 | 201 | - platform: adc 202 | id: source_sensor 203 | pin: GPIO37 204 | name: ADC 205 | attenuation: 11db 206 | internal: true 207 | update_interval: 5s 208 | accuracy_decimals: 1 209 | filters: 210 | - multiply: 0.846153 # 3.9 -> 3.3V 211 | 212 | - platform: ina219 213 | address: 0x40 214 | shunt_resistance: 0.1 ohm 215 | current: 216 | name: "${friendly_name} solar current" 217 | power: 218 | name: "${friendly_name} solar power" 219 | id: solar_power 220 | bus_voltage: 221 | name: "${friendly_name} solar voltage" 222 | icon: mdi:car-battery 223 | shunt_voltage: 224 | name: "${friendly_name} solar shunt voltage" 225 | max_voltage: 26V 226 | max_current: 3.2A 227 | update_interval: 60s 228 | 229 | - platform: total_daily_energy 230 | name: "${friendly_name} total daily solar energy" 231 | power_id: solar_power 232 | unit_of_measurement: "Wh" 233 | accuracy_decimals: 2 234 | 235 | - platform: resistance 236 | sensor: source_sensor 237 | id: resistance_sensor 238 | configuration: DOWNSTREAM 239 | resistor: 10kOhm 240 | internal: true 241 | name: Resistance Sensor 242 | reference_voltage: 3.9V 243 | accuracy_decimals: 1 244 | filters: 245 | - median: 246 | window_size: 7 247 | send_every: 4 248 | send_first_at: 3 249 | #- heartbeat: 30s 250 | on_value: 251 | - if: 252 | condition: 253 | sensor.in_range: 254 | id: resistance_sensor 255 | above: 15000 256 | below: 15500 257 | then: 258 | - text_sensor.template.publish: 259 | id: wind_dir_card 260 | state: "N" 261 | - sensor.template.publish: 262 | id: wind_heading 263 | state: 0.0 264 | - if: 265 | condition: 266 | sensor.in_range: 267 | id: resistance_sensor 268 | above: 5000 269 | below: 5500 270 | then: 271 | - text_sensor.template.publish: 272 | id: wind_dir_card 273 | state: "NE" 274 | - sensor.template.publish: 275 | id: wind_heading 276 | state: 45.0 277 | - if: 278 | condition: 279 | sensor.in_range: 280 | id: resistance_sensor 281 | above: 350 282 | below: 450 283 | then: 284 | - text_sensor.template.publish: 285 | id: wind_dir_card 286 | state: "E" 287 | - sensor.template.publish: 288 | id: wind_heading 289 | state: 90.0 290 | - if: 291 | condition: 292 | sensor.in_range: 293 | id: resistance_sensor 294 | above: 1200 295 | below: 1400 296 | then: 297 | - text_sensor.template.publish: 298 | id: wind_dir_card 299 | state: "SE" 300 | - sensor.template.publish: 301 | id: wind_heading 302 | state: 135.0 303 | - if: 304 | condition: 305 | sensor.in_range: 306 | id: resistance_sensor 307 | above: 2400 308 | below: 2500 309 | then: 310 | - text_sensor.template.publish: 311 | id: wind_dir_card 312 | state: "S" 313 | - sensor.template.publish: 314 | id: wind_heading 315 | state: 180.0 316 | - if: 317 | condition: 318 | sensor.in_range: 319 | id: resistance_sensor 320 | above: 8900 321 | below: 9200 322 | then: 323 | - text_sensor.template.publish: 324 | id: wind_dir_card 325 | state: "SW" 326 | - sensor.template.publish: 327 | id: wind_heading 328 | state: 225.0 329 | - if: 330 | condition: 331 | sensor.in_range: 332 | id: resistance_sensor 333 | above: 37500 334 | below: 38500 335 | then: 336 | - text_sensor.template.publish: 337 | id: wind_dir_card 338 | state: "W" 339 | - sensor.template.publish: 340 | id: wind_heading 341 | state: 270.0 342 | - if: 343 | condition: 344 | sensor.in_range: 345 | id: resistance_sensor 346 | above: 24400 347 | below: 25000 348 | then: 349 | - text_sensor.template.publish: 350 | id: wind_dir_card 351 | state: "NW" 352 | - sensor.template.publish: 353 | id: wind_heading 354 | state: 315.0 355 | 356 | - platform: template 357 | name: "${friendly_name} wind heading" 358 | id: wind_heading 359 | unit_of_measurement: "°" 360 | 361 | - platform: sun 362 | name: "${friendly_name} Sun elevation" 363 | type: elevation 364 | update_interval: 120s 365 | 366 | - platform: sun 367 | name: "${friendly_name} Sun azimuth" 368 | type: azimuth 369 | update_interval: 120s 370 | 371 | text_sensor: 372 | - platform: template 373 | name: "${friendly_name} wind cardinal direction" 374 | id: wind_dir_card 375 | 376 | - platform: sun 377 | name: "${friendly_name} next sunrise" 378 | type: sunrise 379 | update_interval: 4h 380 | - platform: sun 381 | name: "${friendly_name} next sunset" 382 | type: sunset 383 | update_interval: 4h 384 | 385 | - platform: template 386 | name: '${friendly_name} Beaufort wind scale' 387 | icon: 'mdi:tailwind' 388 | id: wind_scale 389 | update_interval: never 390 | 391 | - platform: template 392 | name: '${friendly_name} Beaufort wind scale code' 393 | icon: 'mdi:tailwind' 394 | id: wind_scale_code 395 | 396 | light: 397 | - platform: fastled_clockless 398 | chipset: WS2812 399 | id: status_light 400 | name: "${friendly_name} status light" 401 | pin: GPIO2 402 | num_leds: 1 403 | rgb_order: GRB 404 | restore_mode: ALWAYS_OFF 405 | 406 | interval: 407 | - interval: 60s 408 | then: 409 | - sensor.integration.reset: rain_per_min 410 | 411 | # Enable time component to reset energy at midnight 412 | time: 413 | - platform: sntp 414 | timezone: "Europe/Paris" 415 | --------------------------------------------------------------------------------