#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3
#include <Adafruit_Sensor.h>            // Adafruit Lib for sensors
#include <Adafruit_BME280.h>            // Adafruit Lib for BME280
#include <Adafruit_BMP280.h>            // Adafruit Lib for BMP280
#include <Adafruit_BMP085.h>            // Adafruit Lib for BMP085 and BMP180
#include <HTU21D.h>                     // Lib for SHT21/HTU21
#include "AS5600.h"                     // Lib for magnetic rotation sensor AS5600
#include <INA226.h>                     // Lib for power management IC INA226
#include <Ticker.h>                     // Timer Lib for timer
#include <RTClib.h>                     // DS1388 RTC
#include <OneWire.h>                    // 1Wire Lib
#include <DallasTemperature.h>          // Lib for DS18B20
#include "OBPSensorTask.h"              // Lib for sensor reading
#include "OBP60Hardware.h"              // Hardware definitions
#include "N2kMessages.h"                // Lib for NMEA2000
#include "NMEA0183.h"                   // Lib for NMEA0183
#include "ObpNmea0183.h"                // Check NMEA0183 sentence for uncorrect content
#include "OBP60Extensions.h"            // Lib for hardware extensions
#include "movingAvg.h"                  // Lib for moving average building
#include "time.h"                       // For getting NTP time
#include <ESP32Time.h>                  // Internal ESP32 RTC clock

// Timer for hardware functions
Ticker Timer1(blinkingFlashLED, 500);   // Start Timer1 for flash LED all 500ms

// Initialization for all sensors (RS232, I2C, 1Wire, IOs)
//####################################################################################

