diff --git a/lib/config/GWConfig.cpp b/lib/config/GWConfig.cpp index 151904f..8b92130 100644 --- a/lib/config/GWConfig.cpp +++ b/lib/config/GWConfig.cpp @@ -82,12 +82,14 @@ bool GwConfigHandler::updateValue(const char *name, const char * value){ if (i == NULL) return false; logger->logString("update config %s=>%s",name,value); i->fromString(value); + return true; } bool GwConfigHandler::updateValue(String name, String value){ GwConfigItem *i=findConfig(name); if (i == NULL) return false; logger->logString("update config %s=>%s",name.c_str(),value.c_str()); i->fromString(value); + return true; } bool GwConfigHandler::reset(bool save){ logger->logString("reset config"); diff --git a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp index 7ca2d1b..7f497b7 100644 --- a/lib/nmea2kto0183/N2kDataToNMEA0183.cpp +++ b/lib/nmea2kto0183/N2kDataToNMEA0183.cpp @@ -25,98 +25,22 @@ #include #include +#include "N2kToNMEA0183Functions.h" #include "N2kDataToNMEA0183.h" -const double radToDeg = 180.0 / M_PI; -static void updateDouble(GwBoatItem *item,double value){ - if (value == N2kDoubleNA) return; - if (! item) return; - item->update(value); -} -static double formatCourse(double cv){ - double rt=cv * radToDeg; - if (rt > 360) rt-=360; - if (rt <0) rt+=360; - return rt; -} -static double formatWind(double cv){ - double rt=formatCourse(cv); - if (rt > 180) rt=180-rt; - return rt; -} -static double formatKnots(double cv){ - return cv* 3600.0/1852.0; -} -uint32_t mtr2nm(uint32_t m){ - return m/1852; -} - -static void setMax(GwBoatItem *maxItem,GwBoatItem *item){ - unsigned long now=millis(); - if (! item->isValid(now)) return; - if(item->getData() > maxItem->getData() || ! maxItem->isValid(now)){ - maxItem->update(item->getData()); - } -} N2kDataToNMEA0183::N2kDataToNMEA0183(GwLog * logger, GwBoatData *boatData, tNMEA2000 *NMEA2000, tNMEA0183 *NMEA0183) : tNMEA2000::tMsgHandler(0,NMEA2000){ SendNMEA0183MessageCallback=0; pNMEA0183=NMEA0183; - LastPosSend=0; - lastLoopTime=0; - NextRMCSend=millis()+RMCPeriod; - - this->logger=logger; - this->boatData=boatData; - heading=boatData->getDoubleItem(F("Heading"),true,4000,&formatCourse); - latitude=boatData->getDoubleItem(F("Latitude"),true,4000); - longitude=boatData->getDoubleItem(F("Longitude"),true,4000); - altitude=boatData->getDoubleItem(F("Altitude"),true,4000); - cog=boatData->getDoubleItem(F("COG"),true,4000,&formatCourse); - sog=boatData->getDoubleItem(F("SOG"),true,4000,&formatKnots); - stw=boatData->getDoubleItem(F("STW"),true,4000,&formatKnots); - variation=boatData->getDoubleItem(F("Variation"),true,4000,&formatCourse); - tws=boatData->getDoubleItem(F("TWS"),true,4000,&formatKnots); - twd=boatData->getDoubleItem(F("TWD"),true,4000,&formatCourse); - aws=boatData->getDoubleItem(F("AWS"),true,4000,&formatKnots); - awa=boatData->getDoubleItem(F("AWA"),true,4000,&formatWind); - awd=boatData->getDoubleItem(F("AWD"),true,4000,&formatCourse); - maxAws=boatData->getDoubleItem(F("MaxAws"),true,0,&formatKnots); - maxTws=boatData->getDoubleItem(F("MaxTws"),true,0,&formatKnots); - rudderPosition=boatData->getDoubleItem(F("RudderPosition"),true,4000,&formatCourse); - waterTemperature=boatData->getDoubleItem(F("WaterTemperature"),true,4000,&KelvinToC); - waterDepth=boatData->getDoubleItem(F("WaterDepth"),true,4000); - log=boatData->getUint32Item(F("Log"),true,0,mtr2nm); - tripLog=boatData->getUint32Item(F("TripLog"),true,0,mtr2nm); - daysSince1970=boatData->getUint32Item(F("DaysSince1970"),true,4000); - secondsSinceMidnight=boatData->getDoubleItem(F("SecondsSinceMidnight"),true,4000); + } -//***************************************************************************** -void N2kDataToNMEA0183::HandleMsg(const tN2kMsg &N2kMsg) { - switch (N2kMsg.PGN) { - case 127250UL: HandleHeading(N2kMsg); - case 127258UL: HandleVariation(N2kMsg); - case 128259UL: HandleBoatSpeed(N2kMsg); - case 128267UL: HandleDepth(N2kMsg); - case 129025UL: HandlePosition(N2kMsg); - case 129026UL: HandleCOGSOG(N2kMsg); - case 129029UL: HandleGNSS(N2kMsg); - case 130306UL: HandleWind(N2kMsg); - case 128275UL: HandleLog(N2kMsg); - case 127245UL: HandleRudder(N2kMsg); - case 130310UL: HandleWaterTemp(N2kMsg); - } -} + //***************************************************************************** void N2kDataToNMEA0183::loop() { - unsigned long now=millis(); - if ( now < (lastLoopTime + 100)) return; - lastLoopTime=now; - SendRMC(); } //***************************************************************************** @@ -125,278 +49,7 @@ void N2kDataToNMEA0183::SendMessage(const tNMEA0183Msg &NMEA0183Msg) { if ( SendNMEA0183MessageCallback != 0 ) SendNMEA0183MessageCallback(NMEA0183Msg); } -//***************************************************************************** -void N2kDataToNMEA0183::HandleHeading(const tN2kMsg &N2kMsg) { - unsigned char SID; - tN2kHeadingReference ref; - double _Deviation = 0; - double Variation; - tNMEA0183Msg NMEA0183Msg; - double Heading; - if ( ParseN2kHeading(N2kMsg, SID, Heading, _Deviation, Variation, ref) ) { - if ( ref == N2khr_magnetic ) { - updateDouble(variation,Variation); // Update Variation - if ( !N2kIsNA(Heading) && variation->isValid(millis()) ) Heading -= variation->getData(); - } - updateDouble(heading,Heading); - if ( NMEA0183SetHDG(NMEA0183Msg, heading->getDataWithDefault(NMEA0183DoubleNA), _Deviation, variation->getDataWithDefault(NMEA0183DoubleNA)) ) { - SendMessage(NMEA0183Msg); - } - } +N2kDataToNMEA0183* N2kDataToNMEA0183::create(GwLog *logger, GwBoatData *boatData, tNMEA2000 *NMEA2000, tNMEA0183 *NMEA0183){ + return new N2kToNMEA0183Functions(logger,boatData,NMEA2000,NMEA0183); } - //***************************************************************************** -void N2kDataToNMEA0183::HandleVariation(const tN2kMsg &N2kMsg) { - unsigned char SID; - tN2kMagneticVariation Source; - uint16_t DaysSince1970; - double Variation; - ParseN2kMagneticVariation(N2kMsg, SID, Source, DaysSince1970, Variation); - updateDouble(variation,Variation); - if (DaysSince1970 != N2kUInt16NA && DaysSince1970 != 0) daysSince1970->update(DaysSince1970); -} - -//***************************************************************************** -void N2kDataToNMEA0183::HandleBoatSpeed(const tN2kMsg &N2kMsg) { - unsigned char SID; - double WaterReferenced; - double GroundReferenced; - tN2kSpeedWaterReferenceType SWRT; - - if ( ParseN2kBoatSpeed(N2kMsg, SID, WaterReferenced, GroundReferenced, SWRT) ) { - tNMEA0183Msg NMEA0183Msg; - updateDouble(stw,WaterReferenced); - unsigned long now=millis(); - double MagneticHeading = ( heading->isValid(now) && variation->isValid(now)) ? heading->getData() + variation->getData() : NMEA0183DoubleNA; - if ( NMEA0183SetVHW(NMEA0183Msg, heading->getData(), MagneticHeading, WaterReferenced) ) { - SendMessage(NMEA0183Msg); - } - } -} - -//***************************************************************************** -void N2kDataToNMEA0183::HandleDepth(const tN2kMsg &N2kMsg) { - unsigned char SID; - double DepthBelowTransducer; - double Offset; - double Range; - double WaterDepth; - if ( ParseN2kWaterDepth(N2kMsg, SID, DepthBelowTransducer, Offset, Range) ) { - - WaterDepth=DepthBelowTransducer+Offset; - updateDouble(waterDepth,WaterDepth); - tNMEA0183Msg NMEA0183Msg; - if ( NMEA0183SetDPT(NMEA0183Msg, DepthBelowTransducer, Offset) ) { - SendMessage(NMEA0183Msg); - } - if ( NMEA0183SetDBx(NMEA0183Msg, DepthBelowTransducer, Offset) ) { - SendMessage(NMEA0183Msg); - } - } -} - -//***************************************************************************** -void N2kDataToNMEA0183::HandlePosition(const tN2kMsg &N2kMsg) { - double Latitude; - double Longitude; - if ( ParseN2kPGN129025(N2kMsg, Latitude, Longitude) ) { - updateDouble(latitude,Latitude); - updateDouble(longitude,Longitude); - } -} - -//***************************************************************************** -void N2kDataToNMEA0183::HandleCOGSOG(const tN2kMsg &N2kMsg) { - unsigned char SID; - tN2kHeadingReference HeadingReference; - tNMEA0183Msg NMEA0183Msg; - double COG; - double SOG; - if ( ParseN2kCOGSOGRapid(N2kMsg, SID, HeadingReference, COG, SOG) ) { - updateDouble(cog,COG); - updateDouble(sog,SOG); - double MCOG = ( !N2kIsNA(COG) && variation->isValid()) ? COG - variation->getData() : NMEA0183DoubleNA ; - if ( HeadingReference == N2khr_magnetic ) { - MCOG = COG; - if ( variation->isValid() ) { - COG -= variation->getData(); - updateDouble(cog,COG); - } - } - if ( NMEA0183SetVTG(NMEA0183Msg, COG, MCOG, SOG) ) { - SendMessage(NMEA0183Msg); - } - } -} - -//***************************************************************************** -void N2kDataToNMEA0183::HandleGNSS(const tN2kMsg &N2kMsg) { - unsigned char SID; - tN2kGNSStype GNSStype; - tN2kGNSSmethod GNSSmethod; - unsigned char nSatellites; - double HDOP; - double PDOP; - double GeoidalSeparation; - unsigned char nReferenceStations; - tN2kGNSStype ReferenceStationType; - uint16_t ReferenceSationID; - double AgeOfCorrection; - double Latitude; - double Longitude; - double Altitude; - uint16_t DaysSince1970; - double SecondsSinceMidnight; - if ( ParseN2kGNSS(N2kMsg, SID, DaysSince1970, SecondsSinceMidnight, Latitude, Longitude, Altitude, GNSStype, GNSSmethod, - nSatellites, HDOP, PDOP, GeoidalSeparation, - nReferenceStations, ReferenceStationType, ReferenceSationID, AgeOfCorrection) ) { - updateDouble(latitude,Latitude); - updateDouble(longitude,Longitude); - updateDouble(altitude,Altitude); - updateDouble(secondsSinceMidnight,SecondsSinceMidnight); - if (DaysSince1970 != N2kUInt16NA && DaysSince1970 !=0) daysSince1970->update(DaysSince1970); - } -} - -//***************************************************************************** -void N2kDataToNMEA0183::HandleWind(const tN2kMsg &N2kMsg) { - unsigned char SID; - tN2kWindReference WindReference; - tNMEA0183WindReference NMEA0183Reference = NMEA0183Wind_True; - - double x, y; - double WindAngle, WindSpeed; - - - if ( ParseN2kWindSpeed(N2kMsg, SID, WindSpeed, WindAngle, WindReference) ) { - tNMEA0183Msg NMEA0183Msg; - - if ( WindReference == N2kWind_Apparent ) { - NMEA0183Reference = NMEA0183Wind_Apparent; - updateDouble(awa,WindAngle); - updateDouble(aws,WindSpeed); - setMax(maxAws,aws); - } - - if ( NMEA0183SetMWV(NMEA0183Msg, formatCourse(WindAngle), NMEA0183Reference , WindSpeed)) SendMessage(NMEA0183Msg); - - if (WindReference == N2kWind_Apparent && sog->isValid()) { // Lets calculate and send TWS/TWA if SOG is available - - x = WindSpeed * cos(WindAngle); - y = WindSpeed * sin(WindAngle); - - updateDouble(twd,atan2(y, -sog->getData() + x)); - updateDouble(tws,sqrt(( y*y) + ((- sog->getData()+x)*(-sog->getData()+x)))); - - setMax(maxTws,tws); - - NMEA0183Reference = NMEA0183Wind_True; - if ( NMEA0183SetMWV(NMEA0183Msg, - formatCourse(twd->getData()), - NMEA0183Reference , - tws->getDataWithDefault(NMEA0183DoubleNA))) SendMessage(NMEA0183Msg); - double magnetic=twd->getData(); - if (variation->isValid()) magnetic-=variation->getData(); - if ( !NMEA0183Msg.Init("MWD", "GP") ) return; - if ( !NMEA0183Msg.AddDoubleField(formatCourse(twd->getData())) ) return; - if ( !NMEA0183Msg.AddStrField("T") ) return; - if ( !NMEA0183Msg.AddDoubleField(formatCourse(magnetic)) ) return; - if ( !NMEA0183Msg.AddStrField("M") ) return; - if ( !NMEA0183Msg.AddDoubleField(tws->getData()/0.514444) ) return; - if ( !NMEA0183Msg.AddStrField("N") ) return; - if ( !NMEA0183Msg.AddDoubleField(tws->getData()) ) return; - if ( !NMEA0183Msg.AddStrField("M") ) return; - - SendMessage(NMEA0183Msg); - - } - } -} - //***************************************************************************** - void N2kDataToNMEA0183::SendRMC() { - long now=millis(); - if ( NextRMCSend <= millis() && latitude->isValid(now) ) { - tNMEA0183Msg NMEA0183Msg; - if ( NMEA0183SetRMC(NMEA0183Msg, - - secondsSinceMidnight->getDataWithDefault(NMEA0183DoubleNA), - latitude->getDataWithDefault(NMEA0183DoubleNA), - longitude->getDataWithDefault(NMEA0183DoubleNA), - cog->getDataWithDefault(NMEA0183DoubleNA), - sog->getDataWithDefault(NMEA0183DoubleNA), - daysSince1970->getDataWithDefault(NMEA0183UInt32NA), - variation->getDataWithDefault(NMEA0183DoubleNA) - ) - ) { - SendMessage(NMEA0183Msg); - } - SetNextRMCSend(); - } - } - - - //***************************************************************************** - void N2kDataToNMEA0183::HandleLog(const tN2kMsg & N2kMsg) { - uint16_t DaysSince1970; - double SecondsSinceMidnight; - uint32_t Log,TripLog; - if ( ParseN2kDistanceLog(N2kMsg, DaysSince1970, SecondsSinceMidnight, Log, TripLog) ) { - if (Log != N2kUInt32NA) log->update(Log); - if (TripLog != N2kUInt32NA) tripLog->update(TripLog); - if (DaysSince1970 != N2kUInt16NA && DaysSince1970 != 0) daysSince1970->update(DaysSince1970); - tNMEA0183Msg NMEA0183Msg; - - if ( !NMEA0183Msg.Init("VLW", "GP") ) return; - if ( !NMEA0183Msg.AddDoubleField(Log / 1852.0) ) return; - if ( !NMEA0183Msg.AddStrField("N") ) return; - if ( !NMEA0183Msg.AddDoubleField(TripLog / 1852.0) ) return; - if ( !NMEA0183Msg.AddStrField("N") ) return; - - SendMessage(NMEA0183Msg); - } - } - - //***************************************************************************** - void N2kDataToNMEA0183::HandleRudder(const tN2kMsg & N2kMsg) { - - unsigned char Instance; - tN2kRudderDirectionOrder RudderDirectionOrder; - double AngleOrder; - double RudderPosition; - - if ( ParseN2kRudder(N2kMsg, RudderPosition, Instance, RudderDirectionOrder, AngleOrder) ) { - - updateDouble(rudderPosition,RudderPosition); - if(Instance!=0) return; - - tNMEA0183Msg NMEA0183Msg; - - if ( !NMEA0183Msg.Init("RSA", "GP") ) return; - if ( !NMEA0183Msg.AddDoubleField(formatCourse(RudderPosition)) ) return; - if ( !NMEA0183Msg.AddStrField("A") ) return; - if ( !NMEA0183Msg.AddDoubleField(0.0) ) return; - if ( !NMEA0183Msg.AddStrField("A") ) return; - - SendMessage(NMEA0183Msg); - } - } - -//***************************************************************************** - void N2kDataToNMEA0183::HandleWaterTemp(const tN2kMsg & N2kMsg) { - - unsigned char SID; - double OutsideAmbientAirTemperature; - double AtmosphericPressure; - double WaterTemperature; - if ( ParseN2kPGN130310(N2kMsg, SID, WaterTemperature, OutsideAmbientAirTemperature, AtmosphericPressure) ) { - - updateDouble(waterTemperature,WaterTemperature); - tNMEA0183Msg NMEA0183Msg; - - if ( !NMEA0183Msg.Init("MTW", "GP") ) return; - if ( !NMEA0183Msg.AddDoubleField(KelvinToC(WaterTemperature))) return; - if ( !NMEA0183Msg.AddStrField("C") ) return; - - SendMessage(NMEA0183Msg); - } - } diff --git a/lib/nmea2kto0183/N2kDataToNMEA0183.h b/lib/nmea2kto0183/N2kDataToNMEA0183.h index 05dd4d0..559ab88 100644 --- a/lib/nmea2kto0183/N2kDataToNMEA0183.h +++ b/lib/nmea2kto0183/N2kDataToNMEA0183.h @@ -20,7 +20,8 @@ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTIO CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - +#ifndef _N2KDATATONMEA0183_H +#define _N2KDATATONMEA0183_H #include #include @@ -29,75 +30,31 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include //------------------------------------------------------------------------------ -class N2kDataToNMEA0183 : public tNMEA2000::tMsgHandler { +class N2kDataToNMEA0183 : public tNMEA2000::tMsgHandler +{ public: - using tSendNMEA0183MessageCallback=void (*)(const tNMEA0183Msg &NMEA0183Msg); + using tSendNMEA0183MessageCallback = void (*)(const tNMEA0183Msg &NMEA0183Msg); - protected: - static const unsigned long RMCPeriod=500; - GwBoatItem *latitude; - GwBoatItem *longitude; - GwBoatItem * altitude; - GwBoatItem *variation; - GwBoatItem *heading; - GwBoatItem *cog; - GwBoatItem *sog; - GwBoatItem *stw; - - GwBoatItem *tws; - GwBoatItem *twd; - - GwBoatItem *aws; - GwBoatItem *awa; - GwBoatItem *awd; - - GwBoatItem *maxAws; - GwBoatItem *maxTws; - - GwBoatItem *rudderPosition; - GwBoatItem *waterTemperature; - GwBoatItem *waterDepth; - - GwBoatItem *tripLog; - GwBoatItem *log; - GwBoatItem *daysSince1970; - GwBoatItem *secondsSinceMidnight; - - - unsigned long LastPosSend; - unsigned long NextRMCSend; - unsigned long lastLoopTime; - GwLog *logger; GwBoatData *boatData; tNMEA0183 *pNMEA0183; tSendNMEA0183MessageCallback SendNMEA0183MessageCallback; -protected: - void HandleHeading(const tN2kMsg &N2kMsg); // 127250 - void HandleVariation(const tN2kMsg &N2kMsg); // 127258 - void HandleBoatSpeed(const tN2kMsg &N2kMsg); // 128259 - void HandleDepth(const tN2kMsg &N2kMsg); // 128267 - void HandlePosition(const tN2kMsg &N2kMsg); // 129025 - void HandleCOGSOG(const tN2kMsg &N2kMsg); // 129026 - void HandleGNSS(const tN2kMsg &N2kMsg); // 129029 - void HandleWind(const tN2kMsg &N2kMsg); // 130306 - void HandleLog(const tN2kMsg &N2kMsg); // 128275 - void HandleRudder(const tN2kMsg &N2kMsg); // 127245 - void HandleWaterTemp(const tN2kMsg &N2kMsg); // 130310 - - void SetNextRMCSend() { NextRMCSend=millis()+RMCPeriod; } - void SendRMC(); void SendMessage(const tNMEA0183Msg &NMEA0183Msg); + N2kDataToNMEA0183(GwLog *logger, GwBoatData *boatData, tNMEA2000 *NMEA2000, tNMEA0183 *NMEA0183); + public: - N2kDataToNMEA0183(GwLog * logger, GwBoatData *boatData, tNMEA2000 *NMEA2000, tNMEA0183 *NMEA0183) ; - void HandleMsg(const tN2kMsg &N2kMsg); - void SetSendNMEA0183MessageCallback(tSendNMEA0183MessageCallback _SendNMEA0183MessageCallback) { - SendNMEA0183MessageCallback=_SendNMEA0183MessageCallback; + static N2kDataToNMEA0183* create(GwLog *logger, GwBoatData *boatData, tNMEA2000 *NMEA2000, tNMEA0183 *NMEA0183); + virtual void HandleMsg(const tN2kMsg &N2kMsg) = 0; + void SetSendNMEA0183MessageCallback(tSendNMEA0183MessageCallback _SendNMEA0183MessageCallback) + { + SendNMEA0183MessageCallback = _SendNMEA0183MessageCallback; } - void loop(); + virtual void loop(); + virtual ~N2kDataToNMEA0183(){} }; +#endif \ No newline at end of file diff --git a/lib/nmea2kto0183/N2kToNMEA0183Functions.h b/lib/nmea2kto0183/N2kToNMEA0183Functions.h new file mode 100644 index 0000000..3d3fa2e --- /dev/null +++ b/lib/nmea2kto0183/N2kToNMEA0183Functions.h @@ -0,0 +1,502 @@ +#ifndef _N2KTONMEA0183FUNCTIONS_H +#define _N2KTONMEA0183FUNCTIONS_H +/** + * The class N2kToNMEA0183Functions is intended to be used for implementing all converter + * functions + * For easy extendability the implementation can be done completely within + * this header file (java like) without the need to change multiple files + */ +#include +#include +#include +#include "N2kDataToNMEA0183.h" +class N2kToNMEA0183Functions : public N2kDataToNMEA0183 +{ +private: + static const unsigned long RMCPeriod = 500; + static void setMax(GwBoatItem *maxItem, GwBoatItem *item) + { + unsigned long now = millis(); + if (!item->isValid(now)) + return; + if (item->getData() > maxItem->getData() || !maxItem->isValid(now)) + { + maxItem->update(item->getData()); + } + } + static void updateDouble(GwBoatItem *item, double value) + { + if (value == N2kDoubleNA) + return; + if (!item) + return; + item->update(value); + } + static double formatCourse(double cv) + { + double rt = cv * 180.0 / M_PI; + if (rt > 360) + rt -= 360; + if (rt < 0) + rt += 360; + return rt; + } + static double formatWind(double cv) + { + double rt = formatCourse(cv); + if (rt > 180) + rt = 180 - rt; + return rt; + } + static double formatKnots(double cv) + { + return cv * 3600.0 / 1852.0; + } + + static uint32_t mtr2nm(uint32_t m) + { + return m / 1852; + } + GwBoatItem *latitude; + GwBoatItem *longitude; + GwBoatItem *altitude; + GwBoatItem *variation; + GwBoatItem *heading; + GwBoatItem *cog; + GwBoatItem *sog; + GwBoatItem *stw; + + GwBoatItem *tws; + GwBoatItem *twd; + + GwBoatItem *aws; + GwBoatItem *awa; + GwBoatItem *awd; + + GwBoatItem *maxAws; + GwBoatItem *maxTws; + + GwBoatItem *rudderPosition; + GwBoatItem *waterTemperature; + GwBoatItem *waterDepth; + + GwBoatItem *tripLog; + GwBoatItem *log; + GwBoatItem *daysSince1970; + GwBoatItem *secondsSinceMidnight; + + unsigned long LastPosSend; + unsigned long NextRMCSend; + unsigned long lastLoopTime; + void SetNextRMCSend() { NextRMCSend = millis() + RMCPeriod; } + + //*************** the converters *********************** + void HandleHeading(const tN2kMsg &N2kMsg) + { + unsigned char SID; + tN2kHeadingReference ref; + double _Deviation = 0; + double Variation; + tNMEA0183Msg NMEA0183Msg; + double Heading; + if (ParseN2kHeading(N2kMsg, SID, Heading, _Deviation, Variation, ref)) + { + if (ref == N2khr_magnetic) + { + updateDouble(variation, Variation); // Update Variation + if (!N2kIsNA(Heading) && variation->isValid(millis())) + Heading -= variation->getData(); + } + updateDouble(heading, Heading); + if (NMEA0183SetHDG(NMEA0183Msg, heading->getDataWithDefault(NMEA0183DoubleNA), _Deviation, variation->getDataWithDefault(NMEA0183DoubleNA))) + { + SendMessage(NMEA0183Msg); + } + } + } + + //***************************************************************************** + void HandleVariation(const tN2kMsg &N2kMsg) + { + unsigned char SID; + tN2kMagneticVariation Source; + uint16_t DaysSince1970; + double Variation; + ParseN2kMagneticVariation(N2kMsg, SID, Source, DaysSince1970, Variation); + updateDouble(variation, Variation); + if (DaysSince1970 != N2kUInt16NA && DaysSince1970 != 0) + daysSince1970->update(DaysSince1970); + } + + //***************************************************************************** + void HandleBoatSpeed(const tN2kMsg &N2kMsg) + { + unsigned char SID; + double WaterReferenced; + double GroundReferenced; + tN2kSpeedWaterReferenceType SWRT; + + if (ParseN2kBoatSpeed(N2kMsg, SID, WaterReferenced, GroundReferenced, SWRT)) + { + tNMEA0183Msg NMEA0183Msg; + updateDouble(stw, WaterReferenced); + unsigned long now = millis(); + double MagneticHeading = (heading->isValid(now) && variation->isValid(now)) ? heading->getData() + variation->getData() : NMEA0183DoubleNA; + if (NMEA0183SetVHW(NMEA0183Msg, heading->getData(), MagneticHeading, WaterReferenced)) + { + SendMessage(NMEA0183Msg); + } + } + } + + //***************************************************************************** + void HandleDepth(const tN2kMsg &N2kMsg) + { + unsigned char SID; + double DepthBelowTransducer; + double Offset; + double Range; + double WaterDepth; + if (ParseN2kWaterDepth(N2kMsg, SID, DepthBelowTransducer, Offset, Range)) + { + + WaterDepth = DepthBelowTransducer + Offset; + updateDouble(waterDepth, WaterDepth); + tNMEA0183Msg NMEA0183Msg; + if (NMEA0183SetDPT(NMEA0183Msg, DepthBelowTransducer, Offset)) + { + SendMessage(NMEA0183Msg); + } + if (NMEA0183SetDBx(NMEA0183Msg, DepthBelowTransducer, Offset)) + { + SendMessage(NMEA0183Msg); + } + } + } + + //***************************************************************************** + void HandlePosition(const tN2kMsg &N2kMsg) + { + double Latitude; + double Longitude; + if (ParseN2kPGN129025(N2kMsg, Latitude, Longitude)) + { + updateDouble(latitude, Latitude); + updateDouble(longitude, Longitude); + } + } + + //***************************************************************************** + void HandleCOGSOG(const tN2kMsg &N2kMsg) + { + unsigned char SID; + tN2kHeadingReference HeadingReference; + tNMEA0183Msg NMEA0183Msg; + double COG; + double SOG; + if (ParseN2kCOGSOGRapid(N2kMsg, SID, HeadingReference, COG, SOG)) + { + updateDouble(cog, COG); + updateDouble(sog, SOG); + double MCOG = (!N2kIsNA(COG) && variation->isValid()) ? COG - variation->getData() : NMEA0183DoubleNA; + if (HeadingReference == N2khr_magnetic) + { + MCOG = COG; + if (variation->isValid()) + { + COG -= variation->getData(); + updateDouble(cog, COG); + } + } + if (NMEA0183SetVTG(NMEA0183Msg, COG, MCOG, SOG)) + { + SendMessage(NMEA0183Msg); + } + } + } + + //***************************************************************************** + void HandleGNSS(const tN2kMsg &N2kMsg) + { + unsigned char SID; + tN2kGNSStype GNSStype; + tN2kGNSSmethod GNSSmethod; + unsigned char nSatellites; + double HDOP; + double PDOP; + double GeoidalSeparation; + unsigned char nReferenceStations; + tN2kGNSStype ReferenceStationType; + uint16_t ReferenceSationID; + double AgeOfCorrection; + double Latitude; + double Longitude; + double Altitude; + uint16_t DaysSince1970; + double SecondsSinceMidnight; + if (ParseN2kGNSS(N2kMsg, SID, DaysSince1970, SecondsSinceMidnight, Latitude, Longitude, Altitude, GNSStype, GNSSmethod, + nSatellites, HDOP, PDOP, GeoidalSeparation, + nReferenceStations, ReferenceStationType, ReferenceSationID, AgeOfCorrection)) + { + updateDouble(latitude, Latitude); + updateDouble(longitude, Longitude); + updateDouble(altitude, Altitude); + updateDouble(secondsSinceMidnight, SecondsSinceMidnight); + if (DaysSince1970 != N2kUInt16NA && DaysSince1970 != 0) + daysSince1970->update(DaysSince1970); + } + } + + //***************************************************************************** + void HandleWind(const tN2kMsg &N2kMsg) + { + unsigned char SID; + tN2kWindReference WindReference; + tNMEA0183WindReference NMEA0183Reference = NMEA0183Wind_True; + + double x, y; + double WindAngle, WindSpeed; + + if (ParseN2kWindSpeed(N2kMsg, SID, WindSpeed, WindAngle, WindReference)) + { + tNMEA0183Msg NMEA0183Msg; + + if (WindReference == N2kWind_Apparent) + { + NMEA0183Reference = NMEA0183Wind_Apparent; + updateDouble(awa, WindAngle); + updateDouble(aws, WindSpeed); + setMax(maxAws, aws); + } + + if (NMEA0183SetMWV(NMEA0183Msg, formatCourse(WindAngle), NMEA0183Reference, WindSpeed)) + SendMessage(NMEA0183Msg); + + if (WindReference == N2kWind_Apparent && sog->isValid()) + { // Lets calculate and send TWS/TWA if SOG is available + + x = WindSpeed * cos(WindAngle); + y = WindSpeed * sin(WindAngle); + + updateDouble(twd, atan2(y, -sog->getData() + x)); + updateDouble(tws, sqrt((y * y) + ((-sog->getData() + x) * (-sog->getData() + x)))); + + setMax(maxTws, tws); + + NMEA0183Reference = NMEA0183Wind_True; + if (NMEA0183SetMWV(NMEA0183Msg, + formatCourse(twd->getData()), + NMEA0183Reference, + tws->getDataWithDefault(NMEA0183DoubleNA))) + SendMessage(NMEA0183Msg); + double magnetic = twd->getData(); + if (variation->isValid()) + magnetic -= variation->getData(); + if (!NMEA0183Msg.Init("MWD", "GP")) + return; + if (!NMEA0183Msg.AddDoubleField(formatCourse(twd->getData()))) + return; + if (!NMEA0183Msg.AddStrField("T")) + return; + if (!NMEA0183Msg.AddDoubleField(formatCourse(magnetic))) + return; + if (!NMEA0183Msg.AddStrField("M")) + return; + if (!NMEA0183Msg.AddDoubleField(tws->getData() / 0.514444)) + return; + if (!NMEA0183Msg.AddStrField("N")) + return; + if (!NMEA0183Msg.AddDoubleField(tws->getData())) + return; + if (!NMEA0183Msg.AddStrField("M")) + return; + + SendMessage(NMEA0183Msg); + } + } + } + //***************************************************************************** + void SendRMC() + { + long now = millis(); + if (NextRMCSend <= millis() && latitude->isValid(now)) + { + tNMEA0183Msg NMEA0183Msg; + if (NMEA0183SetRMC(NMEA0183Msg, + + secondsSinceMidnight->getDataWithDefault(NMEA0183DoubleNA), + latitude->getDataWithDefault(NMEA0183DoubleNA), + longitude->getDataWithDefault(NMEA0183DoubleNA), + cog->getDataWithDefault(NMEA0183DoubleNA), + sog->getDataWithDefault(NMEA0183DoubleNA), + daysSince1970->getDataWithDefault(NMEA0183UInt32NA), + variation->getDataWithDefault(NMEA0183DoubleNA))) + { + SendMessage(NMEA0183Msg); + } + SetNextRMCSend(); + } + } + + //***************************************************************************** + void HandleLog(const tN2kMsg &N2kMsg) + { + uint16_t DaysSince1970; + double SecondsSinceMidnight; + uint32_t Log, TripLog; + if (ParseN2kDistanceLog(N2kMsg, DaysSince1970, SecondsSinceMidnight, Log, TripLog)) + { + if (Log != N2kUInt32NA) + log->update(Log); + if (TripLog != N2kUInt32NA) + tripLog->update(TripLog); + if (DaysSince1970 != N2kUInt16NA && DaysSince1970 != 0) + daysSince1970->update(DaysSince1970); + tNMEA0183Msg NMEA0183Msg; + + if (!NMEA0183Msg.Init("VLW", "GP")) + return; + if (!NMEA0183Msg.AddDoubleField(Log / 1852.0)) + return; + if (!NMEA0183Msg.AddStrField("N")) + return; + if (!NMEA0183Msg.AddDoubleField(TripLog / 1852.0)) + return; + if (!NMEA0183Msg.AddStrField("N")) + return; + + SendMessage(NMEA0183Msg); + } + } + + //***************************************************************************** + void HandleRudder(const tN2kMsg &N2kMsg) + { + + unsigned char Instance; + tN2kRudderDirectionOrder RudderDirectionOrder; + double AngleOrder; + double RudderPosition; + + if (ParseN2kRudder(N2kMsg, RudderPosition, Instance, RudderDirectionOrder, AngleOrder)) + { + + updateDouble(rudderPosition, RudderPosition); + if (Instance != 0) + return; + + tNMEA0183Msg NMEA0183Msg; + + if (!NMEA0183Msg.Init("RSA", "GP")) + return; + if (!NMEA0183Msg.AddDoubleField(formatCourse(RudderPosition))) + return; + if (!NMEA0183Msg.AddStrField("A")) + return; + if (!NMEA0183Msg.AddDoubleField(0.0)) + return; + if (!NMEA0183Msg.AddStrField("A")) + return; + + SendMessage(NMEA0183Msg); + } + } + + //***************************************************************************** + void HandleWaterTemp(const tN2kMsg &N2kMsg) + { + + unsigned char SID; + double OutsideAmbientAirTemperature; + double AtmosphericPressure; + double WaterTemperature; + if (ParseN2kPGN130310(N2kMsg, SID, WaterTemperature, OutsideAmbientAirTemperature, AtmosphericPressure)) + { + + updateDouble(waterTemperature, WaterTemperature); + tNMEA0183Msg NMEA0183Msg; + + if (!NMEA0183Msg.Init("MTW", "GP")) + return; + if (!NMEA0183Msg.AddDoubleField(KelvinToC(WaterTemperature))) + return; + if (!NMEA0183Msg.AddStrField("C")) + return; + + SendMessage(NMEA0183Msg); + } + } + +public: + N2kToNMEA0183Functions(GwLog *logger, GwBoatData *boatData, tNMEA2000 *NMEA2000, tNMEA0183 *NMEA0183) : N2kDataToNMEA0183(logger, boatData, NMEA2000, NMEA0183) + { + LastPosSend = 0; + lastLoopTime = 0; + NextRMCSend = millis() + RMCPeriod; + + this->logger = logger; + this->boatData = boatData; + heading = boatData->getDoubleItem(F("Heading"), true, 4000, &formatCourse); + latitude = boatData->getDoubleItem(F("Latitude"), true, 4000); + longitude = boatData->getDoubleItem(F("Longitude"), true, 4000); + altitude = boatData->getDoubleItem(F("Altitude"), true, 4000); + cog = boatData->getDoubleItem(F("COG"), true, 4000, &formatCourse); + sog = boatData->getDoubleItem(F("SOG"), true, 4000, &formatKnots); + stw = boatData->getDoubleItem(F("STW"), true, 4000, &formatKnots); + variation = boatData->getDoubleItem(F("Variation"), true, 4000, &formatCourse); + tws = boatData->getDoubleItem(F("TWS"), true, 4000, &formatKnots); + twd = boatData->getDoubleItem(F("TWD"), true, 4000, &formatCourse); + aws = boatData->getDoubleItem(F("AWS"), true, 4000, &formatKnots); + awa = boatData->getDoubleItem(F("AWA"), true, 4000, &formatWind); + awd = boatData->getDoubleItem(F("AWD"), true, 4000, &formatCourse); + maxAws = boatData->getDoubleItem(F("MaxAws"), true, 0, &formatKnots); + maxTws = boatData->getDoubleItem(F("MaxTws"), true, 0, &formatKnots); + rudderPosition = boatData->getDoubleItem(F("RudderPosition"), true, 4000, &formatCourse); + waterTemperature = boatData->getDoubleItem(F("WaterTemperature"), true, 4000, &KelvinToC); + waterDepth = boatData->getDoubleItem(F("WaterDepth"), true, 4000); + log = boatData->getUint32Item(F("Log"), true, 0, mtr2nm); + tripLog = boatData->getUint32Item(F("TripLog"), true, 0, mtr2nm); + daysSince1970 = boatData->getUint32Item(F("DaysSince1970"), true, 4000); + secondsSinceMidnight = boatData->getDoubleItem(F("SecondsSinceMidnight"), true, 4000); + } + virtual void loop() + { + N2kDataToNMEA0183::loop(); + unsigned long now = millis(); + if (now < (lastLoopTime + 100)) + return; + lastLoopTime = now; + SendRMC(); + } + virtual void HandleMsg(const tN2kMsg &N2kMsg) + { + switch (N2kMsg.PGN) + { + case 127250UL: + HandleHeading(N2kMsg); + case 127258UL: + HandleVariation(N2kMsg); + case 128259UL: + HandleBoatSpeed(N2kMsg); + case 128267UL: + HandleDepth(N2kMsg); + case 129025UL: + HandlePosition(N2kMsg); + case 129026UL: + HandleCOGSOG(N2kMsg); + case 129029UL: + HandleGNSS(N2kMsg); + case 130306UL: + HandleWind(N2kMsg); + case 128275UL: + HandleLog(N2kMsg); + case 127245UL: + HandleRudder(N2kMsg); + case 130310UL: + HandleWaterTemp(N2kMsg); + } + } + +private: +}; +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b514767..a717dab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,7 +54,7 @@ Preferences preferences; // Nonvolatile storage on ESP32 - To store bool SendNMEA0183Conversion = true; // Do we send NMEA2000 -> NMEA0183 conversion bool SendSeaSmart = false; // Do we send NMEA2000 messages in SeaSmart format -N2kDataToNMEA0183 nmea0183Converter(&logger, &boatData,&NMEA2000, 0); +N2kDataToNMEA0183 *nmea0183Converter=N2kDataToNMEA0183::create(&logger, &boatData,&NMEA2000, 0); // Set the information for other bus devices, which messages we support const unsigned long TransmitMessages[] PROGMEM = {127489L, // Engine dynamic @@ -250,10 +250,10 @@ void setup() { NMEA2000.ExtendTransmitMessages(TransmitMessages); NMEA2000.ExtendReceiveMessages(ReceiveMessages); - NMEA2000.AttachMsgHandler(&nmea0183Converter); // NMEA 2000 -> NMEA 0183 conversion + NMEA2000.AttachMsgHandler(nmea0183Converter); // NMEA 2000 -> NMEA 0183 conversion NMEA2000.SetMsgHandler(HandleNMEA2000Msg); // Also send all NMEA2000 messages in SeaSmart format - nmea0183Converter.SetSendNMEA0183MessageCallback(SendNMEA0183Message); + nmea0183Converter->SetSendNMEA0183MessageCallback(SendNMEA0183Message); NMEA2000.Open(); @@ -307,7 +307,7 @@ void loop() { preferences.end(); Serial.printf("Address Change: New Address=%d\n", SourceAddress); } - nmea0183Converter.loop(); + nmea0183Converter->loop(); // Dummy to empty input buffer to avoid board to stuck with e.g. NMEA Reader if ( Serial.available() ) {