/*
ard-wunderbresser by Marco Zonca 12/2025, ver. 1.0
this sketch reads Bresser 7-in-1 weather station sensors by the way of LoRa radio on 868Mhz using
LilyGO TTGO LoRa32 T3 1.6.1. (OLED display, micro SD, WiFi, BLE, sx1276); additionally I added an i2c
BME280 pressure/temperature/humidity sensor board and a buzzer; it does data upload to Wunderground.com
website using PWS Protocol via WiFi "https://support.weather.com/s/article/PWS-Upload-Protocol";
it does aswell upload to APRS network as ham radio operator (a proper licence is required or your have
to use a citizen code to send data freely, read https://aprs.fi/doc/guide/guide.html);
it shows failures counters on the display and in case of repeating errors receiving from the weather station,
from NTP server, BME board, or transmitting with WiFi to Wunderground or APRS the buzzer plays an alarm;
the failures counters are resetted every day at 00:00;
Counters shown/display: Time: NTP update missed via network
Bress: Bresser weather station missed, or wrong data reception via radio 868Mhz
BME: BME280 sensor board missed or wrong data via I2C
AR: APRS server refused connection or bad response from it
Rb: Reboot unexpected, i.e. by watchdog
WG: Wunderground server refused connection or bad response from it
WF: WiFi failed to connect to the network
- the circuit works at 3.3v i/o level; it is powered by USB with a consumption around 55-90mA, in case of
blackout it just power down like your WiFi access point, the weather station is battery powered;
if you like to keep the circuit on during blackouts you have to add a rechargeable battery using the onboard
2 pins connector, the USB will provide to recharge it as the MCU board already got a recharging circuit; a battery
model 102535 850/1050 mA/h fits perfectly with my 3D printed enclosure, be sure the connector is a 2 pin JST 1.25mm
and the (+) and (-) wires are correct;
- my experience says to avoid to put the circuit too close to the WiFi access point (1-2m away is fine), in this
way you will have less communication errors;
- special care is taken for some calculation as per the normalized air pressure, the average wind direction,
and the dew point, thanks to Google AI :-). It is active a watchdog so in case of software stuck it will
restart after 60"; the display will be off after 10 minutes, you may read failure counters in APRS raw packets
in the "Comment", aswell for weather station battery status;
- using mac osx and Arduino IDE, to upload the sketch maybe you have to install the USB serial driver for this
circuit, more info here: https://www.wch-ic.com/downloads/CH34XSER_MAC_ZIP.html
- Before compiling and testing you have to specify your Access Point name and Password for your WiFi:
char WiFi_SSID[40] = "XXXXX"; // put your SSID WiFi access point in the code
char WiFi_PASS[40] = "YYYY"; // put your PASSWORD WiFi access point in the code
Another thing to adjust is the local altitude AMSL of the circuit, the mslH constant, to
calculate the normalized air pressure from local to MSL; set aswell your timezone for daily rain
calculation; do not forget to specify your Wunderground weather station ID and PASSWORD, if activated by
the constant "isWUNDERactive=true"; of course you have to insert your APRS data access if activated by
the constant "isAPRSactive=true"; if you are not interested in the Wunderground or the APRS you have
just to put the respective value to "false" and avoid to set other respective values (ID, PASS, callsign, pass, etc.);
due to not precise values of temperature and humidity of my BME280 board I introduced two offset values
maybe you want to personalize aswell:
const float mslH=285; // local altitude AMSL (m) where the circuit is
const long utcOffset = +3600; // timezone for local time in seconds (+3600" = +1h)
char mywunderID[40] = "IDIDID"; // your Wunderground weather station ID
char mywunderPASS[40] = "PsWPswP"; // your Wunderground weather station password
const double bmeTempOffset=-3.4; // BME280 offset internal temperature in C
const double bmeHumiOffset=+12.0; // BME280 offset internal humidity
const char myAPRScallsign[40] = "CALLSIGN-13"; // your ham radio callsign, user
const char myAPRSpasscode[40] = "12345"; // your ham radio passcode, pass
const char myAPRSlatLon[40] = "4018.23N/01435.16E"; // your weather Station Position DDMM.MMy/DDDMM.MMx
const char APRSserver[40] = "rotate.aprs2.net"; // APRS url server
- this sketch is derived from the decoding example code made by Matthias Prinke (read copyright below);
I used the BresserWeatherSensorReceiver library ver. 0.36.3;
before compiling you have to personalize WeatherSensorCfg.h as specified by the author; I modified
the following line numbers commenting/uncommenting as necessary; very important is the line number 93 where you could
enter your station ID if you have more than one around, if you leave it empty you will receive any station ID, but
if you ENTER IT PLEASE NOTE that it will change every station reset or battery change so you need to keep it
updated manually!
89: #define SENSOR_IDS_EXC { }
93: #define SENSOR_IDS_INC { 0xD144 }
101: //#define WIND_DATA_FIXEDPOINT
105: //#define BRESSER_5_IN_1
106: //#define BRESSER_6_IN_1
107: #define BRESSER_7_IN_1
108: //#define BRESSER_LIGHTNING
109: //#define BRESSER_LEAKAGE
151: #define ARDUINO_TTGO_LoRa32_V21new
Modifications log:
------------------
23.12.2025 - APRS_windgustmph was=wu_windgustmph, now=wu_windgustmph_10m
- APRS packet, unused field "p..." (about last 24h rain) now is not present anymore
- appended telemetry_in_comment at the end of the APRS packet (Tn Bn Mn An Rn Wn Nn)
regarding failures counters T=time, B=bresser, M=BME, A=APRS, R=reboots, W=wunder, N=network WiFi
24.12.2025 - corrected APRS_raindaily100in was U.DD (U) now is UDD (U.DD * 100)
- modified code to avoid a leading unwanted space out of String(value,0) for value <10...
- all failure counters >9 will cause the alarm and a MCU restart by watchdog
- print a message log before attempting to receive data from Bresser
06.01.2026 - added options to exclude (active/not active) uploading data to WUNDER and/or APRS
- added sensorBatteryStatus as + (ok) or - (needs change) in telemetry_in_comment
13.01.2026 - OLED is active for 10 minutes then it switch off
- moved wunder and aprs code inside bresser station sensor reading loop
- moved telemetry_in_comment in aprs code
- removed set to zero reboot counter if failures numbers too high (already present during boot)
- added sensorBatteryStatus as + (ok) or - (needs change) display bottom right
- added message log after daily counters reset and after set off the display
*/
//////////////////////////////////////////////////////////////////////////////////////////////////
// BresserWeatherSensorOLED.ino
//
// Example for BresserWeatherSensorReceiver
//
// This sketch prints the received weather sensor values to the on-board OLED display.
// See https://github.com/adafruit/Adafruit_SSD1306/tree/master/examples for text style options
// and scrolling.
//
// Notes:
// - Currently only some boards by LILYGO are supported.
//
// https://github.com/matthias-bs/BresserWeatherSensorReceiver
//
//
// created: 10/2025
//
//
// MIT License
//
// Copyright (c) 2025 Matthias Prinke
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// History:
// 20251006 Created
//
///////////////////////////////////////////////////////////////////////////////////////////////////
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "WeatherSensorCfg.h"
#include "WeatherSensor.h"
#include <WiFi.h>
#include <NTPClient.h>
#include <Adafruit_BME280.h>
#include "TimeLib.h"
#include <esp_task_wdt.h>
#include <Preferences.h>
#include <rom/rtc.h>
const boolean isDebug = true; // set to "true" to activate debug on serial monitor
// if "true" the serial monitor MUST be available or
// the sketch does not run;
//*************************//personalize here//********************************
const unsigned long OLED_time_off = (10 * 60 * 1000); // 10' minutes to switch off display
//*********** WIFI
const char WiFi_SSID[40] = "APNAME"; // put your SSID WiFi access point here
const char WiFi_PASS[40] = "APPASSWORD"; // put your PASSWORD WiFi access point here
const byte maxCONN_attempts = 8; // nr. of WiFi connection attempts
//*********** NTP
const char poolUrl[] = "it.pool.ntp.org"; // NTP url server
const long utcOffset = +3600; // timezone for local time in seconds (+3600" = +1h)
const unsigned long updateNTPRate = (180 * (60 * 1000)); // 180 minutes for NTP update
const byte maxTIME_attempts = 4; // nr. of NTP update attempts
//*********** WUNDERGROUND
const boolean isWUNDERactive=false;
const char mywunderID[40] = "IDIDID"; // your weather station ID
const char mywunderPASS[40] = "PASSPASS"; // your weather station password
const unsigned long wunderUpdateRate = (7 * ((60) * 1000)); // 7 minutes for Wundeground update
//*********** APRS
const boolean isAPRSactive=false;
const char myAPRScallsign[40] = "CALLSIGN-13"; // your ham radio callsign, user
const char myAPRSpasscode[40] = "123456"; // your ham radio passcode, pass
const char myAPRSlatLon[40] = "4512.83N/011369.26E"; // your weather Station Position DDMM.MMy/DDDMM.MMx
const char APRSserver[40] = "rotate.aprs2.net"; // APRS url server
const long APRSport = 14580; // APRS server port
const unsigned long APRSUpdateRate = (11 * ((60) * 1000)); // 11 minutes for APRS update
//*********** BME
const double mslH=210; // local altitude AMSL (m) where the circuit is, for BME280 pressure sensor calc
const double bmeTempOffset=-3.4; // BME280 offset internal temperature in C
const double bmeHumiOffset=+12.0; // BME280 offset internal humidity
//*************************//----------------//********************************
double altitudeft=0;
double Altitude = 0;
double PressurehPa = 0;
double mslP = 0;
double PressurehPaMSL = 0;
double Pressurein = 0;
double PressureinMSL = 0;
double InternalTemperature = 0;
double InternalHumidity = 0;
double initialRain_mm = 0;
double rainLastRead_mm = 0;
double rainLastRead_in = 0;
double rainPreviousReads_mm = 0;
double rainTotalHH_mm = 0;
double rainTotalHH_in = 0;
double rainTotalToday_mm = 0;
double rainTotalToday_in = 0;
double windSpeedkmh = 0;
double windGustkmh = 0;
double min2upd=0;
double dpNC=0;
double dewptC=0;
double dpA=0;
double dpB=0;
double dpC=0;
double calcElaps=0;
byte w02s=0;
byte w02d=0;
byte w10g=0;
byte wunderCount=0;
byte cMM=0;
double wind02speedmph[3]={0,0,0};
double wind02dir[3]={0,0,0};
double wind10gustmph[11]={0,0,0,0,0,0,0,0,0,0,0};
double rainLastHH_mm[60]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
boolean isTimeUpdated = false;
boolean isDailyRainResetted = false;
boolean isTooFailures = false;
boolean isOLEDoff=false;
unsigned long prevNTPMillis = 0;
unsigned long actualNTPMillis = 0;
unsigned int ret = 0;
unsigned long prevSensorMillis = 0;
unsigned long actualSensorMillis = 0;
unsigned long countWindreads = 0;
unsigned long countRainreads = 0;
unsigned long prevOLEDMillis = 0;
unsigned long actualOLEDMillis = 0;
const byte buzzerPin=12;
const byte ledPin=25;
const unsigned long updateSensorRate = (60 * 1000); // 60", Sensors update rate
const double mslL=-0.00065; // standard temperature lapse rate (K/m)
const double mslT=288.15; // standard temperature at sea level (K) [15C]
const double mslG=9.80665; // standard acceleration due to gravity (m/s2)
const double mslR=287.05; // specific gas constant for air (J/(kg*K))
unsigned long prevWundMillis = 0;
unsigned long actualWundMillis = 0;
unsigned long actualAPRSMillis = 0;
unsigned long prevAPRSMillis = 0;
long failures_NTP = 0;
long failures_wunder = 0;
long failures_bresser = 0;
long failures_WIFI = 0;
long failures_BME280 = 0;
long failures_APRS = 0;
long calcRes=0;
String elaps="";
String netResponse="";
String telemetry_in_comment="";
String trimmed="";
int netRsize=640;
int sensorBatteryStatus=0;
time_t epoch = 0;
time_t epochUTC = 0;
WiFiClient client;
Preferences preferences;
double wu_tempf=0;
double wu_humidity=0;
double wu_windspeedmph=0;
double wu_windgustmph=0;
double wu_winddir=0;
double wu_solarradiationwm2=0;
double wu_rainin=0;
double wu_dailyrainin=0;
double wu_UV=0;
double wu_baromin=0;
double wu_indoortempf=0;
double wu_indoorhumidity=0;
double wu_dewptf=0;
double wu_windspdmph_avg2m=0;
double wu_winddir_avg2m=0;
double wu_windgustmph_10m=0;
String wuPacket="";
char wuserver[]="weatherstation.wunderground.com"; // wunderground url
boolean bresser_tempf_ok=false;
boolean bresser_humidity_ok=false;
boolean bresser_wind_ok=false;
boolean bresser_solarradiationwm2_ok=false;
boolean bresser_rainin_ok=false;
boolean bresser_UV_ok=false;
boolean bresser_baromin_ok=false;
boolean bresser_indoortempf_ok=false;
boolean bresser_indoorhumidity_ok=false;
boolean bresser_dewptf_ok=false;
long APRS_winddir=0;
long APRS_windspeedmph=0;
long APRS_windgustmph=0;
long APRS_tempfpos=0;
boolean isAPRS_tempf_negative=false;
long APRS_rain100in=0;
long APRS_raindaily100in=0;
long APRS_barom10hpa=0;
long APRS_humidity=0;
long APRS_day=0;
long APRS_hour=0;
long APRS_minute=0;
long APRS_UV=0;
long APRS_solarradiationwm2=0;
String APRSPacket="";
String APRSlogin = "user " + String(myAPRScallsign) + " pass " + String(myAPRSpasscode) + " vers ArduinoWX 1.0\n";
unsigned int reboot_count=0;
#define WDT_TIMEOUT 60000 // watchdog 60", in millis
#define MAX_SENSORS 1
#define RX_TIMEOUT 180000 // sensor receive timeout [ms]
#define RX_FLAGS (DATA_COMPLETE | DATA_ALL_SLOTS)
#define OLED_RESET -1
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, poolUrl, utcOffset);
WeatherSensor weatherSensor;
Adafruit_BME280 bme;
void setup() {
wuPacket.reserve(640);
APRSPacket.reserve(640);
netResponse.reserve(netRsize);
elaps.reserve(4);
trimmed.reserve(6);
telemetry_in_comment.reserve(128);
if (isDebug) { // Serial port for debugging purposes
Serial.begin(115200);
delay(4000);
while (!Serial) {}
}
if (isDebug == true) {Serial.println();}
if (isDebug == true) {Serial.println(F("------------------------------------------------"));}
if (isDebug == true) {Serial.println(F("-------------------STARTING---------------------"));}
if (isDebug == true) {Serial.println(F("------------------------------------------------"));}
if (isDebug == true) {Serial.println(F("* serial ok *"));}
ret=bme.begin(0x76, &Wire); // default address=0x77
if (ret){
bme.setSampling(Adafruit_BME280::MODE_NORMAL, /* Operating Mode: SLEEP FORCED NORMAL */
Adafruit_BME280::SAMPLING_X16, /* Temperature oversampling: NONE X1 X2 X4 X8 X16 */
Adafruit_BME280::SAMPLING_X16, /* Pressure oversampling: NONE X1 X2 X4 X8 X16 */
Adafruit_BME280::SAMPLING_X16, /* Humidity oversampling: NONE X1 X2 X4 X8 X16 */
Adafruit_BME280::FILTER_X16, /* Filtering: OFF X2 X4 X8 X16 */
Adafruit_BME280::STANDBY_MS_10); /* Standby time: 0_5 10 20 62_5 125 250 500 1000 */
if (isDebug == true) {Serial.println(F("* BME280 ok *"));}
}else{
if (isDebug == true) {Serial.println(F("* BME280 not found! *"));}
failures_BME280++;
}
pinMode(buzzerPin, OUTPUT);
pinMode(ledPin, OUTPUT);
// I2C pin configuration
Wire.begin(OLED_SDA, OLED_SCL);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
log_e("SSD1306 allocation failed");
}
// WATCHDOG
esp_task_wdt_config_t twdt_config = {
.timeout_ms = WDT_TIMEOUT,
.idle_core_mask = (1 << portNUM_PROCESSORS) - 1, // Bitmask of all cores
.trigger_panic = true,
};
esp_task_wdt_deinit(); // wdt is enabled by default, so we need to deinit it first
esp_task_wdt_init(&twdt_config); // panic enabled so ESP32 restarts
esp_task_wdt_add(NULL); // add current thread to WDT watch
if (isDebug) {Serial.println(F("* Watchdog active *"));}
preferences.begin("reboot", false); // reboot counter and why, false means r/w
reboot_count = preferences.getUInt("count", 0);
RESET_REASON reason = rtc_get_reset_reason(0); // Core 0
if (reason != POWERON_RESET) {
if (isDebug) {Serial.println(F("* ALERT: Last reset caused by watchdog or unusual reason! *"));}
switch (reason) {
case 1: if (isDebug) {Serial.println(F("POWERON_RESET, 1, Vbat power on reset")); break;}
case 3: if (isDebug) {Serial.println(F("SW_RESET, 3, Software reset")); break;}
case 4: if (isDebug) {Serial.println(F("OWDT_RESET, 4, Legacy watch dog reset")); break;}
case 5: if (isDebug) {Serial.println(F("DEEPSLEEP_RESET, 5, Deep Sleep reset")); break;}
case 6: if (isDebug) {Serial.println(F("SDIO_RESET, 6, Reset by SLC module")); break;}
case 7: if (isDebug) {Serial.println(F("TG0WDT_SYS_RESET, 7, Timer Group0 Watch dog reset")); break;}
case 8: if (isDebug) {Serial.println(F("TG1WDT_SYS_RESET, 8, Timer Group1 Watch dog reset")); break;}
case 9: if (isDebug) {Serial.println(F("RTCWDT_SYS_RESET, 9, RTC Watch dog reset")); break;}
case 10: if (isDebug) {Serial.println(F("INTRUSION_RESET, 10, Instrusion tested to reset CPU")); break;}
case 11: if (isDebug) {Serial.println(F("TGWDT_CPU_RESET, 11, Time Group reset CPU")); break;}
case 12: if (isDebug) {Serial.println(F("SW_CPU_RESET, 12, Software reset CPU")); break;}
case 13: if (isDebug) {Serial.println(F("RTCWDT_CPU_RESET, 13, RTC Watch dog Reset CPU")); break;}
case 14: if (isDebug) {Serial.println(F("EXT_CPU_RESET, 14, for APP CPU, reset by PRO CPU")); break;}
case 15: if (isDebug) {Serial.println(F("RTCWDT_BROWN_OUT_RESET, 15, Reset when the vdd voltage is not stable")); break;}
case 16: if (isDebug) {Serial.println(F("RTCWDT_RTC_RESET, 16, RTC Watch dog reset digital core and rtc module")); break;}
default: if (isDebug) {Serial.println(F("NO_MEAN, unknown reason")); break;}
}
reboot_count++;
preferences.putUInt("count", reboot_count);
}
preferences.end();
if (isDebug) {Serial.print(F("Actual watchdog/unusual reboot counter = "));}
if (isDebug) {Serial.println(reboot_count);}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0,0); // Start at top-left corner
display.cp437(true); // Use full 256 char 'Code Page 437' font
display.println(F(" Bresser Weather "));
display.println(F(" + BME sensors RX "));
display.println(F(" Wunderground/APRS TX"));
display.println(F(" 868Mhz + WiFi "));
display.display(); // Show initial text
tone (buzzerPin,1000,(1*1000)); // Hz, m/sec
delay(6000);
weatherSensor.begin();
weatherSensor.setSensorsCfg(MAX_SENSORS, RX_FLAGS);
setTime(967680000); // initial datetime: 31/08/2000 00:00:00 in epoch format
if (strcmp(WiFi_SSID,"") != 0) { // update NTP time
connectWiFi();
TimeNTPUpdate();
WiFidisconnect();
} else {
if (isDebug) {Serial.println(F("Empty SSID, no WiFi, noTimeSet!"));}
}
if (isTimeUpdated==false) {
display.clearDisplay();
display.setCursor(0,0);
display.println(F(" Not possible to "));
display.println(F(" get initial Time "));
display.println(F(" via WiFi!! "));
display.println(F(" ** STOPPED ** "));
display.display();
while(true) { // stop running, not possible to continue without actual time, MCU will restart by watchdog
display.invertDisplay(true);
tone (buzzerPin,800,(1*1000)); // Hz, m/sec
delay(1000);
display.invertDisplay(false);
tone (buzzerPin,1100,(1*1000)); // Hz, m/sec
delay(1000);
}
}
}//setup()
void loop() {
//--------------- daily counters reset at 00:00 l.t.
if (((hour() == 0) && (minute() == 0)) && (isDailyRainResetted == false)){
rainTotalToday_mm=0;
failures_NTP = 0;
failures_wunder = 0;
failures_bresser = 0;
failures_WIFI = 0;
failures_BME280 = 0;
failures_APRS = 0;
if (reboot_count != 0) {
preferences.begin("reboot", false);
reboot_count = 0;
preferences.putUInt("count", reboot_count);
preferences.end();
}
isDailyRainResetted=true;
if (isDebug) {Serial.println(F("Daily counters reset done."));}
}
if (((hour() == 0) && (minute() != 0)) && (isDailyRainResetted == true)){ // prepare for next reset
isDailyRainResetted=false;
}
//--------------- time to switch off OLED
actualOLEDMillis=millis();
if (prevOLEDMillis > actualOLEDMillis) { // manage millis() rollover
prevOLEDMillis=actualOLEDMillis;
actualOLEDMillis=prevOLEDMillis + OLED_time_off;
}
if (((prevOLEDMillis + OLED_time_off) <= actualOLEDMillis) && (isOLEDoff==false)) {
display.ssd1306_command(SSD1306_DISPLAYOFF);
isOLEDoff=true;
if (isDebug) {Serial.println(F("OLED timeout, set it OFF."));}
prevOLEDMillis=millis();
}
actualSensorMillis=millis();
if (prevSensorMillis > actualSensorMillis) { // manage millis() rollover
prevSensorMillis=actualSensorMillis;
actualSensorMillis=prevSensorMillis + updateSensorRate;
}
//*********************************************************************************************** Bresser
if (((prevSensorMillis + updateSensorRate) <= actualSensorMillis) || prevSensorMillis==0) {
weatherSensor.clearSlots();
if (isDebug) {Serial.println(F("Attempt to receive data from Bresser sensors until RX_TIMEOUT"));}
#if (CORE_DEBUG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO)
bool decode_ok = weatherSensor.getData(RX_TIMEOUT, RX_FLAGS, 0, nullptr);
#else
weatherSensor.getData(RX_TIMEOUT, RX_FLAGS, 0, nullptr);
#endif
log_i("decode_ok: %d", decode_ok);
if (isDebug) {printTime();}
cMM=minute();
epochUTC = (now() - utcOffset);
APRS_day=day(epochUTC);
APRS_hour=hour(epochUTC);
APRS_minute=minute(epochUTC);
for (size_t i = 0; i < weatherSensor.sensor.size(); i++) {
if (!weatherSensor.sensor[i].valid) continue;
if ((weatherSensor.sensor[i].s_type == SENSOR_TYPE_WEATHER0) || (weatherSensor.sensor[i].s_type == SENSOR_TYPE_WEATHER1)) {
//----------------------------------------------------------------- read external sensors via radio 868Mhz
if (isDebug) {Serial.print(F("Bresser weather sensor actual ID="));}
if (isDebug) {Serial.println(weatherSensor.sensor[i].sensor_id,HEX);}
sensorBatteryStatus=weatherSensor.sensor[i].battery_ok;
if ((isDebug) && (sensorBatteryStatus == 1)) {Serial.println(F("Bresser battery OK"));}
if ((isDebug) && (sensorBatteryStatus == 0)) {Serial.println(F("** Bresser battery LOW! **"));}
if (weatherSensor.sensor[i].w.temp_ok) { // wunderground "tempf"
bresser_tempf_ok=true;
if (isDebug == true) {Serial.print(F("Temperature C = "));}
if (isDebug == true) {Serial.print(weatherSensor.sensor[i].w.temp_c,1);}
wu_tempf=(weatherSensor.sensor[i].w.temp_c * 9 / 5) + 32;
wu_tempf=Round1Dec(wu_tempf);
APRS_tempfpos=Round0Dec(wu_tempf);
if (APRS_tempfpos < 0){
isAPRS_tempf_negative=true;
APRS_tempfpos=(APRS_tempfpos * -1);
}else{
isAPRS_tempf_negative=false;
}
if (isDebug == true) {Serial.print(F(", F = "));}
if (isDebug == true) {Serial.println(wu_tempf,1);}
}else{
bresser_tempf_ok=false;
if (isDebug == true) {Serial.println(F("* failed to read Bresser tempf, value not set *"));}
failures_bresser++;
}
if (weatherSensor.sensor[i].w.humidity_ok) { // wunderground "humidity"
bresser_humidity_ok=true;
if (isDebug == true) {Serial.print(F("Humidity % = "));}
if (isDebug == true) {Serial.print(weatherSensor.sensor[i].w.humidity,1);}
wu_humidity=weatherSensor.sensor[i].w.humidity;
wu_humidity=Round0Dec(wu_humidity);
APRS_humidity=wu_humidity;
if (isDebug == true) {Serial.print(F(" = "));}
if (isDebug == true) {Serial.println(wu_humidity,0);}
}else{
bresser_humidity_ok=false;
if (isDebug == true) {Serial.println(F("* failed to read Bresser humidity, value not set *"));}
failures_bresser++;
}
if ((weatherSensor.sensor[i].w.temp_ok) && (weatherSensor.sensor[i].w.humidity_ok)) { // wunderground dewptf
bresser_dewptf_ok=true;
dpA=(weatherSensor.sensor[i].w.humidity / 100.0F);
dpB=(17.27 * weatherSensor.sensor[i].w.temp_c);
dpC=(237.3 + weatherSensor.sensor[i].w.temp_c);
dpNC=(log(dpA) + (dpB / dpC)) / 17.27;
dewptC=(237.3 * dpNC) / (1 - dpNC);
wu_dewptf=(dewptC * 9 / 5) + 32;
wu_dewptf=Round1Dec(wu_dewptf);
if (isDebug == true) {Serial.print(F("Dew Point C = "));}
if (isDebug == true) {Serial.print(dewptC,1);}
if (isDebug == true) {Serial.print(F(", F = "));}
if (isDebug == true) {Serial.println(wu_dewptf,1);}
}else{
bresser_dewptf_ok=false;
if (isDebug == true) {Serial.println(F("* failed to calculate Bresser dewptf, value not set *"));}
failures_bresser++;
}
if (weatherSensor.sensor[i].w.wind_ok) { // wunderground/APRS "windspeedmph", "windgustmph", "winddir"
bresser_wind_ok=true;
countWindreads++;
if (isDebug == true) {Serial.print(F("Wind counter = "));}
if (isDebug == true) {Serial.println(countWindreads);}
windSpeedkmh=(weatherSensor.sensor[i].w.wind_avg_meter_sec * 3.6);
windSpeedkmh=Round1Dec(windSpeedkmh);
if (isDebug == true) {Serial.print(F("Wind km/h = "));}
if (isDebug == true) {Serial.print(windSpeedkmh,1);}
if (isDebug == true) {Serial.print(F(", m/s = "));}
if (isDebug == true) {Serial.print(weatherSensor.sensor[i].w.wind_avg_meter_sec,1);}
wu_windspeedmph=(weatherSensor.sensor[i].w.wind_avg_meter_sec * 2.237);
wu_windspeedmph=Round1Dec(wu_windspeedmph);
APRS_windspeedmph=Round0Dec(wu_windspeedmph);
if (isDebug == true) {Serial.print(F(", mph = "));}
if (isDebug == true) {Serial.print(wu_windspeedmph,1);}
w02s=(wind02speedmph[0] + 1); // average speed mph last 2 minutes
if (w02s>2) {w02s=1;}
wind02speedmph[w02s]=wu_windspeedmph;
wind02speedmph[0]=w02s;
wu_windspdmph_avg2m=0;
for (byte c=1;c<=2;c++){
wu_windspdmph_avg2m = (wu_windspdmph_avg2m + wind02speedmph[c]);
}
if (countWindreads > 1) {
wu_windspdmph_avg2m = (wu_windspdmph_avg2m / 2.0F);
}else{
wu_windspdmph_avg2m = (wu_windspdmph_avg2m / 1.0F);
}
wu_windspdmph_avg2m=Round1Dec(wu_windspdmph_avg2m);
if (isDebug == true) {Serial.print(F(", avg last 2' mph = "));}
if (isDebug == true) {Serial.println(wu_windspdmph_avg2m,1);}
windGustkmh=(weatherSensor.sensor[i].w.wind_gust_meter_sec * 3.6);
windGustkmh=Round1Dec(windGustkmh);
if (isDebug == true) {Serial.print(F("Wind gust km/h = "));}
if (isDebug == true) {Serial.print(windGustkmh,1);}
if (isDebug == true) {Serial.print(F(", gust m/s = "));}
if (isDebug == true) {Serial.print(weatherSensor.sensor[i].w.wind_gust_meter_sec,1);}
wu_windgustmph=(weatherSensor.sensor[i].w.wind_gust_meter_sec * 2.237);
wu_windgustmph=Round1Dec(wu_windgustmph);
if (isDebug == true) {Serial.print(F(", gust mph = "));}
if (isDebug == true) {Serial.print(wu_windgustmph,1);}
w10g=(wind10gustmph[0] + 1); // top gust mph last 10 minutes
if (w10g>10) {w10g=1;}
wind10gustmph[w10g]=wu_windgustmph;
wind10gustmph[0]=w10g;
wu_windgustmph_10m=0;
for (byte c=1;c<=10;c++){
if (wind10gustmph[c] > wu_windgustmph_10m) {wu_windgustmph_10m = wind10gustmph[c];}
}
wu_windgustmph_10m=Round1Dec(wu_windgustmph_10m);
APRS_windgustmph=Round0Dec(wu_windgustmph_10m);
if (isDebug == true) {Serial.print(F(", last 10' gust mph = "));}
if (isDebug == true) {Serial.println(wu_windgustmph_10m,1);}
if (isDebug == true) {Serial.print(F("Wind Direction deg = "));}
if (isDebug == true) {Serial.print(weatherSensor.sensor[i].w.wind_direction_deg,0);}
wu_winddir=weatherSensor.sensor[i].w.wind_direction_deg;
wu_winddir=Round0Dec(wu_winddir);
APRS_winddir=wu_winddir;
if (isDebug == true) {Serial.print(F(" = "));}
if (isDebug == true) {Serial.print(wu_winddir,0);}
w02d=(wind02dir[0] + 1); // calc average dir last 2 minutes
if (w02d>2) {w02d=1;}
wind02dir[w02d]=wu_winddir;
wind02dir[0]=w02d;
if (countWindreads > 1){ // two values to average
double rad1=(wind02dir[1] * (PI / 180));
double rad2=(wind02dir[2] * (PI / 180));
double sumX=(sin(rad1) + sin(rad2));
double sumY=(cos(rad1) + cos(rad2));
double radResult=(atan2(sumX, sumY));
wu_winddir_avg2m=(radResult * (180 / PI));
if (wu_winddir_avg2m == 360) {wu_winddir_avg2m = 0;}
if (wu_winddir_avg2m < 0) {wu_winddir_avg2m += 360;}
}else{ // just one value, the first
wu_winddir_avg2m = wind02dir[1];
}
wu_winddir_avg2m=Round0Dec(wu_winddir_avg2m);
if (wu_winddir_avg2m == 0) {wu_winddir_avg2m=360;} // I do not want 0 as direction, so 360 instead...
if (isDebug == true) {Serial.print(F(", avg last 2' dir deg = "));}
if (isDebug == true) {Serial.println(wu_winddir_avg2m,0);}
}else{
bresser_wind_ok=false;
if (isDebug == true) {Serial.println(F("* failed to read Bresser wind, values speed/gust/dir not set *"));}
failures_bresser++;
}
if (weatherSensor.sensor[i].w.light_ok) { // wunderground "solarradiation" in W/m2
bresser_solarradiationwm2_ok=true;
if (isDebug == true) {Serial.print(F("Light klx = "));}
if (isDebug == true) {Serial.print(weatherSensor.sensor[i].w.light_klx,2);}
wu_solarradiationwm2=(weatherSensor.sensor[i].w.light_klx * 7.9 );
wu_solarradiationwm2=Round2Dec(wu_solarradiationwm2);
APRS_solarradiationwm2=Round0Dec(wu_solarradiationwm2);
if (isDebug == true) {Serial.print(F(", Light W/m2 = "));}
if (isDebug == true) {Serial.println(wu_solarradiationwm2,2);}
}else{
bresser_solarradiationwm2_ok=false;
if (isDebug == true) {Serial.println(F("* failed to read Bresser solarradiationwm2, value not set *"));}
failures_bresser++;
}
if (weatherSensor.sensor[i].w.rain_ok) { // wunderground "rainin"
bresser_rainin_ok=true;
countRainreads++;
if (isDebug == true) {Serial.print(F("Rain counter = "));}
if (isDebug == true) {Serial.print(countRainreads);}
if ((countRainreads==1) || (initialRain_mm > weatherSensor.sensor[i].w.rain_mm)) { // initial or rollover
initialRain_mm = weatherSensor.sensor[i].w.rain_mm;
rainPreviousReads_mm = 0;
}
if (isDebug == true) {Serial.print(F(", sensor = "));}
if (isDebug == true) {Serial.print(weatherSensor.sensor[i].w.rain_mm,2);}
if (isDebug == true) {Serial.print(F(", initial = "));}
if (isDebug == true) {Serial.print(initialRain_mm,2);}
rainLastRead_mm=(weatherSensor.sensor[i].w.rain_mm - initialRain_mm - rainPreviousReads_mm);
if (isDebug == true) {Serial.print(F(", last = "));}
if (isDebug == true) {Serial.print(rainLastRead_mm,2);}
if (isDebug == true) {Serial.print(F(", previous = "));}
if (isDebug == true) {Serial.println(rainPreviousReads_mm,2);}
if (isDebug == true) {Serial.print(F("Rain mm = "));} // rain last minute
if (isDebug == true) {Serial.print(rainLastRead_mm,2);}
rainLastRead_in=(rainLastRead_mm / 25.4);
rainLastRead_in=Round2Dec(rainLastRead_in);
if (isDebug == true) {Serial.print(F(", in = "));}
if (isDebug == true) {Serial.println(rainLastRead_in,2);}
rainLastHH_mm[cMM]=rainLastRead_mm; // rain last HH (past 60')
rainTotalHH_mm=0;
for (byte m=0;m<=59;m++){
rainTotalHH_mm += rainLastHH_mm[m];
}
if (isDebug == true) {Serial.print(F("Rain last Hour (last 60') mm = "));}
if (isDebug == true) {Serial.print(rainTotalHH_mm,2);}
wu_rainin=(rainTotalHH_mm / 25.4);
wu_rainin=Round2Dec(wu_rainin);
APRS_rain100in=(wu_rainin * 100);
if (isDebug == true) {Serial.print(F(", in = "));}
if (isDebug == true) {Serial.println(wu_rainin,2);}
if (isDebug == true) {Serial.print(F("Rain last 60' (DETAILED) mm = "));}
for (byte m=0;m<=59;m++){
if (isDebug == true) {Serial.print(rainLastHH_mm[m],2);}
if (isDebug == true) {Serial.print(F(" "));}
}
if (isDebug == true) {Serial.println(F(""));}
rainTotalToday_mm += rainLastRead_mm; // rain today since 00:00 local time
if (isDebug == true) {Serial.print(F("Rain Today (since 00:00) mm = "));}
if (isDebug == true) {Serial.print(rainTotalToday_mm,2);}
rainTotalToday_in=(rainTotalToday_mm / 25.4);
rainTotalToday_in=Round2Dec(rainTotalToday_in);
wu_dailyrainin=rainTotalToday_in;
APRS_raindaily100in=(wu_dailyrainin * 100);
if (isDebug == true) {Serial.print(F(", in = "));}
if (isDebug == true) {Serial.println(wu_dailyrainin,2);}
rainPreviousReads_mm += rainLastRead_mm;
}else{
bresser_rainin_ok=false;
if (isDebug == true) {Serial.println(F("* failed to read Bresser rainin, values rainin/daily not set *"));}
failures_bresser++;
}
if (weatherSensor.sensor[i].w.uv_ok) { // wunderground "UV"
bresser_UV_ok=true;
if (isDebug == true) {Serial.print(F("UV Index = "));}
if (isDebug == true) {Serial.print(weatherSensor.sensor[i].w.uv,1);}
wu_UV=weatherSensor.sensor[i].w.uv;
wu_UV=Round1Dec(wu_UV);
APRS_UV=Round0Dec(wu_UV);
if (isDebug == true) {Serial.print(F(" = "));}
if (isDebug == true) {Serial.println(wu_UV,1);}
}else{
bresser_UV_ok=false;
if (isDebug == true) {Serial.println(F("* failed to read Bresser UV, value not set *"));}
failures_bresser++;
}
//----------------------------------------------------------------- read internal BME280 sensors via I2C
PressurehPa = (bme.readPressure()/100.0F); // wunderground "baromin"
if (PressurehPa >= 356.00 && PressurehPa <= 1100.00) { // ok if lower than 8000m, higher than -700m
bresser_baromin_ok=true;
PressurehPa=Round2Dec(PressurehPa);
mslP=PressurehPa;
if (isDebug == true) {Serial.print(F("Local Pressure hPa = "));}
if (isDebug == true) {Serial.print(PressurehPa,2);}
Pressurein=(PressurehPa * 0.02953);
Pressurein=Round3Dec(Pressurein);
if (isDebug == true) {Serial.print(F(", inHg = "));}
if (isDebug == true) {Serial.println(Pressurein,3);}
PressurehPaMSL = mslP * pow( (1-(mslL * mslH / mslT)), ((mslG * -1) / (mslL * mslR)) ); // normalized pressure to MSL
PressurehPaMSL=Round2Dec(PressurehPaMSL);
if (isDebug == true) {Serial.print(F("Normalized Pressure hPa = "));}
if (isDebug == true) {Serial.print(PressurehPaMSL,2);}
PressureinMSL=(PressurehPaMSL * 0.02953);
wu_baromin=PressureinMSL;
wu_baromin=Round3Dec(wu_baromin);
APRS_barom10hpa=Round0Dec((PressurehPaMSL * 10));
if (isDebug == true) {Serial.print(F(", inHg = "));}
if (isDebug == true) {Serial.println(wu_baromin,3);}
Altitude = bme.readAltitude(PressurehPaMSL); // calculated with normalized MSL pressure
Altitude=Round0Dec(Altitude);
if (isDebug == true) {Serial.print(F("Normalized Altitude m = "));}
if (isDebug == true) {Serial.print(Altitude,0);}
altitudeft=(Altitude * 3.28084);
altitudeft=Round0Dec(altitudeft);
if (isDebug == true) {Serial.print(F(", ft = "));}
if (isDebug == true) {Serial.println(altitudeft,0);}
}else{
bresser_baromin_ok=false;
if (isDebug == true) {Serial.println(F("* failed to read Pressure, crazy values, value baromin not set *"));}
failures_BME280++;
}
InternalTemperature = (bme.readTemperature() + bmeTempOffset); // wunderground "indoortempf"
if (InternalTemperature >= -20.00 && InternalTemperature <= 50) {
bresser_indoortempf_ok=true;
if (isDebug == true) {Serial.print(F("Internal Temp C = "));}
if (isDebug == true) {Serial.print(InternalTemperature,1);}
wu_indoortempf=(InternalTemperature * 9 / 5) + 32;
wu_indoortempf=Round1Dec(wu_indoortempf);
if (isDebug == true) {Serial.print(F(", F = "));}
if (isDebug == true) {Serial.println(wu_indoortempf,1);}
}else{
bresser_indoortempf_ok=false;
if (isDebug == true) {Serial.println(F("* failed to read Internal Temperature, crazy value, not set *"));}
failures_BME280++;
}
InternalHumidity = (bme.readHumidity() + bmeHumiOffset); // wunderground "indoorhumidity"
if (InternalHumidity >= 0 && InternalHumidity <= 100) {
bresser_indoorhumidity_ok=true;
if (isDebug == true) {Serial.print(F("Internal Hum % = "));}
if (isDebug == true) {Serial.print(InternalHumidity,0);}
wu_indoorhumidity=InternalHumidity;
wu_indoorhumidity=Round0Dec(wu_indoorhumidity);
if (isDebug == true) {Serial.print(F(" = "));}
if (isDebug == true) {Serial.println(wu_indoorhumidity,0);}
}else{
bresser_indoorhumidity_ok=false;
if (isDebug == true) {Serial.println(F("* failed to read Internal Humidity, crazy value, not set *"));}
failures_BME280++;
}
delay(100);
prevSensorMillis=millis();
//----------------------------------------------------------------- uploading to Wunderground.com
actualWundMillis=millis();
if (prevWundMillis > actualWundMillis) { // manage millis() rollover
prevWundMillis=actualWundMillis;
actualWundMillis=prevWundMillis + wunderUpdateRate;
}
if ((isWUNDERactive == true) && ((prevWundMillis + wunderUpdateRate) <= actualWundMillis)) {
elaps="WUG";
showDisplay();
wuPacket = "GET /weatherstation/updateweatherstation.php?";
wuPacket += "ID=";
wuPacket += mywunderID;
wuPacket += "&PASSWORD=";
wuPacket += mywunderPASS;
wuPacket += "&dateutc=now";
if (bresser_wind_ok==true) {
trimmed=String(wu_winddir,0);
trimmed.trim();
wuPacket += "&winddir=" + trimmed;
trimmed=String(wu_winddir_avg2m,0);
trimmed.trim();
wuPacket += "&winddir_avg2m=" + trimmed;
wuPacket += "&windspeedmph=" + String(wu_windspeedmph,1);
wuPacket += "&windspdmph_avg2m=" + String(wu_windspdmph_avg2m,1);
wuPacket += "&windgustmph=" + String(wu_windgustmph,1);
wuPacket += "&windgustmph_10m=" + String(wu_windgustmph_10m,1);
}
if (bresser_tempf_ok==true) {wuPacket += "&tempf=" + String(wu_tempf,1);}
if (bresser_dewptf_ok==true) {wuPacket += "&dewptf=" + String(wu_dewptf,1);}
if (bresser_rainin_ok==true) {wuPacket += "&rainin=" + String(wu_rainin,2);}
if (bresser_rainin_ok==true) {wuPacket += "&dailyrainin=" + String(wu_dailyrainin,2);}
if (bresser_baromin_ok==true) {wuPacket += "&baromin=" + String(wu_baromin,3);}
if (bresser_humidity_ok==true) {
trimmed=String(wu_humidity,0);
trimmed.trim();
wuPacket += "&humidity=" + trimmed;
}
if (bresser_solarradiationwm2_ok==true) {wuPacket += "&solarradiation=" + String(wu_solarradiationwm2,2);}
if (bresser_UV_ok==true) {wuPacket += "&UV=" + String(wu_UV,1);}
if (bresser_indoortempf_ok==true) {wuPacket += "&indoortempf=" + String(wu_indoortempf,1);}
if (bresser_indoorhumidity_ok==true) {
trimmed=String(wu_indoorhumidity,0);
trimmed.trim();
wuPacket += "&indoorhumidity=" + trimmed;
}
wuPacket += "&softwaretype=ABW%20version1.0&action=updateraw";
wuPacket += " HTTP/1.1\r\n";
wuPacket += "Host: ";
wuPacket += wuserver;
wuPacket += "\r\n";
wuPacket += "Connection: close\r\n\r\n";
if (isDebug == true) {Serial.println(F("------------------------------------------------"));}
if (isDebug == true) {Serial.println(F("WuPacket ============ START"));}
if (isDebug == true) {Serial.print(F("''"));}
if (isDebug == true) {Serial.print(wuPacket);}
if (isDebug == true) {Serial.println(F("''"));}
if (isDebug == true) {Serial.println(F("WuPacket ============ END"));}
connectWiFi();
sendWUNDERnet();
WiFidisconnect();
prevWundMillis=millis();
}
//----------------------------------------------------------------- uploading to aprs.fi
actualAPRSMillis=millis();
if (prevAPRSMillis > actualAPRSMillis) { // manage millis() rollover
prevAPRSMillis=actualAPRSMillis;
actualAPRSMillis=prevAPRSMillis + APRSUpdateRate;
}
if ((isAPRSactive == true) && ((prevAPRSMillis + APRSUpdateRate) <= actualAPRSMillis)) {
elaps="APR";
showDisplay();
// telemtry_in_comment
telemetry_in_comment="";
telemetry_in_comment +="T";
if (failures_NTP <= 9) {
telemetry_in_comment += String(failures_NTP);
}else{
telemetry_in_comment += "!";
}
telemetry_in_comment += " B";
if (failures_bresser <= 9) {
telemetry_in_comment += String(failures_bresser);
}else{
telemetry_in_comment += "!";
}
telemetry_in_comment += " M";
if (failures_BME280 <= 9) {
telemetry_in_comment += String(failures_BME280);
}else{
telemetry_in_comment += "!";
}
telemetry_in_comment += " A";
if (failures_APRS <= 9) {
telemetry_in_comment += String(failures_APRS);
}else{
telemetry_in_comment += "!";
}
telemetry_in_comment += " R";
if (reboot_count <= 9) {
telemetry_in_comment += String(reboot_count);
}else{
telemetry_in_comment += "!";
}
telemetry_in_comment += " W";
if (failures_wunder <= 9) {
telemetry_in_comment += String(failures_wunder);
}else{
telemetry_in_comment += "!";
}
telemetry_in_comment += " N";
if (failures_WIFI <= 9) {
telemetry_in_comment += String(failures_WIFI);
}else{
telemetry_in_comment += "!";
}
if (sensorBatteryStatus == 1){
telemetry_in_comment += " +";
}else{
telemetry_in_comment += " -";
}
// APRS PACKET
APRSPacket="";
APRSPacket += myAPRScallsign;
APRSPacket += ">APRS,TCPIP*:";
APRSPacket += "@";
if (APRS_day < 10) {APRSPacket += "0";}
APRSPacket += String(APRS_day);
if (APRS_hour < 10) {APRSPacket += "0";}
APRSPacket += String(APRS_hour);
if (APRS_minute < 10) {APRSPacket += "0";}
APRSPacket += String(APRS_minute);
APRSPacket += "z";
APRSPacket += myAPRSlatLon;
if (bresser_wind_ok == true) {
APRSPacket += "_";
if ((APRS_winddir > 9) && (APRS_winddir <100)) {APRSPacket += "0";}
if (APRS_winddir < 10) {APRSPacket += "00";}
APRSPacket += String(APRS_winddir);
APRSPacket += "/";
if ((APRS_windspeedmph > 9) && (APRS_windspeedmph <100)) {APRSPacket += "0";}
if (APRS_windspeedmph < 10) {APRSPacket += "00";}
APRSPacket += String(APRS_windspeedmph);
APRSPacket += "g";
if ((APRS_windgustmph > 9) && (APRS_windgustmph <100)) {APRSPacket += "0";}
if (APRS_windgustmph < 10) {APRSPacket += "00";}
APRSPacket += String(APRS_windgustmph);
}else{
APRSPacket += "_.../...g...";
}
if (bresser_tempf_ok==true) {
APRSPacket += "t";
if (isAPRS_tempf_negative == true) {
if (APRS_tempfpos < 10) {APRSPacket += "-0";}
if (APRS_tempfpos > 9) {APRSPacket += "-";}
}else{
if ((APRS_tempfpos > 9) && (APRS_tempfpos <100)) {APRSPacket += "0";}
if (APRS_tempfpos < 10) {APRSPacket += "00";}
}
APRSPacket += String(APRS_tempfpos);
}else{
APRSPacket += "t...";
}
if (bresser_rainin_ok==true) {
...
This file has been truncated, please download it to see its full contents.
Comments