void sensorTask(void *param){
    SharedData *shared = (SharedData *)param;
    GwApi *api = shared->api;
    GwLog *logger = api->getLogger();
    LOG_DEBUG(GwLog::LOG, "Sensor task started");
    SensorData sensors;
    ObpNmea0183 NMEA0183;

    Adafruit_BME280 bme280;         // Evironment sensor BME280
    Adafruit_BMP280 bmp280;         // Evironment sensor BMP280
    Adafruit_BMP085 bmp085;         // Evironment sensor BMP085 and BMP180
    HTU21D sht21(HTU21D_RES_RH12_TEMP14); // Environment sensor SHT21 and HTU21
    AMS_5600 as5600;                // Rotation sensor AS5600
    INA226 ina226_1(INA226_I2C_ADDR1);// Power management sensor INA226 Battery
    INA226 ina226_2(INA226_I2C_ADDR2);// Power management sensor INA226 Solar
    INA226 ina226_3(INA226_I2C_ADDR3);// Power management sensor INA226 Generator
    RTC_DS1388 ds1388;              // RTC DS1388
    OneWire oneWire(OBP_1WIRE);     // 1Wire bus
    DallasTemperature ds18b20(&oneWire);// Sensors for DS18B20
    DeviceAddress tempDeviceAddress;// Table for DS18B20 device addresses 

    // Init sensor stuff
    bool oneWire_ready = false;     // 1Wire initialized and ready to use
    bool iRTC_ready = false;        // Software RTC initialized and ready to use
    bool RTC_ready = false;         // DS1388 initialized and ready to use
    bool GPS_ready = false;         // GPS initialized and ready to use
    bool N2K_GPS_ready = false;     // GPS time on N2K bus
    bool BME280_ready = false;      // BME280 initialized and ready to use
    bool BMP280_ready = false;      // BMP280 initialized and ready to use
    bool BMP180_ready = false;      // BMP180 initialized and ready to use
    bool SHT21_ready = false;       // SHT21 initialized and ready to use
    bool AS5600_ready = false;      // AS5600 initialized and ready to use
    bool INA226_1_ready = false;    // INA226_1 initialized and ready to use
    bool INA226_2_ready = false;    // INA226_2 initialized and ready to use
    bool INA226_3_ready = false;    // INA226_3 initialized and ready to use

    // Create integer arrays for average building
    const int avgsize = 300;
    constexpr int arrayBatV{avgsize};
    constexpr int arrayBatC{avgsize};
    movingAvg batV(arrayBatV);
    movingAvg batC(arrayBatC);
    batV.begin();
    batC.begin();

    // Start timer
    Timer1.start(); // Start Timer1 for blinking LED

    // Direction settings for NMEA0183
    String nmea0183Mode = api->getConfig()->getConfigItem(api->getConfig()->serialDirection, true)->asString();
    api->getLogger()->logDebug(GwLog::LOG, "NMEA0183 Mode is: %s", nmea0183Mode.c_str());
    pinMode(OBP_DIRECTION_PIN, OUTPUT);
    if (String(nmea0183Mode) == "receive" || String(nmea0183Mode) == "off")
    {
        digitalWrite(OBP_DIRECTION_PIN, false);
    }
    if (String(nmea0183Mode) == "send")
    {
        digitalWrite(OBP_DIRECTION_PIN, true);
    }

    // Internal voltage sensor initialization
    String powsensor1 = api->getConfig()->getConfigItem(api->getConfig()->usePowSensor1, true)->asString();
    double voffset = (api->getConfig()->getConfigItem(api->getConfig()->vOffset,true)->asString()).toFloat();
    double vslope = (api->getConfig()->getConfigItem(api->getConfig()->vSlope,true)->asString()).toFloat();
    if(String(powsensor1) == "off"){
        #ifdef VOLTAGE_SENSOR
        float rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2;   // Vin = 1/2 for OBP40
        #else
        float rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20;   // Vin = 1/20 for OBP60    
        #endif
        sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
        #ifdef LIPO_ACCU_1200
        sensors.BatteryChargeStatus = 0;    // Set to discharging
        sensors.batteryLevelLiPo = 0;       // Level 0...100%
        #endif
        sensors.batteryCurrent = 0;
        sensors.batteryPower = 0;
        // Fill average arrays with start values
        for (int i=1; i<=avgsize+1; ++i) {
            batV.reading(int(sensors.batteryVoltage * 100));
            batC.reading(int(sensors.batteryCurrent * 10));
        }
    }

    // Settings for 1Wire bus
    String oneWireOn=api->getConfig()->getConfigItem(api->getConfig()->useTempSensor,true)->asString();
    int numberOfDevices;
    if(String(oneWireOn) == "DS18B20"){
        ds18b20.begin();
        DeviceAddress tempDeviceAddress;
        numberOfDevices = ds18b20.getDeviceCount();
        // Limit for 8 sensors
        if(numberOfDevices > 8){
            numberOfDevices = 8;
        }
        if (numberOfDevices < 1) {
            oneWire_ready = false;
            api->getLogger()->logDebug(GwLog::ERROR,"Modul DS18B20 not found, check wiring");
        }
        else{
            api->getLogger()->logDebug(GwLog::LOG,"1Wire modul found at:");
            for(int i=0;i<numberOfDevices; i++){
                // Search the wire for address
                if(ds18b20.getAddress(tempDeviceAddress, i)){
                    api->getLogger()->logDebug(GwLog::LOG,"DS18B20-%01d: %12d", i, tempDeviceAddress[i]);
                } else {
                    api->getLogger()->logDebug(GwLog::LOG,"DS18B20-%01d: Sensor with errors, check wiring!", i);
                }
            }        
            oneWire_ready = true;
        }
    }

    // Settings for RTC
    String rtcOn=api->getConfig()->getConfigItem(api->getConfig()->useRTC,true)->asString();
    if(String(rtcOn) == "DS1388"){
        if (!ds1388.begin()) {
            RTC_ready = false;
            api->getLogger()->logDebug(GwLog::ERROR,"Modul DS1388 not found, check wiring");
        }
        else{
            api->getLogger()->logDebug(GwLog::LOG,"Modul DS1388 found");
            uint year = ds1388.now().year();
            if(year < 2023){
                // ds1388.adjust(DateTime(__DATE__, __TIME__));  // Set date and time from PC file time
            }
            RTC_ready = true;
            sensors.rtcValid = true;
        }
    }

    // Settings for GPS sensors
    String gpsOn=api->getConfig()->getConfigItem(api->getConfig()->useGPS,true)->asString();
    uint hdopAccuracy = uint(api->getConfig()->getConfigItem(api->getConfig()->hdopAccuracy,true)->asInt());
    if(String(gpsOn) == "NEO-6M"){
        Serial2.begin(9600, SERIAL_8N1, OBP_GPS_RX, OBP_GPS_TX, false); // not inverted (false)
        if (!Serial2) {     
            api->getLogger()->logDebug(GwLog::ERROR,"GPS modul NEO-6M not found, check wiring");
            GPS_ready = false;
            }
        else{
            api->getLogger()->logDebug(GwLog::LOG,"GPS modul NEO-M6 found");
            NMEA0183.SetMessageStream(&Serial2);
            NMEA0183.Open();
            GPS_ready = true;
        }
    }
    if(String(gpsOn) == "NEO-M8N"){
        Serial2.begin(9600, SERIAL_8N1, OBP_GPS_RX, OBP_GPS_TX, false); // not inverted (false)
        if (!Serial2) {     
            api->getLogger()->logDebug(GwLog::ERROR,"GPS modul NEO-M8N not found, check wiring");
            GPS_ready = false;
            }
        else{
            api->getLogger()->logDebug(GwLog::LOG,"GPS modul NEO-M8N found");
            NMEA0183.SetMessageStream(&Serial2);
            NMEA0183.Open();
            GPS_ready = true;
        }
    }
    if(String(gpsOn) == "ATGM336H"){
        Serial2.begin(9600, SERIAL_8N1, OBP_GPS_RX, OBP_GPS_TX, false); // not inverted (false)
        if (!Serial2) {     
            api->getLogger()->logDebug(GwLog::ERROR,"GPS modul ATGM336H not found, check wiring");
            GPS_ready = false;
            }
        else{
            api->getLogger()->logDebug(GwLog::LOG,"GPS modul ATGM336H found");
            NMEA0183.SetMessageStream(&Serial2);
            NMEA0183.Open();
            GPS_ready = true;
        }
    }
    
    // Settings for environment sensors on I2C bus
    String envSensors=api->getConfig()->getConfigItem(api->getConfig()->useEnvSensor,true)->asString();

    if(String(envSensors) == "BME280"){
        if (!bme280.begin(BME280_I2C_ADDR)) {
            api->getLogger()->logDebug(GwLog::ERROR,"Modul BME280 not found, check wiring");
        }
        else{
            api->getLogger()->logDebug(GwLog::LOG,"Modul BME280 found");
            sensors.airTemperature = bme280.readTemperature();
            sensors.airPressure = bme280.readPressure();
            sensors.airHumidity = bme280.readHumidity();
            BME280_ready = true;
        }
    }
    else if(String(envSensors) == "BMP280"){
        if (!bmp280.begin(BMP280_I2C_ADDR)) {
            api->getLogger()->logDebug(GwLog::ERROR,"Modul BMP280 not found, check wiring");
        }
        else{
            api->getLogger()->logDebug(GwLog::LOG,"Modul BMP280 found");
            sensors.airTemperature = bmp280.readTemperature();
            sensors.airPressure = bmp280.readPressure();
            BMP280_ready = true;
        }
    }
    else if(String(envSensors) == "BMP085" || String(envSensors) == "BMP180"){
        if (!bmp085.begin()) {
            api->getLogger()->logDebug(GwLog::ERROR,"Modul BMP085/BMP180 not found, check wiring");
        }
        else{
            api->getLogger()->logDebug(GwLog::LOG,"Modul BMP085/BMP180 found");
            sensors.airTemperature = bmp085.readTemperature();
            sensors.airPressure = bmp085.readPressure();
            BMP180_ready = true;
        }
    }
    else if(String(envSensors) == "HTU21" || String(envSensors) == "SHT21"){
        if (!sht21.begin()) {
            api->getLogger()->logDebug(GwLog::ERROR,"Modul HTU21/SHT21 not found, check wiring");
        }
        else{
            api->getLogger()->logDebug(GwLog::LOG,"Modul HTU21/SHT21 found");
            sensors.airHumidity = sht21.readCompensatedHumidity();
            sensors.airTemperature = sht21.readTemperature();
            SHT21_ready = true;
        }
    }

    // Settings for rotation sensors AS5600 on I2C bus
    String envsensor = api->getConfig()->getConfigItem(api->getConfig()->useEnvSensor, true)->asString();
    String rotsensor = api->getConfig()->getConfigItem(api->getConfig()->useRotSensor, true)->asString();
    String rotfunction = api->getConfig()->getConfigItem(api->getConfig()->rotFunction, true)->asString();
    String rotSensor=api->getConfig()->getConfigItem(api->getConfig()->useRotSensor,true)->asString();
    
    if(String(rotSensor) == "AS5600"){
        Wire.beginTransmission(AS5600_I2C_ADDR);
        if (Wire.endTransmission() != 0) {
            api->getLogger()->logDebug(GwLog::ERROR,"Modul AS5600 not found, check wiring");
        }
        else{
            api->getLogger()->logDebug(GwLog::LOG,"Modul AS5600 found");
            sensors.rotationAngle = DegToRad(as5600.getRawAngle() * 0.087);   // 0...4095 segments = 0.087 degree
            //sensors.magnitude = as5600.getMagnitude();              // Magnetic magnitude in [mT]
            AS5600_ready = true;
        }
    }

    // Settings for power amangement sensors INA226 #1 for Battery on I2C bus
    String shunt1 = api->getConfig()->getConfigItem(api->getConfig()->shunt1, true)->asString();
    // Settings for power amangement sensors INA226 #1 for Solar on I2C bus
    String powsensor2 = api->getConfig()->getConfigItem(api->getConfig()->usePowSensor2, true)->asString();
    String shunt2 = api->getConfig()->getConfigItem(api->getConfig()->shunt2, true)->asString();
    // Settings for power amangement sensors INA226 #1 for Generator on I2C bus
    String powsensor3 = api->getConfig()->getConfigItem(api->getConfig()->usePowSensor3, true)->asString();
    String shunt3 = api->getConfig()->getConfigItem(api->getConfig()->shunt3, true)->asString();

    float shuntResistor = 1.0;  // Default value for shunt resistor
    float maxCurrent = 10.0;    // Default value for max. current
    float corrFactor = 1;       // Correction factor for fix calibration
    
    // Battery sensor initialization
    if(String(powsensor1) == "INA226"){
        if (!ina226_1.begin()){
            api->getLogger()->logDebug(GwLog::ERROR,"Modul 1 INA226 not found, check wiring");
        }
        else{
            api->getLogger()->logDebug(GwLog::LOG,"Modul 1 INA226 found");
            shuntResistor = SHUNT_VOLTAGE / float(shunt1.toInt());  // Calculate shunt resisitor for max. shunt voltage 75mV
            maxCurrent = shunt1.toFloat();
            api->getLogger()->logDebug(GwLog::LOG,"Calibation Modul 2 INA226, Imax:%3.0fA Rs:%7.5fOhm Us:%5.3f", maxCurrent, shuntResistor, SHUNT_VOLTAGE);
//            ina226_1.setMaxCurrentShunt(maxCurrent, shuntResistor);
            ina226_1.setMaxCurrentShunt(10, 0.01);  // Calibration with fix values (because the original values outer range)
            corrFactor = (maxCurrent / 10) * (0.001 / shuntResistor) / (maxCurrent / 100); // Correction factor for fix calibration
            sensors.batteryVoltage = ina226_1.getBusVoltage();
            sensors.batteryCurrent = ina226_1.getCurrent() * corrFactor;
            sensors.batteryPower = ina226_1.getPower() * corrFactor;
            // Fill average arrays with start values
            for (int i=1; i<=avgsize+1; ++i) {
                batV.reading(int(sensors.batteryVoltage * 100));
                batC.reading(int(sensors.batteryCurrent * 10));
            }
            INA226_1_ready = true;
        }
    }

    // Solar sensor initialization
    if(String(powsensor2) == "INA226"){
        if (!ina226_2.begin()){
            api->getLogger()->logDebug(GwLog::ERROR,"Modul 2 INA226 not found, check wiring");
        }
        else{
            api->getLogger()->logDebug(GwLog::LOG,"Modul 2 INA226 found");
            shuntResistor = SHUNT_VOLTAGE / float(shunt2.toInt());  // Calculate shunt resisitor for max. shunt voltage 75mV
            maxCurrent = shunt2.toFloat();
            api->getLogger()->logDebug(GwLog::LOG,"Calibation Modul 2 INA226, Imax:%3.0fA Rs:%7.5fOhm Us:%5.3f", maxCurrent, shuntResistor, SHUNT_VOLTAGE);
//            ina226_1.setMaxCurrentShunt(maxCurrent, shuntResistor);
            ina226_2.setMaxCurrentShunt(10, 0.01);  // Calibration with fix values (because the original values outer range)
            corrFactor = (maxCurrent / 10) * (0.001 / shuntResistor) / (maxCurrent / 100); // Correction factor for fix calibration
            sensors.solarVoltage = ina226_2.getBusVoltage();
            sensors.solarCurrent = ina226_2.getCurrent() * corrFactor;
            sensors.solarPower = ina226_2.getPower() * corrFactor;
            // Fill average arrays with start values
            INA226_2_ready = true;
        }
    }

    // Generator sensor initialization
    if(String(powsensor3) == "INA226"){
        if (!ina226_3.begin()){
            api->getLogger()->logDebug(GwLog::ERROR,"Modul 3 INA226 not found, check wiring");
        }
        else{
            api->getLogger()->logDebug(GwLog::LOG,"Modul 3 INA226 found");
            shuntResistor = SHUNT_VOLTAGE / float(shunt3.toInt());  // Calculate shunt resisitor for max. shunt voltage 75mV
            maxCurrent = shunt3.toFloat();
            api->getLogger()->logDebug(GwLog::LOG,"Calibation Modul 3 INA226, Imax:%3.0fA Rs:%7.5fOhm Us:%5.3f", maxCurrent, shuntResistor, SHUNT_VOLTAGE);
//            ina226_1.setMaxCurrentShunt(maxCurrent, shuntResistor);
            ina226_3.setMaxCurrentShunt(10, 0.01);  // Calibration with fix values (because the original values outer range)
            corrFactor = (maxCurrent / 10) * (0.001 / shuntResistor) / (maxCurrent / 100); // Correction factor for fix calibration
            sensors.generatorVoltage = ina226_3.getBusVoltage();
            sensors.generatorCurrent = ina226_3.getCurrent() * corrFactor;
            sensors.generatorPower = ina226_3.getPower() * corrFactor;
            // Fill average arrays with start values
            INA226_3_ready = true;
        }
    }

    int rotoffset = api->getConfig()->getConfigItem(api->getConfig()->rotOffset,true)->asInt();

    static long loopCounter = 0;    // Loop counter for 1Wire data transmission
    long starttime0 = millis();     // GPS update all 100ms
    long starttime5 = millis();     // Voltage update all 1s
    long starttime6 = millis();     // Environment sensor update all 1s
    long starttime7 = millis();     // Rotation sensor update all 500ms
    long starttime8 = millis();     // Battery power sensor update all 1s
    long starttime9 = millis();     // Solar power sensor update all 1s
    long starttime10 = millis();    // Generator power sensor update all 1s
    long starttime11 = millis();    // Copy GPS data to RTC all 5min
    long starttime12 = millis();    // Get RTC data all 500ms
    long starttime13 = millis();    // Get 1Wire sensor data all 2s

    tN2kMsg N2kMsg;
    shared->setSensorData(sensors); //set initially read values

    GwApi::BoatValue *gpsdays=new GwApi::BoatValue(GwBoatData::_GPSD);
    GwApi::BoatValue *gpsseconds=new GwApi::BoatValue(GwBoatData::_GPST);
    GwApi::BoatValue *hdop=new GwApi::BoatValue(GwBoatData::_HDOP);
    GwApi::BoatValue *valueList[]={gpsdays, gpsseconds, hdop};

    // Internal iRTC with NTP init
    ESP32Time rtc(0);
    if (api->getConfig()->getString(api->getConfig()->timeSource) == "iRTC") {
        GwApi::Status status;
        api->getStatus(status);
        if (status.wifiClientConnected) {
            const char *ntpServer = api->getConfig()->getCString(api->getConfig()->timeServer);
            api->getLogger()->logDebug(GwLog::LOG,"Fetching date and time from NTP server '%s'.", ntpServer);
            configTime(0, 0, ntpServer); // get time in UTC
            struct tm timeinfo;
            if (getLocalTime(&timeinfo)) {
                api->getLogger()->logDebug(GwLog::LOG,"NTP time: %04d-%02d-%02d %02d:%02d:%02d UTC", timeinfo.tm_year+1900, timeinfo.tm_mon+1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
                rtc.setTimeStruct(timeinfo);
                iRTC_ready = true;
                sensors.rtcValid = true;
            } else {
                api->getLogger()->logDebug(GwLog::LOG,"NTP time fetch failed!");
            }
        } else {
            api->getLogger()->logDebug(GwLog::LOG,"Wifi client not connected, NTP not available.");
        }
    }

    // Sensor task loop runs with 100ms
    //####################################################################################

    while (true){
        delay(100);                 // Loop time 100ms
        Timer1.update();            // Update for Timer2
        if (millis() > starttime0 + 100)
        {
            starttime0 = millis();
            // Send NMEA0183 GPS data on several bus systems (N2K an 0183) all 100ms
            if (GPS_ready == true && hdop->value <= hdopAccuracy)
            {
                SNMEA0183Msg NMEA0183Msg;
                while (NMEA0183.GetMessageCor(NMEA0183Msg))
                {
                    api->sendNMEA0183Message(NMEA0183Msg);
                }
            }
            
        }

        /*
        Time set logic for RTC and N2K
        ###############################

        iRTC = Software RTC updatetd with NTP via internet
        RTC = RTC chip on PCB
        GPS = GPS Receiver on PCB
        N2K = GPS time on N2K od 183 bus
        0 = device not ready
        1 = device ready
        X = independend
        () = source for set time N2K
        -> = set RTC via iRTC
        <- = set RTC via GPS

        iRTC  RTC  GPS N2K
          0    0    0  (1)
          0    0   (1)  X 
          0   (1)   0   X 
          0    1 <-(1)  X 
         (1)   0    0   X 
          1    0   (1)  X 
          1 ->(1)   0   X 
          1    1 <-(1)  X 

        */

        // If RTC DS1388 ready, then copy iRTC and GPS data to RTC all 1 min
        if(millis() > starttime11 + 1*60*1000){
            starttime11 = millis();
            // Set RTC chip via iRTC (NTP)
            if(iRTC_ready == true && RTC_ready == true && GPS_ready == false){
                GwApi::Status status;
                api->getStatus(status);
                // Check WiFi connection
                if (status.wifiClientConnected) {
                    sensors.rtcTime = rtc.getTimeStruct();  // Get time from software RTC (iRTC)      
                    DateTime now = DateTime(
                        sensors.rtcTime.tm_year + 1900,
                        sensors.rtcTime.tm_mon + 1,
                        sensors.rtcTime.tm_mday,
                        sensors.rtcTime.tm_hour,
                        sensors.rtcTime.tm_min,
                        sensors.rtcTime.tm_sec
                    );   
                    ds1388.adjust(now);
                }    
            }
            // Set RTC chip via internal GPS
            if(rtcOn == "DS1388" && RTC_ready == true && GPS_ready == true){
                api->getBoatDataValues(3,valueList);
                if(gpsdays->valid && gpsseconds->valid && hdop->valid){
                    long ts = tNMEA0183Msg::daysToTime_t(gpsdays->value - (30*365+7))+floor(gpsseconds->value); // Adjusted to reference year 2000 (-30 years and 7 days for switch years)
                    // sample input: date = "Dec 26 2009", time = "12:34:56"
                    // ds1388.adjust(DateTime("Dec 26 2009", "12:34:56"));
                    DateTime adjusttime(ts);
                    api->getLogger()->logDebug(GwLog::LOG,"Adjust RTC time via internal GPS: %04d/%02d/%02d %02d:%02d:%02d",adjusttime.year(), adjusttime.month(), adjusttime.day(), adjusttime.hour(), adjusttime.minute(), adjusttime.second());
                    // Adjust RTC time as unix time value
                    ds1388.adjust(adjusttime);
                }
            }    
        }

        // Set RTC chip via N2K or 183 in case the internal GPS is off (only one time)
        if(N2K_GPS_ready == false && RTC_ready == true && GPS_ready == false){
            api->getBoatDataValues(3,valueList);
            if(gpsdays->valid && gpsseconds->valid && hdop->valid){
                long ts = tNMEA0183Msg::daysToTime_t(gpsdays->value - (30*365+7))+floor(gpsseconds->value); // Adjusted to reference year 2000 (-30 years and 7 days for switch years)
                // sample input: date = "Dec 26 2009", time = "12:34:56"
                // ds1388.adjust(DateTime("Dec 26 2009", "12:34:56"));
                DateTime adjusttime(ts);
                api->getLogger()->logDebug(GwLog::LOG,"Adjust RTC time via N2K/183: %04d/%02d/%02d %02d:%02d:%02d",adjusttime.year(), adjusttime.month(), adjusttime.day(), adjusttime.hour(), adjusttime.minute(), adjusttime.second());
                // Adjust RTC time as unix time value
                ds1388.adjust(adjusttime);
                // N2K GPS time ready
                N2K_GPS_ready = true;
            }    
        }

        // Send RTC date and time to N2K all 500ms
        if (millis() > starttime12 + 500) {
            starttime12 = millis();
            // Send date and time from RTC chip if GPS not ready
            if (rtcOn == "DS1388" && RTC_ready) {
                DateTime dt = ds1388.now();
                sensors.rtcTime.tm_year  = dt.year() - 1900; // Save values in SensorData
                sensors.rtcTime.tm_mon = dt.month() - 1;
                sensors.rtcTime.tm_mday = dt.day();
                sensors.rtcTime.tm_hour = dt.hour();
                sensors.rtcTime.tm_min = dt.minute();
                sensors.rtcTime.tm_sec = dt.second();
                sensors.rtcTime.tm_isdst = 0; // Not considering daylight saving time

                // If GPS not ready or installed then send RTC time on bus
                // TODO If there are other time sources on the bus there should
                //      be a logic not to send or to send with lower frequency
                //      or something totally different
                if ((GPS_ready == false) || (GPS_ready == true && hdop->valid == false)) {
                    // TODO implement daysAt1970 and sysTime as methods of DateTime
                    const short daysOfYear[12] = {0,31,59,90,120,151,181,212,243,273,304,334};
                    uint16_t switchYear = ((dt.year()-1)-1968)/4 - ((dt.year()-1)-1900)/100 + ((dt.year()-1)-1600)/400;
                    long daysAt1970 = (dt.year()-1970)*365 + switchYear + daysOfYear[dt.month()-1] + dt.day()-1;
                    // If switch year then add one day
                    if ((dt.month() > 2) && (dt.year() % 4 == 0  && (dt.year() % 100 != 0 || dt.year() % 400 == 0))) {
                        daysAt1970 += 1;
                    }
                    // N2K sysTime is double in n2klib
                    double sysTime = (dt.hour() * 3600) + (dt.minute() * 60) + dt.second();
                    if(!isnan(daysAt1970) && !isnan(sysTime)){
                        //api->getLogger()->logDebug(GwLog::LOG,"RTC time: %04d/%02d/%02d %02d:%02d:%02d",sensors.rtcTime.tm_year+1900,sensors.rtcTime.tm_mon+1, sensors.rtcTime.tm_mday, sensors.rtcTime.tm_hour, sensors.rtcTime.tm_min, sensors.rtcTime.tm_sec);
                        //api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
                        SetN2kPGN126992(N2kMsg,0,daysAt1970,sysTime,N2ktimes_LocalCrystalClock);
                        api->sendN2kMessage(N2kMsg);
                    }
                }
            }
            // Send date and time from software RTC (iRTC)
            if (iRTC_ready == true && RTC_ready == false && GPS_ready == false) {
                sensors.rtcTime = rtc.getTimeStruct();

                const short daysOfYear[12] = {0,31,59,90,120,151,181,212,243,273,304,334};
                int year  = sensors.rtcTime.tm_year + 1900;
                int month = sensors.rtcTime.tm_mon;
                int day   = sensors.rtcTime.tm_mday;
                uint16_t switchYear = ((year - 1) - 1968) / 4 - ((year - 1) - 1900) / 100 + ((year - 1) - 1600) / 400;
                long daysAt1970 = (year - 1970) * 365L + switchYear + daysOfYear[month] + day - 1;

                // Leap day add if date is after Feb (i.e. month >= March)
                if (month >= 2 && (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) {
                    daysAt1970 += 1;
                }
                double sysTime = sensors.rtcTime.tm_hour * 3600.0 + sensors.rtcTime.tm_min  * 60.0 + sensors.rtcTime.tm_sec;
                //api->getLogger()->logDebug(GwLog::LOG, "iRTC time: %04d/%02d/%02d %02d:%02d:%02d", year, month + 1, day, sensors.rtcTime.tm_hour, sensors.rtcTime.tm_min, sensors.rtcTime.tm_sec);
                //api->getLogger()->logDebug(GwLog::LOG,"Send PGN126992: %10d %10d",daysAt1970, (uint16_t)sysTime);
                SetN2kPGN126992(N2kMsg, 0, daysAt1970, sysTime, N2ktimes_LocalCrystalClock);
                api->sendN2kMessage(N2kMsg);
            }

        }

        // Send 1Wire data for all temperature sensors to N2K all 2s
        if(millis() > starttime13 + 2000 && String(oneWireOn) == "DS18B20" && oneWire_ready == true){
            starttime13 = millis();
            float tempC;
            ds18b20.requestTemperatures();  // Collect all temperature values (max.8)
            for(int i=0;i<numberOfDevices; i++){
                // Send only one 1Wire data per loop step (time reduction)
                if(i == loopCounter % numberOfDevices){
                    if(ds18b20.getAddress(tempDeviceAddress, i)){
                        // Read temperature value in Celsius
                        tempC = ds18b20.getTempC(tempDeviceAddress); 
                    }
                    // Send to NMEA200 bus for each sensor with instance number
                    if(!isnan(tempC)){
                        sensors.onewireTemp[i] = tempC; // Save values in SensorData
                        api->getLogger()->logDebug(GwLog::DEBUG,"DS18B20-%1d Temp: %.1f",i,tempC);
                        SetN2kPGN130316(N2kMsg, 0, i, N2kts_OutsideTemperature, CToKelvin(tempC), N2kDoubleNA);
                        api->sendN2kMessage(N2kMsg);
                    }
                }    
            }
            loopCounter++;
        }

        // Send supply voltage value to N2K all 1s
        if(millis() > starttime5 + 1000 && String(powsensor1) == "off"){
            starttime5 = millis();
            float rawVoltage = 0;       // Default value
            #ifdef BOARD_OBP40S3
            sensors.batteryVoltage = 0; // If no sensor then zero voltage
            #endif
            #if defined(BOARD_OBP40S3)  && defined(VOLTAGE_SENSOR)
            rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.53) * 2;   // Vin = 1/2 for OBP40
            sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration
            #endif
            #ifdef BOARD_OBP60S3
            rawVoltage = (float(analogRead(OBP_ANALOG0)) * 3.3 / 4096 + 0.17) * 20;   // Vin = 1/20 for OBP60
            sensors.batteryVoltage = rawVoltage * vslope + voffset; // Calibration  
            #endif
            // Save new data in average array
            batV.reading(int(sensors.batteryVoltage * 100));
            // Calculate the average values for different time lines from integer values
            sensors.batteryVoltage10 = batV.getAvg(10) / 100.0;
            sensors.batteryVoltage60 = batV.getAvg(60) / 100.0;
            sensors.batteryVoltage300 = batV.getAvg(300) / 100.0;
            #if BOARD_OBP40S3 && defined LIPO_ACCU_1200 && defined VOLTAGE_SENSOR
            // Polynomfit for LiPo capacity calculation for 3,7V LiPo accus, 0...100%
            sensors.batteryLevelLiPo = sensors.batteryVoltage60 * 203.8312 -738.1635;
            // Limiter
            if(sensors.batteryLevelLiPo > 100){
                sensors.batteryLevelLiPo = 100;
            }
            if(sensors.batteryLevelLiPo < 0){
                sensors.batteryLevelLiPo = 0;
            }
            // Charging detection
            float deltaV = sensors.batteryVoltage - sensors.batteryVoltage10;
            // Higher limits for lower voltages
            if(sensors.batteryVoltage10 < 4.0){
                if(deltaV > 0.045){
                    sensors.BatteryChargeStatus = 1;    // Charging active
                }
                if(deltaV < -0.04){
                    sensors.BatteryChargeStatus = 0;    // Discharging
                }
            }
            // Lower limits for higher voltages
            else{
                if(deltaV > 0.03){
                    sensors.BatteryChargeStatus = 1;    // Charging active
                }
                if(deltaV < -0.03){
                    sensors.BatteryChargeStatus = 0;    // Discharging
                }
            }
            // Charging stops by grater than 4,15V
            if(sensors.batteryVoltage10 > 4.15){
                sensors.BatteryChargeStatus = 0;        // Discharging
            }
            // Send to NMEA200 bus as instance 10
            if(!isnan(sensors.batteryVoltage)){
                SetN2kDCBatStatus(N2kMsg, 10, sensors.batteryVoltage, N2kDoubleNA, N2kDoubleNA, 0);
                api->sendN2kMessage(N2kMsg);
            }
            #endif
            #ifdef BOARD_OBP60S3
            // Send to NMEA200 bus
            if(!isnan(sensors.batteryVoltage)){
                SetN2kDCBatStatus(N2kMsg, 0, sensors.batteryVoltage, N2kDoubleNA, N2kDoubleNA, 1);
                api->sendN2kMessage(N2kMsg);
            }
            #endif
        }

        // Send data from environment sensor to N2K all 2s
        if(millis() > starttime6 + 2000){
            starttime6 = millis();
            unsigned char TempSource = 2;       // Inside temperature
            unsigned char PressureSource = 0;   // Atmospheric pressure
            unsigned char HumiditySource = 0;   // Inside humidity
            if(envsensor == "BME280" && BME280_ready == true){
                sensors.airTemperature = bme280.readTemperature();
                sensors.airPressure = bme280.readPressure();
                sensors.airHumidity = bme280.readHumidity();
                // Send to NMEA200 bus
                if(!isnan(sensors.airTemperature)){
                    SetN2kPGN130312(N2kMsg, 0, 0,(tN2kTempSource) TempSource, CToKelvin(sensors.airTemperature), N2kDoubleNA);
                    api->sendN2kMessage(N2kMsg);
                }
                if(!isnan(sensors.airHumidity)){
                    SetN2kPGN130313(N2kMsg, 0, 0,(tN2kHumiditySource) HumiditySource, sensors.airHumidity, N2kDoubleNA);
                    api->sendN2kMessage(N2kMsg);
                }
                if(!isnan(sensors.airPressure)){
                    SetN2kPGN130314(N2kMsg, 0, 0, (tN2kPressureSource) PressureSource, sensors.airPressure);
                    api->sendN2kMessage(N2kMsg);
                }
            }
            else if(envsensor == "BMP280" && BMP280_ready == true){
                sensors.airTemperature = bmp280.readTemperature();
                sensors.airPressure = bmp280.readPressure();
                // Send to NMEA200 bus
                if(!isnan(sensors.airTemperature)){
                    SetN2kPGN130312(N2kMsg, 0, 0,(tN2kTempSource) TempSource, CToKelvin(sensors.airTemperature), N2kDoubleNA);
                    api->sendN2kMessage(N2kMsg);
                }
                if(!isnan(sensors.airPressure)){
                    SetN2kPGN130314(N2kMsg, 0, 0, (tN2kPressureSource) PressureSource, sensors.airPressure);
                    api->sendN2kMessage(N2kMsg);
                }
            }
            else if((envsensor == "BMP085" || envsensor == "BMP180") && BMP180_ready == true){
                sensors.airTemperature = bmp085.readTemperature();
                sensors.airPressure = bmp085.readPressure();
                // Send to NMEA200 bus
                if(!isnan(sensors.airTemperature)){
                    SetN2kPGN130312(N2kMsg, 0, 0,(tN2kTempSource) TempSource, CToKelvin(sensors.airTemperature), N2kDoubleNA);
                    api->sendN2kMessage(N2kMsg);
                }
                if(!isnan(sensors.airPressure)){
                    SetN2kPGN130314(N2kMsg, 0, 0, (tN2kPressureSource) PressureSource, sensors.airPressure);
                    api->sendN2kMessage(N2kMsg);
                }
            }
            else if((envsensor == "SHT21" || envsensor == "HTU21") && SHT21_ready == true){
                sensors.airHumidity = sht21.readCompensatedHumidity();
                sensors.airTemperature = sht21.readTemperature();
                // Send to NMEA200 bus
                if(!isnan(sensors.airTemperature)){
                    SetN2kPGN130312(N2kMsg, 0, 0,(tN2kTempSource) TempSource, CToKelvin(sensors.airTemperature), N2kDoubleNA);
                    api->sendN2kMessage(N2kMsg);
                }
                if(!isnan(sensors.airHumidity)){
                    SetN2kPGN130313(N2kMsg, 0, 0,(tN2kHumiditySource) HumiditySource, sensors.airHumidity, N2kDoubleNA);
                    api->sendN2kMessage(N2kMsg);
                }
            }                
        }

        // Send rotation angle to N2K all 500ms
        if(millis() > starttime7 + 500){
            starttime7 = millis();
            double rotationAngle=0;
            if(String(rotsensor) == "AS5600" && AS5600_ready == true && as5600.detectMagnet() == 1){
                rotationAngle = as5600.getRawAngle() * 0.087;       // 0...4095 segments = 0.087 degree
                // Offset correction
                if(rotoffset >= 0){
                    rotationAngle = rotationAngle + rotoffset;
                    rotationAngle = int(rotationAngle) % 360;
                }
                else{
                    rotationAngle = rotationAngle + 360 + rotoffset;
                    rotationAngle = int(rotationAngle) % 360;
                }
                // Send to NMEA200 bus as rudder angle values
                if(!isnan(rotationAngle) && String(rotfunction) == "Rudder"){
                    double rudder = rotationAngle - 180;            // Center position is 180°
                    // Rudder limits to +/-45°
                    if(rudder < -45){
                        rudder = -45;
                    }
                    if(rudder > 45){
                        rudder = 45;
                    }
                    SetN2kRudder(N2kMsg, DegToRad(rudder), 0, N2kRDO_NoDirectionOrder, PI);
                    api->sendN2kMessage(N2kMsg);
                }
                // Send to NMEA200 bus as wind angle values
                if(!isnan(rotationAngle) && String(rotfunction) == "Wind"){
                    SetN2kWindSpeed(N2kMsg, 1, 0, DegToRad(rotationAngle), N2kWind_Apprent);
                    api->sendN2kMessage(N2kMsg);
                }
                // Send to NMEA200 bus as trim angle values in [%]
                if(!isnan(rotationAngle) && (String(rotfunction) == "Mast" || String(rotfunction) == "Keel" || String(rotfunction) == "Trim" || String(rotfunction) == "Boom")){
                    int trim = rotationAngle * 100 / 360;           // 0...360° -> 0...100%
                    SetN2kTrimTab(N2kMsg, trim, trim);
                    api->sendN2kMessage(N2kMsg);
                }
                sensors.rotationAngle = DegToRad(rotationAngle);    // Data take over to page
                sensors.validRotAngle = true;                       // Valid true, magnet present
            }
            else{
                sensors.rotationAngle = 0;      // Center position 0°
                sensors.validRotAngle = false;  // Valid false, magnet missing
            }    
        }

        // Send battery power value to N2K all 1s
        if(millis() > starttime8 + 1000 && (String(powsensor1) == "INA219" || String(powsensor1) == "INA226")){
            starttime8 = millis();
            if(String(powsensor1) == "INA226" && INA226_1_ready == true){
                double voltage = ina226_1.getBusVoltage();
                // Limiter for voltage average building
                if(voltage < 0){
                    voltage = 0;
                }
                if(voltage > 30){
                    voltage = 30;
                }
                sensors.batteryVoltage = voltage;
                sensors.batteryCurrent = ina226_1.getCurrent() * corrFactor;
                // Eliminates bit jitter by zero current values
                float factor = maxCurrent / 100;
                if(sensors.batteryCurrent >= (-0.015 * factor) && sensors.batteryCurrent <= (0.015 * factor)){
                    sensors.batteryCurrent = 0;
                }
                // Save actual values in average arrays as integer values
                batV.reading(int(sensors.batteryVoltage * 100));
                batC.reading(int(sensors.batteryCurrent * 10));
                // Calculate the average values for different time lines from integer values
                sensors.batteryVoltage10 = batV.getAvg(10) / 100.0;
                sensors.batteryVoltage60 = batV.getAvg(60) / 100.0;
                sensors.batteryVoltage300 = batV.getAvg(300) / 100.0;
                sensors.batteryCurrent10 = batC.getAvg(10) / 10.0;
                sensors.batteryCurrent60 = batC.getAvg(60) / 10.0;
                sensors.batteryCurrent300 = batC.getAvg(300) / 10.0;
                sensors.batteryPower10 = sensors.batteryVoltage10 * sensors.batteryCurrent10;
                sensors.batteryPower60 = sensors.batteryVoltage60 * sensors.batteryCurrent60;
                sensors.batteryPower300 = sensors.batteryVoltage300 * sensors.batteryCurrent300;
//                sensors.batteryPower = ina226_1.getPower() * corrFactor;  // Real value
                sensors.batteryPower = sensors.batteryVoltage * sensors.batteryCurrent;  // Calculated power value (more stable)
            }
            // Send battery live data to NMEA200 bus
            if(!isnan(sensors.batteryVoltage) && !isnan(sensors.batteryCurrent)){
                SetN2kDCBatStatus(N2kMsg, 0, sensors.batteryVoltage, sensors.batteryCurrent, N2kDoubleNA, 1);
                api->sendN2kMessage(N2kMsg);
            }
        }

        // Send solar power value to N2K all 1s
        if(millis() > starttime9 + 1000 && (String(powsensor2) == "INA219" || String(powsensor2) == "INA226")){
            starttime9 = millis();
            if(String(powsensor2) == "INA226" && INA226_2_ready == true){
                double voltage = ina226_2.getBusVoltage();
                // Limiter for voltage average building
                if(voltage < 0){
                    voltage = 0;
                }
                if(voltage > 30){
                    voltage = 30;
                }
                sensors.solarVoltage = voltage;
                sensors.solarCurrent = ina226_2.getCurrent() * corrFactor;
                // Eliminates bit jitter by zero current values
                float factor = maxCurrent / 100;
                if(sensors.solarCurrent >= (-0.015 * factor) && sensors.solarCurrent <= (0.015 * factor)){
                    sensors.solarCurrent = 0;
                }
                
                // Calculate power value
                sensors.solarPower = sensors.solarVoltage * sensors.solarCurrent; // more stable
            }
            // Send solar live data to NMEA200 bus
            if(!isnan(sensors.solarVoltage) && !isnan(sensors.solarCurrent)){
                SetN2kDCBatStatus(N2kMsg, 1, sensors.solarVoltage, sensors.solarCurrent, N2kDoubleNA, 1);
                api->sendN2kMessage(N2kMsg);
            }
        }

        // Send generator power value to N2K all 1s
        if(millis() > starttime10 + 1000 && (String(powsensor3) == "INA219" || String(powsensor3) == "INA226")){
            starttime10 = millis();
            if(String(powsensor3) == "INA226" && INA226_3_ready == true){
                double voltage = ina226_3.getBusVoltage();
                // Limiter for voltage average building
                if(voltage < 0){
                    voltage = 0;
                }
                if(voltage > 30){
                    voltage = 30;
                }
                sensors.generatorVoltage = voltage;
                sensors.generatorCurrent = ina226_3.getCurrent() * corrFactor;
                // Eliminates bit jitter by zero current values
                float factor = maxCurrent / 100;
                if(sensors.generatorCurrent >= (-0.015 * factor) && sensors.generatorCurrent <= (0.015 * factor)){
                    sensors.generatorCurrent = 0;
                }
                
                // Calculate power value
                sensors.generatorPower = sensors.generatorVoltage * sensors.generatorCurrent; // more stable
            }
            // Send solar live data to NMEA200 bus
            if(!isnan(sensors.generatorVoltage) && !isnan(sensors.generatorCurrent)){
                SetN2kDCBatStatus(N2kMsg, 2, sensors.generatorVoltage, sensors.generatorCurrent, N2kDoubleNA, 1);
                api->sendN2kMessage(N2kMsg);
            }
        }

        shared->setSensorData(sensors);
    }
    vTaskDelete(NULL);
}


void createSensorTask(SharedData *shared){
    xTaskCreate(sensorTask,"readSensors",10000,shared,3,NULL);
}
#endif
