├── assets ├── README.md ├── THsensor.png ├── vortex.jpeg └── voltage_limiter.svg ├── docs ├── index.md ├── _config.yml └── README.md ├── _config.yml ├── weewx ├── README.md └── driver │ ├── README.md │ └── wxMesh.py ├── sensors ├── MQTT_TH_UV_rain │ ├── src │ │ ├── passwords.h.example │ │ └── main.cpp │ ├── README.md │ ├── platformio.ini │ └── lib │ │ ├── readme.txt │ │ └── SDL_ESP8266_AM2315-master │ │ ├── README.txt │ │ ├── Adafruit_AM2315.h │ │ ├── examples │ │ └── am2315 │ │ │ └── am2315.ino │ │ └── Adafruit_AM2315.cpp ├── MQTT_wind │ ├── src │ │ ├── passwords.h.example │ │ └── main.cpp │ ├── lib │ │ └── README │ └── include │ │ └── README └── MQTT_Barometer │ ├── README.md │ └── ms5611 └── README.md /assets/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | Hello world 2 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect 2 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # test github.io 2 | https://bonjour81.github.io/station_meteo/ 3 | -------------------------------------------------------------------------------- /assets/THsensor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bonjour81/station_meteo/HEAD/assets/THsensor.png -------------------------------------------------------------------------------- /assets/vortex.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bonjour81/station_meteo/HEAD/assets/vortex.jpeg -------------------------------------------------------------------------------- /weewx/README.md: -------------------------------------------------------------------------------- 1 | All files relates to weewx : 2 | 3 | - MQTT python driver 4 | 5 | - skins (maybe later) 6 | 7 | 8 | -------------------------------------------------------------------------------- /sensors/MQTT_TH_UV_rain/src/passwords.h.example: -------------------------------------------------------------------------------- 1 | // credentials for wifi & mqtt 2 | 3 | const char* ssid1 = "your ssid"; 4 | const char* password1 = "wifi password"; 5 | const char* ssid2 = "your backup ssid"; // if you have only one, use same in both, or update code 6 | const char* password2 = "wifi password"; 7 | 8 | 9 | #define BROKER_USERNAME "user" 10 | #define BROKER_KEY "password" 11 | -------------------------------------------------------------------------------- /sensors/MQTT_wind/src/passwords.h.example: -------------------------------------------------------------------------------- 1 | // credentials for wifi & mqtt 2 | 3 | const char* ssid = "your ssid"; 4 | const char* ssid2 = "your backup ssid"; // if you have only one, use same in both, or update code 5 | const char* password = "wifi password"; // assume you have same for both AP. 6 | 7 | #define AIO_SERVER "your mosquitto ip address" 8 | #define AIO_SERVERPORT 1883 9 | #define AIO_USERNAME "your mosquitto user name" 10 | #define AIO_KEY "your mosquitto password" 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # station_meteo 2 | 3 | This project is a full weather station. 4 | It rely on [WeeWX](http://weewx.com/) to process the data and generate database, archive, graphics and web output 5 | 6 | The weather station hardware is based on ESP8266 and other arduino stuff. 7 | Related code is in the [sensors directory](./sensors) . 8 | 9 | The datatransmission from sensors to [WeeWX](http://weewx.com/) is based on MQTT. So a specifi MQTT driver is needed for weewx. 10 | It's available in the [weewx section](./weewx). 11 | 12 | 13 | [See wiki for a lot more details](https://github.com/bonjour81/station_meteo/wiki) 14 | -------------------------------------------------------------------------------- /sensors/MQTT_TH_UV_rain/README.md: -------------------------------------------------------------------------------- 1 | The library are not available here but handled directly by ATOM/platformio. 2 | 3 | If you need to download manually, it should be easy to find: 4 | 5 | #include 6 | #include 7 | From ESP8266 platform 8 | #include 9 | From ESP8266 platform 10 | #include 11 | From ESP8266 platform 12 | #include 13 | PCF8583 Library by Xose Perez 14 | https://bitbucket.org/xoseperez/pcf8583.git?utm_source=platformio&utm_medium=piohome 15 | #include 16 | From Adafruit 17 | #include 18 | From Sparkfun 19 | #include 20 | #include 21 | From Adafruit 22 | #include "Adafruit_MQTT.h" 23 | From Adafruit 24 | #include "Adafruit_MQTT_Client.h" 25 | From Adafruit 26 | -------------------------------------------------------------------------------- /sensors/MQTT_TH_UV_rain/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:d1_mini_pro] 12 | ;platform = espressif8266@2.6.3 13 | platform = espressif8266@3.2.0 14 | board = d1_mini_pro 15 | framework = arduino 16 | lib_deps = 17 | PubSubClient@2.8 ; work with 2.7, try 2.8 18 | ESP8266WiFi@1.0 19 | ESP8266HTTPClient@1.2 20 | PCF8583@1.0.0 21 | Adafruit INA219@1.1.1 22 | ;Adafruit INA219@1.0.6 23 | ;SparkFun VEML6075 Arduino Library@1.0.4 24 | ESP8266httpUpdate@1.3 25 | Wire@1.0 26 | Adafruit SHT31 Library@1.1.6 27 | monitor_speed = 115200 28 | -------------------------------------------------------------------------------- /sensors/MQTT_TH_UV_rain/lib/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for the project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link to executable file. 4 | 5 | The source code of each library should be placed in separate directory, like 6 | "lib/private_lib/[here are source files]". 7 | 8 | For example, see how can be organized `Foo` and `Bar` libraries: 9 | 10 | |--lib 11 | | |--Bar 12 | | | |--docs 13 | | | |--examples 14 | | | |--src 15 | | | |- Bar.c 16 | | | |- Bar.h 17 | | |--Foo 18 | | | |- Foo.c 19 | | | |- Foo.h 20 | | |- readme.txt --> THIS FILE 21 | |- platformio.ini 22 | |--src 23 | |- main.c 24 | 25 | Then in `src/main.c` you should use: 26 | 27 | #include 28 | #include 29 | 30 | // rest H/C/CPP code 31 | 32 | PlatformIO will find your libraries automatically, configure preprocessor's 33 | include paths and build them. 34 | 35 | More information about PlatformIO Library Dependency Finder 36 | - http://docs.platformio.org/page/librarymanager/ldf.html 37 | -------------------------------------------------------------------------------- /sensors/MQTT_wind/lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /sensors/MQTT_wind/include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /sensors/MQTT_Barometer/README.md: -------------------------------------------------------------------------------- 1 | MS5611 to MQTT python script 2 | 3 | This script read pressure and temperature from a MS5611 sensor connected to the **OrangePI** I2C bus. Value are published on MQTT (mosquitto). 4 | Current script use basic altitude compensation to calculate barometer value. More evolution may come to use more accurate calculations. 5 | 6 | You can execute this scrip every 5 minutes by adding a line in /etc/crontab: 7 | 8 | */5 * * * * username /update-your-path/ms5611 9 | 10 | Despite it's a python script, it looks like crontab does not like file xxx.py... 11 | better to keep the filename with no extensions. 12 | 13 | 14 | Dont forget to: 15 | * make the script executable (chmod +x ms5611) 16 | * update values in the script: 17 | line 12-13 : mqtt username and password 18 | line 107: the altitude of your sensor in meter. 19 | line 119: the IP address of your mosquitto broker 20 | 21 | 22 | May work on raspberry pi too (not tested). 23 | 24 | [See wiki for more technical detail](https://github.com/bonjour81/station_meteo/wiki/MS5611-Barometer-by-MQTT-on-OrangePi) about sensor characteristics and how to use it. 25 | 26 | 27 | --- 28 | 29 | The following modification was reported by @gdlesage to work on **Raspberry pi 2**: 30 | 31 | mqttuser = "user_name" #double quotes instead of single quotes 32 | 33 | mqttpass = "password" #double quotes instead of single quotes 34 | 35 | bus = smbus.SMBus(1) #1 instead of 0 (work on RPi2 but may vary on other Raspberry type) 36 | 37 | mqttc.username_pw_set(mqttuser,mqttpass) #no quotes around mqttuser or mqttpass 38 | 39 | https://github.com/bonjour81/station_meteo/issues/5#issue-952261133 40 | -------------------------------------------------------------------------------- /sensors/MQTT_TH_UV_rain/lib/SDL_ESP8266_AM2315-master/README.txt: -------------------------------------------------------------------------------- 1 | SwitchDoc Labs Modifications for ESP8266 2 | This is a library for the AM2315 Humidity & Temp Sensor 3 | www.switchdoc.com 4 | 5 | This works for both the SwitchDoc Labs AM2315 and the Adafruit AM2315 6 | 7 | Note: There are ESP8266-12 modules that have pins 4 and 5 reversed. The one we are testing has these lines reversed, hence the statement: 8 | 9 | Wire.begin(5,4); 10 | 11 | If you have the lines in your ESP8266 correct, then change this to Wire.begin(4,5) 12 | 13 | This is a library for the AM2315 Humidity + Temp sensor 14 | 15 | Designed specifically to work with the AM2315 in the Adafruit shop 16 | ----> https://www.adafruit.com/products/1293 17 | 18 | These displays use I2C to communicate, 2 pins are required to interface 19 | Adafruit invests time and resources providing this open source code, 20 | please support Adafruit and open-source hardware by purchasing 21 | products from Adafruit! 22 | 23 | Written by Limor Fried/Ladyada for Adafruit Industries. 24 | BSD license, all text above must be included in any redistribution 25 | 26 | Check out the links above for our tutorials and wiring diagrams 27 | 28 | To download. click the ZIP button in the top-middle navbar, 29 | rename the uncompressed folder Adafruit_AM2315. 30 | Check that the Adafruit_AM2315 folder contains Adafruit_AM2315.cpp and Adafruit_AM2315.h 31 | 32 | Place the Adafruit_AM2315 library folder your arduinosketchfolder/libraries/ folder. 33 | You may need to create the libraries subfolder if its your first library. Restart the IDE. 34 | 35 | We also have a great tutorial on Arduino library installation at: 36 | http://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use 37 | -------------------------------------------------------------------------------- /weewx/driver/README.md: -------------------------------------------------------------------------------- 1 | To be added in your /etc/weewx/weewx.conf: 2 | 3 | [wxMesh] 4 | driver = user.wxMesh 5 | 6 | # MQTT specifics 7 | host = your_mqtt_broker_IP # ex 192.168.1.xx 8 | client = weewx_mqtt # just a client name for mqtt, must be unique! 9 | username = mqtt_username # your mqtt username 10 | password = mqtt_password # your mqtt password 11 | topic = yourtopic # your mqtt topic root, your sensor will publish as "yourtopic/outTemp" etc 12 | poll_interval = 2 # the driver will process the receiver data every poll_interval (seconds) 13 | 14 | [[label_map]] 15 | 16 | # mqtt topic = weewx variable 17 | barometer = barometer 18 | pressure = pressure 19 | altimeter = altimeter 20 | inTemp = inTemp 21 | outTemp = outTemp 22 | inHumidity = inHumidity 23 | outHumidity = outHumidity 24 | windSpeed = windSpeed 25 | windDir = windDir 26 | windGust = windGust 27 | windGustDir = windGustDir 28 | rainRate = rainRate 29 | rain = rain 30 | dewpoint = dewpoint 31 | windchill = windchill 32 | heatindex = heatindex 33 | ET = ET 34 | radiation = radiation 35 | UV = UV 36 | extraTemp1 = extraTemp1 37 | extraTemp2 = extraTemp2 38 | extraTemp3 = extraTemp3 39 | ----- 40 | 41 | The driver will take any data (number) published to yourtopic/yourlabel. 42 | Example : 43 | 44 | if yourtopic is 'weewx', outTemp shall be avaible in topic weewx/outTemp with payload 23.2 (if out temperature is 23.2°C or 23.2°F (follows your weewx units). 45 | -------------------------------------------------------------------------------- /sensors/MQTT_TH_UV_rain/lib/SDL_ESP8266_AM2315-master/Adafruit_AM2315.h: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | This is a modified library from SwitchDoc Labs / Adafruit from 17/04/2019 3 | 4 | I have done the following modifications: 5 | 1/ twisted scl/sda pins in: Wire.begin(4, 5); 6 | 7 | 2/ add negative temp handling 8 | ****************************************************/ 9 | 10 | /*************************************************** 11 | SwitchDoc Labs Modifications for ESP8266 12 | This is a library for the AM2315 Humidity & Temp Sensor 13 | www.switchdoc.com 14 | 15 | This works for both the SwitchDoc Labs AM2315 and the Adafruit AM2315 16 | 17 | 18 | 19 | This is a library for the AM2315 Humidity Pressure & Temp Sensor 20 | 21 | Designed specifically to work with the AM2315 sensor from Adafruit 22 | ----> https://www.adafruit.com/products/1293 23 | 24 | These displays use I2C to communicate, 2 pins are required to 25 | interface 26 | Adafruit invests time and resources providing this open source code, 27 | please support Adafruit and open-source hardware by purchasing 28 | products from Adafruit! 29 | 30 | Written by Limor Fried/Ladyada for Adafruit Industries. 31 | BSD license, all text above must be included in any redistribution 32 | ****************************************************/ 33 | 34 | #if (ARDUINO >= 100) 35 | #include "Arduino.h" 36 | #else 37 | #include "WProgram.h" 38 | #endif 39 | #include "Wire.h" 40 | 41 | #define AM2315_I2CADDR 0x5C 42 | #define AM2315_READREG 0x03 43 | 44 | class Adafruit_AM2315 { 45 | public: 46 | Adafruit_AM2315(); 47 | boolean begin(void); 48 | float readTemperature(void); 49 | float readHumidity(void); 50 | bool readTemperatureAndHumidity(float &t, float &h); 51 | 52 | private: 53 | boolean readData(void); 54 | float humidity, temp; 55 | }; 56 | -------------------------------------------------------------------------------- /sensors/MQTT_TH_UV_rain/lib/SDL_ESP8266_AM2315-master/examples/am2315/am2315.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /*************************************************** 5 | This is an example for the AM2315 Humidity + Temp sensor 6 | 7 | 8 | SwitchDoc Labs Modifications for ESP8266 9 | This is a library for the AM2315 Humidity & Temp Sensor 10 | www.switchdoc.com 11 | 12 | This works for both the SwitchDoc Labs AM2315 and the Adafruit AM2315 13 | 14 | 15 | Designed specifically to work with the Adafruit BMP085 Breakout 16 | ----> https://www.adafruit.com/products/1293 17 | 18 | These displays use I2C to communicate, 2 pins are required to 19 | interface 20 | Adafruit invests time and resources providing this open source code, 21 | please support Adafruit and open-source hardware by purchasing 22 | products from Adafruit! 23 | 24 | Written by Limor Fried/Ladyada for Adafruit Industries. 25 | BSD license, all text above must be included in any redistribution 26 | ****************************************************/ 27 | 28 | // Connect RED of the AM2315 sensor to 5.0V 29 | // Connect BLACK to Ground 30 | // Connect WHITE to i2c clock - on '168/'328 Arduino Uno/Duemilanove/etc thats Analog 5 31 | // Connect YELLOW to i2c data - on '168/'328 Arduino Uno/Duemilanove/etc thats Analog 4 32 | 33 | Adafruit_AM2315 am2315; 34 | 35 | void setup() { 36 | Serial.begin(9600); 37 | Serial.println("AM2315 Test!"); 38 | 39 | if (! am2315.begin()) { 40 | Serial.println("Sensor not found, check wiring & pullups!"); 41 | // while (1); // this bombs the latest version of ESP8266 software 42 | while (1) 43 | { 44 | yield(); 45 | delay(10); 46 | } 47 | } 48 | } 49 | 50 | void loop() { 51 | Serial.print("Hum: "); Serial.println(am2315.readHumidity()); 52 | Serial.print("Temp: "); Serial.println(am2315.readTemperature()); 53 | 54 | delay(1000); 55 | }} 56 | -------------------------------------------------------------------------------- /sensors/MQTT_TH_UV_rain/lib/SDL_ESP8266_AM2315-master/Adafruit_AM2315.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | This is a modified library from SwitchDoc Labs / Adafruit from 17/04/2019 3 | 4 | I have done the following modifications: 5 | 1/ twisted scl/sda pins in: Wire.begin(4, 5); 6 | 7 | 2/ add negative temp handling 8 | ****************************************************/ 9 | 10 | /*************************************************** 11 | SwitchDoc Labs Modifications for ESP8266 12 | This is a library for the AM2315 Humidity & Temp Sensor 13 | www.switchdoc.com 14 | 15 | Designed specifically to work with the AM2315 sensor from Adafruit 16 | ----> https://www.adafruit.com/products/1293 17 | 18 | These displays use I2C to communicate, 2 pins are required to 19 | interface 20 | Adafruit invests time and resources providing this open source code, 21 | please support Adafruit and open-source hardware by purchasing 22 | products from Adafruit! 23 | 24 | Written by Limor Fried/Ladyada for Adafruit Industries. 25 | BSD license, all text above must be included in any redistribution 26 | ****************************************************/ 27 | 28 | #include "Adafruit_AM2315.h" 29 | //#include 30 | 31 | Adafruit_AM2315::Adafruit_AM2315() { 32 | } 33 | 34 | 35 | boolean Adafruit_AM2315::begin(void) { 36 | //Wire.begin(5, 4); 37 | Wire.begin(4, 5); // modified compared to original library 38 | 39 | // try to read data, as a test 40 | return readData(); 41 | 42 | } 43 | 44 | boolean Adafruit_AM2315::readData(void) { 45 | uint8_t reply[10]; 46 | 47 | Wire.beginTransmission(AM2315_I2CADDR); 48 | Wire.write(AM2315_READREG); 49 | Wire.write(0x00); // start at address 0x0 50 | Wire.write(4); // request 4 bytes data 51 | Wire.endTransmission(); 52 | delay(10); 53 | 54 | // for reasons unknown we have to send the data twice :/ 55 | // whats the bug here? 56 | Wire.beginTransmission(AM2315_I2CADDR); 57 | Wire.write(AM2315_READREG); 58 | Wire.write(0x00); // start at address 0x0 59 | Wire.write(4); // request 4 bytes data 60 | Wire.endTransmission(); 61 | 62 | delay(10); 63 | Wire.requestFrom(AM2315_I2CADDR, 8); 64 | for (uint8_t i = 0; i < 8; i++) { 65 | reply[i] = Wire.read(); 66 | //Serial.println(reply[i], HEX); 67 | } 68 | 69 | if (reply[0] != AM2315_READREG) return false; 70 | if (reply[1] != 4) return false; // bytes req'd 71 | 72 | humidity = reply[2]; 73 | humidity *= 256; 74 | humidity += reply[3]; 75 | humidity /= 10; 76 | //Serial.print("H"); Serial.println(humidity); 77 | 78 | 79 | 80 | temp = reply[4] & 0x7F; // mask to remove sign bit 81 | temp *= 256; 82 | temp += reply[5]; 83 | temp /= 10; 84 | //Serial.print("T"); Serial.println(temp); 85 | 86 | // change sign for negative temps 87 | if (reply[4] >> 7) temp = -temp; 88 | 89 | return true; 90 | } 91 | 92 | 93 | float Adafruit_AM2315::readTemperature(void) { 94 | if (! readData()) return NAN; 95 | return temp; 96 | } 97 | 98 | float Adafruit_AM2315::readHumidity(void) { 99 | if (! readData()) return NAN; 100 | return humidity; 101 | } 102 | 103 | /* 104 | * This method returns both temperature and humidity in a single call and using a single I2C request. 105 | * 106 | * If you want to obtain both temperature and humidity when you sample the sensor, be aware that calling 107 | * readTemperature() and readHumidity() in rapid succession may swamp the sensor and result in invalid 108 | * readingings (the AM2315 manual advisess that continuous samples must be at least 2 seconds apart). 109 | * Calling this method avoids the double I2C request. 110 | */ 111 | bool Adafruit_AM2315::readTemperatureAndHumidity(float &t, float &h) { 112 | if (!readData()) return false; 113 | 114 | t = temp; 115 | h = humidity; 116 | 117 | return true; 118 | } 119 | 120 | /*********************************************************************/ 121 | -------------------------------------------------------------------------------- /sensors/MQTT_Barometer/ms5611: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Distributed with a free-will license. 3 | # Use it any way you want, profit or free, provided it fits in the licenses of its associated works. 4 | # MS5611_01BXXX 5 | # This code is designed to work with the MS5611_01BXXX_I2CS I2C Mini Module available from ControlEverything.com. 6 | # https://www.controleverything.com/content/Analog-Digital-Converters?sku=MS5611-01BXXX_I2CS_A01#tabs-0-product_tabset-2 7 | 8 | import smbus 9 | import time 10 | import paho.mqtt.client as mqtt 11 | 12 | mqttuser = 'mqtt_username' 13 | mqttpass = 'mqtt_password' 14 | 15 | 16 | # Get I2C bus - place here your i2c bus (0 or 1) 17 | bus = smbus.SMBus(0) 18 | 19 | # MS5611_01BXXX address, 0x77(118) replace 0x77 by 0x76 if your sensor has a different address 20 | # 0x1E(30) Reset command 21 | bus.write_byte(0x77, 0x1E) 22 | 23 | time.sleep(0.1) 24 | 25 | # Read 12 bytes of calibration data 26 | # Read pressure sensitivity 27 | data = bus.read_i2c_block_data(0x77, 0xA2, 2) 28 | C1 = data[0] * 256 + data[1] 29 | 30 | # Read pressure offset 31 | data = bus.read_i2c_block_data(0x77, 0xA4, 2) 32 | C2 = data[0] * 256 + data[1] 33 | 34 | # Read temperature coefficient of pressure sensitivity 35 | data = bus.read_i2c_block_data(0x77, 0xA6, 2) 36 | C3 = data[0] * 256 + data[1] 37 | 38 | # Read temperature coefficient of pressure offset 39 | data = bus.read_i2c_block_data(0x77, 0xA8, 2) 40 | C4 = data[0] * 256 + data[1] 41 | 42 | # Read reference temperature 43 | data = bus.read_i2c_block_data(0x77, 0xAA, 2) 44 | C5 = data[0] * 256 + data[1] 45 | 46 | # Read temperature coefficient of the temperature 47 | data = bus.read_i2c_block_data(0x77, 0xAC, 2) 48 | C6 = data[0] * 256 + data[1] 49 | 50 | # MS5611_01BXXX address, 0x77(118) 51 | # 0x48(64) Pressure conversion(OSR = 4096) command 52 | bus.write_byte(0x77, 0x48) 53 | 54 | time.sleep(0.1) 55 | 56 | # Read digital pressure value 57 | # Read data back from 0x00(0), 3 bytes 58 | # D1 MSB2, D1 MSB1, D1 LSB 59 | value = bus.read_i2c_block_data(0x77, 0x00, 3) 60 | D1 = value[0] * 65536 + value[1] * 256 + value[2] 61 | 62 | # MS5611_01BXXX address, 0x77(118) 63 | # 0x58(64) Temperature conversion(OSR = 4096) command 64 | bus.write_byte(0x77, 0x58) 65 | 66 | time.sleep(0.1) 67 | 68 | # Read digital temperature value 69 | # Read data back from 0x00(0), 3 bytes 70 | # D2 MSB2, D2 MSB1, D2 LSB 71 | value = bus.read_i2c_block_data(0x77, 0x00, 3) 72 | D2 = value[0] * 65536 + value[1] * 256 + value[2] 73 | 74 | dT = D2 - C5 * 256 75 | TEMP = 2000 + dT * C6 / 8388608 76 | OFF = C2 * 65536 + (C4 * dT) / 128 77 | SENS = C1 * 32768 + (C3 * dT ) / 256 78 | T2 = 0 79 | OFF2 = 0 80 | SENS2 = 0 81 | 82 | 83 | # compensation algorythm as stated in datasheet 84 | if TEMP >= 2000 : 85 | T2 = 0 86 | OFF2 = 0 87 | SENS2 = 0 88 | elif TEMP < 2000 : 89 | T2 = (dT * dT) / 2147483648 90 | OFF2 = 5 * ((TEMP - 2000) * (TEMP - 2000)) / 2 91 | SENS2 = 5 * ((TEMP - 2000) * (TEMP - 2000)) / 4 92 | if TEMP < -1500 : 93 | OFF2 = OFF2 + 7 * ((TEMP + 1500) * (TEMP + 1500)) 94 | SENS2 = SENS2 + 11 * ((TEMP + 1500) * (TEMP + 1500)) / 2 95 | 96 | TEMP = TEMP - T2 97 | OFF = OFF - OFF2 98 | SENS = SENS - SENS2 99 | pressure = ((((D1 * SENS) / 2097152) - OFF) / 32768.0) / 100.0 100 | cTemp = TEMP / 100.0 101 | fTemp = cTemp * 1.8 + 32 102 | 103 | 104 | # barometer calculation => barometer is pressure at see level. 105 | # measurement has to be compensated with altitude 1mbar / 8.36m 106 | # Barometer = Pressure + Altitude / 8.36 107 | altitude = 111 #meter 108 | barometer = pressure + altitude / 8.36 109 | 110 | 111 | # Output data to screen uncomment if needed for debug 112 | #print "Pressure : %.2f mbar" %pressure 113 | #print "Barometer : %.2f mbar" %barometer 114 | #print "Temperature in Celsius : %.2f C" %cTemp 115 | #print "Temperature in Fahrenheit : %.2f F" %fTemp 116 | 117 | 118 | # set here your mqtt broker address 119 | broker_address="broker ip address" 120 | 121 | # mqtt publish 122 | mqttc = mqtt.Client("barometre") 123 | mqttc.username_pw_set('mqttuser','mqttpass') 124 | mqttc.connect(broker_address, 1883, 60) 125 | #mqttc.loop_start() 126 | #barometer value is rounded to keep only 2 number after decimal sign ( 0.01mbar ) 127 | # mqtt QoS = 0 128 | mqttc.publish("weewx/barometer", round(barometer,2), 0) 129 | mqttc.publish("weewx/pressure", round(pressure,2), 0) 130 | mqttc.publish("weewx/inTemp", cTemp, 0) 131 | mqttc.loop(5) #5sec timout 132 | mqttc.disconnect() 133 | 134 | -------------------------------------------------------------------------------- /weewx/driver/wxMesh.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # weewx driver that reads data from MQTT subscription for Python 3 and Weewx 4.x 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU General Public License as published by the Free Software 7 | # Foundation, either version 3 of the License, or any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT 10 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | # FOR A PARTICULAR PURPOSE. 12 | # 13 | # See http://www.gnu.org/licenses/ 14 | # 15 | # The units must be weewx.US: 16 | # degree_F, inHg, inch, inch_per_hour, mile_per_hour 17 | # 18 | # To use this driver, put this file in the weewx user directory, then make 19 | # the following changes to weewx.conf: 20 | # 21 | # [Station] 22 | # station_type = wxMesh 23 | # [wxMesh] 24 | # host = localhost # MQTT broker hostname 25 | # topic = weather/+ # topic 26 | # driver = user.wxMesh 27 | # 28 | # If the variables in the file have names different from those in weewx, then 29 | # create a mapping such as this: 30 | # 31 | # [wxMesh] 32 | # ... 33 | # [[label_map]] 34 | # temp = outTemp 35 | # humi = outHumidity 36 | # in_temp = inTemp 37 | # in_humid = inHumidity 38 | 39 | from __future__ import with_statement 40 | import syslog 41 | import time 42 | import queue 43 | import paho.mqtt.client as mqtt 44 | import weewx.drivers 45 | 46 | DRIVER_VERSION = "0.1" 47 | 48 | def logmsg(dst, msg): 49 | syslog.syslog(dst, 'wxMesh: %s' % msg) 50 | 51 | def logdbg(msg): 52 | #logmsg(syslog.LOG_DEBUG, msg) 53 | logmsg(syslog.LOG_DEBUG, msg) 54 | 55 | def loginf(msg): 56 | #logmsg(syslog.LOG_INFO, msg) 57 | logmsg(syslog.LOG_INFO, msg) 58 | 59 | def logerr(msg): 60 | logmsg(syslog.LOG_ERR, msg) 61 | 62 | def _get_as_float(d, s): 63 | v = None 64 | if s in d: 65 | try: 66 | v = float(d[s]) 67 | except ValueError as e: 68 | logerr("cannot read value for '%s': %s" % (s, e)) 69 | return v 70 | 71 | def loader(config_dict, engine): 72 | return wxMesh(**config_dict['wxMesh']) 73 | 74 | class wxMesh(weewx.drivers.AbstractDevice): 75 | """weewx driver that reads data from a file""" 76 | 77 | def __init__(self, **stn_dict): 78 | # where to find the data file 79 | self.host = stn_dict.get('host', 'localhost') 80 | self.topic = stn_dict.get('topic', 'weather') + "/#" 81 | self.username = stn_dict.get('username', 'no default') 82 | self.password = stn_dict.get('password', 'no default') 83 | self.client_id = stn_dict.get('client', 'wxclient') # MQTT client id - adjust as desired 84 | 85 | # how often to poll the weather data file, seconds 86 | self.poll_interval = float(stn_dict.get('poll_interval', 5.0)) 87 | # mapping from variable names to weewx names 88 | self.label_map = stn_dict.get('label_map', {}) 89 | 90 | loginf("MQTT host is %s" % self.host) 91 | loginf("MQTT topic is %s" % self.topic) 92 | loginf("MQTT client is %s" % self.client_id) 93 | loginf("polling interval is %s" % self.poll_interval) 94 | loginf('label map is %s' % self.label_map) 95 | 96 | self.payload = queue.Queue() 97 | self.connected = False 98 | 99 | self.client = mqtt.Client(client_id="weewxMesh", clean_session=False, userdata=None, protocol=mqtt.MQTTv311, transport="tcp") 100 | 101 | self.client.on_connect = self.on_connect 102 | self.client.on_message = self.on_message 103 | 104 | 105 | # TODO - need some reconnect on disconnect logic 106 | #self.client.on_disconnect = self.on_disconnect 107 | 108 | self.client.username_pw_set(self.username, self.password) 109 | self.client.connect(self.host, 1883, 60) 110 | self.client.loop_start() 111 | self.receive_buffer = {} 112 | 113 | # The callback for when the client rEceives a CONNACK response from the server. 114 | def on_connect(self, client, userdata, flags, rc): 115 | loginf("Connected on mqtt server with result code "+str(rc)) 116 | if rc == 0: 117 | self.connected = True 118 | # Subscribing in on_connect() means that if we lose the connection and 119 | # reconnect then subscriptions will be renewed. 120 | self.client.subscribe(self.topic) 121 | 122 | 123 | 124 | # The callback for when a PUBLISH message is received from the MQTT server. 125 | def on_message(self, client, userdata, msg): 126 | msg.payload = msg.payload.decode('UTF-8') 127 | self.payload = msg.payload 128 | string_topic = str(msg.topic) 129 | key = string_topic.split('/')[-1] 130 | self.receive_buffer[key] = str(msg.payload) 131 | logdbg("mqtt message received %s" %string_topic) 132 | logdbg("mqtt message payload %s" %self.payload) 133 | 134 | def closePort(self): 135 | self.client.disconnect() 136 | self.client.loop_stop() 137 | 138 | def genLoopPackets(self): 139 | self.client.loop_start() # enable to receive ('on_message') in background 140 | while True: 141 | time.sleep(self.poll_interval) # wait for some MQTT data to be published 142 | if self.receive_buffer: 143 | data = self.receive_buffer.copy() # copy receive_buffer in data for packet building 144 | self.receive_buffer.clear() # clear receive_buffer to make it ready for any next incoming mqtt data 145 | # if data: # if data is not empty then prepare loop packet 146 | _packet = {'dateTime': int(time.time() + 0.5),'usUnits': weewx.METRICWX} 147 | logerr("dateTime %s" % _packet["dateTime"]) 148 | for vname in data: 149 | _packet[self.label_map.get(vname, vname)] = _get_as_float(data, vname) 150 | logerr("packet content: %s = %s" %(self.label_map.get(vname, vname), data[vname])) 151 | yield _packet 152 | data.clear() 153 | # self.client.disconnect() 154 | # self.client.loop_stop() 155 | 156 | @property 157 | def hardware_name(self): 158 | return "wxMesh" 159 | -------------------------------------------------------------------------------- /sensors/MQTT_TH_UV_rain/src/main.cpp: -------------------------------------------------------------------------------- 1 | const float FW_VERSION = 1.71; 2 | //v1.70,71 : update crédentials 3 | //v1.69 : publish temp before humi 4 | //v1.68 minor tunning for debug 5 | //V1.67 : add TPL5010 watchdog : pin "DONE" of TPL5010 connected to D6 / GPIO12 of the Wemos 6 | // the TPL5010 is set with 100k resistor = ~30min timeout 7 | //V1.66 : retry pubsubclient 2.8 8 | //V1.65 : back to pubsub 2.7 9 | //V1.64 : try pub sub client v2.8 instead of 2.7 => not compiling 10 | //V1.63 : minor corrections 11 | //V1.60 : replacement of wemos mini pro, new IP address 12 | //V1.59 : some IP address update 13 | //V1.58 : some IP address update 14 | //V1.57 : bug correction 15 | //V1.52 : change parameters /timings in case of wifi connexion failure to save battery. 16 | //V1.50 : switched to SHT31 TH sensor, change INA219 address. 17 | //V1.49 bug correction 18 | //V1.48 add filter on T&H measurement: take 3 samples, keep 2 best ones (filter sporadic spikes) 19 | //V1.30 - V1.46 debug, change to pubsubclient lib instead of adafruit mqtt lib 20 | // added subscription to receive config message (not used yet) 21 | //V1.29 22 | //History: 23 | // change Isolar unit in Amp instead of milliamp to match weewx default unit for current. 24 | //v1.28 25 | // added average (on 2 samples) of UV, T & H measurement. 26 | //V1.27 27 | //History: 28 | // switched to AM2315 for temp & humidity sensor 29 | //V1.26 30 | //History: 31 | // added OTA via http server 32 | // added debug mode for serial monitoring 33 | 34 | //#define DEBUGMODE //If you comment this line, the DPRINT & DPRINTLN lines are defined as blank. 35 | #ifdef DEBUGMODE //Macros are usually in all capital letters. 36 | #define DPRINT(...) Serial.print(__VA_ARGS__) //DPRINT is a macro, debug print 37 | #define DPRINTLN(...) Serial.println(__VA_ARGS__) //DPRINTLN is a macro, debug print with new line 38 | #else 39 | #define DPRINT(...) //now defines a blank line 40 | #define DPRINTLN(...) //now defines a blank line 41 | #endif 42 | 43 | const char* fwImageURL = "http://192.168.1.184/fota/THrain/firmware.bin"; // update with your link to the new firmware bin file. 44 | const char* fwVersionURL = "http://192.168.1.184/fota/THrain/firmware.version"; // update with your link to a text file with new version (just a single line with a number) 45 | // version is used to do OTA only one time, even if you let the firmware file available on the server. 46 | // flashing will occur only if a greater number is available in the "firmware.version" text file. 47 | // take care the number in text file is compared to "FW_VERSION" in the code => this const shall be incremented at each update. 48 | 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include // may be used to optimise connexion to best AP. 54 | #include 55 | #include 56 | //#include 57 | #include 58 | //#include 59 | //#include // => Modified library from https://github.com/switchdoclabs/SDL_ESP8266_AM2315 I have switched pin 4&5 in cpp file wire.begin 60 | #include "Adafruit_SHT31.h" 61 | #include 62 | 63 | // wifi & mqtt credentials 64 | #include "passwords.h" 65 | 66 | // I2C Setup SCL: D1/GPIO5 SDA: D2/GPIO4 ///////////////////////////////////////////// 67 | // setup of sensors 68 | // counter for rain gauge tipping bucket I2C address 0xA0 69 | PCF8583 rtc(0xA0); 70 | // INA219 current and voltage sensors: to monitor solar panel & battery 71 | Adafruit_INA219 ina219_solar(0x41); // I2C address 0x41 !default is 0x40, conflict with hdc1080 72 | Adafruit_INA219 ina219_battery(0x45); // I2C address 0x45 !default is 0x40, conflict with hdc1080 73 | // UV sensor 74 | //VEML6075 veml6075 = VEML6075(); 75 | //VEML6075 uv; // sparkfun lib sensor declaration I2C address of 0x10 and cannot be changed! 76 | //Temp and humidity sensor. 77 | //ClosedCube_HDC1080 hdc1080; // default address is 0x40 78 | //Adafruit_AM2315 am2315; // default address is 0x05C (!cannot be changed) 79 | Adafruit_SHT31 sht31 = Adafruit_SHT31(); 80 | 81 | // WiFi connexion informations ////////////////////////////////////////////////////////////// 82 | IPAddress ip(192, 168, 5, 22); // hard coded IP address (make the wifi connexion faster (save battery), no need for DHCP) 83 | IPAddress gateway(192, 168, 5, 254); // set gateway to match your network 84 | IPAddress subnet(255, 255, 255, 0); // set subnet mask to match your network 85 | 86 | // MQTT Configuration ////////////////////////////////////////////////////////////////////// 87 | WiFiClient client; 88 | char received_topic[128]; 89 | byte received_payload[128]; 90 | unsigned int received_length; 91 | bool received_msg = false; 92 | void callback(char* topic, byte* payload, unsigned int length) 93 | { 94 | strcpy(received_topic, topic); 95 | memcpy(received_payload, payload, length); 96 | received_msg = true; 97 | received_length = length; 98 | } 99 | IPAddress broker(192, 168, 5, 183); 100 | PubSubClient mqtt(broker, 1883, callback, client); 101 | #define STATUS_TOPIC "THrain/Status" 102 | #define VERSION_TOPIC "THrain/Version" 103 | #define CONFIG_TOPIC "THrain/Config" 104 | #define RAIN_TOPIC "weewx/rain" 105 | #define VSOLAR_TOPIC "weewx/Vsolar" 106 | #define ISOLAR_TOPIC "weewx/Isolar" 107 | #define VBAT_TOPIC "weewx/Vbat" 108 | #define HUMI_TOPIC "weewx/outHumidity" 109 | #define TEMP_TOPIC "weewx/outTemp" 110 | #define UV_TOPIC "weewx/UV" 111 | #define MQTT_CLIENT_NAME "ESP_THrain2" // make sure it's a unique identifier on your MQTT broker 112 | 113 | // variables ///////////////////////////////////////////////////////////////////////////// 114 | unsigned long top = 0; 115 | int sleep_coef = 1; 116 | float rain = -1; 117 | float temp = -100; 118 | float temp_array[3] = { -100, -100, -100 }; 119 | float humi = -1; 120 | float humi_array[3] = { -100, -100, -100 }; 121 | float temp_buffer = -100; 122 | float humi_buffer = -1; 123 | float UVindex = -1; 124 | float UVA = -1; 125 | float UVB = -1; 126 | float solar_voltage = -1; 127 | float solar_current = -1; // to check if battery is charging well. 128 | float battery_voltage = -1; 129 | float battery_voltage2 = -1; 130 | 131 | 132 | // set faster emission in debug mode (take care battery will empty faster!) 133 | #ifdef DEBUGMODE 134 | int sleep_duration = 5; // deep sleep duration in seconds 135 | #else 136 | int sleep_duration = 150; // deep sleep duration in seconds 137 | #endif 138 | 139 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 140 | // setup_wifi() : connexion to wifi hotspot 141 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 142 | void wifi_connect(const char* ssid, const char* password, uint8_t timeout) 143 | { 144 | uint8_t timeout_wifi = timeout; 145 | digitalWrite(LED_BUILTIN, LOW); 146 | #ifdef DEBUGMODE 147 | Serial.println(""); 148 | Serial.print("Scan start ... "); 149 | int nnn = WiFi.scanNetworks(); 150 | Serial.print(nnn); 151 | Serial.println(" network(s) found"); 152 | for (int iii = 0; iii < nnn; iii++) 153 | { 154 | Serial.print(WiFi.SSID(iii)); 155 | Serial.print(" ("); 156 | Serial.print(WiFi.RSSI(iii));//Signal strength in dBm ) 157 | Serial.println("dBm)"); 158 | } 159 | Serial.println(); 160 | Serial.print("connecting to: "); 161 | Serial.print(ssid); 162 | 163 | #endif 164 | WiFi.begin(ssid, password); 165 | while ((WiFi.status() != WL_CONNECTED) && (timeout_wifi > 0)) { 166 | delay(200); 167 | digitalWrite(LED_BUILTIN, HIGH); // led will get one 100ms after 1st try to connect so we can distinguish the very short flash (~10ms) done before 168 | delay(800); 169 | DPRINT("."); 170 | timeout_wifi--; 171 | } 172 | digitalWrite(LED_BUILTIN, LOW); 173 | } 174 | //Connexion au réseau WiFi 175 | void setup_wifi() 176 | { 177 | //config static IP 178 | WiFi.mode(WIFI_STA); 179 | WiFi.config(ip, gateway, subnet); 180 | DPRINT("Wifi status: "); 181 | switch (WiFi.status()) { 182 | case WL_NO_SHIELD: 183 | DPRINT("255 :WL_NO_SHIELD"); 184 | break; 185 | case WL_IDLE_STATUS: 186 | DPRINT("0 :WL_IDLE_STATUS"); 187 | break; 188 | case WL_NO_SSID_AVAIL: 189 | DPRINT("1 :WL_NO_SSID_AVAIL"); 190 | break; 191 | case WL_SCAN_COMPLETED: 192 | DPRINT("2 :WL_SCAN_COMPLETED"); 193 | break; 194 | case WL_CONNECTED: 195 | DPRINT("3 :WL_CONNECTED"); 196 | break; 197 | case WL_CONNECT_FAILED: 198 | DPRINT("4 :WL_CONNECT_FAILED"); 199 | break; 200 | case WL_CONNECTION_LOST: 201 | DPRINT("5 :WL_CONNECTION_LOST"); 202 | break; 203 | case WL_DISCONNECTED: 204 | DPRINT("6 :WL_DISCONNECTED"); 205 | break; 206 | } 207 | DPRINT(" "); 208 | if (WiFi.status() == WL_CONNECTED) { 209 | DPRINTLN("Wifi already connected"); 210 | return; 211 | } 212 | // try 1st SSID (if you have 2 hotspots) 213 | DPRINT("Try to connect 1st wifi:"); 214 | wifi_connect(ssid1, password1, 6); 215 | // if connexion is successful, let's go to next, no need for SSID2 216 | DPRINTLN(""); 217 | if (WiFi.status() == WL_CONNECTED) { 218 | DPRINTLN("Wifi ssid1 connected"); 219 | return; 220 | } else { 221 | switch (WiFi.status()) { 222 | case WL_NO_SHIELD: 223 | DPRINTLN("255 :WL_NO_SHIELD"); 224 | break; 225 | case WL_IDLE_STATUS: 226 | DPRINTLN("0 :WL_IDLE_STATUS"); 227 | break; 228 | case WL_NO_SSID_AVAIL: 229 | DPRINTLN("1 :WL_NO_SSID_AVAIL"); 230 | break; 231 | case WL_SCAN_COMPLETED: 232 | DPRINTLN("2 :WL_SCAN_COMPLETED"); 233 | break; 234 | case WL_CONNECTED: 235 | DPRINTLN("3 :WL_CONNECTED"); 236 | break; 237 | case WL_CONNECT_FAILED: 238 | DPRINTLN("4 :WL_CONNECT_FAILED"); 239 | break; 240 | case WL_CONNECTION_LOST: 241 | DPRINTLN("5 :WL_CONNECTION_LOST"); 242 | break; 243 | case WL_DISCONNECTED: 244 | DPRINTLN("6 :WL_DISCONNECTED"); 245 | break; 246 | } 247 | DPRINTLN("Let's try connecting 2nd wifi SSID"); 248 | } 249 | // let's try SSID2 (if ssid1 did not worked) 250 | wifi_connect(ssid2, password2, 6); 251 | if (WiFi.status() == WL_CONNECTED) { 252 | DPRINTLN("Wifi ssid2 connected"); 253 | return; // if connexion is successful, let's go to next, no need for SSID2 254 | } else { 255 | DPRINT("2nd try Failed: "); 256 | switch (WiFi.status()) { 257 | case WL_NO_SHIELD: 258 | DPRINTLN("255 :WL_NO_SHIELD"); 259 | break; 260 | case WL_IDLE_STATUS: 261 | DPRINTLN("0 :WL_IDLE_STATUS"); 262 | break; 263 | case WL_NO_SSID_AVAIL: 264 | DPRINTLN("1 :WL_NO_SSID_AVAIL"); 265 | break; 266 | case WL_SCAN_COMPLETED: 267 | DPRINTLN("2 :WL_SCAN_COMPLETED"); 268 | break; 269 | case WL_CONNECTED: 270 | DPRINTLN("3 :WL_CONNECTED"); 271 | break; 272 | case WL_CONNECT_FAILED: 273 | DPRINTLN("4 :WL_CONNECT_FAILED"); 274 | break; 275 | case WL_CONNECTION_LOST: 276 | DPRINTLN("5 :WL_CONNECTION_LOST"); 277 | break; 278 | case WL_DISCONNECTED: 279 | DPRINTLN("6 :WL_DISCONNECTED"); 280 | break; 281 | } 282 | DPRINTLN("let's sleep and retry later..."); 283 | WiFi.disconnect(); 284 | delay(50); 285 | ESP.deepSleep(sleep_duration * 48 * 1000000); // if no connexion, deepsleep some time, after will restart & retry (= reset) 286 | } 287 | } 288 | 289 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 290 | // setup_mqtt() : connexion to mosquitto server 291 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 292 | void setup_mqtt() 293 | { 294 | DPRINTLN("Entering mqtt setup"); 295 | if (mqtt.connected()) { 296 | DPRINTLN("Already connected to MQTT broker"); 297 | return; 298 | } 299 | DPRINTLN("Connecting to MQTT broker"); 300 | 301 | uint8_t retries = 3; 302 | DPRINT("Connecting..."); 303 | while (!client.connected()) { 304 | mqtt.disconnect(); 305 | digitalWrite(LED_BUILTIN, LOW); 306 | delay(200); 307 | digitalWrite(LED_BUILTIN, HIGH); 308 | delay(100); 309 | digitalWrite(LED_BUILTIN, LOW); 310 | delay(200); 311 | mqtt.connect(MQTT_CLIENT_NAME, BROKER_USERNAME, BROKER_KEY); 312 | DPRINT("MQTT connexion state is: "); 313 | switch (mqtt.state()) { 314 | case MQTT_CONNECTION_TIMEOUT: 315 | DPRINTLN("-4 : MQTT_CONNECTION_TIMEOUT - the server didn't respond within the keepalive time"); 316 | break; 317 | case MQTT_CONNECTION_LOST: 318 | DPRINTLN("-3 : MQTT_CONNECTION_LOST - the network connection was broken"); 319 | break; 320 | case MQTT_CONNECT_FAILED: 321 | DPRINTLN("-2 : MQTT_CONNECT_FAILED - the network connection failed"); 322 | break; 323 | case MQTT_DISCONNECTED: 324 | DPRINTLN("-1 : MQTT_DISCONNECTED - the client is disconnected cleanly"); 325 | break; 326 | case MQTT_CONNECTED: 327 | DPRINTLN("0 : MQTT_CONNECTED - the client is connected !! \\o/"); 328 | break; 329 | case MQTT_CONNECT_BAD_PROTOCOL: 330 | DPRINTLN("1 : MQTT_CONNECT_BAD_PROTOCOL - the server doesn't support the requested version of MQTT"); 331 | break; 332 | case MQTT_CONNECT_BAD_CLIENT_ID: 333 | DPRINTLN("2 : MQTT_CONNECT_BAD_CLIENT_ID - the server rejected the client identifier"); 334 | break; 335 | case MQTT_CONNECT_UNAVAILABLE: 336 | DPRINTLN("3 : MQTT_CONNECT_UNAVAILABLE - the server was unable to accept the connection"); 337 | break; 338 | case MQTT_CONNECT_BAD_CREDENTIALS: 339 | DPRINTLN("4 : MQTT_CONNECT_BAD_CREDENTIALS - the username/password were rejected"); 340 | break; 341 | case MQTT_CONNECT_UNAUTHORIZED: 342 | DPRINTLN("5 : MQTT_CONNECT_UNAUTHORIZED - the client was not authorized to connect"); 343 | break; 344 | } 345 | retries--; 346 | if (retries < 1) { 347 | WiFi.disconnect(); 348 | delay(50); 349 | ESP.deepSleep(sleep_duration * 48 * 1000000); // if no connexion, deepsleep for 20sec, after will restart (= reset) 350 | } 351 | } 352 | mqtt.publish(STATUS_TOPIC, "Online!"); 353 | mqtt.publish(VERSION_TOPIC, String(FW_VERSION).c_str()); 354 | mqtt.subscribe(CONFIG_TOPIC); 355 | } 356 | 357 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 358 | // check_OTA() : check for some available new firmware on server? 359 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 360 | void check_OTA() 361 | { 362 | // setup_wifi(); must be called before check_OTA(); 363 | DPRINT("Firmware:<"); 364 | DPRINT(FW_VERSION); 365 | DPRINTLN(">"); 366 | DPRINTLN("Check for OTA"); 367 | HTTPClient http; 368 | if (http.begin(client, fwVersionURL)) { 369 | DPRINTLN("http begin"); 370 | int httpCode = http.GET(); 371 | DPRINT("httpCode:"); 372 | DPRINTLN(httpCode); 373 | if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { 374 | String newFWVersion = http.getString(); 375 | DPRINT("Server FWVersion: "); 376 | DPRINT(newFWVersion); 377 | DPRINT(" / "); 378 | DPRINT("Current FWVersion: "); 379 | DPRINTLN(FW_VERSION); 380 | float newVersion = newFWVersion.toFloat(); 381 | if (newVersion > FW_VERSION) { 382 | DPRINTLN("start OTA !"); 383 | http.end(); 384 | delay(100); 385 | t_httpUpdate_return ret = ESPhttpUpdate.update(client, "http://192.168.1.184/fota/THrain/firmware.bin"); 386 | switch (ret) { 387 | case HTTP_UPDATE_FAILED: 388 | DPRINT("HTTP_UPDATE_FAILD Error"); 389 | DPRINT(ESPhttpUpdate.getLastError()); 390 | DPRINT(": "); 391 | DPRINTLN(ESPhttpUpdate.getLastErrorString().c_str()); 392 | //USE_SERIAL.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); 393 | break; 394 | case HTTP_UPDATE_NO_UPDATES: 395 | DPRINTLN("HTTP_UPDATE_NO_UPDATES"); 396 | break; 397 | case HTTP_UPDATE_OK: 398 | DPRINTLN("HTTP_UPDATE_OK"); 399 | break; 400 | } 401 | } else { 402 | DPRINTLN("no new version available"); 403 | } 404 | } 405 | } // end if http.begin 406 | DPRINTLN("End of OTA"); 407 | } // end check_OTA() 408 | 409 | void measure_temp_humi(byte index) 410 | { 411 | DPRINT("Temperature & Humidity measurement index: "); 412 | DPRINTLN(index); 413 | if (sht31.begin(0x44)) { 414 | DPRINT("SHT31 detected: "); 415 | temp_buffer = sht31.readTemperature(); 416 | DPRINT(temp_buffer); 417 | DPRINT("°C "); 418 | humi_buffer = sht31.readHumidity(); 419 | DPRINT(humi_buffer); 420 | DPRINTLN("%"); 421 | if (!isnan(temp_buffer)) { // check if 'is not a number' 422 | temp_array[index] = temp_buffer; 423 | } else { 424 | temp_array[index] = -100; 425 | } 426 | if (!isnan(temp_buffer)) { // check if 'is not a number' 427 | humi_array[index] = humi_buffer; 428 | } else { 429 | humi_array[index] = -100; 430 | } 431 | } 432 | /* 433 | if (am2315.begin()) { 434 | if (am2315.readTemperatureAndHumidity(temp_buffer, humi_buffer)) { 435 | temp_array[index] = temp_buffer; // if function return false, make sure the value are considered wrong and not emitted 436 | humi_array[index] = humi_buffer; 437 | } else { 438 | temp_array[index] = -100; // if function return false, make sure the value are considered wrong and not emitted 439 | humi_array[index] = -100; 440 | } 441 | top = millis(); 442 | DPRINTLN("am2315 detected"); 443 | DPRINT("T1:"); 444 | DPRINTLN(temp); 445 | DPRINT("H1:"); 446 | DPRINTLN(humi); 447 | } else { 448 | DPRINTLN("am2315 non detected"); 449 | }*/ 450 | } 451 | 452 | void process_temp_humi() 453 | { 454 | // TEMPERATURE 455 | DPRINT("Temp_array brut: "); 456 | DPRINT(temp_array[0]); 457 | DPRINT(" "); 458 | DPRINT(temp_array[1]); 459 | DPRINT(" "); 460 | DPRINTLN(temp_array[2]); 461 | float buffer = -1000; 462 | if (temp_array[0] > temp_array[1]) { 463 | buffer = temp_array[1]; 464 | temp_array[1] = temp_array[0]; 465 | temp_array[0] = buffer; 466 | } 467 | if (temp_array[1] > temp_array[2]) { 468 | buffer = temp_array[2]; 469 | temp_array[2] = temp_array[1]; 470 | temp_array[1] = buffer; 471 | if (temp_array[0] > temp_array[1]) { 472 | buffer = temp_array[1]; 473 | temp_array[1] = temp_array[0]; 474 | temp_array[0] = buffer; 475 | } 476 | } 477 | DPRINT("Temp_array trié: "); 478 | DPRINT(temp_array[0]); 479 | DPRINT(" "); 480 | DPRINT(temp_array[1]); 481 | DPRINT(" "); 482 | DPRINTLN(temp_array[2]); 483 | if ((temp_array[1] - temp_array[0]) < (temp_array[2] - temp_array[1])) { 484 | temp = (temp_array[0] + temp_array[1]) / 2; 485 | DPRINT("best samples are 0-1: ave temp = "); 486 | DPRINTLN(temp); 487 | } else { 488 | temp = (temp_array[1] + temp_array[2]) / 2; 489 | DPRINT("best samples are 1-2: ave temp = "); 490 | DPRINTLN(temp); 491 | } 492 | // HUMIDITY 493 | DPRINT("Humi_array brut: "); 494 | DPRINT(humi_array[0]); 495 | DPRINT(" "); 496 | DPRINT(humi_array[1]); 497 | DPRINT(" "); 498 | DPRINTLN(humi_array[2]); 499 | if (humi_array[0] > humi_array[1]) { 500 | buffer = humi_array[1]; 501 | humi_array[1] = humi_array[0]; 502 | humi_array[0] = buffer; 503 | } 504 | if (humi_array[1] > humi_array[2]) { 505 | buffer = humi_array[2]; 506 | humi_array[2] = humi_array[1]; 507 | humi_array[1] = buffer; 508 | if (humi_array[0] > humi_array[1]) { 509 | buffer = humi_array[1]; 510 | humi_array[1] = humi_array[0]; 511 | humi_array[0] = buffer; 512 | } 513 | } 514 | DPRINT("Temp_array trié: "); 515 | DPRINT(humi_array[0]); 516 | DPRINT(" "); 517 | DPRINT(humi_array[1]); 518 | DPRINT(" "); 519 | DPRINTLN(humi_array[2]); 520 | if ((humi_array[1] - humi_array[0]) < (humi_array[2] - humi_array[1])) { 521 | humi = (humi_array[0] + humi_array[1]) / 2; 522 | DPRINT("best samples are 0-1: ave temp = "); 523 | DPRINTLN(humi); 524 | } else { 525 | humi = (humi_array[1] + humi_array[2]) / 2; 526 | DPRINT("best samples are 1-2: ave humi = "); 527 | DPRINTLN(humi); 528 | } 529 | } 530 | 531 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 532 | // Start of main programm ! 533 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 534 | 535 | void setup() 536 | { 537 | #ifdef DEBUGMODE 538 | Serial.begin(115200); 539 | #endif 540 | ESP.eraseConfig(); // added 29/05/2022 following wifi connexion issues 541 | pinMode(LED_BUILTIN, OUTPUT); 542 | digitalWrite(LED_BUILTIN, HIGH); 543 | DPRINTLN(""); 544 | DPRINT("MAC address:"); 545 | DPRINTLN(WiFi.macAddress()); 546 | WiFi.mode(WIFI_STA); 547 | WiFi.config(ip, gateway, subnet); 548 | WiFi.disconnect(); 549 | 550 | measure_temp_humi(0); 551 | top = millis(); 552 | 553 | // check if POR occured ( @POR, register 0x00 of PCF8583 is set to 0 ) 554 | if (rtc.getRegister(0) == 0) { // POR occured, let's clear the SRAM of PCF8583 555 | for (uint8_t i = 0x10; i < 0x20; i++) { // PCF8583 SRAM start @0x10 to 0xFF, let's clear a few bytes only. 556 | rtc.setRegister(i, 0); 557 | } 558 | rtc.setMode(MODE_EVENT_COUNTER); // will set non zero value in register 0x00, so if no POR occured at next loop, register will not be cleared 559 | rtc.setCount(0); // reset rain counter 560 | } 561 | 562 | if (ina219_solar.begin()) { 563 | ina219_solar.powerSave(false); 564 | delay(5); 565 | solar_voltage = ina219_solar.getBusVoltage_V(); 566 | DPRINT("solar_voltage:"); 567 | DPRINTLN(solar_voltage); 568 | solar_current = (ina219_solar.getCurrent_mA()); 569 | DPRINT("solar_current:"); 570 | DPRINTLN(solar_current); 571 | solar_current = 0.001 * solar_current; // convert mA to Amps 572 | ina219_solar.powerSave(true); 573 | } 574 | if (ina219_battery.begin()){ 575 | ina219_battery.powerSave(false); 576 | delay(5); 577 | battery_voltage = ina219_battery.getBusVoltage_V(); 578 | DPRINT("battery_voltage:"); 579 | DPRINTLN(battery_voltage); 580 | ina219_battery.powerSave(true); 581 | } 582 | 583 | rain = 0.2 * float(rtc.getCount()); 584 | DPRINT("rain :"); 585 | DPRINTLN(rain); 586 | digitalWrite(LED_BUILTIN, LOW); 587 | 588 | /* if (uv.begin()) { // power ON veml6075 early to let it wakeup & warmup 589 | uv.powerOn(); 590 | }*/ 591 | DPRINT("Entering setup_wifi()...."); 592 | setup_wifi(); 593 | DPRINTLN("End of setup_wifi()"); 594 | 595 | // 2nd temp & humi sample: AM2315 needs to way 2 sec between samples 596 | if (millis() > (top + 2000)) { 597 | measure_temp_humi(1); 598 | } else { 599 | delay(top + 2000 - millis()); 600 | measure_temp_humi(1); 601 | } 602 | top = millis(); 603 | 604 | 605 | check_OTA(); // check for new firmware available on server, if check OTA with occur 606 | //OTA must be checked before connecting to MQTT (or after disconnecting MQTT) 607 | //measurements done, time to send them all ! 608 | setup_mqtt(); 609 | delay(15); 610 | 611 | // to receive mqtt message (retained) not used. 612 | /* uint8_t loops = 10; 613 | DPRINT("Check for mqtt reception:"); 614 | while ((received_msg == false) && (loops > 0)) { 615 | delay(30); 616 | DPRINT("."); 617 | mqtt.loop(); 618 | loops--; 619 | } 620 | DPRINT("MQTT Message arrived ["); 621 | DPRINT(received_topic); 622 | DPRINT("] "); 623 | for (unsigned int i = 0; i < received_length; i++) { 624 | DPRINT((char)received_payload[i]); 625 | } 626 | DPRINTLN(""); 627 | DPRINT("length:"); 628 | DPRINTLN(received_length); 629 | */ 630 | 631 | //setup_mqtt(); 632 | if (ina219_battery.begin()){ 633 | ina219_battery.powerSave(false); 634 | delay(5); 635 | battery_voltage2 = ina219_battery.getBusVoltage_V(); 636 | ina219_battery.powerSave(true); 637 | } 638 | 639 | 640 | if (millis() > (top + 2000)) { 641 | measure_temp_humi(2); 642 | } else { 643 | delay(top + 2000 - millis()); 644 | measure_temp_humi(2); 645 | } 646 | 647 | process_temp_humi(); 648 | 649 | mqtt.publish(STATUS_TOPIC, "Publishing!"); 650 | //Status_pub.publish("Publishing!"); 651 | delay(100); 652 | if (rain >= 0 && rain < 500) { 653 | setup_wifi(); 654 | setup_mqtt(); 655 | DPRINT("publishing rain:"); 656 | DPRINT(rain); 657 | DPRINTLN(" mm"); 658 | if (mqtt.publish(RAIN_TOPIC, String(rain).c_str())) { 659 | rtc.setCount(0); // reset rain counter only if it was able to send the date to the mqtt broker 660 | DPRINTLN("successfull publish of rain, reset counter"); 661 | }; 662 | delay(100); 663 | } else { 664 | mqtt.publish(STATUS_TOPIC, "Rain out of range"); 665 | delay(100); 666 | } 667 | if ((temp > -40) && (temp < 80)) { 668 | setup_wifi(); 669 | setup_mqtt(); 670 | DPRINT("publishing temp:"); 671 | DPRINT(temp); 672 | DPRINTLN(" deg"); 673 | if (mqtt.publish(TEMP_TOPIC, String(temp).c_str())){ 674 | DPRINTLN("successfull publish of TEMP"); 675 | } 676 | delay(100); 677 | } else { 678 | mqtt.publish(STATUS_TOPIC, "OutTemp out of range"); 679 | delay(100); 680 | } 681 | 682 | if (humi >= 0 && humi <= 100) { 683 | setup_wifi(); 684 | setup_mqtt(); 685 | DPRINT("publishing humi:"); 686 | DPRINT(humi); 687 | DPRINTLN(" %"); 688 | if (mqtt.publish(HUMI_TOPIC, String(humi).c_str())) { 689 | DPRINTLN("successfull publish of HUMI"); 690 | } 691 | delay(100); 692 | } else { 693 | mqtt.publish(STATUS_TOPIC, "Humi out of range"); 694 | delay(100); 695 | } 696 | 697 | if ((battery_voltage >= 0) && (battery_voltage < 20.0)) { 698 | setup_wifi(); 699 | setup_mqtt(); 700 | DPRINT("Vbat:"); 701 | DPRINT(battery_voltage); 702 | DPRINTLN(" V"); 703 | mqtt.publish(VBAT_TOPIC, String(battery_voltage).c_str()); 704 | delay(100); 705 | } else { 706 | mqtt.publish(STATUS_TOPIC, "Bat V out of range"); 707 | delay(100); 708 | } 709 | if ((solar_voltage >= 0) && (solar_voltage < 20.0)) { 710 | setup_wifi(); 711 | setup_mqtt(); 712 | DPRINT("Vsolar:"); 713 | DPRINT(solar_voltage); 714 | DPRINTLN(" V"); 715 | mqtt.publish(VSOLAR_TOPIC, String(solar_voltage).c_str()); 716 | delay(100); 717 | } else { 718 | mqtt.publish(STATUS_TOPIC, "Solar V out of range!"); 719 | delay(100); 720 | } 721 | if ((solar_current >= 0) && (solar_current < 1)) { 722 | setup_wifi(); 723 | setup_mqtt(); 724 | DPRINT("Isolar:"); 725 | DPRINT(solar_current); 726 | DPRINTLN(" Amps"); 727 | char solar_currant_str[5]; 728 | dtostrf(solar_current, 5, 3, solar_currant_str); 729 | mqtt.publish(ISOLAR_TOPIC, solar_currant_str); 730 | delay(100); 731 | } else { 732 | mqtt.publish(STATUS_TOPIC, "Solar I out of range!"); 733 | delay(100); 734 | } 735 | 736 | // measure UV index and publish 737 | /*if (uv.begin()) { 738 | UVindex = uv.index(); 739 | delay(2); 740 | UVindex = UVindex + uv.index(); 741 | UVindex = UVindex + uv.index(); 742 | UVindex = UVindex / 3; 743 | uv.shutdown(); 744 | } 745 | if ((UVindex > 0) && (UVindex < 25)) { 746 | setup_wifi(); 747 | setup_mqtt(); 748 | DPRINT("UV:"); 749 | DPRINTLN(UVindex); 750 | mqtt.publish(UV_TOPIC, String(UVindex).c_str()); 751 | }*/ 752 | // job is done, let's disconnect 753 | DPRINTLN("End of measurements & MQTT publish"); 754 | //Status_pub.publish("Offline!"); 755 | mqtt.publish(STATUS_TOPIC, "Offline!"); 756 | delay(100); 757 | mqtt.disconnect(); 758 | delay(25); 759 | WiFi.disconnect(); 760 | //Serial.println("Sleep"); 761 | DPRINTLN("Go to sleep"); 762 | // feed the TPL5010 watchdog: pulse 1ms on D6 763 | pinMode(D6, OUTPUT); 764 | digitalWrite(D6, HIGH); 765 | delay(1); 766 | digitalWrite(D6, LOW); 767 | // go to sleep 768 | delay(14); 769 | sleep_coef = 1; 770 | if ((battery_voltage < 3.6) && (battery_voltage2 < 3.6)) { 771 | sleep_coef = 3; 772 | } 773 | if ((battery_voltage < 3.4) && (battery_voltage2 < 3.4)) { 774 | sleep_coef = 10; //25min 775 | } 776 | if ((battery_voltage < 0) && (battery_voltage2 < 0)) { // mesurement error, issue suspected ? 777 | sleep_coef = 10; //25min 778 | } 779 | ESP.deepSleep((sleep_duration * sleep_coef * 1000000) - (1000 * millis())); 780 | } 781 | 782 | void loop() 783 | { 784 | // not used due to deepsleep 785 | } 786 | -------------------------------------------------------------------------------- /sensors/MQTT_wind/src/main.cpp: -------------------------------------------------------------------------------- 1 | // MQTT wind sensor for weewx 2 | const float FW_VERSION = 1.51; 3 | // history: 4 | // 1.50/51 : update credentials 5 | // 1.49 : update IP addresses 6 | // 1.48 : improve watchdog. 7 | 8 | 9 | const char* fwImageURL = "http://192.168.1.184/fota/Wind/firmware.bin"; // update with your link to the new firmware bin file. 10 | const char* fwVersionURL = "http://192.168.1.184/fota/Wind/firmware.version"; 11 | // update with your link to a text file with new version (just a single line with a number) 12 | // version is used to do OTA only one time, even if you let the firmware file available on the server. 13 | // flashing will occur only if a greater number is available in the "firmware.version" text file. 14 | // take care the number in text file is compared to "FW_VERSION" in the code => this const shall be incremented at each update. 15 | 16 | #include 17 | #include 18 | 19 | #include "PCF8583.h" // https://github.com/xoseperez/pcf8583 20 | #include // library for ADS1115 too 21 | #include 22 | #include 23 | #include // https://github.com/JChristensen/DS3232RTC 24 | #include 25 | #include 26 | #include 27 | #include 28 | // wifi & mqtt credentials 29 | #include "passwords.h" 30 | 31 | // pining 32 | #define enable_dir_sensor 32 // GPIO36 'A4' used to drive lowside switch on sensor GND. 33 | #define led 13 34 | 35 | // general timings 36 | #define RATIO_KMH_TO_HZ 4 37 | #define RATIO_MPS_TO_HZ 1.111112 // meter per second 38 | #define TSAMPLE 10 39 | // Define the sample rate: the ESP will wake every "TSAMPLE" second to measure speed & direction, ex every 15sec 40 | // TSAMPLE must be a subdivision of 60sec, ex 10,12,15, but not 8, 11, 13... 41 | #define RATIO 30 // define how many TSAMPLE period are needed to perform average, example 8 42 | const uint16_t Taverage = TSAMPLE * RATIO; // Define the average rate: the ESP will process average value "Taverage" second , example 8x15 = 120sec = 2min 43 | //#define STILL_ALIVE 15 // will emit every STILL_ALIVE min even if no wind, must be a multiple of Taverage 44 | 45 | // macro for debug 46 | //#define DEBUGMODE //If you comment this line, the DPRINT & DPRINTLN lines are defined as blank. 47 | #ifdef DEBUGMODE //Macros are usually in all capital letters. 48 | #define DPRINT(...) Serial.print(__VA_ARGS__) //DPRINT is a macro, debug print 49 | #define DPRINTLN(...) Serial.println(__VA_ARGS__) //DPRINTLN is a macro, debug print with new line 50 | #else 51 | #define DPRINT(...) //now defines a blank line 52 | #define DPRINTLN(...) //now defines a blank line 53 | #endif 54 | 55 | // WiFi connexion informations ////////////////////////////////////////////////////////////// 56 | 57 | IPAddress ip(192, 168, 5, 21); // hard coded IP address (make the wifi connexion faster (save battery), no need for DHCP) 58 | IPAddress gateway(192, 168, 5, 254); // set gateway to match your network 59 | IPAddress subnet(255, 255, 255, 0); // set subnet mask to match your network 60 | //IPAddress primaryDNS(212, 27, 40, 241); //optional free.fr 212.27.40.241 61 | //IPAddress secondaryDNS(91, 121, 161, 184); //optional ovh 91.121.161.184 62 | 63 | // MQTT Configuration ////////////////////////////////////////////////////////////////////// 64 | WiFiClient client; 65 | char received_topic[128]; 66 | byte received_payload[128]; 67 | unsigned int received_length; 68 | bool received_msg = false; 69 | void callback(char* topic, byte* payload, unsigned int length) 70 | { 71 | strcpy(received_topic, topic); 72 | memcpy(received_payload, payload, length); 73 | received_msg = true; 74 | received_length = length; 75 | } 76 | IPAddress broker(192, 168, 5, 183); 77 | PubSubClient mqtt(broker, 1883, callback, client); 78 | #define STATUS_TOPIC "wind/Status" 79 | #define VERSION_TOPIC "wind/Version" 80 | #define CONFIG_TOPIC "wind/Config" 81 | #define ERROR_TOPIC "wind/Error" 82 | #define WINDSPEED_TOPIC "weewx/windSpeed" 83 | #define WINDDIR_TOPIC "weewx/windDir" 84 | #define WINDGUST_TOPIC "weewx/windGust" 85 | #define WINDGUSTDIR_TOPIC "weewx/windGustDir" 86 | #define VSOLAR_TOPIC "weewx/WVsolar" 87 | #define ISOLAR_TOPIC "weewx/WIsolar" 88 | #define VBAT_TOPIC "weewx/WVbat" 89 | #define MQTT_CLIENT_NAME "ESP32_WIND" // make sure it's a unique identifier on your MQTT broker 90 | 91 | #define WINDSPEED_TTOPIC "tttweewx/windSpeed" 92 | #define WINDDIR_TTOPIC "tttweewx/windDir" 93 | #define WINDGUST_TTOPIC "tttweewx/windGust" 94 | #define WINDGUSTDIR_TTOPIC "tttweewx/windGustDir" 95 | #define WINDGUSTMAX_TTOPIC "tttweewx/windGustMax" 96 | #define WINDGUSTMAXDIR_TTOPIC "tttweewx/windGustMaxDir" 97 | 98 | #define ISOLAR_TTOPIC "tttweewx/Isolar" 99 | #define VDIR_TTOPIC "tttweewx/Vdir" 100 | #define VREF_TTOPIC "tttweewx/Vref" 101 | #define INDEX_TTOPIC "tttweewx/index" 102 | /* 103 | Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT,"ESP_wind", AIO_USERNAME, AIO_KEY); 104 | Adafruit_MQTT_Publish windSpeed_pub = Adafruit_MQTT_Publish(&mqtt, "tweewx/windSpeed", 1); 105 | Adafruit_MQTT_Publish windDir_pub = Adafruit_MQTT_Publish(&mqtt, "tweewx/windDir", 1); 106 | Adafruit_MQTT_Publish windGust_pub = Adafruit_MQTT_Publish(&mqtt, "tweewx/windGust", 1); 107 | Adafruit_MQTT_Publish windGustDir_pub = Adafruit_MQTT_Publish(&mqtt, "tweewx/windGustDir", 1); 108 | Adafruit_MQTT_Publish Status_pub = Adafruit_MQTT_Publish(&mqtt, "wind/Status", 1); 109 | Adafruit_MQTT_Publish Error_pub = Adafruit_MQTT_Publish(&mqtt, "wind/Errorcode", 1); 110 | 111 | Adafruit_MQTT_Publish Version_pub = Adafruit_MQTT_Publish(&mqtt, "wind/Version", 1); 112 | Adafruit_MQTT_Publish Debug_pub = Adafruit_MQTT_Publish(&mqtt, "wind/Debug", 1); 113 | Adafruit_MQTT_Publish Vsolar_pub = Adafruit_MQTT_Publish(&mqtt, "weewx/WVsolar", 1); 114 | Adafruit_MQTT_Publish Isolar_pub = Adafruit_MQTT_Publish(&mqtt, "weewx/WIsolar", 1); 115 | Adafruit_MQTT_Publish Vbat_pub = Adafruit_MQTT_Publish(&mqtt, "weewx/WVbat", 1); 116 | */ 117 | 118 | /********************** I2C Components *************************************/ 119 | // RTC DS3232 120 | DS3232RTC RTC(false); // DS3232M I2C address = D0h. 121 | // PCF8583 RTC used as even counter (will count the turns of the wind speed sensor) 122 | PCF8583 counter(0xA0); // PCF8583 RTC used as event counter // I2C address A0 123 | // ADS1115 I2C ADC 124 | Adafruit_ADS1115 ads; // 0x48-0x4B, selectable with jumpers 125 | // INA219 current and voltage sensors: to monitor solar panel & battery 126 | Adafruit_INA219 ina219_battery(0x40); // I2C address 0x40 !default is 0x40 127 | Adafruit_INA219 ina219_solar(0x41); // I2C address 0x41 !default is 0x40 128 | 129 | /********************************** variables *********************************/ 130 | uint8_t All_is_fine; 131 | uint16_t Vref = 0; // measure reference voltage of the wind dir sensor 132 | uint16_t Vdir = 0; // measure wind dir sensor 133 | 134 | // RTC memory (not lost during deepsleep 135 | RTC_DATA_ATTR uint16_t Table_pulsecount[RATIO]; 136 | RTC_DATA_ATTR uint16_t Table_windDir[RATIO]; 137 | RTC_DATA_ATTR uint8_t Tindex; 138 | RTC_DATA_ATTR uint8_t ready; // data are valids when it ready 0...used to avoid sending during 1st minutes when we don't have valid average value 139 | RTC_DATA_ATTR uint16_t pulsecount; 140 | RTC_DATA_ATTR uint16_t prev_pulsecount; 141 | RTC_DATA_ATTR int windGustMaxDir; 142 | RTC_DATA_ATTR float windGustMax; 143 | 144 | uint16_t total_pulsecount; 145 | //uint16_t total_dircount; 146 | long total_dircount; 147 | uint8_t nextwake; 148 | time_t t; 149 | // values ready to emit 150 | int windGustDir = -1; // -1 is just to help detecting an untouched value 151 | int windDir = -1; 152 | float windGust = -1; 153 | float windSpeed = -1; 154 | 155 | float solar_voltage = -1; 156 | float solar_current = -1; 157 | float battery_voltage = -1; 158 | 159 | // Declaration of function 160 | void setup_wifi(); 161 | void setup_mqtt(); 162 | void report_wake_source(); 163 | void check_OTA(); 164 | void wifi_scan(); 165 | 166 | // watchdog timer in case of unexpected crash ? 167 | #define WDT_TIMEOUT 30 168 | 169 | /******************************************************************************/ 170 | /* BEGINNING OF PROGRAMM */ 171 | /******************************************************************************/ 172 | 173 | void setup() 174 | { 175 | 176 | #ifdef DEBUGMODE 177 | Serial.begin(115200); 178 | DPRINT(millis()); 179 | //delay(2000); 180 | DPRINTLN(" HELLO WORLD!"); 181 | //DPRINT("MAC Address: "); 182 | //DPRINTLN(WiFi.macAddress()); 183 | //DPRINTLN("Init watchdog"); 184 | #endif 185 | 186 | esp_task_wdt_init(WDT_TIMEOUT, true); //enable panic so ESP32 restarts 187 | esp_task_wdt_add(NULL); //add current thread to WDT watch 188 | esp_task_wdt_delete(NULL); 189 | 190 | #ifdef DEBUGMODE 191 | //DPRINTLN("Watchdog init done!"); 192 | #endif 193 | 194 | WiFi.mode(WIFI_STA); 195 | if (!WiFi.config(ip, gateway, subnet)) { 196 | DPRINTLN("STA Failed to configure"); 197 | }; 198 | WiFi.disconnect(); 199 | RTC.begin(); // init of RTC DS3232M 200 | setSyncProvider(RTC.get); // the function to get the time from the RTC 201 | t = now(); // save the wakeup time for later processing. 202 | prev_pulsecount = pulsecount; 203 | pulsecount = counter.getCount(); // record pulsecount as early as possible after wake to be as accurate as possible on timings. 204 | pinMode(enable_dir_sensor, OUTPUT); 205 | pinMode(led, OUTPUT); 206 | digitalWrite(led, HIGH); 207 | ina219_solar.begin(); 208 | ina219_battery.begin(); 209 | ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV 0.125mV 210 | digitalWrite(led, LOW); // short blink at startup 211 | // first, let's check if everything is All_is_fine 212 | // a/ check if the wakeup source is the DS3232M, in case the wake is due to ESP internal timer, something is wrong with RTC, let's try to reinit 213 | // b/ check if oscStopped flag from DS3232M is ok, if not, we cannot rely on its timing, let's try to reinit too 214 | // c/ check if the PCF8583 event counter has seen a Power ON reset); 215 | if ((esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_EXT0)) { // check if the ESP was wakeup by it's backup timer, normal wake is due to DS3232M 216 | #ifdef DEBUGMODE 217 | report_wake_source(); 218 | #endif 219 | All_is_fine = All_is_fine + 1; 220 | } 221 | if (RTC.oscStopped(true)) { // OSC stopped for some reason (could be power on) 222 | DPRINTLN("OSC stop flag occured on DS3232M"); 223 | All_is_fine = All_is_fine + 2; 224 | } 225 | if (counter.getRegister(0) == 0) { // POR occured on PCF8583 226 | All_is_fine = All_is_fine + 4; 227 | } 228 | if (All_is_fine == 0) { // skipped if the ESP doest not wake in expected situation. 229 | 230 | // every 15sec task //////////////////////////////////////// 15sec task /// 231 | // enable wind dir sensor 232 | if ((pulsecount - prev_pulsecount) > 0) { // perform direction measurement if there are some wind only (to save some battery) 233 | digitalWrite(enable_dir_sensor, HIGH); 234 | delay(10); // turn on delay of the sensor,value to be checked 235 | Vref = ads.readADC_Differential_0_1(); 236 | Vdir = ads.readADC_Differential_2_3(); 237 | digitalWrite(enable_dir_sensor, LOW); 238 | DPRINT("Vref:"); 239 | DPRINT(Vref); 240 | DPRINT(" Vdir:"); 241 | DPRINTLN(Vdir); 242 | if (Vdir <= Vref && Vref > 17000 && Vref < 21000 && Vdir >= 0) { // calculate wind direction 243 | if (Vdir > (Vref - 256)) { 244 | windGustDir = 360; // just in case the calibration is wrong and Vdir gets higher than corrected reference. 245 | } else { 246 | windGustDir = int(360 * (float(Vdir) / float(Vref - 256))); 247 | //apparently, the sensor output cannot reach supply voltage, remove 256 seems make possible that the output reach supply = 360° 248 | } 249 | } 250 | } else { // If no wind, keep same dir as previous: we need a value to calculate average windDir 251 | if (Tindex == 0) { 252 | windGustDir = int(Table_windDir[RATIO - 1]); 253 | } else { 254 | windGustDir = int(Table_windDir[Tindex - 1]); 255 | } 256 | } 257 | 258 | // Store measured wind / winddir 259 | Table_pulsecount[Tindex] = pulsecount - prev_pulsecount; 260 | Table_windDir[Tindex] = windGustDir; 261 | // calculate average wind speed & dir 262 | total_pulsecount = 0; 263 | total_dircount = 0; 264 | for (byte i = 0; i < RATIO; i = i + 1) { 265 | total_pulsecount = total_pulsecount + Table_pulsecount[i]; 266 | total_dircount = total_dircount + Table_windDir[i]; 267 | } 268 | windGust = RATIO_MPS_TO_HZ * float(pulsecount - prev_pulsecount) / TSAMPLE; 269 | windSpeed = RATIO_MPS_TO_HZ * float(total_pulsecount) / Taverage; 270 | windDir = int(total_dircount / RATIO); 271 | if (windGustMax < windGust) { 272 | windGustMax = windGust; 273 | windGustMaxDir = windGustDir; 274 | } 275 | 276 | //************************************************ for debug ***************************************** 277 | /* setup_wifi(); 278 | setup_mqtt(); 279 | delay(10); 280 | mqtt.publish(STATUS_TOPIC, "debug!"); 281 | delay(10); 282 | mqtt.publish(INDEX_TTOPIC, String(Tindex).c_str()); 283 | delay(10); 284 | 285 | //mqtt.publish(WINDGUSTDIR_TTOPIC, String(windGustDir).c_str()); 286 | //delay(10); 287 | mqtt.publish(WINDGUST_TTOPIC, String(windGust).c_str()); 288 | delay(10); 289 | //mqtt.publish(WINDGUSTMAXDIR_TTOPIC, String(windGustMaxDir).c_str()); 290 | //delay(10); 291 | mqtt.publish(WINDGUSTMAX_TTOPIC, String(windGustMax).c_str()); 292 | delay(10); 293 | //mqtt.publish(VDIR_TTOPIC, String(Vdir).c_str()); 294 | //delay(10); 295 | //mqtt.publish(VREF_TTOPIC, String(Vref).c_str()); 296 | //delay(10); 297 | mqtt.publish(STATUS_TOPIC, "enddebug!"); 298 | delay(10); 299 | if (mqtt.connected()) { 300 | mqtt.publish(STATUS_TOPIC, "Offline!"); 301 | mqtt.disconnect(); 302 | } 303 | delay(10); 304 | if (WiFi.status() == WL_CONNECTED) { 305 | WiFi.disconnect(); 306 | } 307 | */ 308 | //************************************************ end for debug ***************************************** 309 | 310 | #ifdef DEBUGMODE 311 | DPRINT("Tindex:"); 312 | DPRINT(Tindex); 313 | DPRINT(" ready:"); 314 | DPRINT(ready); 315 | DPRINT(" pulsecount:"); 316 | DPRINT(pulsecount); 317 | DPRINT(" prev_pulsecount:"); 318 | DPRINT(prev_pulsecount); 319 | DPRINT(" windGustDir="); 320 | DPRINTLN(windGustDir); 321 | DPRINT("Table_pulsecount:"); 322 | for (byte i = 0; i < RATIO; i = i + 1) { 323 | DPRINT(" ["); 324 | DPRINT(i); 325 | DPRINT("]={"); 326 | DPRINT(Table_pulsecount[i]); 327 | DPRINT("}"); 328 | } 329 | DPRINTLN(" #"); 330 | DPRINT("Table_windDir: "); 331 | for (byte i = 0; i < RATIO; i = i + 1) { 332 | DPRINT(" ["); 333 | DPRINT(i); 334 | DPRINT("]={"); 335 | DPRINT(Table_windDir[i]); 336 | DPRINT("}"); 337 | } 338 | DPRINTLN(" #"); 339 | DPRINT("windSpeed:"); 340 | DPRINT(windSpeed); 341 | DPRINT("km/h / windDir:"); 342 | DPRINT(windDir); 343 | DPRINT("° "); 344 | DPRINT("windGust:"); 345 | DPRINT(windGust); 346 | DPRINT("km/h / windGustDir:"); 347 | DPRINT(windGustDir); 348 | DPRINTLN("° "); 349 | #endif 350 | 351 | if (ready > 0) { 352 | ready--; 353 | } 354 | 355 | if (ready == 0) { // does not allow emit a while after power on (until we have enough sample for average measurements) 356 | if (Tindex == 0) { 357 | if ((windSpeed > 1) && (windSpeed < 400) && (windDir >= 0) && (windDir <= 360)) { 358 | // let's emit every 2min, if valid data & if speed > 1km/h (let's save battery if below 1km/h) 359 | DPRINTLN("Let's emit average wind ************************************************"); 360 | setup_wifi(); 361 | setup_mqtt(); 362 | delay(10); 363 | mqtt.publish(WINDSPEED_TOPIC, String(windSpeed).c_str()); 364 | delay(10); 365 | mqtt.publish(WINDDIR_TOPIC, String(windDir).c_str()); 366 | } 367 | // if ((windGust > (windSpeed + 5)) && (windGust < 400) && (windGustDir >= 0) && (windGustDir <= 360) && (windSpeed > 1)) { 368 | // little filter here too,emit gust only if high enought 369 | if ((windSpeed > 1) && (windGustMax > windSpeed) && (windGustMax < 400) && (windGustMaxDir >= 0) && (windGustMaxDir <= 360)) { 370 | // little filter here too,emit gust only if high enought 371 | DPRINTLN("Let's emit Gust *********************************************************"); 372 | setup_wifi(); 373 | setup_mqtt(); 374 | /*if (Tindex == 0) { 375 | delay(10); 376 | }*/ 377 | delay(10); 378 | mqtt.publish(WINDGUST_TOPIC, String(windGustMax).c_str()); 379 | delay(10); 380 | mqtt.publish(WINDGUSTDIR_TOPIC, String(windGustMaxDir).c_str()); 381 | } 382 | //if (((minute() % STILL_ALIVE) == 0)) { 383 | // let's emit a few times even if there is no wind (so we know the sensor is alive), we can use it to send also battery & solar situation 384 | DPRINTLN("Let's emit STILL_ALIVE min message"); 385 | solar_voltage = ina219_solar.getBusVoltage_V(); 386 | DPRINT("solar_voltage:"); 387 | DPRINTLN(solar_voltage); 388 | solar_current = ina219_solar.getCurrent_mA(); 389 | DPRINT("solar_current:"); 390 | DPRINTLN(solar_current); 391 | solar_current = 0.001 * solar_current; // convert to Amps instead of mA 392 | battery_voltage = ina219_battery.getBusVoltage_V(); 393 | DPRINT("battery_voltage:"); 394 | DPRINTLN(battery_voltage); 395 | delay(10); 396 | if ((solar_voltage >= 0) && (solar_voltage < 20.0)) { 397 | setup_wifi(); 398 | setup_mqtt(); 399 | delay(10); 400 | mqtt.publish(VSOLAR_TOPIC, String(solar_voltage).c_str()); 401 | } 402 | if ((solar_current >= -0.10) && (solar_current < 2000.0)) { 403 | setup_wifi(); 404 | setup_mqtt(); 405 | if (solar_current < 0) { 406 | solar_current = 0; 407 | } 408 | char solar_currant_str[5]; 409 | dtostrf(solar_current, 5, 3, solar_currant_str); 410 | delay(10); 411 | mqtt.publish(ISOLAR_TOPIC, solar_currant_str); 412 | //mqtt.publish(ISOLAR_TOPIC, String(solar_current).c_str()); 413 | } 414 | if ((battery_voltage >= 0) && (battery_voltage < 20.0)) { 415 | setup_wifi(); 416 | setup_mqtt(); 417 | delay(10); 418 | mqtt.publish(VBAT_TOPIC, String(battery_voltage).c_str()); 419 | } 420 | 421 | if ((windSpeed >= 0) && (windSpeed <= 1) && (windDir >= 0) && (windDir <= 360)) { 422 | setup_wifi(); 423 | setup_mqtt(); 424 | delay(10); 425 | mqtt.publish(WINDSPEED_TOPIC, String(windSpeed).c_str()); 426 | delay(10); 427 | mqtt.publish(WINDDIR_TOPIC, String(windDir).c_str()); 428 | if ((windGustMax >= windSpeed) && (windGustMax >= 0) && (windGustMax <= 10) && (windGustMaxDir >= 0) && (windGustMaxDir <= 360)) { 429 | delay(10); 430 | mqtt.publish(WINDGUST_TOPIC, String(windGustMax).c_str()); 431 | delay(10); 432 | mqtt.publish(WINDGUSTDIR_TOPIC, String(windGustMaxDir).c_str()); 433 | } 434 | // } // end if (((minute() % STILL_ALIVE) 435 | } 436 | windGustMax = -1; 437 | windGustMaxDir = -1; 438 | } // end if (Tindex == 0) 439 | if (mqtt.connected()) { 440 | //setup_wifi(); 441 | //setup_mqtt(); 442 | delay(10); 443 | mqtt.publish(STATUS_TOPIC, "Offline!"); 444 | delay(10); 445 | mqtt.publish(STATUS_TOPIC, "Check_OTA!"); 446 | mqtt.disconnect(); 447 | delay(20); 448 | check_OTA(); 449 | } 450 | } //if (ready == 0) 451 | Tindex++; 452 | if (Tindex == RATIO) { 453 | Tindex = 0; 454 | prev_pulsecount = counter.getCount(); // check counter in case a few pulse was counted since wakeup 455 | DPRINT("getCount()="); 456 | DPRINT(prev_pulsecount - pulsecount); 457 | counter.setCount(prev_pulsecount - pulsecount); 458 | // reset counter, initiale with the pulses that occured since wake (should be small but let's be accurate) 459 | DPRINT(" setCount()="); 460 | DPRINTLN(counter.getCount()); 461 | prev_pulsecount = 0; 462 | pulsecount = 0; 463 | } 464 | 465 | } // if All_is_fine == 0 466 | 467 | /*********** something goes wrong, let's clean up and re-init *****************/ 468 | if (All_is_fine > 0) { 469 | DPRINTLN("*************************************************************"); 470 | DPRINT(" Errors or power on occured, code:"); 471 | DPRINTLN(All_is_fine); 472 | DPRINTLN("RE-INIT on going !"); 473 | digitalWrite(led, HIGH); 474 | setTime(00, 00, 00, 13, 4, 2019); // set time to 0 on DS3232M, we don't care about day or hours, only minutes & seconds 475 | RTC.set(now()); 476 | counter.setMode(MODE_EVENT_COUNTER); // will set non zero value in register 0x00, so if no POR occured at next loop, register will not be cleared 477 | counter.setCount(0); // reset anemometer pulse counter 478 | RTC.squareWave(SQWAVE_NONE); 479 | 480 | // clear variables 481 | for (byte i = 0; i < RATIO; i = i + 1) { 482 | Table_pulsecount[i] = 0; 483 | Table_windDir[i] = 0; 484 | } 485 | t = now(); 486 | Tindex = 0; 487 | ready = RATIO; 488 | pulsecount = 0; 489 | prev_pulsecount = 0; 490 | total_pulsecount = 0; 491 | windGustDir = -1; 492 | windGustMaxDir = -1; 493 | windGust = -1; 494 | windGustMax = -1; 495 | windSpeed = -1; 496 | windDir = -1; 497 | nextwake = TSAMPLE; 498 | 499 | DPRINTLN("RE-INIT done !"); 500 | delay(1000); 501 | setup_wifi(); 502 | delay(1000); 503 | check_OTA(); 504 | DPRINTLN("*************************************************************"); 505 | setup_mqtt(); 506 | mqtt.publish(STATUS_TOPIC, "Re-init done!"); 507 | mqtt.publish(ERROR_TOPIC, String(All_is_fine).c_str()); 508 | //delay(800); 509 | digitalWrite(led, LOW); 510 | delay(100); 511 | digitalWrite(led, HIGH); 512 | delay(100); 513 | digitalWrite(led, LOW); 514 | delay(100); 515 | digitalWrite(led, HIGH); 516 | delay(100); 517 | digitalWrite(led, LOW); 518 | All_is_fine = 0; 519 | DPRINTLN("Let's emit Voltage/Current at init"); 520 | solar_voltage = ina219_solar.getBusVoltage_V(); 521 | DPRINT("solar_voltage:"); 522 | DPRINTLN(solar_voltage); 523 | solar_current = ina219_solar.getCurrent_mA(); 524 | DPRINT("solar_current:"); 525 | DPRINTLN(solar_current); 526 | solar_current = 0.001 * solar_current; // convert to Amps instead of mA 527 | battery_voltage = ina219_battery.getBusVoltage_V(); 528 | DPRINT("battery_voltage:"); 529 | DPRINTLN(battery_voltage); 530 | if ((solar_voltage >= 0) && (solar_voltage < 20.0)) { 531 | setup_wifi(); 532 | setup_mqtt(); 533 | mqtt.publish(VSOLAR_TOPIC, String(solar_voltage).c_str()); 534 | delay(10); 535 | } 536 | if ((solar_current >= 0) && (solar_current < 2000.0)) { 537 | setup_wifi(); 538 | setup_mqtt(); 539 | char solar_currant_str[5]; 540 | dtostrf(solar_current, 5, 3, solar_currant_str); 541 | mqtt.publish(ISOLAR_TOPIC, solar_currant_str); 542 | //mqtt.publish(ISOLAR_TOPIC, String(solar_current).c_str()); 543 | delay(10); 544 | } 545 | if ((battery_voltage >= 0) && (battery_voltage < 20.0)) { 546 | setup_wifi(); 547 | setup_mqtt(); 548 | mqtt.publish(VBAT_TOPIC, String(battery_voltage).c_str()); 549 | delay(10); 550 | } 551 | } // end if All_is_fine > 0 552 | 553 | // set alarm every Tsample seconds 554 | if ((second(now())) < (second(t) + TSAMPLE)) { // check if previous processing were not too long. if yes, something goes wrong, let's go for a reset. 555 | nextwake = TSAMPLE * (second(t) / TSAMPLE) + TSAMPLE; 556 | if (nextwake == 60) 557 | nextwake = 0; 558 | RTC.setAlarm(ALM1_MATCH_SECONDS, nextwake, 0, 0, 1); 559 | RTC.alarm(ALARM_1); 560 | RTC.alarmInterrupt(ALARM_1, true); 561 | } else { 562 | All_is_fine = 13; // will perform a reset of all stats at next boot. 563 | } 564 | #ifdef DEBUGMODE 565 | if (timeStatus() != timeSet) { 566 | DPRINTLN("Unable to sync with the RTC"); 567 | } else { 568 | DPRINT("time: "); 569 | if (hour() < 10) 570 | DPRINT("0"); 571 | DPRINT(hour()); 572 | DPRINT(":"); 573 | if (minute() < 10) 574 | DPRINT("0"); 575 | DPRINT(minute()); 576 | DPRINT(":"); 577 | if (second() < 10) 578 | DPRINT("0"); 579 | DPRINT(second()); 580 | DPRINT(" nextwake at:"); 581 | if (hour() < 10) 582 | DPRINT("0"); 583 | DPRINT(hour()); 584 | DPRINT(":"); 585 | if (minute() < 10) 586 | DPRINT("0"); 587 | DPRINT(minute()); 588 | DPRINT(":"); 589 | if (nextwake < 10) 590 | DPRINT("0"); 591 | DPRINT(nextwake); 592 | } 593 | #endif 594 | if (mqtt.connected()) { 595 | mqtt.publish(STATUS_TOPIC, "Offline!"); 596 | mqtt.disconnect(); 597 | } 598 | if (WiFi.status() == WL_CONNECTED) { 599 | WiFi.disconnect(); 600 | } 601 | esp_sleep_enable_timer_wakeup((TSAMPLE + 5) * 1000000); 602 | // backup if RTC had a poweron reset or whatever and loose it's config. then internal timer will wake us in 30sec 603 | esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 0); 604 | //1 = Low to High, 0 = High to low DS3232M will wake us if "/INT" is connected to GPIO33 (dont forget it's an opendrain, pullup is mandatory) 605 | DPRINT(" Going to sleep now "); 606 | DPRINTLN(millis()); 607 | #ifdef DEBUGMODE 608 | Serial.flush(); 609 | #endif 610 | 611 | esp_task_wdt_delete(NULL); // detach watchdog 612 | esp_task_wdt_deinit(); // disable watchdog 613 | esp_deep_sleep_start(); // go to deep sleep ! 614 | } 615 | 616 | /******************************************************************************/ 617 | /******************************************************************************/ 618 | /******************************************************************************/ 619 | void loop() 620 | { 621 | } 622 | 623 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 624 | // setup_wifi() : connexion to wifi hotspot 625 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 626 | void wifi_connect(const char* ssid, const char* password, uint8_t timeout) 627 | { 628 | uint8_t timeout_wifi = timeout; 629 | //WiFiMulti.addAP(ssid, password); 630 | WiFi.begin(ssid, password); 631 | while ((WiFi.status() != WL_CONNECTED) && (timeout_wifi > 0)) { 632 | 633 | delay(1000); 634 | DPRINT("."); 635 | WiFi.begin(ssid, password); 636 | //WiFiMulti.addAP(ssid, password); 637 | timeout_wifi--; 638 | } 639 | } 640 | //Connexion au réseau WiFi 641 | void setup_wifi() 642 | { 643 | //wifi_scan(); 644 | //config static IP 645 | WiFi.mode(WIFI_STA); 646 | WiFi.config(ip, gateway, subnet); 647 | #ifdef DEBUGMODE //Macros are usually in all capital letters. 648 | DPRINT("Wifi status: "); 649 | switch (WiFi.status()) { 650 | case WL_NO_SHIELD: 651 | DPRINTLN("255 :WL_NO_SHIELD"); 652 | break; 653 | case WL_IDLE_STATUS: 654 | DPRINTLN("0 :WL_IDLE_STATUS"); 655 | break; 656 | case WL_NO_SSID_AVAIL: 657 | DPRINTLN("1 :WL_NO_SSID_AVAIL"); 658 | break; 659 | case WL_SCAN_COMPLETED: 660 | DPRINTLN("2 :WL_SCAN_COMPLETED"); 661 | break; 662 | case WL_CONNECTED: 663 | DPRINTLN("3 :WL_CONNECTED"); 664 | break; 665 | case WL_CONNECT_FAILED: 666 | DPRINTLN("4 :WL_CONNECT_FAILED"); 667 | break; 668 | case WL_CONNECTION_LOST: 669 | DPRINTLN("5 :WL_CONNECTION_LOST"); 670 | break; 671 | case WL_DISCONNECTED: 672 | DPRINTLN("6 :WL_DISCONNECTED"); 673 | break; 674 | } 675 | DPRINT(" "); 676 | #endif 677 | if (WiFi.status() == WL_CONNECTED) { 678 | DPRINTLN("Wifi already connected"); 679 | return; 680 | } 681 | 682 | // try 1st SSID (if you have 2 hotspots) 683 | DPRINT("Try to connect 1st wifi:"); 684 | wifi_connect(ssid1, password1, 10); 685 | // if connexion is successful, let's go to next, no need for SSID2 686 | if (WiFi.status() == WL_CONNECTED) { 687 | DPRINTLN("Wifi ssid1 connected"); 688 | return; 689 | } else { 690 | #ifdef DEBUGMODE //Macros are usually in all capital letters. 691 | DPRINTLN("Wifi ssid1 FAILED"); 692 | switch (WiFi.status()) { 693 | case WL_NO_SHIELD: 694 | DPRINTLN("255 :WL_NO_SHIELD"); 695 | break; 696 | case WL_IDLE_STATUS: 697 | DPRINTLN("0 :WL_IDLE_STATUS"); 698 | break; 699 | case WL_NO_SSID_AVAIL: 700 | DPRINTLN("1 :WL_NO_SSID_AVAIL"); 701 | break; 702 | case WL_SCAN_COMPLETED: 703 | DPRINTLN("2 :WL_SCAN_COMPLETED"); 704 | break; 705 | case WL_CONNECTED: 706 | DPRINTLN("3 :WL_CONNECTED"); 707 | break; 708 | case WL_CONNECT_FAILED: 709 | DPRINTLN("4 :WL_CONNECT_FAILED"); 710 | break; 711 | case WL_CONNECTION_LOST: 712 | DPRINTLN("5 :WL_CONNECTION_LOST"); 713 | break; 714 | case WL_DISCONNECTED: 715 | DPRINTLN("6 :WL_DISCONNECTED"); 716 | break; 717 | } 718 | #endif 719 | DPRINTLN("Let's try connecting 2nd wifi SSID"); 720 | } 721 | // let's try SSID2 (if ssid1 did not worked) 722 | wifi_connect(ssid2, password2, 10); 723 | if (WiFi.status() == WL_CONNECTED) { 724 | DPRINTLN("Wifi ssid2 connected"); 725 | return; // if connexion is successful, let's go to next, no need for SSID2 726 | } else { 727 | #ifdef DEBUGMODE //Macros are usually in all capital letters. 728 | DPRINTLN("Wifi ssid2 FAILED"); 729 | switch (WiFi.status()) { 730 | case WL_NO_SHIELD: 731 | DPRINTLN("255 :WL_NO_SHIELD"); 732 | break; 733 | case WL_IDLE_STATUS: 734 | DPRINTLN("0 :WL_IDLE_STATUS"); 735 | break; 736 | case WL_NO_SSID_AVAIL: 737 | DPRINTLN("1 :WL_NO_SSID_AVAIL"); 738 | break; 739 | case WL_SCAN_COMPLETED: 740 | DPRINTLN("2 :WL_SCAN_COMPLETED"); 741 | break; 742 | case WL_CONNECTED: 743 | DPRINTLN("3 :WL_CONNECTED"); 744 | break; 745 | case WL_CONNECT_FAILED: 746 | DPRINTLN("4 :WL_CONNECT_FAILED"); 747 | break; 748 | case WL_CONNECTION_LOST: 749 | DPRINTLN("5 :WL_CONNECTION_LOST"); 750 | break; 751 | case WL_DISCONNECTED: 752 | DPRINTLN("6 :WL_DISCONNECTED"); 753 | break; 754 | } 755 | #endif 756 | DPRINTLN("Failed to connect wifi, let's sleep and retry later..."); 757 | WiFi.disconnect(); 758 | #ifdef DEBUGMODE 759 | Serial.flush(); 760 | #endif 761 | delay(50); 762 | esp_sleep_enable_timer_wakeup(300 * 1000000); //300 seconds 763 | esp_task_wdt_delete(NULL); // detach watchdog 764 | esp_task_wdt_deinit(); // disable watchdog 765 | esp_deep_sleep_start(); 766 | } 767 | } 768 | 769 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 770 | // setup_mqtt() : connexion to mosquitto server 771 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 772 | void setup_mqtt() 773 | { 774 | DPRINTLN("Entering mqtt setup"); 775 | if (mqtt.connected()) { 776 | DPRINTLN("Already connected to MQTT broker"); 777 | return; 778 | } 779 | DPRINTLN("Connecting to MQTT broker"); 780 | 781 | uint8_t retries = 3; 782 | DPRINT("Connecting..."); 783 | while (!client.connected()) { 784 | mqtt.disconnect(); 785 | delay(500); 786 | mqtt.connect(MQTT_CLIENT_NAME, BROKER_USERNAME, BROKER_KEY); 787 | #ifdef DEBUGMODE //Macros are usually in all capital letters. 788 | DPRINT("MQTT connexion state is: "); 789 | switch (mqtt.state()) { 790 | case MQTT_CONNECTION_TIMEOUT: 791 | DPRINTLN("-4 : MQTT_CONNECTION_TIMEOUT - the server didn't respond within the keepalive time"); 792 | break; 793 | case MQTT_CONNECTION_LOST: 794 | DPRINTLN("-3 : MQTT_CONNECTION_LOST - the network connection was broken"); 795 | break; 796 | case MQTT_CONNECT_FAILED: 797 | DPRINTLN("-2 : MQTT_CONNECT_FAILED - the network connection failed"); 798 | break; 799 | case MQTT_DISCONNECTED: 800 | DPRINTLN("-1 : MQTT_DISCONNECTED - the client is disconnected cleanly"); 801 | break; 802 | case MQTT_CONNECTED: 803 | DPRINTLN("0 : MQTT_CONNECTED - the client is connected !! \\o/"); 804 | break; 805 | case MQTT_CONNECT_BAD_PROTOCOL: 806 | DPRINTLN("1 : MQTT_CONNECT_BAD_PROTOCOL - the server doesn't support the requested version of MQTT"); 807 | break; 808 | case MQTT_CONNECT_BAD_CLIENT_ID: 809 | DPRINTLN("2 : MQTT_CONNECT_BAD_CLIENT_ID - the server rejected the client identifier"); 810 | break; 811 | case MQTT_CONNECT_UNAVAILABLE: 812 | DPRINTLN("3 : MQTT_CONNECT_UNAVAILABLE - the server was unable to accept the connection"); 813 | break; 814 | case MQTT_CONNECT_BAD_CREDENTIALS: 815 | DPRINTLN("4 : MQTT_CONNECT_BAD_CREDENTIALS - the username/password were rejected"); 816 | break; 817 | case MQTT_CONNECT_UNAUTHORIZED: 818 | DPRINTLN("5 : MQTT_CONNECT_UNAUTHORIZED - the client was not authorized to connect"); 819 | break; 820 | } 821 | #endif 822 | retries--; 823 | if (retries < 1) { 824 | DPRINTLN("Failed to connect MQTT broker, will restart in 10min..."); 825 | WiFi.disconnect(); 826 | #ifdef DEBUGMODE 827 | Serial.flush(); 828 | #endif 829 | delay(50); 830 | esp_sleep_enable_timer_wakeup(300 * 1000000); //300 seconds 831 | esp_task_wdt_delete(NULL); // detach watchdog 832 | esp_task_wdt_deinit(); // disable watchdog 833 | esp_deep_sleep_start(); 834 | } 835 | } 836 | mqtt.publish(STATUS_TOPIC, "Online!"); 837 | mqtt.publish(VERSION_TOPIC, String(FW_VERSION).c_str()); 838 | //mqtt.subscribe(CONFIG_TOPIC); 839 | } 840 | 841 | void report_wake_source() 842 | { 843 | switch (esp_sleep_get_wakeup_cause()) { 844 | case ESP_SLEEP_WAKEUP_EXT0: 845 | DPRINTLN("Wakeup caused by external signal using RTC_IO"); 846 | break; 847 | case ESP_SLEEP_WAKEUP_EXT1: 848 | DPRINTLN("Wakeup caused by external signal using RTC_CNTL"); 849 | break; 850 | case ESP_SLEEP_WAKEUP_TIMER: 851 | DPRINTLN("Wakeup caused by timer"); 852 | break; 853 | case ESP_SLEEP_WAKEUP_TOUCHPAD: 854 | DPRINTLN("Wakeup caused by touchpad"); 855 | break; 856 | case ESP_SLEEP_WAKEUP_ULP: 857 | DPRINTLN("Wakeup caused by ULP program"); 858 | break; 859 | //default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break; 860 | } 861 | } 862 | 863 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 864 | // check_OTA() : check for some available new firmware on server? 865 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 866 | void check_OTA() 867 | { 868 | // setup_wifi(); must be called before check_OTA(); 869 | DPRINT("Firmware:<"); 870 | DPRINT(FW_VERSION); 871 | DPRINTLN(">"); 872 | DPRINTLN("Check for OTA"); 873 | HTTPClient http; 874 | if (http.begin(client, fwVersionURL)) { 875 | DPRINT("http begin | "); 876 | int httpCode = http.GET(); 877 | DPRINT("httpCode:"); 878 | DPRINTLN(httpCode); 879 | if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { 880 | String newFWVersion = http.getString(); 881 | DPRINT("Server FWVersion: "); 882 | DPRINT(newFWVersion); 883 | DPRINT(" / "); 884 | DPRINT("Current FWVersion: "); 885 | DPRINTLN(FW_VERSION); 886 | float newVersion = newFWVersion.toFloat(); 887 | if (newVersion > FW_VERSION) { 888 | DPRINTLN("start OTA !"); 889 | http.end(); 890 | delay(100); 891 | t_httpUpdate_return ret = httpUpdate.update(client, "http://192.168.1.184/fota/Wind/firmware.bin"); 892 | switch (ret) { 893 | case HTTP_UPDATE_FAILED: 894 | DPRINT("HTTP_UPDATE_FAILD Error"); 895 | DPRINT(httpUpdate.getLastError()); 896 | DPRINT(": "); 897 | DPRINTLN(httpUpdate.getLastErrorString().c_str()); 898 | //USE_SERIAL.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); 899 | break; 900 | case HTTP_UPDATE_NO_UPDATES: 901 | DPRINTLN("HTTP_UPDATE_NO_UPDATES"); 902 | break; 903 | case HTTP_UPDATE_OK: 904 | DPRINTLN("HTTP_UPDATE_OK"); 905 | break; 906 | } 907 | } else { 908 | DPRINTLN("no new version available"); 909 | } 910 | } 911 | } // end if http.begin 912 | DPRINTLN("End of OTA"); 913 | } // end check_OTA() 914 | 915 | void wifi_scan() 916 | { 917 | #ifdef DEBUGMODE //Macros are usually in all capital letters. 918 | Serial.println("scan start"); 919 | // WiFi.scanNetworks will return the number of networks found 920 | int n = WiFi.scanNetworks(); 921 | Serial.println("scan done"); 922 | if (n == 0) { 923 | Serial.println("no networks found"); 924 | } else { 925 | Serial.print(n); 926 | Serial.println(" networks found"); 927 | for (int i = 0; i < n; ++i) { 928 | // Print SSID and RSSI for each network found 929 | Serial.print(i + 1); 930 | Serial.print(": "); 931 | Serial.print(WiFi.SSID(i)); 932 | Serial.print(" ("); 933 | Serial.print(WiFi.RSSI(i)); 934 | Serial.print(")"); 935 | Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*"); 936 | delay(10); 937 | } 938 | } 939 | Serial.println(""); 940 | #endif 941 | } -------------------------------------------------------------------------------- /assets/voltage_limiter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 26 | 34 | 40 | 41 | 49 | 55 | 56 | 64 | 70 | 71 | 79 | 85 | 86 | 94 | 100 | 101 | 109 | 115 | 116 | 124 | 130 | 131 | 139 | 145 | 146 | 154 | 160 | 161 | 169 | 175 | 176 | 184 | 190 | 191 | 199 | 205 | 206 | 214 | 220 | 221 | 229 | 235 | 236 | 244 | 250 | 251 | 259 | 265 | 266 | 274 | 280 | 281 | 289 | 295 | 296 | 304 | 310 | 311 | 319 | 325 | 326 | 334 | 340 | 341 | 349 | 355 | 356 | 364 | 370 | 371 | 379 | 385 | 386 | 394 | 400 | 401 | 409 | 415 | 416 | 424 | 430 | 431 | 439 | 445 | 446 | 454 | 460 | 461 | 469 | 475 | 476 | 484 | 490 | 491 | 499 | 505 | 506 | 514 | 518 | 523 | 528 | 533 | 541 | 549 | 555 | 561 | 562 | 563 | 571 | 577 | 578 | 586 | 592 | 593 | 601 | 607 | 608 | 616 | 622 | 623 | 631 | 637 | 638 | 646 | 652 | 653 | 661 | 667 | 668 | 676 | 682 | 683 | 691 | 697 | 698 | 706 | 712 | 713 | 721 | 727 | 728 | 736 | 742 | 743 | 751 | 757 | 758 | 766 | 772 | 773 | 781 | 787 | 788 | 796 | 802 | 803 | 811 | 817 | 818 | 826 | 832 | 833 | 841 | 847 | 848 | 856 | 862 | 863 | 871 | 877 | 878 | 886 | 892 | 893 | 901 | 907 | 908 | 916 | 922 | 923 | 931 | 937 | 938 | 941 | 945 | 946 | 953 | 961 | 967 | 968 | 976 | 982 | 983 | 991 | 997 | 998 | 1006 | 1012 | 1013 | 1021 | 1027 | 1028 | 1036 | 1042 | 1043 | 1051 | 1057 | 1058 | 1066 | 1072 | 1073 | 1081 | 1087 | 1088 | 1096 | 1102 | 1103 | 1111 | 1117 | 1118 | 1126 | 1132 | 1133 | 1141 | 1147 | 1148 | 1149 | 1178 | 1186 | 1187 | 1189 | 1190 | 1192 | image/svg+xml 1193 | 1195 | 1196 | 1197 | 1198 | 1199 | 1204 | 1212 | 1231 | 1236 | 1241 | 1248 | LiPo Rideror equ.6V max 1270 | TLE42743V3 1287 | BZX852V7 1304 | 2.2k 1316 | 1321 | 1326 | 1331 | 1336 | 1342 | 1347 | 1352 | 6V 4WBut max openvoltage >7V 1374 | 1375 | 1376 | --------------------------------------------------------------------------------