From ecede07bdbced37a03af094ce23bce6b039b8396 Mon Sep 17 00:00:00 2001 From: Lesage Franck Date: Mon, 25 Dec 2023 23:59:00 +0100 Subject: [PATCH] Bug fixes (#57) * SQM feature. Code cleaning. Remove explicit use of String class which causes random bugs * Update for the 1.2 branch * Update README.md * Got the old breadboard design * Got the old BOM * Reference for lux to W/m2 * Using referenced formulas for SQM * MSAS cannot be negative * Code cleaning * Initial commit of 1.3 * Removed required commented code and a couple of bugs * Small fixes * Created 2.0 after changes in relays and their commands. Bigger perfboard. * Fixed glitches * Big refactoring: do not power sensors in case of rain event / Use of HW config * Update README.md * Update BOM * Update README.md * Fixed bug in runtime config management causing crash. Minor improvements * Add SN74AHCT125 and LM7805 to output 5V. Replace mosfets to match the new 5V output * Update README.md * Added pictures of the perfboard * Re-exported perfboard pictures as jpg * Added some debug info. Changed GPIO pins to match the new HW design * JsonDocument size increased to accomodate new OTA parameters reporting. Couple of cosmetic changes * Minor improvements. Capability to disable sensors in runtime config * Major rework, OO design. WIP. * housekeeping * Update README.md * Code cleanup. Added support for ultrasonic wind sensor VMS-3003-CFSFX-N01. Fixed IP or DHCP for wifi. Thread for sensor readings * 12V version: gerber and schematics * 12V:add pcb view * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * AWSGPS files * Cleanup * Update README.md * Update README.md * Close #4 #5 #8 #11 #12 #16 * Added one dome status input pin, small part moves and added ground fill * Close #1 #8 #9 #10 #11 #12 #16 #18 #19 #27 #29 * Small fixes after merge with main * Close #34, Close #40, Close #35, Close #35, Close #37, Close #38, Close #39, Close #40, Close #41 * Close #22, #43, #44, #45, #47 --- src/AWS.cpp | 193 ++++++----- src/AWS.h | 15 +- src/AWSConfig.cpp | 108 +++--- src/AWSConfig.h | 8 +- src/AWSGPS.cpp | 8 - src/AWSGPS.h | 6 +- src/AWSSensorManager.cpp | 516 +++++++---------------------- src/AWSSensorManager.h | 97 ++---- src/AWSWeb.cpp | 19 +- src/AWSWeb.h | 13 +- src/AWSWind.cpp | 222 +++++++++++++ src/AWSWind.h | 47 +++ src/AstroWeatherStation.h | 60 +--- src/AstroWeatherStation.ino | 28 +- src/Hydreon.cpp | 201 +++++++++++ src/Hydreon.h | 61 ++++ src/SC16IS750.cpp | 4 - src/SC16IS750.h | 4 +- src/SQM.cpp | 14 +- src/SQM.h | 4 - src/alpaca.cpp | 35 +- src/alpaca.h | 8 +- src/alpaca_dome.cpp | 11 - src/alpaca_observingconditions.cpp | 72 ++-- src/alpaca_safetymonitor.cpp | 11 - src/alpaca_telescope.cpp | 2 + src/defaults.h | 2 +- src/dome.cpp | 2 +- src/gpio_config.h | 25 +- 29 files changed, 962 insertions(+), 834 deletions(-) create mode 100644 src/AWSWind.cpp create mode 100644 src/AWSWind.h create mode 100644 src/Hydreon.cpp create mode 100644 src/Hydreon.h diff --git a/src/AWS.cpp b/src/AWS.cpp index 8e3b882..a4b7ae6 100644 --- a/src/AWS.cpp +++ b/src/AWS.cpp @@ -16,10 +16,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 -#define ASYNCWEBSERVER_REGEX 1 #include #include @@ -31,8 +27,6 @@ #include #include #include -#include -#include #include #include #include @@ -40,9 +34,7 @@ #include "defaults.h" #include "gpio_config.h" #include "SC16IS750.h" -#include "AWSGPS.h" #include "AstroWeatherStation.h" -#include "SQM.h" #include "AWSSensorManager.h" #include "AWSConfig.h" #include "AWSWeb.h" @@ -94,13 +86,13 @@ void AstroWeatherStation::check_rain_event_guard_time( void ) if ( ( time( NULL) - rain_event_timestamp ) <= config->get_rain_event_guard_time() ) return; - if ( config->get_has_rg9() ) { + if ( config->get_has_rain_sensor() ) { if ( debug_mode ) - Serial.printf( "\n[DEBUG] Now monitoring rain event pin from RG-9\n" ); + Serial.printf( "\n[DEBUG] Now monitoring rain event pin from rain sensor\n" ); - pinMode( GPIO_RG9_RAIN, INPUT ); - attachInterrupt( GPIO_RG9_RAIN, _handle_rain_event, FALLING ); + pinMode( GPIO_RAIN_SENSOR_RAIN, INPUT ); + attachInterrupt( GPIO_RAIN_SENSOR_RAIN, _handle_rain_event, FALLING ); catch_rain_event = true; } } @@ -206,19 +198,15 @@ void AstroWeatherStation::display_banner() Serial.println( "# GPIO PIN CONFIGURATION #" ); Serial.println( "#--------------------------------------------------------------------------------------------#" ); - print_config_string( "# Anemometer : RX=%d TX=%d CTRL=%d", GPIO_ANEMOMETER_RX, GPIO_ANEMOMETER_TX, GPIO_ANEMOMETER_CTRL ); - if ( config->get_wind_vane_model() != 0x02 ) - print_config_string( "# Wind vane : RX=%d TX=%d CTRL=%d", GPIO_WIND_VANE_RX, GPIO_WIND_VANE_TX, GPIO_WIND_VANE_CTRL ); - else - print_config_string( "# Wind vane : Same as anemometer (2-in-1 device)" ); - print_config_string( "# RG9 : RX=%d TX=%d MCLR=%d RAIN=%d", GPIO_RG9_RX, GPIO_RG9_TX, GPIO_RG9_MCLR, GPIO_RG9_RAIN ); + print_config_string( "# Wind sensors : RX=%d TX=%d CTRL=%d", GPIO_WIND_SENSOR_RX, GPIO_WIND_SENSOR_TX, GPIO_WIND_SENSOR_CTRL ); + print_config_string( "# Rain sensor : RX=%d TX=%d MCLR=%d RAIN=%d", GPIO_RAIN_SENSOR_RX, GPIO_RAIN_SENSOR_TX, GPIO_RAIN_SENSOR_MCLR, GPIO_RAIN_SENSOR_RAIN ); if ( solar_panel ) { - print_config_string( "# 3.3V SWITCH: %d", GPIO_ENABLE_3_3V ); - print_config_string( "# 12V SWITCH : %d", GPIO_ENABLE_12V ); - print_config_string( "# BAT LVL : SW=%d ADC=%d", GPIO_BAT_ADC_EN, GPIO_BAT_ADC ); + print_config_string( "# 3.3V SWITCH : %d", GPIO_ENABLE_3_3V ); + print_config_string( "# 12V SWITCH : %d", GPIO_ENABLE_12V ); + print_config_string( "# BAT LVL : SW=%d ADC=%d", GPIO_BAT_ADC_EN, GPIO_BAT_ADC ); } - print_config_string( "# DEBUG : %d", GPIO_DEBUG ); + print_config_string( "# DEBUG : %d", GPIO_DEBUG ); Serial.println( "#--------------------------------------------------------------------------------------------#" ); Serial.println( "# RUNTIME CONFIGURATION #" ); @@ -262,10 +250,10 @@ byte AstroWeatherStation::get_eth_cidr_prefix( void ) bool AstroWeatherStation::get_location_coordinates( double *latitude, double *longitude ) { - if ( sensor_manager.get_sensor_data()->gps.fix ) { + if ( sensor_manager->get_sensor_data()->gps.fix ) { - *longitude = sensor_manager.get_sensor_data()->gps.longitude; - *latitude = sensor_manager.get_sensor_data()->gps.latitude; + *longitude = sensor_manager->get_sensor_data()->gps.longitude; + *latitude = sensor_manager->get_sensor_data()->gps.latitude; return true; } return false; @@ -289,7 +277,7 @@ IPAddress *AstroWeatherStation::get_eth_ip( void ) char *AstroWeatherStation::get_json_sensor_data( void ) { DynamicJsonDocument json_data(768); - sensor_data_t *sensor_data = sensor_manager.get_sensor_data(); + sensor_data_t *sensor_data = sensor_manager->get_sensor_data(); json_data["battery_level"] = sensor_data->battery_level; json_data["timestamp"] = sensor_data->timestamp; @@ -298,10 +286,11 @@ char *AstroWeatherStation::get_json_sensor_data( void ) json_data["pressure"] = sensor_data->pressure; json_data["sl_pressure"] = sensor_data->sl_pressure; json_data["rh"] = sensor_data->rh; - json_data["ota_board"] = sensor_data->ota_board; - json_data["ota_device"] = sensor_data->ota_device; - json_data["ota_config"] = sensor_data->ota_config; + json_data["ota_board"] = ota_board; + json_data["ota_device"] = ota_device; + json_data["ota_config"] = ota_config; json_data["wind_speed"] = sensor_data->wind_speed; + json_data["wind_gust"] = sensor_data->wind_gust; json_data["wind_direction"] = sensor_data->wind_direction; json_data["dew_point"] = sensor_data->dew_point; json_data["rain_intensity"] = sensor_data->rain_intensity; @@ -341,7 +330,7 @@ char* AstroWeatherStation::get_root_ca( void ) sensor_data_t *AstroWeatherStation::get_sensor_data( void ) { - return sensor_manager.get_sensor_data(); + return sensor_manager->get_sensor_data(); } char *AstroWeatherStation::get_uptime( void ) @@ -385,13 +374,13 @@ const char *AstroWeatherStation::get_wind_vane_sensorname( void ) void AstroWeatherStation::handle_rain_event( void ) { - detachInterrupt( GPIO_RG9_RAIN ); + detachInterrupt( GPIO_RAIN_SENSOR_RAIN ); if ( debug_mode ) Serial.printf( "[DEBUG] Rain event.\n" ); time( &rain_event_timestamp ); - sensor_manager.set_rain_event(); + sensor_manager->set_rain_event(); if ( config->get_has_dome() && config->get_close_dome_on_rain() ) dome->trigger_close(); @@ -405,7 +394,7 @@ bool AstroWeatherStation::has_gps( void ) bool AstroWeatherStation::has_rain_sensor( void ) { - return config->get_has_rg9(); + return config->get_has_rain_sensor(); } bool AstroWeatherStation::initialise( void ) @@ -413,6 +402,8 @@ bool AstroWeatherStation::initialise( void ) unsigned long start = micros(), guard; int rain_intensity; + char string[64]; + uint8_t mac[6]; pinMode( GPIO_DEBUG, INPUT ); @@ -431,30 +422,45 @@ bool AstroWeatherStation::initialise( void ) return false; solar_panel = ( config->get_pwr_mode() == panel ); + snprintf( string, 64, "%s_%d", ESP.getChipModel(), ESP.getChipRevision() ); + ota_board = strdup( string ); + + esp_read_mac( mac, ESP_MAC_WIFI_STA ); + snprintf( string, 64, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); + ota_device = strdup( string ); + snprintf( string, 64, "%d-%s-%s-%s",config->get_pwr_mode(), config->get_pcb_version(), REV, BUILD_DATE ); + ota_config = strdup( string ); + + // Initialise here and read battery level before initialising WiFi as GPIO_13 is on ADC_2, and is not available when WiFi is on. + sensor_manager = new AWSSensorManager( solar_panel, debug_mode ); + + if ( solar_panel ) + sensor_manager->read_battery_level(); + if ( !initialise_network()) { config->rollback(); return false; } - + if ( config->get_has_sc16is750() ) { - sc16is750 = new I2C_SC16IS750( DEFAULT_SC16IS750_ADDR ); - } - // Do not enable earlier as some HW configs rely on SC16IS750 to pilot the dome. - if ( config->get_has_dome() ) - dome = new AWSDome( sc16is750, sensor_manager.get_i2c_mutex(), debug_mode ); + if ( debug_mode ) + Serial.printf( "[DEBUG] Initialising SC16IS750.\n" ); + sc16is750 = new I2C_SC16IS750( DEFAULT_SC16IS750_ADDR ); + } + if ( !solar_panel ) { - if ( config->get_has_rg9() ) { + if ( config->get_has_rain_sensor() ) { if ( debug_mode ) Serial.printf( "[DEBUG] Now monitoring rain event pin from RG-9\n" ); - pinMode( GPIO_RG9_RAIN, INPUT ); - attachInterrupt( GPIO_RG9_RAIN, _handle_rain_event, FALLING ); + pinMode( GPIO_RAIN_SENSOR_RAIN, INPUT ); + attachInterrupt( GPIO_RAIN_SENSOR_RAIN, _handle_rain_event, FALLING ); } } @@ -471,23 +477,26 @@ bool AstroWeatherStation::initialise( void ) start_config_server(); } - alpaca = new alpaca_server( debug_mode ); - switch ( config->get_alpaca_iface() ) { + if ( !solar_panel ) { + + alpaca = new alpaca_server( debug_mode ); + switch ( config->get_alpaca_iface() ) { - case sta: - alpaca->start( WiFi.localIP() ); - break; + case sta: + alpaca->start( WiFi.localIP() ); + break; - case ap: - alpaca->start( WiFi.softAPIP() ); - break; + case ap: + alpaca->start( WiFi.softAPIP() ); + break; - case eth: - alpaca->start( Ethernet.localIP() ); - break; + case eth: + alpaca->start( Ethernet.localIP() ); + break; + } } - + if ( !startup_sanity_check() ) { config->rollback(); @@ -497,13 +506,20 @@ bool AstroWeatherStation::initialise( void ) if ( debug_mode ) display_banner(); + // Do not enable earlier as some HW configs rely on SC16IS750 to pilot the dome. + if ( config->get_has_dome() ) + dome = new AWSDome( sc16is750, sensor_manager->get_i2c_mutex(), debug_mode ); + + if ( solar_panel ) + sync_time(); + if ( rain_event ) { Serial.printf("RAIN EVENT\n"); - sensor_manager.initialise( sc16is750, config, debug_mode ); - sensor_manager.initialise_RG9(); - sensor_manager.read_RG9(); - rain_intensity = sensor_manager.get_sensor_data()->rain_intensity; + sensor_manager->initialise( sc16is750, config ); + sensor_manager->initialise_rain_sensor(); + sensor_manager->read_rain_sensor(); + rain_intensity = sensor_manager->get_sensor_data()->rain_intensity; if ( rain_intensity > 0 ) // Avoid false positives send_rain_event_alarm( rain_intensity ); else @@ -511,15 +527,19 @@ bool AstroWeatherStation::initialise( void ) return true; } - if ( !sensor_manager.initialise( sc16is750, config, debug_mode )) + if ( !sensor_manager->initialise( sc16is750, config )) return false; - std::function _feed = std::bind( &AstroWeatherStation::periodic_tasks, this, std::placeholders::_1 ); - xTaskCreatePinnedToCore( - [](void *param) { - std::function* periodic_tasks_proxy = static_cast*>( param ); - (*periodic_tasks_proxy)( NULL ); - }, "GPSFeed", 2000, &_feed, 5, &aws_periodic_task_handle, 1 ); + if ( !solar_panel ) { + + std::function _feed = std::bind( &AstroWeatherStation::periodic_tasks, this, std::placeholders::_1 ); + xTaskCreatePinnedToCore( + [](void *param) { + std::function* periodic_tasks_proxy = static_cast*>( param ); + (*periodic_tasks_proxy)( NULL ); + }, "GPSFeed", 2000, &_feed, 5, &aws_periodic_task_handle, 1 ); + + } return true; } @@ -597,7 +617,7 @@ bool AstroWeatherStation::initialise_network( void ) void AstroWeatherStation::initialise_sensors( void ) { - sensor_manager.initialise_sensors( sc16is750 ); + sensor_manager->initialise_sensors( sc16is750 ); } bool AstroWeatherStation::initialise_wifi( void ) @@ -618,7 +638,7 @@ bool AstroWeatherStation::initialise_wifi( void ) case sta: if ( debug_mode ) - Serial.printf( "[DEBUG] Booting in Ethernet mode.\n" ); + Serial.printf( "[DEBUG] Booting in STA mode.\n" ); WiFi.mode( WIFI_STA ); return connect_to_wifi(); @@ -728,7 +748,7 @@ void AstroWeatherStation::periodic_tasks( void *dummy ) bool AstroWeatherStation::poll_sensors( void ) { - return sensor_manager.poll_sensors(); + return sensor_manager->poll_sensors(); } void AstroWeatherStation::post_content( const char *endpoint, const char *jsonString ) @@ -869,18 +889,18 @@ void AstroWeatherStation::print_runtime_config( void ) print_config_string( "# RH/TEMP/PRES : %s", config->get_has_bme() ? "Yes" : "No" ); print_config_string( "# WINDVANE : %s", config->get_has_wv() ? "Yes" : "No" ); print_config_string( "# ANEMOMETER : %s", config->get_has_ws() ? "Yes" : "No" ); - print_config_string( "# RAIN SENSOR : %s", config->get_has_rg9() ? "Yes" : "No" ); + print_config_string( "# RAIN SENSOR : %s", config->get_has_rain_sensor() ? "Yes" : "No" ); } bool AstroWeatherStation::rain_sensor_available( void ) { - return sensor_manager.rain_sensor_available(); + return sensor_manager->rain_sensor_available(); } void AstroWeatherStation::read_sensors( void ) { - sensor_manager.read_sensors(); + sensor_manager->read_sensors(); } void AstroWeatherStation::reboot( void ) @@ -890,16 +910,16 @@ void AstroWeatherStation::reboot( void ) void AstroWeatherStation::report_unavailable_sensors( void ) { - const char sensor_name[7][12] = { "MLX96014 ", "TSL2591 ", "BME280 ", "WIND VANE ", "ANEMOMETER ", "RG9 ", "GPS " }; + const char sensor_name[7][13] = { "MLX96014 ", "TSL2591 ", "BME280 ", "WIND VANE ", "ANEMOMETER ", "RAIN_SENSOR ", "GPS " }; char unavailable_sensors[96] = "Unavailable sensors: "; - uint8_t j = sensor_manager.get_available_sensors(), + uint8_t j = sensor_manager->get_available_sensors(), k; k = j; for ( uint8_t i = 0; i < 7; i++ ) { if ( !( k & 1 )) - strncat( unavailable_sensors, sensor_name[i], 12 ); + strncat( unavailable_sensors, sensor_name[i], 13 ); k >>= 1; } @@ -928,17 +948,24 @@ void AstroWeatherStation::send_alarm( const char *subject, const char *message ) void AstroWeatherStation::send_data( void ) { - while ( xSemaphoreTake( sensors_read_mutex, 5000 / portTICK_PERIOD_MS ) != pdTRUE ) - if ( debug_mode ) - Serial.printf( "[DEBUG] Waiting for sensor data update to complete.\n" ); + if ( !solar_panel ) { + + while ( xSemaphoreTake( sensors_read_mutex, 5000 / portTICK_PERIOD_MS ) != pdTRUE ) + + if ( debug_mode ) + Serial.printf( "[DEBUG] Waiting for sensor data update to complete.\n" ); + } + get_json_sensor_data(); if ( debug_mode ) Serial.printf( "[DEBUG] Sensor data: %s\n", json_sensor_data ); post_content( "newData.php", json_sensor_data ); - xSemaphoreGive( sensors_read_mutex ); + + if ( !solar_panel ) + xSemaphoreGive( sensors_read_mutex ); } void AstroWeatherStation::send_rain_event_alarm( uint8_t rain_intensity ) @@ -1029,9 +1056,9 @@ bool AstroWeatherStation::sync_time( void ) uint8_t ntp_retries = 5; struct tm timeinfo; - if ( config->get_has_gps() && sensor_manager.get_sensor_data()->gps.fix ) + if ( config->get_has_gps() && sensor_manager->get_sensor_data()->gps.fix ) return true; - return true; + configTzTime( config->get_tzname(), ntp_server ); while ( !( ntp_synced = getLocalTime( &timeinfo )) && ( --ntp_retries > 0 ) ) { @@ -1045,6 +1072,12 @@ bool AstroWeatherStation::sync_time( void ) Serial.print( "Time and date: " ); Serial.println( &timeinfo, "%Y-%m-%d %H:%M:%S" ); } + + if ( ntp_synced ) { + + time( &sensor_manager->get_sensor_data()->timestamp ); + sensor_manager->get_sensor_data()->ntp_time.tv_sec = sensor_manager->get_sensor_data()->timestamp; + } return ntp_synced; } diff --git a/src/AWS.h b/src/AWS.h index 316ac10..c300484 100644 --- a/src/AWS.h +++ b/src/AWS.h @@ -21,9 +21,10 @@ #ifndef _AWS_H #define _AWS_H -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 +#include "AWSWeb.h" +#include "AWSSensorManager.h" +#include "dome.h" +#include "alpaca.h" void OTA_callback( int, int ); @@ -38,7 +39,7 @@ class AstroWeatherStation { solar_panel; char *json_sensor_data; char uptime[32]; - uint8_t eth_mac[6] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }, + uint8_t eth_mac[6] = { 0xFE, 0xED, 0xDE, 0xAD, 0xBE, 0xEF }, wifi_mac[6]; EthernetClient *ethernet; @@ -60,7 +61,7 @@ class AstroWeatherStation { TaskHandle_t aws_periodic_task_handle; - AWSSensorManager sensor_manager; + AWSSensorManager *sensor_manager; AWSConfig *config; AWSWebServer *server; alpaca_server *alpaca; @@ -91,6 +92,10 @@ class AstroWeatherStation { bool startup_sanity_check( void ); bool stop_hotspot( void ); void wakeup_reason_to_string( esp_sleep_wakeup_cause_t, char * ); + char *ota_board; + char *ota_device; + char *ota_config; + public: diff --git a/src/AWSConfig.cpp b/src/AWSConfig.cpp index 61417a9..fe37ffd 100644 --- a/src/AWSConfig.cpp +++ b/src/AWSConfig.cpp @@ -16,31 +16,14 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 -#define ASYNCWEBSERVER_REGEX 1 - -#include -#include -#include -#include -#include -#include + #include #include -#include -#include +#include +#include #include "defaults.h" -#include "SC16IS750.h" -#include "AWSGPS.h" #include "AstroWeatherStation.h" -#include "SQM.h" -#include "AWSSensorManager.h" -#include "AWSWeb.h" -#include "dome.h" -#include "alpaca.h" #include "AWSConfig.h" #include "AWS.h" @@ -159,9 +142,9 @@ bool AWSConfig::get_has_mlx( void ) return has_mlx; } -bool AWSConfig::get_has_rg9( void ) +bool AWSConfig::get_has_rain_sensor( void ) { - return has_rg9; + return has_rain_sensor; } bool AWSConfig::get_has_sc16is750( void ) @@ -244,7 +227,7 @@ char *AWSConfig::get_json_string_config( void ) aws_json_config["anemometer_model"] = anemometer_model; aws_json_config["has_wv"] = has_wv; aws_json_config["windvane_model"] = wind_vane_model; - aws_json_config["has_rg9"] = has_rg9; + aws_json_config["has_rain_sensor"] = has_rain_sensor; aws_json_config["rain_event_guard_time"] = rain_event_guard_time; aws_json_config["has_gps"] = has_gps; aws_json_config["wifi_mode"] = wifi_mode; @@ -527,9 +510,9 @@ bool AWSConfig::read_config( void ) if ( has_mlx != x ) has_mlx = x; - x = aws_json_config.containsKey( "has_rg9" ) ? ( aws_json_config["has_rg9"] == 1 ) : DEFAULT_HAS_RG9; - if ( has_rg9 != x ) - has_rg9 = x; + x = aws_json_config.containsKey( "has_rain_sensor" ) ? ( aws_json_config["has_rain_sensor"] == 1 ) : DEFAULT_HAS_RAIN_SENSOR; + if ( has_rain_sensor != x ) + has_rain_sensor = x; z = aws_json_config.containsKey( "rain_event_guard_time" ) ? atof( aws_json_config["rain_event_guard_time"] ) : DEFAULT_RAIN_EVENT_GUARD_TIME; if ( rain_event_guard_time != z ) @@ -697,46 +680,45 @@ bool AWSConfig::verify_entries( JsonVariant &proposed_config ) for( JsonPair item : config_items ) { - switch( str2int( item.key().c_str(), 0 )) { - - case str2int("alpaca_iface", 0 ): - case str2int("anemometer_model", 0 ): - case str2int("ap_ssid", 0 ): - case str2int("eth_dns", 0 ): - case str2int("eth_gw", 0 ): - case str2int("eth_ip", 0 ): - case str2int("pref_iface", 0 ): - case str2int("rain_event_guard_time", 0 ): - case str2int("remote_server", 0 ): - case str2int("root_ca", 0 ): - case str2int("sta_ssid", 0 ): - case str2int("tzname", 0 ): - case str2int("url_path", 0 ): - case str2int("wifi_ap_dns", 0 ): - case str2int("wifi_ap_gw", 0 ): - case str2int("wifi_ap_ip", 0 ): - case str2int("wifi_ap_password", 0 ): - case str2int("wifi_mode", 0 ): - case str2int("wifi_sta_dns", 0 ): - case str2int("wifi_sta_gw", 0 ): - case str2int("wifi_sta_ip", 0 ): - case str2int("wifi_sta_ip_mode", 0 ): - case str2int("wifi_sta_password", 0 ): - case str2int("windvane_model", 0 ): + switch( str2int( item.key().c_str() )) { + + case str2int( "alpaca_iface" ): + case str2int( "anemometer_model" ): + case str2int( "ap_ssid" ): + case str2int( "eth_dns" ): + case str2int( "eth_gw" ): + case str2int( "eth_ip" ): + case str2int( "pref_iface" ): + case str2int( "rain_event_guard_time" ): + case str2int( "remote_server" ): + case str2int( "root_ca" ): + case str2int( "sta_ssid" ): + case str2int( "tzname" ): + case str2int( "url_path" ): + case str2int( "wifi_ap_dns" ): + case str2int( "wifi_ap_gw" ): + case str2int( "wifi_ap_ip" ): + case str2int( "wifi_ap_password" ): + case str2int( "wifi_mode" ): + case str2int( "wifi_sta_dns" ): + case str2int( "wifi_sta_gw" ): + case str2int( "wifi_sta_ip" ): + case str2int( "wifi_sta_ip_mode" ): + case str2int( "wifi_sta_password" ): + case str2int( "windvane_model" ): break; - case str2int("eth_ip_mode", 0): + case str2int( "eth_ip_mode" ): x = ( item.value() == "0" ) ? dhcp : fixed; break; - case str2int("clone_dome_on_rain", 0): - case str2int("has_bme", 0): - case str2int("has_dome", 0): - case str2int("has_gps", 0): - case str2int("has_mlx", 0): - case str2int("has_rg9", 0): - case str2int("has_tsl", 0): - case str2int("has_ws", 0): - case str2int("has_wv", 0): - case str2int("has_dome",0): + case str2int( "clone_dome_on_rain" ): + case str2int( "has_bme" ): + case str2int( "has_dome" ): + case str2int( "has_gps" ): + case str2int( "has_mlx" ): + case str2int( "has_rain_sensor" ): + case str2int( "has_tsl" ): + case str2int( "has_ws" ): + case str2int( "has_wv" ): config_items[item.key().c_str()] = ( item.value() == "on" ) ? 1 : 0; break; default: diff --git a/src/AWSConfig.h b/src/AWSConfig.h index a3eda61..eac5021 100644 --- a/src/AWSConfig.h +++ b/src/AWSConfig.h @@ -21,9 +21,7 @@ #ifndef _AWSConfig_H #define _AWSConfig_H -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 +#include extern const char *_anemometer_model[3]; extern const char *_windvane_model[3]; @@ -82,7 +80,7 @@ class AWSConfig { bool get_has_dome( void ); bool get_has_gps( void ); bool get_has_mlx( void ); - bool get_has_rg9( void ); + bool get_has_rain_sensor( void ); bool get_has_sc16is750( void ); bool get_has_tsl( void ); bool get_has_ws( void ); @@ -138,7 +136,7 @@ class AWSConfig { has_dome, has_gps, has_mlx, - has_rg9, + has_rain_sensor, has_sc16is750, has_tsl, has_ws, diff --git a/src/AWSGPS.cpp b/src/AWSGPS.cpp index b5de90d..1afac37 100644 --- a/src/AWSGPS.cpp +++ b/src/AWSGPS.cpp @@ -17,15 +17,7 @@ with this program. If not, see . */ -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 - -#include -#include #include "defaults.h" -#include "SC16IS750.h" - #include "AWSGPS.h" AWSGPS::AWSGPS( bool _debug_mode) diff --git a/src/AWSGPS.h b/src/AWSGPS.h index 557689e..4726097 100644 --- a/src/AWSGPS.h +++ b/src/AWSGPS.h @@ -2,9 +2,9 @@ #ifndef _AWSGPS_H #define _AWSGPS_H -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 +#include +#include +#include "SC16IS750.h" struct gps_data_t { diff --git a/src/AWSSensorManager.cpp b/src/AWSSensorManager.cpp index 886245d..e274dd3 100644 --- a/src/AWSSensorManager.cpp +++ b/src/AWSSensorManager.cpp @@ -16,57 +16,51 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 -#define ASYNCWEBSERVER_REGEX 1 #include #include -#include -#include +// Keep these two to get rid of compile time errors because of incompatibilities between libraries +#include #include -#include -#include -#include -#include -#include #include "defaults.h" #include "gpio_config.h" +#include "Hydreon.h" #include "SC16IS750.h" #include "AWSGPS.h" #include "AstroWeatherStation.h" -#include "AWSWeb.h" #include "AWSConfig.h" #include "SQM.h" - +#include "AWSWind.h" #include "AWSSensorManager.h" - -const char *_anemometer_model[3] = { "PR-3000-FSJT-N01", "GD-FS-RS485", "VMS-3003-CFSFX-N01" }; -const uint64_t _anemometer_cmd[3] = { 0x010300000001840a, 0x010300000001840a, 0x010300000002c40b }; -const uint16_t _anemometer_speed[3] = { 4800, 9600, 4800 }; - -const char *_windvane_model[3] = { "PR-3000-FXJT-N01", "GD-FX-RS485", "VMS-3003-CFSFX-N01" }; -const uint64_t _windvane_cmd[3] = { 0x010300000002c40b, 0x020300000002c438, 0x010300000002c40b }; -const uint16_t _windvane_speed[3] = { 4800, 9600, 4800 }; +#include "AWS.h" SemaphoreHandle_t sensors_read_mutex = NULL; +extern AstroWeatherStation station; -AWSSensorManager::AWSSensorManager( void ) +AWSSensorManager::AWSSensorManager( bool _solar_panel, bool _debug_mode ) { + debug_mode = _debug_mode; + bme = new Adafruit_BME280(); mlx = new Adafruit_MLX90614; tsl = new Adafruit_TSL2591( 2591 ); - rg9 = new HardwareSerial( RG9_UART ); sqm = new SQM( tsl, &sensor_data ); - + + wind_sensors = NULL; available_sensors = 0; - rg9_initialised = 0; sensor_data = {0}; + solar_panel = _solar_panel; polling_ms_interval = DEFAULT_SENSOR_POLLING_MS_INTERVAL; i2c_mutex = xSemaphoreCreateMutex(); + + if ( solar_panel ) { + + pinMode( GPIO_BAT_ADC_EN, OUTPUT ); + pinMode( GPIO_BAT_ADC, INPUT ); + + } } uint8_t AWSSensorManager::get_available_sensors( void ) @@ -89,34 +83,19 @@ sensor_data_t *AWSSensorManager::get_sensor_data( void ) return &sensor_data; } -bool AWSSensorManager::initialise( I2C_SC16IS750 *sc16is750, AWSConfig *_config, bool _debug_mode ) +bool AWSSensorManager::initialise( I2C_SC16IS750 *sc16is750, AWSConfig *_config ) { - char string[64]; - uint8_t mac[6]; - config = _config; - debug_mode = _debug_mode; - - solar_panel = ( config->get_pwr_mode() == panel ); - snprintf( string, 64, "%s_%d", ESP.getChipModel(), ESP.getChipRevision() ); - sensor_data.ota_board = strdup( string ); - - esp_read_mac( mac, ESP_MAC_WIFI_STA ); - snprintf( string, 64, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); - sensor_data.ota_device = strdup( string ); - snprintf( string, 64, "%d-%s-%s-%s",config->get_pwr_mode(), config->get_pcb_version(), REV, BUILD_DATE ); - sensor_data.ota_config = strdup( string ); +//FIXME solar_panel = ( config->get_pwr_mode() == panel ); // FIXME: needed? ESP32Time rtc( 0 ); + initialise_sensors( sc16is750 ); + if ( !solar_panel ) { - initialise_sensors( sc16is750 ); - wind_speeds_size = ( 2*60*1000 / polling_ms_interval ); - wind_speeds = (float *)malloc( wind_speeds_size * sizeof( float )); - wind_speed_index = 0; sensors_read_mutex = xSemaphoreCreateMutex(); std::function _poll_sensors_task = std::bind( &AWSSensorManager::poll_sensors_task, this, std::placeholders::_1 ); xTaskCreatePinnedToCore( @@ -129,14 +108,6 @@ bool AWSSensorManager::initialise( I2C_SC16IS750 *sc16is750, AWSConfig *_config, return true; } -void AWSSensorManager::initialise_anemometer( void ) -{ - anemometer.device = new SoftwareSerial( GPIO_ANEMOMETER_RX, GPIO_ANEMOMETER_TX ); - anemometer.device->begin( _anemometer_speed[ config->get_anemometer_model() ] ); - uint64_t_to_uint8_t_array( _anemometer_cmd[ config->get_anemometer_model() ], anemometer.request_cmd ); - pinMode( GPIO_ANEMOMETER_CTRL, OUTPUT ); -} - void AWSSensorManager::initialise_BME( void ) { if ( !bme->begin( 0x76 ) ) @@ -185,108 +156,18 @@ void AWSSensorManager::initialise_MLX( void ) } } -bool AWSSensorManager::initialise_RG9( void ) +void AWSSensorManager::initialise_sensors( I2C_SC16IS750 *_sc16is750 ) { - const int bps[ RG9_SERIAL_SPEEDS ] = { 1200, 2400, 4800, 9600, 19200, 38400, 57600 }; - char str[ 128 ], - status; - uint8_t l; - - for ( uint8_t i = 0; i < RG9_PROBE_RETRIES; i++ ) { - - if ( debug_mode ) - Serial.printf( "[DEBUG] Probing RG9, attempt #%d: ...", i ); - - for ( uint8_t j = 0; j < RG9_SERIAL_SPEEDS; j++ ) { - - esp_task_wdt_reset(); - rg9->begin( bps[j], SERIAL_8N1, GPIO_RG9_RX, GPIO_RG9_TX ); - rg9->println(); - rg9->println(); - - if ( debug_mode ) { - - Serial.printf( " @ %dbps: got [", bps[j] ); - Serial.flush(); - } - - rg9->println( "B" ); - l = RG9_read_string( str, 128 ); - - if ( debug_mode ) - Serial.printf( "%s] ", str ); - - if ( !strncmp( str, "Baud ", 5 )) { - - char bitrate[8]; - strncpy( bitrate, str+5, l-5 ); - Serial.printf( "%s[INFO] Found RG9 @ %s bps\n", debug_mode?"\n":"", bitrate ); - available_sensors |= RG9_SENSOR; - status = RG9_OK; - goto handle_status; - } - - if ( !strncmp( str, "Reset " , 6 )) { - - char reset_cause = str[6]; - Serial.printf( "%s[INFO] Found RG9 @ %dbps after it was reset because of '%s'\n[INFO] RG9 boot message:\n", debug_mode?"\n":"", bps[j], RG9_reset_cause( reset_cause )); - - while (( l = RG9_read_string( str, 128 ))) - Serial.println( str ); - - available_sensors |= RG9_SENSOR; - status = reset_cause; - goto handle_status; - } - - rg9->end(); - } - if ( debug_mode ) - printf( "\n" ); - } - - Serial.printf( "[ERROR] Could not find RG9, resetting sensor.\n" ); - - pinMode( GPIO_RG9_MCLR, OUTPUT ); - digitalWrite( GPIO_RG9_MCLR, LOW ); - delay( 500 ); - digitalWrite( GPIO_RG9_MCLR, HIGH ); - - status = RG9_FAIL; + if ( solar_panel ) { -handle_status: + pinMode( GPIO_ENABLE_3_3V, OUTPUT ); + pinMode( GPIO_ENABLE_12V, OUTPUT ); + digitalWrite( GPIO_ENABLE_3_3V, HIGH ); + digitalWrite( GPIO_ENABLE_12V, HIGH ); - // FIXME: restore alarms - switch( status ) { - - case RG9_OK: - rg9_initialised = true; - break; - case RG9_FAIL: - rg9_initialised = false; - break; - case 'B': - rg9_initialised = true; -// send_alarm( runtime_config, "RG9 low voltage", "", debug_mode ); - break; - case 'O': - rg9_initialised = true; - // send_alarm( runtime_config, "RG9 problem", "Reset because of stack overflow, report problem to support.", debug_mode ); - break; - case 'U': - rg9_initialised = true; - // send_alarm( runtime_config, "RG9 problem", "Reset because of stack underflow, report problem to support.", debug_mode ); - break; - default: - Serial.printf( "[INFO] Unhandled RG9 reset code: %d. Report to support.\n", status ); - rg9_initialised = false; - break; + delay( 500 ); // MLX96014 seems to take some time to properly initialise } - return rg9_initialised; -} - -void AWSSensorManager::initialise_sensors( I2C_SC16IS750 *_sc16is750 ) -{ + if ( config->get_has_bme() ) initialise_BME(); @@ -300,20 +181,56 @@ void AWSSensorManager::initialise_sensors( I2C_SC16IS750 *_sc16is750 ) sqm->set_debug_mode( debug_mode ); } - if ( config->get_has_ws() ) - initialise_anemometer(); + if ( config->get_has_ws() ) { + + wind_sensors = new AWSWindSensor( polling_ms_interval, debug_mode ); + if ( !wind_sensors->initialise_anemometer( config->get_anemometer_model() )) { + + if ( config->get_anemometer_model() == 0x02 ) + available_sensors &= ~WIND_VANE_SENSOR; + available_sensors &= ~ANEMOMETER_SENSOR; + + } else { + + if ( config->get_anemometer_model() == 0x02 ) + available_sensors |= WIND_VANE_SENSOR; + available_sensors |= ANEMOMETER_SENSOR; + } + } // Model 0x02 (VMS-3003-CFSFX-N01) is a 2-in-1 device - if ( config->get_has_wv() && ( config->get_wind_vane_model() != 0x02 )) - initialise_wind_vane(); + if ( config->get_has_wv() && ( config->get_wind_vane_model() != 0x02 )) { + + if ( !wind_sensors ) + wind_sensors = new AWSWindSensor( polling_ms_interval, debug_mode ); + + if ( !wind_sensors->initialise_wind_vane( config->get_wind_vane_model() )) { + + if ( config->get_wind_vane_model() == 0x02 ) + available_sensors &= ~ANEMOMETER_SENSOR; + available_sensors &= ~WIND_VANE_SENSOR; + + } else { + + if ( config->get_anemometer_model() == 0x02 ) + available_sensors |= ANEMOMETER_SENSOR; + available_sensors |= WIND_VANE_SENSOR; + } + } - if ( config->get_has_rg9() ) - initialise_RG9(); + if ( config->get_has_rain_sensor() && initialise_rain_sensor()) + available_sensors |= RAIN_SENSOR; if ( config->get_has_gps() ) initialise_GPS( _sc16is750 ); } +bool AWSSensorManager::initialise_rain_sensor( void ) +{ + rain_sensor = new Hydreon( RAIN_SENSOR_UART, GPIO_RAIN_SENSOR_TX, GPIO_RAIN_SENSOR_RX, GPIO_RAIN_SENSOR_MCLR, debug_mode ); + return rain_sensor->initialise(); +} + void AWSSensorManager::initialise_TSL( void ) { if ( !tsl->begin() ) { @@ -332,97 +249,18 @@ void AWSSensorManager::initialise_TSL( void ) } } -void AWSSensorManager::initialise_wind_vane( void ) -{ - wind_vane.device = new SoftwareSerial( GPIO_WIND_VANE_RX, GPIO_WIND_VANE_TX ); - wind_vane.device->begin( _windvane_speed[ config->get_wind_vane_model() ] ); - pinMode( GPIO_WIND_VANE_CTRL, OUTPUT ); - uint64_t_to_uint8_t_array( _windvane_cmd[ config->get_wind_vane_model() ], wind_vane.request_cmd ); -} - -void AWSSensorManager::read_anemometer( void ) -{ - uint8_t answer[7] = {0}, - i = 0, - j; - - if ( debug_mode ) { - - Serial.printf( "[DEBUG] Sending command to the anemometer:" ); - for ( j = 0; j < 8; Serial.printf( " %02x", anemometer.request_cmd[ j++ ] )); - Serial.printf( "\n" ); - } - - while ( answer[1] != 0x03 ) { - - digitalWrite( GPIO_ANEMOMETER_CTRL, SEND ); - anemometer.device->write( anemometer.request_cmd, 8 ); - anemometer.device->flush(); - - digitalWrite( GPIO_ANEMOMETER_CTRL, RECV ); - anemometer.device->readBytes( answer, 7 ); - - if ( debug_mode ) { - - Serial.print( "[DEBUG] Anemometer answer: " ); - for ( j = 0; j < 7; j++ ) - Serial.printf( "%02x ", answer[j] ); - - } - - if ( answer[1] == 0x03 ) { - - if ( config->get_anemometer_model() != 0x02 ) - - sensor_data.wind_speed = static_cast(answer[4]) / 10.F; - - else { - - sensor_data.wind_speed = static_cast( (answer[3]<< 8) + answer[4] ) / 100.F; - sensor_data.wind_direction = ( answer[5] << 8 ) + answer[6]; - - if ( debug_mode ) - Serial.printf( "\n[DEBUG] Wind direction: %d°", sensor_data.wind_direction ); - } - - if ( debug_mode ) - Serial.printf( "\n[DEBUG] Wind speed: %02.2f m/s\n", sensor_data.wind_speed ); - - } else { - - if ( debug_mode ) - Serial.println( "(Error)." ); - delay( 500 ); - } - - if ( ++i == ANEMOMETER_MAX_TRIES ) { - - if ( config->get_anemometer_model() == 0x02 ) - available_sensors &= ~WIND_VANE_SENSOR; - - available_sensors &= ~ANEMOMETER_SENSOR; - sensor_data.wind_speed = -1.F; - return; - } - } - - if ( config->get_anemometer_model() == 0x02 ) - available_sensors |= WIND_VANE_SENSOR; - - available_sensors |= ANEMOMETER_SENSOR; -} - void AWSSensorManager::read_battery_level( void ) { int adc_value = 0; float adc_v_in, bat_v; - + if ( debug_mode ) Serial.print( "[DEBUG] Battery level: " ); digitalWrite( GPIO_BAT_ADC_EN, HIGH ); delay( 500 ); + for( uint8_t i = 0; i < 5; i++ ) { adc_value += analogRead( GPIO_BAT_ADC ); @@ -515,73 +353,41 @@ void AWSSensorManager::read_MLX( void ) sensor_data.sky_temperature = -99.F; } - -void AWSSensorManager::read_RG9( void ) +void AWSSensorManager::read_rain_sensor( void ) { - char msg[128]; - - if (( !rg9_initialised ) && ( !initialise_RG9() )) { - - Serial.printf( "[INFO] Not returning rain data.\n" ); - return; - } - - rg9->println( "R" ); - memset( msg, 0, 128 ); - RG9_read_string( msg, 128 ); - - if ( debug_mode ) - Serial.printf( "[DEBUG] RG9 STATUS = [%s]\n", msg ); - - sensor_data.rain_intensity = static_cast( msg[2] - '0' ); + sensor_data.rain_intensity = rain_sensor->rain_intensity(); + if ( sensor_data.rain_intensity == 0 ) + sensor_data.rain_event = rain_event = false; } void AWSSensorManager::read_sensors( void ) { char string[64]; - // TODO: handle gps time -// if ( ntp_synced = sync_time() ) -// gettimeofday( &sensor_data.ntp_time, NULL ); - if ( solar_panel ) { - - pinMode( GPIO_BAT_ADC_EN, OUTPUT ); - delay( 100 ); - - time( &sensor_data.timestamp ); - read_battery_level(); - - pinMode( GPIO_ENABLE_3_3V, OUTPUT ); - pinMode( GPIO_ENABLE_12V, OUTPUT ); - digitalWrite( GPIO_ENABLE_3_3V, HIGH ); - digitalWrite( GPIO_ENABLE_12V, HIGH ); - - delay( 500 ); // MLX96014 seems to take some time to properly initialise - initialise_sensors( NULL ); - + retrieve_sensor_data(); digitalWrite( GPIO_ENABLE_3_3V, LOW ); digitalWrite( GPIO_ENABLE_12V, LOW ); - } else - sensor_data.battery_level = 100.0L; // When running on 12V, convention is to report 100% - - if ( sensor_data.battery_level <= BAT_LEVEL_MIN ) { + if ( sensor_data.battery_level <= BAT_LEVEL_MIN ) { - memset( string, 0, 64 ); - snprintf( string, 64, "LOW Battery level = %03.2f%%\n", sensor_data.battery_level ); + memset( string, 0, 64 ); + snprintf( string, 64, "LOW Battery level = %03.2f%%\n", sensor_data.battery_level ); - // Deal with ADC output accuracy, no need to stress about it, a few warnings are enough to get the message through :-) -/* if (( low_battery_event_count++ >= LOW_BATTERY_COUNT_MIN ) && ( low_battery_event_count++ <= LOW_BATTERY_COUNT_MAX )) - send_alarm( runtime_config, "Low battery", string, debug_mode ); + // Deal with ADC output accuracy, no need to stress about it, a few warnings are enough to get the message through :-) +/* if (( low_battery_event_count++ >= LOW_BATTERY_COUNT_MIN ) && ( low_battery_event_count++ <= LOW_BATTERY_COUNT_MAX )) + send_alarm( runtime_config, "Low battery", string, debug_mode ); */ - if ( debug_mode ) - Serial.printf( "[DEBUG] %s", string ); + if ( debug_mode ) + Serial.printf( "[DEBUG] %s", string ); + } + + } else + sensor_data.battery_level = 100.0L; // When running on 12V, convention is to report 100% - } /* if ( prev_available_sensors != available_sensors ) { @@ -614,69 +420,6 @@ void AWSSensorManager::read_TSL( void ) sensor_data.irradiance = ( lux == -1 ) ? 0 : lux * LUX_TO_IRRADIANCE_FACTOR; } -void AWSSensorManager::read_wind_vane( void ) -{ - uint8_t answer[7], - i = 0, - j; - - answer[1] = 0; - - if ( debug_mode ) { - - Serial.printf( "[DEBUG] Sending command to the wind vane:" ); - for ( j = 0; j < 8; Serial.printf( " %02x", wind_vane.request_cmd[ j++ ] )); - Serial.printf( "\n" ); - } - - while ( answer[1] != 0x03 ) { - - if ( config->get_wind_vane_model() == 0x02 ) - digitalWrite( GPIO_ANEMOMETER_CTRL, SEND ); //FIXME: make it a struct item - else - digitalWrite( GPIO_WIND_VANE_CTRL, SEND ); - - wind_vane.device->write( wind_vane.request_cmd, 8 ); - wind_vane.device->flush(); - - if ( config->get_wind_vane_model() == 0x02 ) - digitalWrite( GPIO_ANEMOMETER_CTRL, RECV ); - else - digitalWrite( GPIO_WIND_VANE_CTRL, RECV ); - - wind_vane.device->readBytes( answer, 7 ); - - if ( debug_mode ) { - - Serial.print( "[DEBUG] Wind vane answer : " ); - for ( j = 0; j < 6; j++ ) - Serial.printf( "%02x ", answer[j] ); - } - - if ( answer[1] == 0x03 ) { - - sensor_data.wind_direction = ( answer[5] << 8 ) + answer[6]; - - if ( debug_mode ) - Serial.printf( "\n[DEBUG] Wind direction: %d°\n", sensor_data.wind_direction ); - - } else { - - if ( debug_mode ) - Serial.println( "(Error)." ); - delay( 500 ); - } - - if ( ++i == WIND_VANE_MAX_TRIES ) { - - available_sensors &= ~WIND_VANE_SENSOR; - sensor_data.wind_direction = -1; - return; - } - } - available_sensors |= WIND_VANE_SENSOR; - -} void AWSSensorManager::reset_rain_event( void ) { @@ -726,8 +469,8 @@ void AWSSensorManager::retrieve_sensor_data( void ) xSemaphoreGive( i2c_mutex ); - if ( config->get_has_rg9() ) - read_RG9(); + if ( config->get_has_rain_sensor() ) + sensor_data.rain_intensity = rain_sensor->rain_intensity(); if ( config->get_has_gps() ) read_GPS(); @@ -735,46 +478,13 @@ void AWSSensorManager::retrieve_sensor_data( void ) } } -uint8_t AWSSensorManager::RG9_read_string( char *str, uint8_t len ) -{ - uint8_t i; - - delay( 500 ); - - if ( rg9->available() > 0 ) { - - i = rg9->readBytes( str, len ); - if ( i >= 2 ) - str[ i-2 ] = 0; // trim trailing \n - return i-1; - } - return 0; -} - -const char *AWSSensorManager::RG9_reset_cause( char code ) -{ - switch ( code ) { - case 'N': return "Normal power up"; - case 'M': return "MCLR"; - case 'W': return "Watchdog timer reset"; - case 'O': return "Start overflow"; - case 'U': return "Start underflow"; - case 'B': return "Low voltage"; - case 'D': return "Other"; - default: return "Unknown"; - } -} - bool AWSSensorManager::poll_sensors( void ) { if ( xSemaphoreTake( sensors_read_mutex, 2000 / portTICK_PERIOD_MS ) == pdTRUE ) { retrieve_sensor_data(); xSemaphoreGive( sensors_read_mutex ); - if ( wind_speed_index == wind_speeds_size ) - wind_speed_index = 0; - wind_speeds[ wind_speed_index++ ] = sensor_data.wind_speed; - sensor_data.wind_gust = *std::max_element( wind_speeds, wind_speeds + wind_speeds_size ); + sensor_data.wind_gust = wind_sensors->get_wind_gust(); return true; } return false; @@ -788,18 +498,38 @@ void AWSSensorManager::poll_sensors_task( void *dummy ) retrieve_sensor_data(); xSemaphoreGive( sensors_read_mutex ); - if ( wind_speed_index == wind_speeds_size ) - wind_speed_index = 0; - wind_speeds[ wind_speed_index++ ] = sensor_data.wind_speed; - sensor_data.wind_gust = *std::max_element( wind_speeds, wind_speeds + wind_speeds_size ); + if ( config->get_has_ws() ) + sensor_data.wind_gust = wind_sensors->get_wind_gust(); + else + sensor_data.wind_gust = -1.0F; + } delay( polling_ms_interval ); } } -void AWSSensorManager::uint64_t_to_uint8_t_array( uint64_t cmd, uint8_t *cmd_array ) +void AWSSensorManager::read_anemometer( void ) +{ + float x; + + if ( !wind_sensors->anemometer_initialised() ) + return; + + if ( ( x = wind_sensors->read_anemometer() ) == -1 ) + return; + + sensor_data.wind_speed = x; +} + +void AWSSensorManager::read_wind_vane( void ) { - uint8_t i = 0; - for ( i = 0; i < 8; i++ ) - cmd_array[ i ] = (uint8_t)( ( cmd >> (56-(8*i))) & 0xff ); + int16_t x; + + if ( !wind_sensors->wind_vane_initialised() ) + return; + + if ( ( x = wind_sensors->read_wind_vane() ) == -1 ) + return; + + sensor_data.wind_direction = x; } diff --git a/src/AWSSensorManager.h b/src/AWSSensorManager.h index 03762b7..faf4677 100644 --- a/src/AWSSensorManager.h +++ b/src/AWSSensorManager.h @@ -21,11 +21,6 @@ #ifndef _AWSSensorManager_H #define _AWSSensorManager_H -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 - -#include #include #include #include "Adafruit_TSL2591.h" @@ -33,20 +28,11 @@ #include #include - +#include "AWSGPS.h" #include "AWSConfig.h" - -#define SEND HIGH -#define RECV LOW - -#define WIND_VANE_MAX_TRIES 3 // Tests show that if there is no valid answer after 3 attempts, the sensor is not available -#define ANEMOMETER_MAX_TRIES 3 // Tests show that if there is no valid answer after 3 attempts, the sensor is not available - -#define RG9_SERIAL_SPEEDS 7 -#define RG9_PROBE_RETRIES 3 -#define RG9_OK 0 -#define RG9_FAIL 127 -#define RG9_UART 2 +#include "SQM.h" +#include "Hydreon.h" +#include "AWSWind.h" #define GPS_SPEED 9600 @@ -67,19 +53,6 @@ #define LUX_TO_IRRADIANCE_FACTOR 0.88 #define TSL_MAX_LUX 88000 -struct anemometer_t { - - SoftwareSerial *device; - uint8_t request_cmd[8]; - byte byte_idx; -}; - -struct wind_vane_t { - - SoftwareSerial *device; - uint8_t request_cmd[8]; - byte byte_idx; -}; class AWSSensorManager { @@ -89,71 +62,57 @@ class AWSSensorManager { Adafruit_BME280 *bme; Adafruit_MLX90614 *mlx; Adafruit_TSL2591 *tsl; - HardwareSerial *rg9; + Hydreon *rain_sensor; SQM *sqm; AWSGPS *gps; - wind_vane_t wind_vane; - anemometer_t anemometer; - AWSConfig *config; + AWSWindSensor *wind_sensors; + + AWSConfig *config; + char hw_version[6]; uint8_t available_sensors; sensor_data_t sensor_data; bool debug_mode, - rg9_initialised, rain_event, solar_panel; TaskHandle_t sensors_task_handle; SemaphoreHandle_t i2c_mutex = NULL; - float *wind_speeds; uint32_t polling_ms_interval; - byte wind_speed_index, - wind_speeds_size; - - /* - #ifdef USE_SC16IS750 - SC16IS750 *sc16is750; - #endif - */ + public: - AWSSensorManager( void ); - bool begin( void ); - uint8_t get_available_sensors( void ); - bool get_debug_mode( void ); + AWSSensorManager( bool, bool ); + bool begin( void ); + uint8_t get_available_sensors( void ); + bool get_debug_mode( void ); SemaphoreHandle_t get_i2c_mutex( void ); - sensor_data_t *get_sensor_data( void ); - bool initialise( I2C_SC16IS750 *, AWSConfig *, bool ); - bool initialise_RG9( void ); - void initialise_sensors( I2C_SC16IS750 * ); - bool poll_sensors( void ); - bool rain_sensor_available( void ); - void read_RG9( void ); - void read_sensors( void ); - void reset_rain_event( void ); - void set_rain_event( void ); - + sensor_data_t *get_sensor_data( void ); + bool initialise( I2C_SC16IS750 *, AWSConfig * ); + bool initialise_rain_sensor( void ); + void initialise_sensors( I2C_SC16IS750 * ); + bool poll_sensors( void ); + bool rain_sensor_available( void ); + void read_battery_level( void ); + void read_rain_sensor( void ); + void read_sensors( void ); + void reset_rain_event( void ); + void set_rain_event( void ); private: - bool sync_time( void ); - void read_battery_level( void ); - void initialise_anemometer( void ); +bool sync_time( void ); + void initialise_BME( void ); void initialise_GPS( I2C_SC16IS750 * ); void initialise_MLX( void ); void initialise_TSL( void ); - void initialise_wind_vane( void ); void poll_sensors_task( void * ); - void read_anemometer( void ); + void read_anemometer(); void read_BME( void ); void read_GPS( void ); void read_MLX( void ); void read_TSL( void ); void read_wind_vane( void ); void retrieve_sensor_data( void ); - uint8_t RG9_read_string( char *, uint8_t ); - const char *RG9_reset_cause( char ); - void uint64_t_to_uint8_t_array( uint64_t, uint8_t * ); - }; diff --git a/src/AWSWeb.cpp b/src/AWSWeb.cpp index 41c855d..496682f 100644 --- a/src/AWSWeb.cpp +++ b/src/AWSWeb.cpp @@ -17,35 +17,20 @@ with this program. If not, see . */ -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 -#define ASYNCWEBSERVER_REGEX 1 - #include #include #include #include #include #include -#include -#include +#include +#include -#include "time.h" #include "AsyncJson.h" #include "ArduinoJson.h" -#include "FS.h" -#include "SPIFFS.h" -#include "SC16IS750.h" -#include "AWSGPS.h" #include "AstroWeatherStation.h" -#include "SQM.h" #include "AWSConfig.h" #include "AWSWeb.h" -#include "SC16IS750.h" -#include "AWSSensorManager.h" -#include "dome.h" -#include "alpaca.h" #include "AWS.h" diff --git a/src/AWSWeb.h b/src/AWSWeb.h index 33a43ef..6c62f88 100644 --- a/src/AWSWeb.h +++ b/src/AWSWeb.h @@ -21,9 +21,16 @@ #ifndef _AWSWebServer_H #define _AWSWebServer_H -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 +#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 +#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 +#define ASYNCWEBSERVER_REGEX 1 + +#include +#include +#include +//#include +#include +#include class AWSWebServer { diff --git a/src/AWSWind.cpp b/src/AWSWind.cpp new file mode 100644 index 0000000..2af2fa2 --- /dev/null +++ b/src/AWSWind.cpp @@ -0,0 +1,222 @@ +#include + +#include "defaults.h" +#include "gpio_config.h" +#include "AWSWind.h" + +const char *_anemometer_model[3] = { "PR-3000-FSJT-N01", "GD-FS-RS485", "VMS-3003-CFSFX-N01" }; +const uint64_t _anemometer_cmd[3] = { 0x010300000001840a, 0x010300000001840a, 0x010300000002c40b }; +const uint16_t _anemometer_speed[3] = { 4800, 9600, 4800 }; + +const char *_windvane_model[3] = { "PR-3000-FXJT-N01", "GD-FX-RS485", "VMS-3003-CFSFX-N01" }; +const uint64_t _windvane_cmd[3] = { 0x010300000002c40b, 0x020300000002c438, 0x010300000002c40b }; +const uint16_t _windvane_speed[3] = { 4800, 9600, 4800 }; + +AWSWindSensor::AWSWindSensor( uint32_t polling_ms_interval, bool _debug_mode ) +{ + wind_speeds_size = ( 2*60*1000 / polling_ms_interval ); + wind_speeds = (float *)malloc( wind_speeds_size * sizeof( float )); + wind_speed_index = 0; + wind_gust = 0; + bps = 0; + debug_mode = _debug_mode; + _anemometer_initialised = _wind_vane_initialised = false; + sensor_bus = new SoftwareSerial( GPIO_WIND_SENSOR_RX, GPIO_WIND_SENSOR_TX ); + pinMode( GPIO_WIND_SENSOR_CTRL, OUTPUT ); +} + +bool AWSWindSensor::anemometer_initialised( void ) +{ + return _anemometer_initialised; +} + +bool AWSWindSensor::wind_vane_initialised( void ) +{ + return _wind_vane_initialised; +} + +bool AWSWindSensor::initialise_anemometer( byte model ) +{ + anemometer_model = model; + uint64_t_to_uint8_t_array( _anemometer_cmd[ model ], anemometer_cmd ); + + if ( !bps ) + + bps = _anemometer_speed[ model ]; + + else { + + if ( bps != _anemometer_speed[ model ] ) { + + bps = _anemometer_speed[ model ]; + sensor_bus->end(); + _wind_vane_initialised = false; + + } else + + return ( _anemometer_initialised = ( read_anemometer() >= 0 )); + } + + sensor_bus->begin( bps ); + return ( _anemometer_initialised = ( read_anemometer() >= 0 )); +} + +bool AWSWindSensor::initialise_wind_vane( byte model ) +{ + uint64_t_to_uint8_t_array( _windvane_cmd[ model ], wind_vane_cmd ); + + if ( !bps ) + + bps = _windvane_speed[ model ]; + + else { + + if ( bps != _windvane_speed[ model ] ) { + + bps = _windvane_speed[ model ]; + _anemometer_initialised = false; + sensor_bus->end(); + + } else + + return ( _wind_vane_initialised = ( read_wind_vane() >= 0 )); + } + + sensor_bus->begin( bps ); + return ( _wind_vane_initialised = ( read_wind_vane() >= 0 )); +} + +float AWSWindSensor::read_anemometer( void ) +{ + byte i = 0, + j; + + memset( answer, 0, 7 ); + + if ( debug_mode ) { + + Serial.printf( "[DEBUG] Sending command to the anemometer:" ); + for ( j = 0; j < 8; Serial.printf( " %02x", anemometer_cmd[ j++ ] )); + Serial.printf( "\n" ); + } + + while ( answer[1] != 0x03 ) { + + digitalWrite( GPIO_WIND_SENSOR_CTRL, SEND ); + sensor_bus->write( anemometer_cmd, 8 ); + sensor_bus->flush(); + + digitalWrite( GPIO_WIND_SENSOR_CTRL, RECV ); + sensor_bus->readBytes( answer, 7 ); + + if ( debug_mode ) { + + Serial.print( "[DEBUG] Anemometer answer: " ); + for ( j = 0; j < 7; j++ ) + Serial.printf( "%02x ", answer[j] ); + + } + + if ( answer[1] == 0x03 ) { + + if ( anemometer_model != 0x02 ) + + wind_speed = static_cast(answer[4]) / 10.F; + + else { + + wind_speed = static_cast( (answer[3]<< 8) + answer[4] ) / 100.F; + wind_direction = ( answer[5] << 8 ) + answer[6]; + + if ( debug_mode ) + Serial.printf( "\n[DEBUG] Wind direction: %d°", wind_direction ); + } + + if ( debug_mode ) + Serial.printf( "\n[DEBUG] Wind speed: %02.2f m/s\n", wind_speed ); + + } else { + + if ( debug_mode ) + Serial.println( "(Error)." ); + delay( 500 ); + } + + if ( ++i == WIND_SENSOR_MAX_TRIES ) { + + if ( wind_speed_index == wind_speeds_size ) + wind_speed_index = 0; + wind_speeds[ wind_speed_index++ ] = 0; + return ( wind_speed = -1.F ); + } + } + + if ( wind_speed_index == wind_speeds_size ) + wind_speed_index = 0; + wind_speeds[ wind_speed_index++ ] = wind_speed; + + return wind_speed; +} + +float AWSWindSensor::get_wind_gust( void ) +{ + return ( wind_gust = *std::max_element( wind_speeds, wind_speeds + wind_speeds_size )); +} + +int16_t AWSWindSensor::read_wind_vane( void ) +{ + byte i = 0, + j; + + memset( answer, 0, 7 ); + + if ( debug_mode ) { + + Serial.printf( "[DEBUG] Sending command to the wind vane:" ); + for ( j = 0; j < 8; Serial.printf( " %02x", wind_vane_cmd[ j++ ] )); + Serial.printf( "\n" ); + } + + while ( answer[1] != 0x03 ) { + + digitalWrite( GPIO_WIND_SENSOR_CTRL, SEND ); + + sensor_bus->write( wind_vane_cmd, 8 ); + sensor_bus->flush(); + + digitalWrite( GPIO_WIND_SENSOR_CTRL, RECV ); + sensor_bus->readBytes( answer, 7 ); + + if ( debug_mode ) { + + Serial.print( "[DEBUG] Wind vane answer : " ); + for ( j = 0; j < 7; j++ ) + Serial.printf( "%02x ", answer[j] ); + } + + if ( answer[1] == 0x03 ) { + + wind_direction = ( answer[5] << 8 ) + answer[6]; + + if ( debug_mode ) + Serial.printf( "\n[DEBUG] Wind direction: %d°\n", wind_direction ); + + } else { + + if ( debug_mode ) + Serial.println( "(Error)." ); + delay( 500 ); + } + + if ( ++i == WIND_SENSOR_MAX_TRIES ) + return ( wind_direction = -1 ); + } + return wind_direction; +} + +void AWSWindSensor::uint64_t_to_uint8_t_array( uint64_t cmd, uint8_t *cmd_array ) +{ + uint8_t i = 0; + for ( i = 0; i < 8; i++ ) + cmd_array[ i ] = (uint8_t)( ( cmd >> (56-(8*i))) & 0xff ); +} diff --git a/src/AWSWind.h b/src/AWSWind.h new file mode 100644 index 0000000..411cac9 --- /dev/null +++ b/src/AWSWind.h @@ -0,0 +1,47 @@ +#pragma once +#ifndef _AWS_WIND_H +#define _AWS_WIND_H + +#include + +#define SEND HIGH +#define RECV LOW + +#define WIND_SENSOR_MAX_TRIES 3 // Tests have shown that if there is no valid answer after 3 attempts, the sensor is not available + +class AWSWindSensor { + + public: + + AWSWindSensor( uint32_t, bool ); + bool anemometer_initialised( void ); + float get_wind_gust( void ); + bool initialise_anemometer( byte ); + bool initialise_wind_vane( byte ); + float read_anemometer( void ); + int16_t read_wind_vane( void ); + bool wind_vane_initialised( void ); + + private: + + bool _anemometer_initialised, + debug_mode, + _wind_vane_initialised; + byte anemometer_model, + wind_speed_index, + wind_speeds_size; + float wind_gust, + wind_speed, + *wind_speeds; + int16_t wind_direction; + uint16_t bps; + uint8_t anemometer_cmd[8], + answer[7], + wind_vane_cmd[8]; + uint32_t polling_ms_interval; + SoftwareSerial *sensor_bus; + + void uint64_t_to_uint8_t_array( uint64_t cmd, uint8_t *cmd_array ); +}; + +#endif diff --git a/src/AstroWeatherStation.h b/src/AstroWeatherStation.h index 11c9521..20e35b3 100644 --- a/src/AstroWeatherStation.h +++ b/src/AstroWeatherStation.h @@ -22,65 +22,41 @@ #ifndef _AstroWeatherStation_H #define _AstroWeatherStation_H -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 - -#define DEBUG_MODE 1 - #define REV "3.0.0.0" -#define BUILD_DATE "20231124" +#define BUILD_DATE "2023122101" -// Misc -#define GPIO_DEBUG GPIO_NUM_34 - -//#define CONFIG_MODE_GUARD 5000000 // 5 seconds -#define CONFIG_MODE_GUARD 1000000 // 5 seconds -#define US_SLEEP 5 * 60 * 1000000 // 5 minutes -#define US_HIBERNATE 30 * 24 * 60 * 60 * 1000000ULL // 30 days -#define USE_MLX "1" -#define USE_BME "1" -#define USE_TSL "1" -#define USE_WV "1" -#define USE_WS "1" -#define USE_RG9 "1" -#define USE_GPS "0" +// Force DEBUG output even if not activated by external button +#define DEBUG_MODE 1 +// ------------------------------------------------------------------------------ +// // DO NOT CHANGE ANYTHING BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING! - -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 +// +// ------------------------------------------------------------------------------ #define SPI_CLOCK_MHZ 18 #define MLX_SENSOR 0x00000001 #define TSL_SENSOR 0x00000002 -#define BME_SENSOR 0x00000004 // 0000 0000 0000 0000 0000 0000 0000 0100 -#define WIND_VANE_SENSOR 0x00000008 // 0000 0000 0000 0000 0000 0000 0000 1000 -#define ANEMOMETER_SENSOR 0x00000010 // 0000 0000 0000 0000 0000 0000 0001 0000 -#define RG9_SENSOR 0x00000020 // 0000 0000 0000 0000 0000 0000 0010 0000 -#define GPS_SENSOR 0x00000040 // 0000 0000 0000 0000 0000 0000 0100 0000 -#define ALL_SENSORS ( MLX_SENSOR | TSL_SENSOR | BME_SENSOR | WIND_VANE_SENSOR | ANEMOMETER_SENSOR | RG9_SENSOR | GPS_SENSOR ) +#define BME_SENSOR 0x00000004 +#define WIND_VANE_SENSOR 0x00000008 +#define ANEMOMETER_SENSOR 0x00000010 +#define RAIN_SENSOR 0x00000020 +#define GPS_SENSOR 0x00000040 +#define ALL_SENSORS ( MLX_SENSOR | TSL_SENSOR | BME_SENSOR | WIND_VANE_SENSOR | ANEMOMETER_SENSOR | RAIN_SENSOR | GPS_SENSOR ) #define FORMAT_SPIFFS_IF_FAILED true -#define ASCOM_RG9_DESCRIPTION "Hydreon RG-9 Rain sensor" -#define ASCOM_RG9_DRIVER_INFO "Hydreon RG-9 Driver (c) OpenAstroDevices 2023" -#define ASCOM_RG9_DRIVER_VERSION "1.0.1.0" -#define ASCOM_RG9_NAME "Hydreon RG-9" +// Only applicable for solar panel version +#define CONFIG_MODE_GUARD 5000000 // 5 seconds +#define US_SLEEP 5 * 60 * 1000000 // 5 minutes +#define US_HIBERNATE 1 * 24 * 60 * 60 * 1000000ULL // 1 day -#define ASCOM_DOME_DESCRIPTION "Generic roof top" -#define ASCOM_DOME_DRIVER_INFO "Generic roof top driver (c) OpenAstroDevices 2023" -#define ASCOM_DOME_DRIVER_VERSION "1.0.0.0" -#define ASCOM_DOMEffff_NAME "Generic roof top" +#include "AWSGPS.h" struct sensor_data_t { - char *ota_board; - char *ota_device; - char *ota_config; - time_t timestamp; float battery_level; diff --git a/src/AstroWeatherStation.ino b/src/AstroWeatherStation.ino index 2c5610e..1b063e6 100644 --- a/src/AstroWeatherStation.ino +++ b/src/AstroWeatherStation.ino @@ -23,35 +23,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 -#define ASYNCWEBSERVER_REGEX 1 -#include +// Keep these two to get rid of compile time errors because of incompatibilities between libraries #include #include -#include -#include -#include -#include -#include - -extern const char *_anemometer_model[3]; -extern const char *_windvane_model[3]; #include "gpio_config.h" -#include "SC16IS750.h" -#include "AWSGPS.h" #include "AstroWeatherStation.h" -#include "SQM.h" -#include "AWSConfig.h" -#include "AWSWeb.h" -#include "AWSSensorManager.h" -#include "dome.h" -#include "alpaca.h" #include "AWS.h" +extern const char *_anemometer_model[3]; +extern const char *_windvane_model[3]; + AstroWeatherStation station; void setup() @@ -80,7 +63,7 @@ void setup() Serial.printf( "[INFO] Entering sleep mode.\n" ); esp_sleep_enable_timer_wakeup( US_SLEEP ); - esp_sleep_enable_ext0_wakeup( GPIO_RG9_RAIN, LOW ); + esp_sleep_enable_ext0_wakeup( GPIO_RAIN_SENSOR_RAIN, LOW ); esp_deep_sleep_start(); } @@ -99,7 +82,6 @@ void loop() station.send_data(); station.check_ota_updates(); delay( 1000 * 60 * 5 ); - //delay( 1000 * 1 * 5 ); } } diff --git a/src/Hydreon.cpp b/src/Hydreon.cpp new file mode 100644 index 0000000..12c8d3e --- /dev/null +++ b/src/Hydreon.cpp @@ -0,0 +1,201 @@ +/* + Hydreon.cpp + + (c) 2023 F.Lesage + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with this program. If not, see . +*/ + +#include +#include +#include +#include "Hydreon.h" + +RTC_DATA_ATTR uint16_t baud=0; + +static constexpr int bps[ HYDREON_SERIAL_SPEEDS ] = { 1200, 2400, 4800, 9600, 19200, 38400, 57600 }; + +Hydreon::Hydreon( uint8_t _uart_nr, uint8_t tx, uint8_t rx, uint8_t reset, bool _debug_mode ) +{ + initialised = false; + uart_nr = _uart_nr; + rx_pin = rx; + tx_pin = tx; + reset_pin = reset; + debug_mode = _debug_mode; + status = ' '; +} + +void Hydreon::probe( uint16_t baudrate ) +{ + int l; + char bitrate[18]={0}; + + status = RAIN_SENSOR_FAIL; + + sensor->begin( baudrate, SERIAL_8N1, rx_pin, tx_pin ); + sensor->println(); + sensor->println(); + + if ( debug_mode ) { + + Serial.printf( " @ %dbps: got [", baudrate ); + Serial.flush(); + } + + sensor->println( "B" ); + l = read_string(); + + if ( debug_mode ) + Serial.printf( "%s] ", str ); + + if ( !strncmp( str, "Baud ", 5 )) { + + Serial.printf( "%s[INFO] Found rain sensor @ %d bps\n", debug_mode?"\n":"", baudrate ); + status = RAIN_SENSOR_OK; + baud = baudrate; + + } else if ( !strncmp( str, "Reset " , 6 )) { + + status = str[6]; + Serial.printf( "%s[INFO] Found rain sensor @ %dbps after it was reset because of '%s'\n[INFO] Rain sensor boot message:\n", debug_mode?"\n":"", baudrate, reset_cause() ); + + while (( l = read_string())) + Serial.println( str ); + + baud = baudrate; + } +} + +bool Hydreon::initialise( void ) +{ + sensor = new HardwareSerial( uart_nr ); + + if ( baud ) { + + if ( debug_mode ) + Serial.printf( "[DEBUG] Probing rain sensor using previous birate " ); + probe( baud ); + + } else + for ( byte i = 0; i < HYDREON_PROBE_RETRIES; i++ ) { + + if ( debug_mode ) + Serial.printf( "[DEBUG] Probing rain sensor, attempt #%d: ...", i ); + + for ( byte j = 0; j < HYDREON_SERIAL_SPEEDS; j++ ) { + + esp_task_wdt_reset(); + probe( bps[j] ); + if ( status == RAIN_SENSOR_FAIL ) + sensor->end(); + else + break; + } + + if ( debug_mode ) + printf( "\n" ); + + if ( status != RAIN_SENSOR_FAIL ) + break; + } + + if ( status == RAIN_SENSOR_FAIL ) { + + Serial.printf( "[ERROR] Could not find rain sensor, resetting.\n" ); + pinMode( reset_pin, OUTPUT ); + digitalWrite( reset_pin, LOW ); + delay( 500 ); + digitalWrite( reset_pin, HIGH ); + } + + // FIXME: restore alarms + switch( status ) { + + case RAIN_SENSOR_OK: + initialised = true; + break; + case RAIN_SENSOR_FAIL: + initialised = false; + break; + case 'B': + initialised = true; +// send_alarm( runtime_config, "Rain sensor low voltage", "", debug_mode ); + break; + case 'O': + initialised = true; + // send_alarm( runtime_config, "Rain sensor problem", "Reset because of stack overflow, report problem to support.", debug_mode ); + break; + case 'U': + initialised = true; + // send_alarm( runtime_config, "Rain sensor problem", "Reset because of stack underflow, report problem to support.", debug_mode ); + break; + default: + Serial.printf( "[INFO] Unhandled rain sensor reset code: %d. Report to support.\n", status ); + initialised = false; + break; + } + return initialised; +} + +byte Hydreon::rain_intensity( void ) +{ + if (( !initialised ) && ( !initialise() )) { + + Serial.printf( "[ERROR] Cannot initialise rain sensor. Not returning rain data.\n" ); + return -1; + } + + sensor->println( "R" ); + memset( str, 0, 128 ); + read_string(); + + if ( debug_mode ) + Serial.printf( "[DEBUG] Rain sensor status string = [%s]\n", str ); + + return static_cast( str[2] - '0' ); +} + +uint8_t Hydreon::read_string( void ) +{ + uint8_t i; + + //FIXME: check if really needed + delay( 500 ); + + if ( sensor->available() > 0 ) { + + i = sensor->readBytes( str, 128 ); + if ( i >= 2 ) + str[ i-2 ] = 0; // trim trailing \n + return i-1; + } + return 0; +} + +const char *Hydreon::reset_cause() +{ + switch ( status ) { + + case 'N': return "Normal power up"; + case 'M': return "MCLR"; + case 'W': return "Watchdog timer reset"; + case 'O': return "Start overflow"; + case 'U': return "Start underflow"; + case 'B': return "Low voltage"; + case 'D': return "Other"; + default: return "Unknown"; + + } +} diff --git a/src/Hydreon.h b/src/Hydreon.h new file mode 100644 index 0000000..e6e55d1 --- /dev/null +++ b/src/Hydreon.h @@ -0,0 +1,61 @@ +/* + Hydreon.h + + (c) 2023 F.Lesage + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with this program. If not, see . +*/ + +#pragma once +#ifndef _HYDREON_H +#define _HYDREON_H + +#define HYDREON_SERIAL_SPEEDS 7 +#define HYDREON_PROBE_RETRIES 3 + +#define RAIN_SENSOR_OK 0 +#define RAIN_SENSOR_FAIL 127 +#define RAIN_SENSOR_UART 2 + +#include + +class Hydreon { + + + private: + + bool debug_mode, + initialised; + uint8_t uart_nr, + reset_pin, + rx_pin, + tx_pin; + char status, + str[128]; + + HardwareSerial *sensor; + + void probe( uint16_t ); + uint8_t read_string( void ); + + public: + + Hydreon( uint8_t, uint8_t, uint8_t, uint8_t, bool ); + bool initialise( void ); + byte rain_intensity( void ); + const char *reset_cause( void ); + +}; + +#endif diff --git a/src/SC16IS750.cpp b/src/SC16IS750.cpp index c07976b..9977877 100644 --- a/src/SC16IS750.cpp +++ b/src/SC16IS750.cpp @@ -29,10 +29,6 @@ * */ -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 - #include #include #include "SC16IS750.h" diff --git a/src/SC16IS750.h b/src/SC16IS750.h index 6c6e286..67ff8d6 100644 --- a/src/SC16IS750.h +++ b/src/SC16IS750.h @@ -22,9 +22,7 @@ #ifndef _SC16IS750_H_ #define _SC16IS750_H_ -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 +#include #define SC16IS750_RHR (0x00) // RX FIFO #define SC16IS750_THR (0X00) // TX FIFO diff --git a/src/SQM.cpp b/src/SQM.cpp index 0da9a0f..70cbbd6 100644 --- a/src/SQM.cpp +++ b/src/SQM.cpp @@ -20,20 +20,8 @@ */ -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 -#define ASYNCWEBSERVER_REGEX 1 - #include -#include -#include -#include -#include -#include - -#include "SC16IS750.h" -#include "AWSGPS.h" + #include "AstroWeatherStation.h" #include "SQM.h" #include "AWSSensorManager.h" diff --git a/src/SQM.h b/src/SQM.h index cca160b..85f4d76 100644 --- a/src/SQM.h +++ b/src/SQM.h @@ -2,10 +2,6 @@ #ifndef _SQM_H #define _SQM_H -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 - #define DOWN -1 #define UP 1 diff --git a/src/alpaca.cpp b/src/alpaca.cpp index 8cb7e6f..17629a1 100644 --- a/src/alpaca.cpp +++ b/src/alpaca.cpp @@ -18,11 +18,9 @@ */ -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 - -#define ASYNCWEBSERVER_REGEX 1 +#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 +#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 +#define ASYNCWEBSERVER_REGEX 1 #include #include @@ -31,15 +29,8 @@ #include #include #include -#include -#include -#include "SC16IS750.h" -#include "AWSGPS.h" #include "AstroWeatherStation.h" -#include "SQM.h" -#include "AWSSensorManager.h" -#include "AWSWeb.h" #include "dome.h" #include "alpaca_dome.h" #include "alpaca_observingconditions.h" @@ -50,7 +41,7 @@ extern AstroWeatherStation station; -constexpr unsigned int str2int(const char* str, int h=0 ) +constexpr unsigned int str2int(const char* str, int h = 0 ) { return !str[h] ? 5381 : (str2int(str, h+1) * 33) ^ str[h]; } @@ -373,7 +364,7 @@ void alpaca_server::dispatch_safetymonitor_request( AsyncWebServerRequest *reque void alpaca_server::dispatch_observingconditions_request( AsyncWebServerRequest *request ) { - Serial.printf("URL: [%s]\n", request->pathArg(1).c_str() ); + Serial.printf("OC URL: [%s]\n", request->pathArg(1).c_str() ); switch( str2int( request->pathArg(1).c_str() )) { @@ -887,6 +878,20 @@ void alpaca_server::does_not_exist( AsyncWebServerRequest *request ) request->send( 400, "application/json", "Endpoint does not exist" ); } +void alpaca_server::does_not_exist2( AsyncWebServerRequest *request ) +{ + int params = request->params(); + + server_transaction_id++; + if ( debug_mode ) { + + Serial.printf( "\n[DEBUG] ALPACA: [2] unimplemented endpoint: %x %s, with parameters: ", request->method(), request->url().c_str()); + for( int i = 0; i < params; i++ ) + Serial.printf( "(%s=%s) ", request->getParam(i)->name().c_str(), request->getParam(i)->value().c_str() ); + Serial.printf("\n"); + } + request->send( 400, "application/json", "Endpoint does not exist" ); +} void alpaca_server::get_config( AsyncWebServerRequest *request ) { @@ -1081,7 +1086,7 @@ bool alpaca_server::start( IPAddress address ) server->on( "^\\/api\\/v1\\/([a-zA-Z]+)\\/([0-9]+)\\/.+$", HTTP_GET, std::bind( &alpaca_server::does_not_exist, this, std::placeholders::_1 )); - server->onNotFound( std::bind( &alpaca_server::does_not_exist, this, std::placeholders::_1 )); + server->onNotFound( std::bind( &alpaca_server::does_not_exist2, this, std::placeholders::_1 )); server->begin(); return true; diff --git a/src/alpaca.h b/src/alpaca.h index f48bf66..ddaad23 100644 --- a/src/alpaca.h +++ b/src/alpaca.h @@ -22,12 +22,7 @@ #ifndef _ALPACA_H #define _ALPACA_H -#undef CONFIG_DISABLE_HAL_LOCKS -#define _ASYNC_WEBSERVER_LOGLEVEL_ 0 -#define _ETHERNET_WEBSERVER_LOGLEVEL_ 0 - -#define ASYNCWEBSERVER_REGEX - +#include #include #define ALPACA_SERVER_PORT 8080 @@ -260,6 +255,7 @@ class alpaca_server { void dispatch_telescope_request( AsyncWebServerRequest * ); bool extract_transaction_details( AsyncWebServerRequest *, bool ); void does_not_exist( AsyncWebServerRequest * ); + void does_not_exist2( AsyncWebServerRequest * ); void get_config( AsyncWebServerRequest * ); bool get_configured_devices( char *, size_t ); void not_implemented( AsyncWebServerRequest * ); diff --git a/src/alpaca_dome.cpp b/src/alpaca_dome.cpp index 06f18a2..eee3f70 100644 --- a/src/alpaca_dome.cpp +++ b/src/alpaca_dome.cpp @@ -1,22 +1,11 @@ -#include -#include #include #include -#include -#include #include "defaults.h" #include "gpio_config.h" -#include "SC16IS750.h" -#include "AWSGPS.h" #include "AstroWeatherStation.h" -#include "dome.h" #include "alpaca.h" #include "alpaca_dome.h" -#include "SQM.h" -#include "AWSConfig.h" -#include "AWSWeb.h" -#include "AWSSensorManager.h" #include "AWS.h" extern AstroWeatherStation station; diff --git a/src/alpaca_observingconditions.cpp b/src/alpaca_observingconditions.cpp index ce4a4d8..8e5247a 100644 --- a/src/alpaca_observingconditions.cpp +++ b/src/alpaca_observingconditions.cpp @@ -1,27 +1,16 @@ -#include -#include #include #include -#include -#include #include "defaults.h" #include "gpio_config.h" -#include "SC16IS750.h" -#include "AWSGPS.h" #include "AstroWeatherStation.h" -#include "dome.h" #include "alpaca.h" #include "alpaca_observingconditions.h" -#include "SQM.h" -#include "AWSConfig.h" -#include "AWSWeb.h" -#include "AWSSensorManager.h" #include "AWS.h" extern AstroWeatherStation station; -constexpr unsigned int str2int( const char* str, int h ) +constexpr unsigned int str2int( const char* str, int h = 0 ) { return !str[h] ? 5381 : (str2int(str, h+1) * 33) ^ str[h]; } @@ -153,37 +142,38 @@ void alpaca_observingconditions::sensordescription( AsyncWebServerRequest *reque strncpy( tmp, request->getParam(i)->value().c_str(), 31 ); for( j = 0; j < strlen( tmp ); tmp[j]= tolower( tmp[j] ), j++ ); ok = true; - switch( str2int( tmp,0 )) { - case str2int("pressure",0): - case str2int("temperature",0): - case str2int("humidity",0): - case str2int("dewpoint",0): + switch( str2int( tmp )) { + + case str2int("pressure"): + case str2int("temperature"): + case str2int("humidity"): + case str2int("dewpoint"): snprintf( (char *)message_str, 255, "{\"ErrorNumber\":0,\"ErrorMessage\":\"\",\"Value\":\"BME280\",%s}", transaction_details ); break; - case str2int("skybrightness",0): - case str2int("skyquality",0): + case str2int("skybrightness"): + case str2int("skyquality"): snprintf( (char *)message_str, 255, "{\"ErrorNumber\":0,\"ErrorMessage\":\"\",\"Value\":\"TSL 2591\",%s}", transaction_details ); break; - case str2int("cloudcover",0): - case str2int("skytemperature",0): + case str2int("cloudcover"): + case str2int("skytemperature"): snprintf( (char *)message_str, 255, "{\"ErrorNumber\":0,\"ErrorMessage\":\"\",\"Value\":\"MLX 96014\",%s}", transaction_details ); break; - case str2int("rainrate",0): + case str2int("rainrate"): snprintf( (char *)message_str, 255, "{\"ErrorNumber\":0,\"ErrorMessage\":\"\",\"Value\":\"Hydreon RG-9\",%s}", transaction_details ); break; - case str2int("windspeed",0): - case str2int("windgust",0): + case str2int("windspeed"): + case str2int("windgust"): snprintf( (char *)message_str, 255, "{\"ErrorNumber\":0,\"ErrorMessage\":\"\",\"Value\":\"%s\",%s}", station.get_anemometer_sensorname(), transaction_details ); break; - case str2int("winddirection",0): + case str2int("winddirection"): snprintf( (char *)message_str, 255, "{\"ErrorNumber\":0,\"ErrorMessage\":\"\",\"Value\":\"%s\",%s}", station.get_wind_vane_sensorname(), transaction_details ); break; - case str2int("starfwhm",0): + case str2int("starfwhm"): snprintf( (char *)message_str, 255, "{%s,\"ErrorNumber\":1024,\"ErrorMessage\":\"No such sensor: %s\"}", transaction_details, tmp ); break; default: - snprintf( (char *)message_str, 255, "{\"ErrorNumber\":1025,\"ErrorMessage\":\"\",\"Value\":\"No such sensor name\",%s}", station.get_wind_vane_sensorname(), transaction_details ); + snprintf( (char *)message_str, 255, "{\"ErrorNumber\":1025,\"ErrorMessage\":\"\",\"Value\":\"No such sensor name\",%s}", transaction_details ); } } } @@ -315,22 +305,22 @@ void alpaca_observingconditions::timesincelastupdate( AsyncWebServerRequest *req ok = true; switch( str2int( tmp,0 )) { - case str2int("pressure",0): - case str2int("temperature",0): - case str2int("humidity",0): - case str2int("dewpoint",0): - case str2int("skybrightness",0): - case str2int("skyquality",0): - case str2int("cloudcover",0): - case str2int("skytemperature",0): - case str2int("rainrate",0): - case str2int("windspeed",0): - case str2int("windgust",0): - case str2int("winddirection",0): - case str2int("",0): + case str2int("pressure"): + case str2int("temperature"): + case str2int("humidity"): + case str2int("dewpoint"): + case str2int("skybrightness"): + case str2int("skyquality"): + case str2int("cloudcover"): + case str2int("skytemperature"): + case str2int("rainrate"): + case str2int("windspeed"): + case str2int("windgust"): + case str2int("winddirection"): + case str2int(""): snprintf( (char *)message_str, 255, "{\"ErrorNumber\":0,\"ErrorMessage\":\"\",\"Value\":%3.1f,%s}", (double)( now - station.get_sensor_data()->timestamp ), transaction_details ); break; - case str2int("starfwhm",0): + case str2int("starfwhm"): snprintf( (char *)message_str, 255, "{%s,\"ErrorNumber\":1024,\"ErrorMessage\":\"No such sensor: %s\"}", transaction_details, tmp ); break; default: diff --git a/src/alpaca_safetymonitor.cpp b/src/alpaca_safetymonitor.cpp index 2fa12d0..1fa0590 100644 --- a/src/alpaca_safetymonitor.cpp +++ b/src/alpaca_safetymonitor.cpp @@ -1,22 +1,11 @@ -#include -#include #include #include -#include -#include #include "defaults.h" #include "gpio_config.h" -#include "SC16IS750.h" -#include "AWSGPS.h" #include "AstroWeatherStation.h" -#include "dome.h" #include "alpaca.h" #include "alpaca_safetymonitor.h" -#include "SQM.h" -#include "AWSConfig.h" -#include "AWSWeb.h" -#include "AWSSensorManager.h" #include "AWS.h" extern AstroWeatherStation station; diff --git a/src/alpaca_telescope.cpp b/src/alpaca_telescope.cpp index 6253c1e..7bf29dd 100644 --- a/src/alpaca_telescope.cpp +++ b/src/alpaca_telescope.cpp @@ -18,6 +18,8 @@ #include "SQM.h" #include "AWSConfig.h" #include "AWSWeb.h" +#include "Hydreon.h" +#include "AWSWind.h" #include "AWSSensorManager.h" #include "AWS.h" diff --git a/src/defaults.h b/src/defaults.h index 6e67104..d05459f 100644 --- a/src/defaults.h +++ b/src/defaults.h @@ -10,7 +10,7 @@ #define DEFAULT_HAS_ETHERNET 1 #define DEFAULT_HAS_GPS 0 #define DEFAULT_HAS_MLX 0 -#define DEFAULT_HAS_RG9 0 +#define DEFAULT_HAS_RAIN_SENSOR 0 #define DEFAULT_HAS_SC16IS750 1 #define DEFAULT_HAS_TSL 0 #define DEFAULT_HAS_WS 0 diff --git a/src/dome.cpp b/src/dome.cpp index 3f89e28..f441351 100644 --- a/src/dome.cpp +++ b/src/dome.cpp @@ -30,7 +30,7 @@ AWSDome::AWSDome( I2C_SC16IS750 *_sc16is750, SemaphoreHandle_t _i2c_mutex, bool sc16is750 = _sc16is750; close_dome = false; -#ifdef DEFAULT_HAS_SC16IS750 && GPIO_DOME_INDIRECT +#if defined( DEFAULT_HAS_SC16IS750 ) && defined( GPIO_DOME_INDIRECT ) if ( debug_mode ) Serial.printf( "[DEBUG] Enabling dome control through SC16IS750.\n" ); diff --git a/src/gpio_config.h b/src/gpio_config.h index ba183d6..8ae2543 100644 --- a/src/gpio_config.h +++ b/src/gpio_config.h @@ -6,27 +6,26 @@ #include "defaults.h" #endif -// Wind sensors -#define GPIO_WIND_VANE_RX GPIO_NUM_26 // RO Pin -#define GPIO_WIND_VANE_TX GPIO_NUM_14 // DI Pin -#define GPIO_WIND_VANE_CTRL GPIO_NUM_27 // RE+DE Pins +// Misc +#define GPIO_DEBUG GPIO_NUM_34 -#define GPIO_ANEMOMETER_RX GPIO_NUM_33 // RO Pin -#define GPIO_ANEMOMETER_TX GPIO_NUM_25 // DI Pin -#define GPIO_ANEMOMETER_CTRL GPIO_NUM_32 // RE+DE Pins +// Wind sensors +#define GPIO_WIND_SENSOR_RX GPIO_NUM_32 // RO Pin +#define GPIO_WIND_SENSOR_TX GPIO_NUM_25 // DI Pin +#define GPIO_WIND_SENSOR_CTRL GPIO_NUM_33 // RE+DE Pins // Rain sensor -#define GPIO_RG9_RX GPIO_NUM_17 // J2 SO PIN -#define GPIO_RG9_TX GPIO_NUM_16 // J2 SI PIN -#define GPIO_RG9_RAIN GPIO_NUM_35 // J1 OUT PIN +#define GPIO_RAIN_SENSOR_RX GPIO_NUM_17 // J2 SO PIN +#define GPIO_RAIN_SENSOR_TX GPIO_NUM_16 // J2 SI PIN +#define GPIO_RAIN_SENSOR_RAIN GPIO_NUM_35 // J1 OUT PIN #if DEFAULT_HAS_ETHERNET == 0 -#define GPIO_RG9_MCLR GPIO_NUM_23 // J2 MCLR PIN +#define GPIO_RAIN_SENSOR_MCLR GPIO_NUM_23 // J2 MCLR PIN #else -#define GPIO_RG9_MCLR GPIO_NUM_15 // J2 MCLR PIN +#define GPIO_RAIN_SENSOR_MCLR GPIO_NUM_15 // J2 MCLR PIN #endif -// MOSTFET SWITCHES +// MOSFET SWITCHES #define GPIO_ENABLE_3_3V GPIO_NUM_5 #define GPIO_ENABLE_12V GPIO_NUM_18