NMEA2000
NMEA2000 copied to clipboard
Binary Switch Control
Hi Timo,
thanks for the great library!
I want to build a binary switch bank and I'm currently unsure, what's the correct way to control the switches.
Should it be done by a pgn 127502 or by a write or command request group function (pgn 126208) to pgn 127501? I did some research on the internet and I'm still unsure.
I'm aware, that I have to extend the group function code to handle it by a group function for pgn 127501.
Thanks, T.
To be honest - I am not sure. 127502 name is "Switch Bank Control", so I would expect that is for setting binary bank status. Binary bank itself would then inform its staus with 127501. You logic to use group function is in principle OK, but what is 127502 then for?
Note that with 127502 you can also set single switch status by setting other to NA.
I think CZone uses those for controlling, spying network with CZone devices would give more information.
Should I implement 127502 support?
I found this information about switch control: https://www.panbo.com/maretron-dcr100-nmea-2000-switching/
The information is older, but I haven't found any more up-to-date information about it.
Empirbus seems to use a Proprietary PGN 65280. It's documented here: https://www.empirbus.com/wp-content/uploads/Application-Specific-PGN-65280_1.pdf
I think CZone uses also some proprietary PGNs, because there's no support for a dimmer in N2K. Do you have access to a CZone device to do a trace?
CZone surely uses proprietary, but it probably uses also available PGN:s.
I thought to by units for spying, they are too expensive just for testing. If you are doing that for your own, use 127501 ans 127502 and if necessary some proprietary for your own. If you think to publish it in future, just have good layers on program so you can easily change the communication way after joining to NMEA organization and getting necessary info.
I'll do it this way. Thanks for your support.
Hi, Schwinn: Do you plan to publish your efforts? And to Timo: Now that i'm here I might as well ask you one of the dumb questions. I have installed and run your great work on Arduino like boards. Now I want to to up in the world, and am wondering what would be your "best" among lager boards? Raspberry (do to its rich environment) Best Jorgen
What you are looking for? I have made extension board for RPi to connect it directly to NMEA2000. RPi works also with PiCAN2 board. But I have not yet tried to send anything with RPi and have not investigated how they handle sending. The problem is that CAN drivers does not normally handle NMEA 2000 fast packet sending right, so I had to modify all supported CAN drivers to get them work with NMEA 2000. One guy was trying to use library on mBed, but there was the same problem and he could not fix the driver problem.
As always very very helpful explanation. Seems as if RPi should do other stuff and Teensy continue in the NMEA role it does so well. Thanks Jorgen
Med venlig hilsen / Best Regards Jørgen B. Christensen
From: Timo Lappalainen [email protected] Sent: Monday, December 3, 2018 3:06:28 PM To: ttlappalainen/NMEA2000 Cc: newbezz; Comment Subject: Re: [ttlappalainen/NMEA2000] Binary Switch Control (#128)
What you are looking for? I have made extension board for RPi to connect it directly to NMEA2000. RPi works also with PiCAN2 board. But I have not yet tried to send anything with RPi and have not investigated how they handle sending. The problem is that CAN drivers does not normally handle NMEA 2000 fast packet sending right, so I had to modify all supported CAN drivers to get them work with NMEA 2000. One guy was trying to use library on mBed, but there was the same problem and he could not fix the driver problem.
— You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://nam05.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fttlappalainen%2FNMEA2000%2Fissues%2F128%23issuecomment-443721236&data=02%7C01%7C%7C25ab054646e4450fb37a08d65928853b%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636794427897516561&sdata=mLALpBorkwbq%2Fu7TnL9YKBmjXrh3qZ7LLS77Zbq6130%3D&reserved=0, or mute the threadhttps://nam05.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAJx5AMZySSPrfWa58HkpI7yT5MUu3ldvks5u1S_kgaJpZM4Yd7md&data=02%7C01%7C%7C25ab054646e4450fb37a08d65928853b%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636794427897516561&sdata=WVVcxPG7tBDqTQmWuPD5k9KRsm1NnFyrq98KM8Gry6A%3D&reserved=0.
That is sure solution. On the other hand there is no development, if nobody tries anything new and publish it. I would try, but just now for some months I do not have time. And as i mentioned, it depends much, what you are trying to do. Teensy can also act like Actisense NGT-1 and then SignalK can use data from it. Also it can send, but I do not know, does SignalK have sending possibilities.
adding 127502 support would be nice, yachtdevices has a module that works with that pgn. i configured my B&G according to their website, i cannot get it working. maybe you can look in to this. YDCC-04 and YDSC-04 the write: ompatible with other NMEA 2000 digital switching devices managed by standard NMEA 2000 PGNs 127501 and 127502.
I have no devices to test 127502, thats why I have not added it. I do not how to exactly to use it. I would expect that if you turn on some switch, you send once 127502 on for that switch single switch only. The the device should send 127501, where you can check did it turn on.
no it did not turn on, I can also not see a 127502 message beeing transferred over the n2kbus. Can you made a small script sending 127501 and 127502 ? then i can test that.
//*****************************************************************************
void SetN2kPGN127502(tN2kMsg &N2kMsg, unsigned char DeviceBankInstance, tN2kBinaryStatus BankStatus) {
N2kMsg.SetPGN(127502L);
N2kMsg.Priority=3;
BankStatus = (BankStatus << 8) | DeviceBankInstance;
N2kMsg.AddUInt64(BankStatus);
}
//*****************************************************************************
inline void SetN2kSwitchBankCommand(tN2kMsg &N2kMsg, unsigned char DeviceBankInstance, tN2kBinaryStatus BankStatus) {
SetN2kPGN127502(N2kMsg,DeviceBankInstance,BankStatus);
}
//*****************************************************************************
void SetSwitch(unsigned char DeviceBankInstance, uint8_t SwitchIndex, bool ItemStatus) {
tN2kBinaryStatus BankStatus;
tN2kMsg N2kMsg;
N2kResetBinaryStatus(BankStatus);
N2kSetStatusBinaryOnStatus(BankStatus,ItemStatus?N2kOnOff_On:N2kOnOff_Off,SwitchIndex);
SetN2kSwitchBankCommand(N2kMsg,DeviceBankInstance,BankStatus);
NMEA2000.SendMsg(N2kMsg);
}
Then, when you want to turn on switch 0 on bank instance 0 you call: SetSwitch(0,0,true); and off SetSwitch(0,0,false);
Note that B&G may use 126208 for commanding switches.
i put this in my arduino sketch in and set switch in de void loop. the 127502 pgn is send i can see in the actisense nmeareader. no reponse ir what ever on the czone page of my plotter :(
The plotter says the device is offline :( ill think it is not so simple to get this working. i
Please clarify what you try to do. Do you have YDCC-04 or YDSC-04 or do you try to make one? What devices you have on your test system.
i have a teensy3.1 and a vulcan plotter. found the czone config file of the YDCC-04. I want to switch outputs with the vulcan plotter on the teensy3.1.
The YDCC-04 uses pgn 127501 and 127502, so my simple understanding: load the config file in the vulcan plotter, let the teensy send the 127501 and 127502 and see what is going on. When looking at the n2kbus with the actisense reader i see the 127501 and 127502 send from the teensy3.1, but no 127501 and 127502 send from the vulcanplotter to the bus. In the plotter is the fault message that the YDCC-04 is not present.
So you have to send 127501 periodically to bus, where you inform you switch states. Then you have to add listener for 127502. When you receive that PGN, you parse switch states from it, set you switch state according to state. Since you update switch states with 127501, Vulcan should see it. Note that you should also add 127501 to Transmit and 127502 to Receive messages for library.
The problem is that your configuration file seem to contain information about YDCC-04. So it may be that even your device is on the bus, Vulcan will not see it, since it expects to see YDCC-04 there. So test first just send peridically 127501 to inform switch states. If Vulcan does not see your device, then you have to try to set all information (serial number, device info, manufacturer number etc.) to library as on configuration file.
I set all relevant information in the teensy. In the device list there is an YDCC-04 device. The serial numbers are correct as well. Regardless of parsing the 127502 message, the vulcan should send that on the network.
I think Vulcan (or any device) should send 127502 once only, when you change the switch state. It can not send message continuously, since point of digital switches is that you can control them from different places and they may be disabled to force them off.
You do not need to parse 127502 at first. Just see that Vulcan will see your device and send 127502 on switch state change. If you do not change that switch state to 127501, Vulcan should return switch to your state - maybe give error, that it could not switch.
The online tool https://www.yachtd.com/products/ds/?czone should create a valid configuration for the Vulcan.
YDCC-04 Manual https://www.yachtd.com/downloads/ydccydsc.pdf
i made de valid configuration with that tool.
it just looks like the vulcan doesnt get a response and thinks the device is offline. if i use the buttons on the vulcan there is no message send
Yes I have seen that. But have you tested to use it? What information fixes configured device to Vulcan? Serialnumber etc. Has anybody tried to find, what vulcan configuration file should contain?
i put that file in my vulcan. i pressed a switch button but no message on the n2knetwork
As I understand when a new config is detected it will replace the config on ALL czone devices in that group so if any device fails the rest of the network keeps working. If your device is not completing the network initialization correctly perhaps it's being ignored.
ok, that makes sense. now to find a way to let the vulcan think there is a module. Do you know what message need t be end to the czone config to initialise a module ?
That's the mystery of the czone proprietary messages, please let me know when you find out. The fact the there is a YDCC indicates it's possible to reverse engineer. I doubt BEP approved this device as it wipes the configuration of any other czone devices on the network! From the YDCC manual:
If you already have CZone equipment installed, you will overwrite the exiting CZone configuration with our file and your CZone equipment will not function correctly.
The czone devices are not in the config file that you download from the yachtdevice website. I downloaded the czone configurator, opened the config file, nothing special there just the device with 4 outputs.
I work with pgn127501 from the library. On the garmin plotter that I have appears the 10 buttons that I would like to have.The only problem that I have now it is how to take the request from the plotter to turn on the switches.i am searching if it is necessary to use the pgn126208 to can take the request for changing the status of the switches.
can you share your work so far ?
Of course I will do that.
wunderfull, i am also curious how you got the buttons on your plotter.
#include <Arduino.h>
//#define N2k_CAN_INT_PIN 21
#include "NMEA2000_CAN.h"
#include "N2kMessages.h"
const unsigned long TransmitMessages[] PROGMEM={127501L,127502L,0};
void setup() {
NMEA2000.SetProductInformation("112233", 100, "Binary Switches", "2.6.3.20 (2020-3-20)","1.1.1.1 (2020-3-15)", 0,0,0,0);
NMEA2000.SetDeviceInformation(112233,140, 30,333,4,0 );
Serial.begin(115200); NMEA2000.SetForwardStream(&Serial);
NMEA2000.SetMode(tNMEA2000::N2km_NodeOnly,22); NMEA2000.ExtendTransmitMessages(TransmitMessages); NMEA2000.Open();
pinMode(2, INPUT); pinMode(3, INPUT); pinMode(4, INPUT); pinMode(5, INPUT); pinMode(6, INPUT); pinMode(7, INPUT); pinMode(8, INPUT); pinMode(9, INPUT); pinMode(10, INPUT); pinMode(11, INPUT);
}
void loop() {
SendN2kBinaryStatus(); NMEA2000.ParseMessages();
}
#define SwitchUpdatePeriod 1000
typedef int64_t BankStatus;
void SendN2kBinaryStatus(){ unsigned char BankInstance=0; static unsigned long SwitchUpdated=millis(); tN2kMsg N2kMsg; tN2kOnOff Status1,Status2,Status3,Status4, Status5,Status6,Status7,Status8, Status9,Status10;
if ( SwitchUpdated+SwitchUpdatePeriod<millis() ) { SwitchUpdated=millis();
if(digitalRead(2)==LOW )
{ Status1=N2kOnOff_On;
}
else if (digitalRead(2)==HIGH )
{ Status1=N2kOnOff_Off;
}
if(digitalRead(3)==LOW )
{ Status2=N2kOnOff_On;
}
else if (digitalRead(3)==HIGH )
{ Status2=N2kOnOff_Off;
}
if(digitalRead(4)==LOW )
{ Status3=N2kOnOff_On;
}
else if (digitalRead(4)==HIGH )
{ Status3=N2kOnOff_Off;
}
if(digitalRead(5)==LOW )
{ Status4=N2kOnOff_On;
}
else if (digitalRead(5)==HIGH )
{ Status4=N2kOnOff_Off;
}
if(digitalRead(6)==LOW )
{ Status5=N2kOnOff_On;
}
else if (digitalRead(6)==HIGH )
{ Status5=N2kOnOff_Off;
}
if(digitalRead(7)==LOW )
{ Status6=N2kOnOff_On;
}
else if (digitalRead(7)==HIGH )
{ Status6=N2kOnOff_Off;
}
if(digitalRead(8)==LOW )
{ Status7=N2kOnOff_On;
}
else if (digitalRead(8)==HIGH )
{ Status7=N2kOnOff_Off;
}
if(digitalRead(9)==LOW )
{ Status8=N2kOnOff_On;
}
else if (digitalRead(9)==HIGH )
{ Status8=N2kOnOff_Off;
}
if(digitalRead(10)==LOW )
{ Status9=N2kOnOff_On;
}
else if (digitalRead(10)==HIGH )
{ Status9=N2kOnOff_Off;
}
if(digitalRead(11)==LOW )
{ Status10=N2kOnOff_On;
}
else if (digitalRead(11)==HIGH )
{ Status10=N2kOnOff_Off;
}
SetN2kBinaryStatus(N2kMsg,0,Status1,Status2,Status3,Status4, Status5,Status6,Status7,Status8,
Status9,Status10);
NMEA2000.SendMsg(N2kMsg,0);
}
}
And that is the images.
wow that looks great, hos did you make the switches on your plotter ? My B&G doesn't have a predefinded button field of possibility
I check that when running in the network the pgn127501 information automatically recognized from the plotter.Before I did not have these option on my plotter.
Antoni,
which plotter is this? 7** ? curious if that will work on my GPSMAP 4008 and 5008 or they are too old!
cheers
V
I have the gpsmap1022.I think that if you make the new update 7.80 maybe you can have it,but I am not so sure.I check in the Garmin update but I do not find something there.
Have you read the bus how your MFD would handle button press. There are two ways: it sends 127502 with switch information it wants to change or it uses 126208, which is more complex. So extend your ReceivePGNList with 127502 and then spy bus doesgpsmap send that pgn on switch press.
I check the traffic of the nmea 2000 the plotter send pgn127502 and pgn126208 you have right. My problem for now is to collect the information from the pgn127502 but i could not add on the library to call them then. The pgn126208 is much difficult to check it and it is neccecary to send and listen all the commands. If you have any idea please let me know. thanks
Hi, I just managed to send a 127501 to my Garmin 922xs and all of a sudden switches appered on my screen. When I set one bank switch high in 127501 the corresponding switch in my MFD turns high 😀👍. Now my problem is to manage to parse 127502 sent from my MFD when pressing a switch. Has someone managed to do that?
best regards Niklas
would you share your code ?
If you use my code you can see that is working in these way. I make a lot of tries to take the neccessary phrase from the nmea 2000. First I change the divice settings to listen and send NMEA2000.SetMode(tNMEA2000::N2km_ListenAndSend); And now I am try to separate the phrase that running in the network to take only the Pgn127502 and then to change the status of the switch. The phrase of 127501 and 127502 can manage 28 switches in 1 sid.
N2km_ListenAndSend mode is for analysing and test purposes. Device will then not respond to requests. This will cause that it will disappear from the devices list on MFD.
Parsing 127502 is similar as 127501. When you receive 127502, you should change your switch status according received information. Note that you should not touch to any switches, which values on messages is NA. Then you update 127501 as normally.
You have right Timo. I used this NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode,22); But I could read the phrase of pgn 127502.
Hi.
Of course I will share my code. But It might not be all pleasant reading ;) It partial has some swedish names but without the dots. How should I share it?
My project is based on a Arduino Mega2560, a CAN-BUS shield with a MCP2515, a Nextion Enhanced display 7". The system "Diginautic" originally aimed to collect engine RPM, oil pressure and coolant temperature. But while I got to know Arduino and that "world" I realized that a touch display and some relays would easily replace my ordinary switches. And while I was going I found the ACS712 current sensors and now I have:
Arduino Mega2560 CAN-BUS shield Nextion Enhanced 7" display 16 relays 13 current sensors 2 op-amps measuring voltage drops over 3 shunt resistors I2C-bus with 4 16-bits ADC to collect data
Along that in my boat I have Garmin MFD, AIS, VHF, Fusion FM-Radio, Compass and GFL10 Fluid Level sensor on my NMEA2000-bus. I collect lots of data from N2k and send quite a lot of data back on N2k.
When I change state on any of my relays I send a 127501 on N2k to my GPS:
When pressing button 1 I do this N2kSetStatusBinaryOnStatus(SwitchBoard,N2kOnOff_On,1); SendN2kBinaryStatus(true);
My next step is to add support for 127502 to receive that PGN. The thoughts I have is to do the opposite of 127501. Parse SwitchBoard to find out what button was pressed on my GPS. After that I'll send a corresponding 127501 telling my GPS that it actually was turned on.
I'll let you know on my progress /Niklas
Den fre 17 apr. 2020 kl 10:07 skrev AntonisPapa [email protected]:
You have right Timo. I used this NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode,22); But I could read the phrase of pgn 127502.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ttlappalainen/NMEA2000/issues/128#issuecomment-615107141, or unsubscribe https://github.com/notifications/unsubscribe-auth/APG4JCANRTUP2CZ5HFZXIQLRNAE53ANCNFSM4GDXXGOQ .
Hi All,
I have followed this topic with interest, I have been using Timo's excellent and robust libraries and have built a number of projects using the Teensy and Rpi platforms for my yacht.
Digital switching from my Raymarine Axiom plotter being a most desirable addition, after several failed attempts, it is probably worth relating what I have learnt.
The Axiom supports 3 vendors in digital switching, Empirbus, Yacht Devices, and CZone. All require a custom Qt page, designed and loaded. This isn't so hard as Empirbus and Yacht Devices have tools on there websites that generate the required pages.
Both Empirbus and Yacht Devices both publish some information regarding the N2K PGN's their devices use, mainly dealing with the switching PGN's, so far so good.
The problem is the Axion needs to detect the hardware is on the bus before it enable the switching page or the onscreen switches.
This in my opinion, during the startup interaction between the device and Axion, and is probably in the Vendor/Device/Serial Number PGN's exchanges between the two devices.
Having tried several time to simulate these exchanges, I have concluded without an actual vendor device to look at these interactions, it is near impossible.
Regards
Have you tried to just set device manufacturer ID to Yacht Devices 717? They use standard messages 127501 and 127502
Hi!
On my boat I only use 127501 and 127502 and no configuration files in my plotter. I have a Garmin 922xs and it works perfectly.
Regards Niklas
Den ons 24 juni 2020 kl 13:55 skrev Timo Lappalainen < [email protected]>:
Have you tried to just set device manufacturer ID to Yacht Devices 717? They use standard messages 127501 and 127502
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ttlappalainen/NMEA2000/issues/128#issuecomment-648774828, or unsubscribe https://github.com/notifications/unsubscribe-auth/APG4JCFXV7RMFLWV2DPANHLRYHSTVANCNFSM4GDXXGOQ .
Hi guys!
Please if you could can you share with us the code that you use? The Garmin plotters are recognize direct from the pgns the modules that have been installed in the network.
The raymarine with empire bus works in different pgns and to appear in the network it is neccessary to fix an html5 page with the number of the items icons amd etc.
Στις Τετ, 24 Ιουν 2020, 23:46 ο χρήστης diginautic [email protected] έγραψε:
Hi!
On my boat I only use 127501 and 127502 and no configuration files in my plotter. I have a Garmin 922xs and it works perfectly.
Regards Niklas
Den ons 24 juni 2020 kl 13:55 skrev Timo Lappalainen < [email protected]>:
Have you tried to just set device manufacturer ID to Yacht Devices 717? They use standard messages 127501 and 127502
— You are receiving this because you commented. Reply to this email directly, view it on GitHub < https://github.com/ttlappalainen/NMEA2000/issues/128#issuecomment-648774828 , or unsubscribe < https://github.com/notifications/unsubscribe-auth/APG4JCFXV7RMFLWV2DPANHLRYHSTVANCNFSM4GDXXGOQ
.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ttlappalainen/NMEA2000/issues/128#issuecomment-649062393, or unsubscribe https://github.com/notifications/unsubscribe-auth/AO57YUHNJUN3I5FYPU6VBE3RYJQ3JANCNFSM4GDXXGOQ .
Hi Timo,
Last years i was working on an engine interface, that worked fine at home on a B&G vulcan 5 plotter.
Last winter I installed it on my ship, a bigger n2k network with more sensors and I encounter some difficulties.
After playing for a whole season with settings on the network I finally gave up.
It even prevents me from experimenting with outputs on the N2knetwork
The device is not visible and then it is for a while and then it is gone no data on the data fields and there is again in the device list.
Basically it random appears and disappears on the netwok
It look lik It fight’s with the N2k fusion radio , that stops working and need a hard reset,
I changed the network address but that doesn’t do the trick.
Even build in a delay that works sometimes but not consistent
Obviously I am doing something wrong can you help me?
I used your example and modify some setting so it works for me.
Perhaps I need to add or remove something.
The code that is used initial is below:
// Yanmar motor interface, various sensors added to the engine block
// Need about 1 minute starting delay, other wise the plotter wil not recognize the interface,
// i have to sort this out some day........
// Big thank you to Timo Lappalainen for sharing the N2K lib on github.
//
//Demo sketch modified to this version by F. Meijlink. 04-2020
//Running on a Teensy 3.2
#include <Arduino.h>
#include <NMEA2000_CAN.h> // This will automatically choose right CAN library and create suitable NMEA2000 object
#include <N2kMessages.h>
#include <N2kMessagesEnumToStr.h>
#define Serial SerialUSB
#include <U8g2lib.h>
#include <Wire.h>
volatile byte rpmcount;
unsigned int rpm;
unsigned long timeold;
//int refsig=20;//for converting the analog signal coming from hall sensor to digital through arduino code
int val;//the digital value of the incoming analog signals
int prev_val=0;
unsigned long t,cur_t;//time variables
U8G2_SH1106_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// Yanmarlogo
#define yanmarlogo1_width 125
#define yanmarlogo1_height 49
static unsigned char yanmarlogo1_bits[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xfe, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0x7f,
0xe0, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0xff, 0x7f, 0xf8, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0x7f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff,
0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x3f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff,
0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x1f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0xbf, 0x8f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xbf, 0xef, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x87, 0x0f,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xf8, 0x61, 0xdf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xf8, 0x5c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x7c, 0x0c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7c, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xe0, 0x0f, 0xff,
0x01, 0x3c, 0xf0, 0xe0, 0x0f, 0xf0, 0x07, 0xf0, 0x1f, 0xe0, 0xff, 0x0f,
0x3f, 0xe0, 0x8f, 0xff, 0x01, 0x3e, 0xf0, 0xf0, 0x0f, 0xf8, 0x07, 0xf0,
0x3f, 0xf0, 0xff, 0x0f, 0x3e, 0xf0, 0x87, 0xff, 0x01, 0x3e, 0xf0, 0xf0,
0x1f, 0xf8, 0x07, 0xf8, 0x3f, 0xf0, 0xff, 0x1f, 0x3e, 0xf8, 0xc7, 0xff,
0x01, 0x3e, 0xf8, 0xf0, 0x1f, 0xfc, 0x07, 0xf8, 0x3f, 0xf0, 0xff, 0x1f,
0x7c, 0xfc, 0xe3, 0xff, 0x03, 0x7e, 0xf8, 0xf0, 0x1f, 0xfe, 0x07, 0xfc,
0x3f, 0xf8, 0xff, 0x0f, 0xf8, 0xff, 0xe0, 0xcf, 0x03, 0xff, 0xf8, 0xf8,
0xbf, 0xff, 0x03, 0xfe, 0x7f, 0xf8, 0x81, 0x0f, 0xf8, 0x7f, 0xe0, 0xcf,
0x03, 0xff, 0xf9, 0xf8, 0xbf, 0xff, 0x03, 0x7f, 0x7f, 0xf8, 0xc1, 0x07,
0xf0, 0x3f, 0xe0, 0xc7, 0x03, 0xff, 0xff, 0xf8, 0xff, 0xff, 0x03, 0x7f,
0x7f, 0xf8, 0xff, 0x07, 0xf0, 0x1f, 0xf0, 0xc7, 0x07, 0xff, 0x7f, 0xf8,
0xff, 0xff, 0x03, 0x3f, 0x7f, 0xfc, 0xff, 0x03, 0xe0, 0x1f, 0xf0, 0xc3,
0x87, 0xff, 0x7f, 0xf8, 0xff, 0xff, 0x03, 0x3f, 0x7f, 0xfc, 0xff, 0x03,
0xe0, 0x1f, 0xf8, 0xc3, 0x87, 0xff, 0x7f, 0xf8, 0xfe, 0xff, 0x01, 0x3f,
0xff, 0xfc, 0xff, 0x07, 0xe0, 0x0f, 0xf8, 0xff, 0x87, 0xff, 0x7f, 0xfc,
0xfc, 0xf3, 0x81, 0xff, 0xff, 0xfc, 0xe1, 0x07, 0xe0, 0x0f, 0xf8, 0xff,
0x87, 0xff, 0x7f, 0xfc, 0xfc, 0xf3, 0x81, 0xff, 0xff, 0xfc, 0xe1, 0x03,
0xe0, 0x07, 0x7c, 0xc0, 0xcf, 0xdf, 0x7f, 0x7c, 0x78, 0xf0, 0xe1, 0x07,
0xfe, 0xfc, 0xe1, 0x03, 0xe0, 0x07, 0x3e, 0xc0, 0xcf, 0x9f, 0x7f, 0x7c,
0x70, 0xf0, 0xe1, 0x07, 0xfe, 0xfc, 0xe1, 0x03, 0xe0, 0x07, 0x3e, 0xc0,
0xcf, 0x1f, 0x7f, 0x7e, 0x70, 0xf8, 0xf1, 0x03, 0xfe, 0xfd, 0xf1, 0x03,
0xe0, 0x03, 0x1f, 0xc0, 0x8f, 0x0f, 0x7e, 0x7c, 0x60, 0xf0, 0xf1, 0x03,
0xfc, 0xfc, 0xe0, 0x01 };
//----------------------------- nmea2000 parser-----------------------//
typedef struct {
unsigned long PGN;
void (*Handler)(const tN2kMsg &N2kMsg);
} tNMEA2000Handler;
void BinaryStatus(const tN2kMsg &N2kMsg);
tNMEA2000Handler NMEA2000Handlers[]={
{127501L,&BinaryStatus},
{0,0}
};
//---------------------------------------------------------------------//
void draw(void) {
// graphic commands to redraw the complete screen should be placed here
u8g2.drawXBMP( 0, 0, yanmarlogo1_width, yanmarlogo1_height, yanmarlogo1_bits);
}
void setup()
{
u8g2.begin();
delay (10000); //10sec delay
//draw logo after 10 sec delay
u8g2.firstPage();
do {
draw();
} while ( u8g2.nextPage() );
delay (20000); // display logo for 20sec
u8g2.firstPage();
do {
//u8g2.setFont(u8g2_font_ncenB10_tr);
u8g2.setFont(u8g2_font_t0_13b_te);
u8g2.drawStr(10,14,"Yanmar N2K interface");
u8g2.drawStr(10,24,"Version 1.3.1");
u8g2.drawStr(10,36,"Sproktronics");
} while ( u8g2.nextPage() );
delay (10000);// delay 10 seconden
u8g2.firstPage();
do {
//u8g2.setFont(u8g2_font_ncenB10_tr);
u8g2.setFont(u8g2_font_t0_13b_te);
u8g2.drawStr(10,14,"Waiting 15 sec ");
u8g2.drawStr(10,24,"(c) 2020");
u8g2.drawStr(10,36,"Sproktronics");
} while ( u8g2.nextPage() );
delay (10000);
// Set Product information
NMEA2000.SetN2kCANSendFrameBufSize(150);
NMEA2000.SetN2kCANReceiveFrameBufSize(150);
NMEA2000.SetProductInformation("550440", // Manufacturer's Model serial code
100, // Manufacturer's product code
"Sproktronic", // Manufacturer's Model ID
"1.3.1 (2020-20-01)", // Manufacturer's Software version code
"1.0.0.0, (2017-01-01)" // Manufacturer's Model version
);
// Set device information
NMEA2000.SetDeviceInformation(777888, // Unique number. Use e.g. Serial number.
132, // Device function=Analog to NMEA 2000 Gateway. See codes on http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
25, // Device class=Inter/Intranetwork Device. See codes on http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
381, // Just choosen free from code list on http://www.nmea.org/Assets/20121020%20nmea%202000%20registration%20list.pdf
4 //marine
);
// Uncomment 3 rows below to see, what device will send to bus
Serial.begin(115200);
NMEA2000.SetForwardStream(&Serial); // PC output on due programming port
// NMEA2000.SetForwardType(tNMEA2000::fwdt_Text); // Show in clear text. Leave uncommented for default Actisense format.
// NMEA2000.SetForwardOwnMessages();
// If you also want to see all traffic on the bus use N2km_ListenAndNode instead of N2km_NodeOnly below
// NMEA2000.SetMode(tNMEA2000::N2km_NodeOnly,22);
NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode,96);
//NMEA2000.SetDebugMode(tNMEA2000::dm_ClearText); // Uncomment this, so you can test code without CAN bus chips on Arduino Mega
NMEA2000.EnableForward(false); // Disable all msg forwarding to USB (=Serial)
NMEA2000.SetMsgHandler(HandleNMEA2000Msg);
NMEA2000.Open();
pinMode(14, INPUT_PULLUP);
pinMode(15, INPUT_PULLUP);
pinMode(16, INPUT_PULLUP);
pinMode(17, INPUT_PULLUP);
pinMode(20, INPUT_PULLUP);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
//pinMode(9,
pinMode(21, INPUT);
delay (30);
//freq1.begin(A7); // set rpm input pin
u8g2.enableUTF8Print(); // enable UTF8 support for the Arduino print() function
//int Oledrpm;
//Interrupt 0 is digital pin 2, so that is where the IR detector is connected
//Triggers on FALLING (change from HIGH to LOW)
attachInterrupt(21, rpm_fun, RISING);
rpmcount = 0;
rpm = 0;
timeold = 0;
} // end void setup
float sum1=0;
int count1=0;
//elapsedMillis timeout;
//int rpm = 0;
int Oledrpm = 0;
//----------------------------- coolant temp --------------------------//
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2 // Data wire is plugged into pin 2 on the Arduino
OneWire oneWire(ONE_WIRE_BUS);// Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature.
//DeviceAddress CoolantSensor = { 0x28, 0xFF, 0x9C, 0xA0, 0x81, 0x16, 0x03, 0x1A }; // Define device <- test device
DeviceAddress CoolantSensor = { 0x28, 0xFF, 0xBD, 0xBC, 0x94, 0x16, 0x04, 0xF9 }; // Define device address
//DeviceAddress EngineRoom = { 0x28, 0xFF, 0x71, 0xE3, 0x81, 0x16, 0x03, 0x44 }; // Define device <- test device
DeviceAddress EngineRoom = { 0x28, 0xFF, 0x68, 0xA4, 0x81, 0x16, 0x03, 0xAD }; // Define device address
//DeviceAddress Exhaust = { 0x28, 0xFF, 0x87, 0xF6, 0x81, 0x16, 0x03, 0xC7 }; // Define device <- test device
DeviceAddress Exhaust = { 0x28, 0xFF, 0xD5, 0x80, 0x94, 0x16, 0x04, 0x8C }; // Define device address
double CoolantTemp = 0.0f;
double EngineRoomTemp = 0.0f;
double ExhaustGasTemp = 0.0f;
int engtmp;
int EngRmTmp;
int ExHstTmp;
void readTemp()
{
// Start up the library
sensors.begin();
// set the resolution to 10 bit
sensors.setResolution(CoolantSensor, 10);
sensors.setResolution(EngineRoom, 10);
sensors.requestTemperatures();
float tempC = sensors.getTempC(CoolantSensor);
float tempE = sensors.getTempC(EngineRoom);
float tempU = sensors.getTempC(Exhaust);
//Serial.println(tempC);
CoolantTemp = (tempC);
EngineRoomTemp = (tempE);
ExhaustGasTemp = (tempU);
engtmp = tempC; // coolanttemp for Oled display
EngRmTmp = tempE; // engineroom temp for Oled display
ExHstTmp = tempU;// exhaust temp for Oled display
}
//----------------------------- oil pressure --------------------------//
double OilPressure;
double OilPress;
void Readoilpressure()
{
float ratio = (analogRead (A9) + 0.5) / 1024.0 ; // 0 to 1 range
float pressure_PSI = (ratio - 0.1) * 500.0 / 0.8 ;
if (analogRead(A9) < 170){
pressure_PSI = 0;
}
OilPressure = round (pressure_PSI /10)*68.97 ; // 68.947 psi to mBar
OilPress = OilPressure / 1000; // devide by 1K for Oled display
}
//----------------------------- oil temp --------------------------//
double oiltemp = 0;
double sensorInput = 0.0f;
int Oiltmp;
double Voltpin = 0;
const int N = 16; // aantal te middelen meetwaardes
int meetwaardes[N]; // opslag van individuele meetwaardes
int filteraccumulator = 0; // optelsom van alle individuele meetwaardes
int i = 0; // meetwaardeteller
void readoilTemp()
{
// average over 16 measurement values
filteraccumulator -= meetwaardes[i]; // vergeet oudste meetwaarde
meetwaardes[i] = analogRead(A8); // bewaar nieuwe meetwaarde
filteraccumulator += meetwaardes[i]; // accumuleer nieuwste meetwaarde
i++; i %= N; // teller loopt van 0 tot N-1
sensorInput = filteraccumulator/N; // bereken de gemmiddelde waarde
delay(100);
float voltage = sensorInput * 3.3;
oiltemp = round (voltage - 500) /10 ; //
//((1000 mV - 500) / 10) = 50 degrees Celsius
//oiltemp = round (oiltemp * 4.1 * 100); //multiply by 4.1V to get voltage
oiltemp = round (oiltemp);
Oiltmp = oiltemp; // oiltemp for Oled display
}
//-----------------output---------------------------------------------//
void BinaryStatus(const tN2kMsg &N2kMsg) {
unsigned char BankInstance;
//N2kOnOff Status1,Status2,Status3,Status4;
tN2kBinaryStatus BankStatus;
///naryStatusFull(N2kMsg);
if (ParseN2kBinaryStatus(N2kMsg,BankInstance,BankStatus) ) {
digitalWrite(7, ( N2kGetStatusOnBinaryStatus(BankStatus,1)==N2kOnOff_On ? HIGH : LOW ) );
digitalWrite(8, ( N2kGetStatusOnBinaryStatus(BankStatus,2)==N2kOnOff_On ? HIGH : LOW ) );
}
}
//-------------------------------- NMEA 2000 message handler -----------------------//
void HandleNMEA2000Msg(const tN2kMsg &N2kMsg) {
int iHandler;
// Find handler
//OutputStream->print("In Main Handler: "); OutputStream->println(N2kMsg.PGN);
for (iHandler=0; NMEA2000Handlers[iHandler].PGN!=0 && !(N2kMsg.PGN==NMEA2000Handlers[iHandler].PGN); iHandler++);
if (NMEA2000Handlers[iHandler].PGN!=0) {
NMEA2000Handlers[iHandler].Handler(N2kMsg);
}
}
void rpm_fun()
{
//Each rotation, this interrupt function is run twice, so take that into consideration for
//calculating RPM
//Update count
rpmcount++;
}
void loop()
{
while ( Serial.available()>0 ) Serial.read();
// rpm start here ---------------
//Update RPM every second
delay(1000);
//Don't process interrupts during calculations
detachInterrupt(0);
//Note that this would be 60*1000/(millis() - timeold)*rpmcount if the interrupt
//happened once per revolution instead of twice. Other multiples could be used
//for multi-bladed propellers or fans
rpm = 60*1000/(millis() - timeold)*rpmcount;
timeold = millis();
rpmcount = 0;
Serial.print("RPM=");
Serial.println(rpm);
//Restart the interrupt processing
attachInterrupt(21, rpm_fun, RISING);
NMEA2000.ParseMessages();
readTemp();
Readoilpressure();
SendN2kRapidData();
SendN2kSlowData();
readoilTemp();
//---------------------------- Oled ----------------------------//
u8g2.firstPage();
do {
//u8g2.setFont(u8g2_font_ncenB10_tr);
u8g2.setFont(u8g2_font_t0_13b_te);
u8g2.drawStr(0,12,"EngRoomTmp ");
u8g2.setCursor(90, 12);
u8g2.print(EngRmTmp);u8g2.print("°C ");
u8g2.drawStr(0,30,"Oil");
u8g2.setCursor(25, 30);
u8g2.print(Oiltmp);u8g2.print("°C "); u8g2.print(OilPress);u8g2.print(" Bar");
u8g2.drawStr(0,45,"Engine");
u8g2.setCursor(45, 45);
u8g2.print(engtmp);u8g2.print("°C ");u8g2.print(rpm);
u8g2.setFont (u8g2_font_t0_11b_te);
u8g2.print("/min");
u8g2.setFont(u8g2_font_t0_13b_te);
u8g2.drawStr(0, 61,"ExhaustTmp ");
u8g2.setCursor(90, 61);
u8g2.print(ExHstTmp);u8g2.print("°C ");
} while ( u8g2.nextPage() );
//---------------------------engine room ventilator----------------------------------------------------//
//if(EngineRoomTemp >= 23){
// digitalWrite(9, HIGH);
// } else {
// digitalWrite(9, LOW);
// }
} // end void loop
//double ReadTemp() {
// return CToKelvin(99.5); // Read here the true temperature e.g. from analog input
// }
//double ReadCabinTemp() {
// return CToKelvin(22.22); // Read here the true temperature e.g. from analog input
// }
//double ReadExhaustTemp() {
// return CToKelvin(222);
//}
#define SlowDataUpdatePeriod 1000
void SendN2kSlowData() {
static unsigned long SlowDataUpdated = millis();
tN2kMsg N2kMsg;
//static double Heading = 227.5;
// !delay is not good practise withing loops! I use it in this sample since
// I tested Arduino Mega as receiver without inteerupt and it could not handle all messages
// send without delay. With interrupt, it can do it and you can set DelayBetweenSend to 0.
const int DelayBetweenSend = 0;
if ( SlowDataUpdated + SlowDataUpdatePeriod < millis() ) {
SlowDataUpdated = millis();
//SetN2kDCBatStatus(N2kMsg, 1, 12.72);
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//static unsigned char sid = 0;
// SetN2kDCBatStatus(N2kMsg, 0, 12.45, 5.08, CToKelvin(27.15));
//SetN2kPGN127508(N2kMsg, 0, 13.8, 0.95, N2kDoubleNA, sid++);
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
// SetN2kTemperatureExt(N2kMsg, 1, 1, N2kts_MainCabinTemperature, ReadCabinTemp(), CToKelvin(21.6));
// delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
SetN2kTemperature(N2kMsg,1, 1, N2kts_EngineRoomTemperature, CToKelvin(EngineRoomTemp), CToKelvin(21.6));
delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
SetN2kTemperature(N2kMsg,1, 2, N2kts_EngineRoomTemperature, CToKelvin(ExhaustGasTemp), CToKelvin(21.6));
delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//SetN2kTemperatureExt(N2kMsg, 1, 1, N2kts_FreezerTemperature, CToKelvin(ExhaustGasTemp), CToKelvin(21.6));
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//SetN2kTemperature(N2kMsg, 1, 2, N2kts_ExhaustGasTemperature, CToKelvin(ExhaustGasTemp), CToKelvin(21.6));
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//SetN2kTemperatureExt(N2kMsg, 1, 1, N2kts_ExhaustGasTemperature, CToKelvin(ExhaustGasTemp), CToKelvin(21.6));
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//SetN2kEnvironmentalParameters(N2kMsg, 1, N2kts_MainCabinTemperature, ReadCabinTemp(), N2khs_InsideHumidity, 55, mBarToPascal(1013.5));
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//SetN2kOutsideEnvironmentalParameters(N2kMsg, 1, ReadWaterTemp(), CToKelvin(25.3), mBarToPascal(1013.5));
//elay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//SetN2kBatConf(N2kMsg, 1, N2kDCbt_AGM, N2kDCES_Yes, N2kDCbnv_12v, N2kDCbc_LeadAcid, AhToCoulomb(410), 95, 1.26, 97);
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//SetN2kDCStatus(N2kMsg, 1, 1, N2kDCt_Alternator, 86, 91, 1420, 0.21);
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//SetN2kAttitude(N2kMsg, 1, DegToRad(-3.1), DegToRad(2.4), DegToRad(-7.8));
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//SetN2kEngineDynamicParam(N2kMsg, 0, mBarToPascal(OilPressure), CToKelvin(oiltemp), CToKelvin(CoolantTemp), 14.21, 5.67, hToSeconds(2137.55), N2kDoubleNA, N2kDoubleNA, N2kInt8NA, N2kInt8NA, true);
SetN2kEngineDynamicParam(N2kMsg, 0, mBarToPascal(OilPressure), CToKelvin(oiltemp), CToKelvin(CoolantTemp) ,0 ,0 , hToSeconds(2137.55), N2kDoubleNA, N2kDoubleNA, N2kInt8NA, N2kInt8NA, true);
delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//SetN2kTransmissionParameters(N2kMsg, 0, N2kTG_Forward, 750000, CToKelvin(65.5), true, false, true);
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//SetN2kSystemTime(N2kMsg, 1, 17555, 62000);
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//SetN2kGNSS(N2kMsg, 1, 17555, 62000, 60.1, 22.5, 10.5, N2kGNSSt_GPS, N2kGNSSm_GNSSfix, 12, 0.8, 0.5, 15, 1, N2kGNSSt_GPS, 15, 2);
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//SetN2kGNSSDOPData(N2kMsg, 1, N2kGNSSdm_Auto, N2kGNSSdm_Auto, 1.2, -0.8, N2kDoubleNA);
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
// Serial.print(millis()); Serial.println(", Temperature send ready");
//SetN2kMagneticHeading(N2kMsg, 0, DegToRad(Heading), DegToRad(-3.0), DegToRad(5.5));
//delay(DelayBetweenSend); NMEA2000.SendMsg(N2kMsg);
//Heading+=1; if (Heading>=360.0 ) Heading-=360.0;
//SetN2kAISClassAPosition(N2kMsg, 1, tN2kAISRepeat::N2kaisr_First, 123456789, 26.396, -80.075, 1, 1, 1, 20, 20, 30, 0, tN2kAISNavStatus::N2kaisns_At_Anchor);
//NMEA2000.SendMsg(N2kMsg);
//SetN2kBinaryStatus(N2kMsg, 8, N2kOnOff_On, N2kOnOff_Unavailable, N2kOnOff_Off);
//NMEA2000.SendMsg(N2kMsg);
//SetN2kPressure(N2kMsg, 0, 2, N2kps_Atmospheric, mBarToPascal(1024));
//NMEA2000.SendMsg(N2kMsg);
//SetN2kRudder(N2kMsg, DegToRad(5), 1, N2kRDO_MoveToStarboard, DegToRad(-5));
//NMEA2000.SendMsg(N2kMsg);
//tN2kBinaryStatus SwitchBoard;
//N2kResetBinaryStatus(SwitchBoard);
//N2kSetStatusBinaryOnStatus(SwitchBoard, N2kOnOff_On, 7);
//N2kSetStatusBinaryOnStatus(SwitchBoard,(digitalRead(14)==1?N2kOnOff_Off:N2kOnOff_On),1);
//N2kSetStatusBinaryOnStatus(SwitchBoard,(digitalRead(15)==1?N2kOnOff_Off:N2kOnOff_On),2);
//N2kSetStatusBinaryOnStatus(SwitchBoard,(digitalRead(16)==1?N2kOnOff_Off:N2kOnOff_On),3);
//N2kSetStatusBinaryOnStatus(SwitchBoard,(digitalRead(17)==1?N2kOnOff_Off:N2kOnOff_On),4);
//N2kSetStatusBinaryOnStatus(SwitchBoard,(digitalRead(18)==1?N2kOnOff_Off:N2kOnOff_On),5);
//N2kSetStatusBinaryOnStatus(SwitchBoard,(digitalRead(19)==1?N2kOnOff_Off:N2kOnOff_On),6);
//N2kSetStatusBinaryOnStatus(SwitchBoard,(digitalRead(20)==1?N2kOnOff_Off:N2kOnOff_On),7);
//N2kSetStatusBinaryOnStatus(SwitchBoard,(digitalRead(21)==1?N2kOnOff_Off:N2kOnOff_On),8);
//SetN2kBinaryStatus(N2kMsg, 3, SwitchBoard);
//NMEA2000.SendMsg(N2kMsg);
}
}
#define RapidDataUpdatePeriod 167 // Some strange periot to cause Slow and rapid to run unsync.
void SendN2kRapidData() {
static unsigned long RapidDataUpdated = millis();
tN2kMsg N2kMsg;
if ( RapidDataUpdated + RapidDataUpdatePeriod < millis() ) {
RapidDataUpdated = millis();
SetN2kEngineParamRapid(N2kMsg, 0, rpm); //,820000,48);
//rpm = 0;
NMEA2000.SendMsg(N2kMsg);
//SetN2kCOGSOGRapid(N2kMsg, 1, N2khr_true, DegToRad(115.6), 0.1);
//NMEA2000.SendMsg(N2kMsg);
}
}
Many thanks in advanced
Ferry Meijlink
Unfortunately you have lot of things wrong on you sw. Lets think you are running laundry. So you will wait on the desk for customer. When customer arrives, you will take the laundry and put them to the washing machine. Next you wait on front of washing machine that it is ready and move them to dryer and wait that to be ready. Finally you pack them all and go back to desk waiting for customer. Meanwhile there has been coming and going customers, which did not got any service. But luckily there is just arrived one, who you can server and do the round again. This is how your program is doing now. Any delay call means waiting on washing machine that it is ready. Compared to washing machine 2 hour wait delay(100) does not sound long, but for processors it is like that. So within any loop code call to delay is big no except for testing purposes.
So lets start to fix your code.
- first remove all commented code you do not need and can be found again from examples.
- next start to think modularity. Even in Arduino project you can split code to modules simply creating an other .ino file. E.g. just create YamahaPicture.ino under same folder your main ino is and move that there. It should work. The good thing for beginners on Arduino IDE is that you do not need to declare thing first as in normal programming environment. It is enought it exist in some module inside the project. You can also make one module for drawing routines.
- the most complex thing is to start to think tasks. Arduinos and Teensies does not have internal tasking. Means that all code will run sequentially and any delay stops everything running like on waiting washing machine. So in example "wash laundry" task you simply put thing to machine, wait it ready, move them to dryer, wait it ready, pack laundry and done. You have to chage that to work other way: "wash laundry" task
- If state is not started set washing state, put laundry to washing machine, exit.
- if state is washing if machine is not ready exit.
- move laundry to dryer, set drying state and exit
- if dryer is not ready exit
- take laundry, pack them and deliver them for waiting customer exit So with above task you can server several customers in day instead of two and your laundry may survive. It is exactly same with your sw.
So e.g. your RMP code will change to:
void CountRPM() {
#define RPMSamplePeriod 1000
static unsigned long NextCheck=millis()+RPMSamplePeriod;
static unsigned long StartTime=millis();
unsigned long Now=millis();
if ( NextCheck<Now ) {
NextCheck=Now+RPMSamplePeriod;
rpm = 60*1000/(Now - StartTime)*rpmcount;
StartTime=Now;
}
}
Note I did not wate time for stop start interrupt, since reading rpmcount is atomic and can be meshed up. With float you shoud do stat/stop.
See no delay call and it will do the as your old code. Then do the same for all of your tasks and finally your loop will look like: void loop() { NMEA2000.ParseMessages(); CountRPM(); ReadTemperatures(); SendN2kMessages(); UpdateDisplay(); }
Then more hints:
- RPM measurement code is not very accurate. With 1000 RPM you get 16.6 pulse/s (one pulse per rev). So the reading may see 16 or 17 pulse and RPM will vary 960-1020. So you either need to filter reading or use https://www.pjrc.com/teensy/td_libs_FreqMeasure.html
- DallasTemperature reading also meshes up things since as default it has long (700 ms) delays. You have to change to use it in asynchronous mode and make it similar task as RPM example. See the DallasTemperature library, there should be WaitForConversion2 asynchronous example.
- You have to also check display drawing routines. Sometimes they are orful slow and you have to split them like "wash laundry"
- The goal is that your loop time should never exceed 20 ms. Temperature reading, when it is ready will take about 15 ms, so that will happen time to time. Mostly your sleep time should be <1 ms. You can add loop time checking to your loop:
void CheckLoopTime() {
#define LoopTimePeriod 1000
static unsigned long NextCheck=millis()+LoopTimePeriod ;
static unsigned long AvgCount=0;
static float AvgSum=0;
static unsigned long MaxLoopTime=0;
static unsigned long StartTime=micros();
unsigned long UsedTime=micros()-StartTime;
if ( UsedTime>MaxLoopTime ) MaxLoopTime=UsedTime;
AvgCount++;
AvgSum+=UsedTime;
if ( NextCheck<millis() ) {
NextCheck=millis()+LoopTimePeriod;
Serial.print("- Loop times max:");
Serial.print(MaxLoopTime);
Serial.print(" us, avg:");
Serial.print(AvgSum/AvgCount);
Serial.println(" us");
MaxLoopTime=0;
AvgSum=0;
AvgCount=0;
}
StartTime=micros();
}
Happy programming!
Hi Timo,
Many thankd for youre answer.
What i don’t understand that is working perfectly on my desk.
The n2k network on my ship is has more n2k members, I cannot see how delaying some things inside the void loop has influence on the behavior of the teensy in the network.
I expect that the data is not send regulair to the network but missing the whole device for a while I don’t understand.
I will read your answer a couple of times and see if I can understand your answer, the part about splitting is new for me don’t know how to do that I have to google more ill gues.
Kind regards,
Ferry Meijlink
Van: Timo Lappalainen [mailto:[email protected]] Verzonden: woensdag 6 januari 2021 7:53 Aan: ttlappalainen/NMEA2000 [email protected] CC: sprokkie [email protected]; Comment [email protected] Onderwerp: Re: [ttlappalainen/NMEA2000] Binary Switch Control (#128)
Unfortunately you have lot of things wrong on you sw. Lets think you are running laundry. So you will wain on the desk for customer. When customer arrives, you will take the stuff and put them to the washing machine. Next you wait on front of washing machine that it is ready and move them to dryer and wait that to be ready. Finally you packe them all and go to back to desk waiting for customer. Meanwhile there has been coming and going customers, which did not got any service. But luckily there is just arrived one, who you can server and do the round again. This is how your program is doing now. Any delay call means waiting on washing machine that it is ready. Compared to washing machine 2 hour wait delay(100) does not sound long, but for processors it is like that. So within any loop code call to delay is big no except for testing purposes.
So lets start to fix your code.
- first remove all commented code you do not need and can be found again from examples.
- next start to think modularity. Even in Arduino project you can split code to modules simply creating an other .ino file. E.g. just create YamahaPicture.ino under same folder your main ino is and move that there. It should work. The good thing for beginners on Arduino IDE is that you do not need to declare thing first as in normal programming environment. It is enought it exist in some module inside the project. You can also make one module for drawing routines.
- the most complex thing is to start to think tasks. Arduinos and Teensies does not have internal tasking. Means that all code will run sequentially and any delay stops everything running like on waiting washing machine. So in example "wash laundry" task you simply put thing to machine, wait it ready, move them to dryer, wait it ready, pack laundry and done. You have to chage that to work other way: "wash laundry" task
- If state is not started set washing state, put laundry to washing machine, exit.
- if state is washing if machine is not ready exit.
- move laundry to dryer, set drying state and exit
- if dryer is not ready exit
- take laundry, pack them and deliver them for waiting customer exit So with above task you can server several customers in day instead of two and your laundry may survive. It is exactly same with your sw.
So e.g. your RMP code will change to:
void CountRPM() { #define RPMSamplePeriod 1000 static unsigned long NextCheck=millis()+RPMSamplePeriod; static unsigned long StartTime=millis();
unsigned long Now=millis();
if ( NextCheck<Now ) { NextCheck=Now+RPMSamplePeriod; rpm = 60*1000/(Now - StartTime)*rpmcount; StartTime=Now; } }
Note I did not wate time for stop start interrupt, since reading rpmcount is atomic and can be meshed up. With float you shoud do stat/stop.
See no delay call and it will do the as your old code. Then do the same for all of your tasks and finally your loop will look like: void loop() { NMEA2000.ParseMessages(); CountRPM(); ReadTemperatures(); SendN2kMessages(); UpdateDisplay(); }
Then more hints:
- RPM measurement code is not very accurate. With 1000 RPM you get 16.6 pulse/s (one pulse per rev). So the reading may see 16 or 17 pulse and RPM will vary 960-1020. So you either need to filter reading or use https://www.pjrc.com/teensy/td_libs_FreqMeasure.html
- DallasTemperature reading also meshes up things since as default it has long (700 ms) delays. You have to change to use it in asynchronous mode and make it similar task as RPM example. See the DallasTemperature library, there should be WaitForConversion2 asynchronous example.
- You have to also check display drawing routines. Sometimes they are orful slow and you have to split them like "wash laundry"
- The goal is that your loop time should never exceed 20 ms. Temperature reading, when it is ready will take about 15 ms, so that will happen time to time. Mostly your sleep time should be <1 ms. You can add loop time checking to your loop:
void CheckLoopTime() { #define LoopTimePeriod 1000 static unsigned long NextCheck=millis()+LoopTimePeriod ; static unsigned long AvgCount=0; static float AvgSum=0; static unsigned long MaxLoopTime=0; static unsigned long StartTime=micros();
unsigned long UsedTime=micros()-StartTime; if ( UsedTime>MaxLoopTime ) MaxLoopTime=UsedTime; AvgCount++; AvgSum+=UsedTime;
if ( NextCheck<millis() ) { NextCheck=millis()+LoopTimePeriod; Serial.print("- Loop times max:"); Serial.print(MaxLoopTime); Serial.print(" us, avg:"); Serial.print(AvgSum/AvgCount); Serial.println(" us"); MaxLoopTime=0; AvgSum=0; AvgCount=0; }
StartTime=micros(); }
Happy programming!
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ttlappalainen/NMEA2000/issues/128#issuecomment-755120359 , or unsubscribe https://github.com/notifications/unsubscribe-auth/AG7UQCLW2BJOMT7M5YPWXRLSYQCDDANCNFSM4GDXXGOQ . https://github.com/notifications/beacon/AG7UQCNO3HGIIOPD4MDECDTSYQCDDA5CNFSM4GDXXGO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOFUBDRZY.gif
In my laundry example delay in loop is the same as you miss customers, when you are just looking washing machine going around. When other devices requests something, library can not server them, since you just waiting other task to finish. When they does not get served, they remove your device from the list. And then you again send something and they will request who you are.
Just believe me - you have to do those improvement I listed. Specially no delay calls and loop time avg<1ms.
Please do not copy previous responce, since it is available above. If you need to highlight some sentence take only that.
Hello Boss,
Me again, will you be merging the 127502 code into the main source library?
Have you found document, how it should be used? I expect that one should send only bits one would like to change. Others should be marked as NA.
One problem is that 127502 is deprecated and should be replaced with 126208. But if device does not support 127502, how other device can know that it can do switch control?
Please send your code for review
Hi all, has anyone got the Yachtdevices screen to work on an Axiom plotter? My YachtDevices custom page is saying "no configuration available". I am sending 127501 which I can see in the plotter's diagnostics page.
Hi again, I have got some more information out of Yacht Devices about the YDCC-04N. When the Axiom plotter i starting it is asking units to report back and first if it finds a device matching the Zcone configuration it will enable the digital switching screen, I listened to the traffic of my Axiom at startup and I could see that it was, by PGN 059904 ISO request, asking devices to reply with PGN 126464 ( PGN-list Tansmit PGNs group function). I don't think I can come further without having a YDCC-04N to listen to. Anyone who has one and can listen to what it is reporting back on PGN 126464, or any other PGN for that matter?
That is normal operation for MFD and other devices, which will have any cross communication on N2k bus. Also my library handles tha automatically. If you tell library which PGNs your device support with ExtendTransmitMessages / ExtendReceiveMessages, it will automatically tell others on request for either 059904 or 126208 all supported PGNs. Library user does not need to care of that functionality - just tell library supported PGNs. Instead if you e.g. support 127501, you should add handler for ISO request (PGN 59904) with SetISORqstHandler and send requested PGN and return true or false, if you do not transmit requested PGN. And if you go futher, you should inherit tN2kGroupFunctionHandler for your PGNs and register those handlers.
I can understand that MFD will enable control buttons, if your deveice informs that you support 127501 as tx and 127502 as rx. But as far as I know 127502 has been marked deprecated, so I do not know what would be the replacement for that.
Thanks, Timo. I added the transmitted and received pgns listed in the manual of tge YDCC-04N and I can see them being received in the plotter. Still no switches though. The Yachtd support mentioned that the serial number of the device and the uploaded czone config must be identical so I am suspecting that I somehow is not corretly set. No other PGNs are transmitted from the MFD as part of the initial phase. However , After 5-10 minutes of runtime I could see a PGN within the proprietary PGN range being sent from the MFD.
I use the properties below.
NMEA2000.SetProductInformation("717", // Manufacturer's Model serial code 8212, // Manufacturer's product code (The one Yachdevices uses) "YDCC-04", // Manufacturer's Model ID "1.03", // Manufacturer's Software version code "Circuit Control / YACHTD.COM", // Manufacturer's Model version 00260001, 2100 );
NMEA2000.SetDeviceInformation(00260001, // Unique number. Use e.g. Serial number. 150, // Device function=Analog to NMEA 2000 Gateway. See codes on http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf 30, // Device class=Inter/Intranetwork Device. See codes on http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf 206 // Just choosen free from code list on http://www.nmea.org/Assets/20121020%20nmea%202000%20registration%20list.pdf );
At least your information is wrong. You have LoadEquivalency 00260001. It should be (max. dev. amps)/0.05, so it is factor of how may 50 mA you draw from N2k bus. Should you use Yacht Devices mfg number 717 on device information? If you have generated CZone configuration file, it should match to that. You seem to have chnaged funtion/class definitions. Then I also prefer to remove false comments or update them.
It does not tell anything that MFD sends proprietary message.
Yes. Sorry for the mess. I realised I had some settings mixed up. I have corrected and I believe they should be accurate now and matching whats in the manual http://www.busse-yachtshop.de/pdf/yacht-devices-ydcc04-ydsc04-manual.pdf (screen shot on page 27).
NMEA2000.SetProductInformation("00260029" ,8212, "YDCC-04", "1.06 12/12/2019", "Circuit Control / YACHTD.COM", 3, 2100 ); NMEA2000.SetDeviceInformation(260029,140,30,717, 4);
I still can't get the MFD to pick up the settings so I am considering giving up for now. If anyone else have managed to get it to work on an Axiom MFD, please share the details :)
Hi Guys,
The NMEA2000.SetProductInformation and NMEA2000.SetDeviceInformation don't seem to have any relevance in the authentication with the Axiom plotter. The authentication follows the CZone protocol which there is very little information available.
I made some progress getting a Rpi to respond to the Axiom and the Axiom displaying the YD switch page. It all happens with the PGN65284, 65283 and 65280, also the 65290 seemed to be involved but I could not work out why. The variable CzDipSwitch1 needs the hex value of the YD file. It seemed to work for 4 switches, but was unreliable for 8 switches, never had time to work out why.
I did the project during a Corvid lockdown (so had time), since then the Axiom has been installed on the boat and the Rpi is now a SignalK server, so I don't have test setup anymore. The project was a proof-of-concept type project so the code is rough. A valuable tool I used was Timo's bus monitor and the Actisense software to look at bus activity.
Hope this helps, good luck.
Paul A
// ******************************************************************************************** // PGN65284 CZone Switch Bank Command message. This needs to be sent every 2 seconds as well as on MFD switch // change event. Used to "authenticate" via the CZone dip switch // Universal commands to multiple banks of two - state devices
void SetCZoneN2kPGN65284(unsigned char CzDipSw ) {
tN2kMsg N2kMsg;
// N2kMsg.Destination=0;
N2kMsg.SetPGN(65284L);
N2kMsg.Priority = 7;
N2kMsg.AddByte(0x27);
N2kMsg.AddByte(0x99);
N2kMsg.AddByte(CzDipSw);
N2kMsg.AddByte(0x0f);
if (CzDipSw == CzDipSwitch1)
N2kMsg.AddByte(CzSwitchState1);
else N2kMsg.AddByte(CzSwitchState2);
N2kMsg.AddByte(0x00);
N2kMsg.AddByte(0x00);
N2kMsg.AddByte(0x00);
// printf("PGN65283 Switch state = %x %x\n",CzSwitchState,CzMfdDisplaySyncState); NMEA2000.SendMsg(N2kMsg); }
// ******************************************************************************************** // 65283 MFD Display sync, sent as a resonse to a switch key press // Called as a response to a PGN 65280 "Switch Control" from MFD // PGN 65280 commands a toggle of the state of the switch inicated by // " SwitchToChange "
void SetCZoneN2kPGN65283(unsigned char CzDipSw ) { tN2kMsg N2kMsg; N2kMsg.SetPGN(65283L); N2kMsg.Destination =0; N2kMsg.Priority = 7; N2kMsg.AddByte(0x27); N2kMsg.AddByte(0x99); N2kMsg.AddByte(CzDipSw);
if (CzDipSw == CzDipSwitch1) {
N2kMsg.AddByte(CzMfdDisplaySyncState1);
// printf("PGN65283 Switch state1 = %02x %02x\n",CzSwitchState1,CzMfdDisplaySyncState1);
} else
{
N2kMsg.AddByte(CzMfdDisplaySyncState2);
// printf("PGN65283 Switch state2 = %02x %02x\n",CzSwitchState2,CzMfdDisplaySyncState2);
}
N2kMsg.AddByte(0x00);
N2kMsg.AddByte(0x00);
N2kMsg.AddByte(0x00);
N2kMsg.AddByte(0x10);
NMEA2000.SendMsg(N2kMsg);
}
// ******************************************************************************************** // PGN65280 sent from controlling MFD to request a change of state of a single switch // The command Byte 2 = Bit to toggle 0x05 = Sw 1, 0x06 = Sw 2, 0x07 = Sw 3, 0x08 = Sw // Byte 6 will = 0xF2 to indicate the start of the change and initiates the sending of the following: // PGN65283, PGN65284 (also a PGN12501, PGN12502 for compatability With NMEA2000 switching) // After the response has been sent, the MFD sends a PNG65280 with Byte 6 will = 0x40 to indicate success
void ParseCZoneN2kPGN65280(const tN2kMsg& N2kMsg) { int idx = 0; int len = N2kMsg.DataLen; uint16_t Id = N2kMsg.Get2ByteUInt(idx); if (len != 8) return; // right length if (Id != 0x9927) return; // is Czone msg idx=6; // if byte6 == 0x04 toggle the state of a switch uint8_t iState = N2kMsg.GetByte(idx);
if ( iState == 0x40)
{
cout << "Key Pressed ";
idx = 2;
iState = N2kMsg.GetByte(idx);
cout << iState << endl;
switch (iState) {
case 0x05: CzSwitchState1 ^= 0x01; // state of the four switchs
CzMfdDisplaySyncState1 ^= 0x01; // for MDF display sync
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,1, CzSwitchState1 && 0x01); // send the change out
cout << "Switch 1 changed Switch State " << endl;
break;
case 0x06: CzSwitchState1 ^= 0x02;
CzMfdDisplaySyncState1 ^= 0x04;
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,2, CzSwitchState1 && 0x02); // send the change out
cout << "Switch 2 changed Switch State " << endl;
break;
case 0x07: CzSwitchState1 ^= 0x04;
CzMfdDisplaySyncState1 ^= 0x10;
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,3, CzSwitchState1 && 0x04); // send the change out
cout << "Switch 3 changed Switch State " << endl;
break;
case 0x08: CzSwitchState1 ^= 0x08;
CzMfdDisplaySyncState1 ^= 0x40;
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,4, CzSwitchState1 && 0x08); // send the change out
cout << "Switch 4 changed Switch State " << endl;
break;
case 0x09: CzSwitchState2 ^= 0x01; // state of the four switchs
CzMfdDisplaySyncState2 ^= 0x01; // for MDF display sync
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,5, CzSwitchState2 && 0x01); // send the change out
cout << "Switch 5 changed Switch State " << endl;
break;
case 0x0a: CzSwitchState2 ^= 0x02;
CzMfdDisplaySyncState2 ^= 0x04;
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,6, CzSwitchState2 && 0x04); // send the change out
cout << "Switch 6 changed Switch State " << endl;
break;
case 0x0b: CzSwitchState2 ^= 0x04;
CzMfdDisplaySyncState2 ^= 0x10;
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,7, CzSwitchState2 && 0x10); // send the change out
cout << "Switch 7 changed Switch State " << endl;
break;
case 0x0c: CzSwitchState2 ^= 0x08;
CzMfdDisplaySyncState2 ^= 0x40;
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,8, CzSwitchState2 && 0x40); // send the change out
cout << "Switch 8 changed Switch State " << endl;
}
} else return; // if not 0x04 must be end of change message
idx = 0;
uint32_t PGN = N2kMsg.PGN;
if (len ==0) return;
else
cout << "length : " << len << " ";
cout << PGN << " ";
for (int i =0; i<len; i++)
{
uint8_t x = N2kMsg.GetByte(idx);
printf ("%0X ",x);
}
printf("\n");
if (iState > 0x08) SetCZoneN2kPGN65283(CzDipSwitch2);
else SetCZoneN2kPGN65283(CzDipSwitch1);
//SetCZoneN2kPGN65284(0x1d);
}
From: christianolsman @.> Sent: Monday, 15 March 2021 7:51 AM To: ttlappalainen/NMEA2000 @.> Cc: ando274 @.>; Comment @.> Subject: Re: [ttlappalainen/NMEA2000] Binary Switch Control (#128)
Yes. Sorry for the mess. I realised I had some settings mixed up. I have corrected and I believe they should be accurate now and matching whats in the manual http://www.busse-yachtshop.de/pdf/yacht-devices-ydcc04-ydsc04-manual.pdf (screen shot on page 27).
NMEA2000.SetProductInformation("00260029" ,8212, "YDCC-04", "1.06 12/12/2019", "Circuit Control / YACHTD.COM", 3, 2100 ); NMEA2000.SetDeviceInformation(260029,140,30,717, 4);
I still can't get the MFD to pick up the settings so I am considering giving up for now. If anyone else have managed to get it to work on an Axiom MFD, please share the details :)
— You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://github.com/ttlappalainen/NMEA2000/issues/128#issuecomment-798977516, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AP52IUKEG2RZYLGD7OEQHELTDUOTJANCNFSM4GDXXGOQ.
Thanks Paul. That was very useful. I am not fully there yet, though. You mentioned the hex value from the YD file should be assigned to CzDipSwitch1. Is that the value in the dipswitch config file ( value 200), or is it from the czone binary file? You also hav a CzDipSwitch2 in your code. Is that a typo since there is only once occurence?
Hi.
The value of CzDipSwitch1 is the dipswitch value entered into the YD WEB app to generate the config file. CzDipSwitch2 was a part of the attempt to build an 8-switch version, which was a work in progress when I stopped.
cheers
From: christianolsman @.> Sent: Wednesday, 17 March 2021 7:37 AM To: ttlappalainen/NMEA2000 @.> Cc: ando274 @.>; Comment @.> Subject: Re: [ttlappalainen/NMEA2000] Binary Switch Control (#128)
Thanks Paul. That was very useful. I am not fully there yet, though. You mentioned the hex value from the YD file should be assigned to CzDipSwitch1. Is that the value in the dipswitch config file ( value 200), or is it from the czone binary file? You also hav a CzDipSwitch2 in your code. Is that a typo since there is only once occurence?
— You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://github.com/ttlappalainen/NMEA2000/issues/128#issuecomment-800587278, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AP52IULKIYP3YYPNW2NWBR3TD66RFANCNFSM4GDXXGOQ.
Thanks! I managed to get the screen up and running and I can see that I get the toggling of the switches through to my esp32. Two issues still remains:
- When I boot the MFD it is saying "Czone error: YDCC-04 s/n 00260029 - Device missing. Some cZone functions will not operated"
- The status of the switches are not reflected on the screen. They are just grey. Did you have the similar issues?
In your code i replaced the call to "CzSendPNG12501" (since that was not included) with a call to Timos implementation of
SetN2kPGN127501(N2kMsg1,0,N2kOnOff_On,N2kOnOff_On,N2kOnOff_On,N2kOnOff_On);
NMEA2000.SendMsg(N2kMsg1);
By the way... found a list of Czone PGNs in this document http://assets.bluesea.com/files/resources/instructions/980033760-001-M2-VSM.pdf.
The 65283 is not mentioned there...
65280 PGN_ZONE_COMMAND_MESSAGE Circuits
65281 PGN_ZONE_FEEDBACK_MESSAGE Feedback on Circuits
65284 PGN_CZONE_STATUS_MESSAGE Connection and device info (serial/firmware/..)
65288 PGN_ZONE_COMMAND_MESSAGE
65290 PGN_CONFIG_CLAIM Config file request
65291 PGN_DATA_BLOCK_FEEDBACK Config file
65293 PGN_SI_RAW_DATA_REQUEST_MESSAGE Not used for MV
65295 PGN_CZ_ALARM_MESSAGE Alarm PGN
65297 PGN_CZONE_ENGINEERING_MSG Config tool communication
130816 PGN_DATA_BLOCK For Config.Transfer
Good information!
Did you knew that NMEA Simulator has "Message list" view, where you have all messages in chronogical order. This is better way to follow, what happens in the bus.
Would be nice to have CZone device for tracking traffic.
Some notes:
- The id on if (Id != 0x9927) return; // is Czone msg is from proprietary message definition:
field 1: Mfg code 11 bits
field 2: reserved 2 bits ( must be set to 0x03)
field 3: industry code 3 bits ( normally Marine=0x04
as result you have 0x4 << 13 | 0x3 << 11 | 0x127 so mfg code is 0x127 = 295 = BEB Marine
As in N2kMaretron.cpp there is definition
#define MaretronProprietary 0x9889 // Maretron 137 + reserved + industry code=marine
I prefer to use#define BEBMarineProprietary 0x9927 // Maretron 295 + reserved + industry code=marine
Since CZone messages are defined with BEBMarine mfg code could also be:#define CZoneMessage 0x9927 // Maretron 295 + reserved + industry code=marine
- In parsing one can simply test:
int Index=0;
if ( N2kMsg.PGN!=65280UL || N2kMsg.Get2ByteUInt(Index)!=CZoneMessage ) return false;
- Also building message you simply start:
N2kMsg.SetPGN(65283L);
N2kMsg.Priority = 7;
N2kMsg.Add2ByteUInt(CZoneMessage);
- I personally like more descriptive names, which can be simply done with inline aliases as in N2kMessages: ParseCZoneCommandMessage(...
- On sending comand you set N2kMsg.Destination = 0; I think you should respond to sender, which happens to be 0 at this time. But add Actisense to bus and your device may have address 1.
I got some more information from the vendor about the communication. Apperantly the MFD should request the CZone configuration from the device and I believe that is the message being sent on PGN 65290, which is a message I see once at MFD start up. It has the description "PGN_CONFIG_CLAIM Config file request" which matches that hypothesis. I would guess that the MFD would expect a result back on PGN 65291 (PGN_DATA_BLOCK_FEEDBACK Config file) but that is probably hard to guess the content of that message without having a Czone device in the network.
hi,
Did see that document, I have cleaned up the original source file as there was a lot of other things included as I was looking at the N2K bus and other devices (it was a sand pit / play pen). I don't have my test setup any more, I was using Code blocks to develop the code.
See the attached file, which was not working for a 8 bit switch, but had worked reliably as a 4 bit switch so any code reference to CzSwitchState2 should be removed and a 4 switch YD config file used
From memory, the greyed out switches mean that the axiom sees a YD switching device is on the bus, but hasn't received the sync PGN65283 to set to current state, hence they are greyed out. I was getting "device not present" messages with the current code because it didn't see the second 4 bit switch device, am sure with further work that would have been resolved.
From: CzSwitchState2 @.> Sent: Thursday, 18 March 2021 8:52 AM To: ttlappalainen/NMEA2000 @.> Cc: ando274 @.>; Comment @.> Subject: Re: [ttlappalainen/NMEA2000] Binary Switch Control (#128)
By the way... found a list of Czone PGNs in this document http://assets.bluesea.com/files/resources/instructions/980033760-001-M2-VSM.pdf.
The 65283 is not mentioned there...
65280 PGN_ZONE_COMMAND_MESSAGE Circuits 65281 PGN_ZONE_FEEDBACK_MESSAGE Feedback on Circuits 65284 PGN_CZONE_STATUS_MESSAGE Connection and device info (serial/firmware/..) 65288 PGN_ZONE_COMMAND_MESSAGE 65290 PGN_CONFIG_CLAIM Config file request 65291 PGN_DATA_BLOCK_FEEDBACK Config file 65293 PGN_SI_RAW_DATA_REQUEST_MESSAGE Not used for MV 65295 PGN_CZ_ALARM_MESSAGE Alarm PGN 65297 PGN_CZONE_ENGINEERING_MSG Config tool communication 130816 PGN_DATA_BLOCK For Config.Transfer
— You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://github.com/ttlappalainen/NMEA2000/issues/128#issuecomment-801466730, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AP52IUI54TIZBMI2GTA5GTLTEEQBBANCNFSM4GDXXGOQ.
/*
- File: main.cpp
- Author: al
- Testing for CAN and RPI
- See: https://github.com/thomasonw/NMEA2000_socketCAN
- Created on February 12, 2017, 2:37 PM */
#include
#define CzUpdatePeriod1 2000 #define CzUpdatePeriod2 400 #define CzUpdatePeriod3 10000
void N2kPGN127502Status(const tN2kMsg& N2kMsg); void HandleNMEA2000Msg(const tN2kMsg& N2kMsg); void SendN2k(void); void SetCZoneN2kPGN65283(unsigned char DeviceBankInstance); void SetCZoneN2kPGN65284(unsigned char ); void SetCZoneN2kPGN65290(unsigned char CzDipSw );
void ParseCZoneN2kPGN65280(const tN2kMsg& N2kMsg); void ParseCZoneN2kPGN65290(const tN2kMsg& N2kMsg); void ParseYDN2kPGN127501 (const tN2kMsg& N2kMsg); void SetSwitch(unsigned char,uint8_t, bool); void CzSendPNG12501(unsigned char DeviceInstance);
typedef struct { unsigned long PGN; void (*Handler)(const tN2kMsg& N2kMsg); } tNMEA2000Handler;
tNMEA2000Handler NMEA2000Handlers[] = { {65280L,ParseCZoneN2kPGN65280}, {65290L,ParseCZoneN2kPGN65290}, {127501L,ParseYDN2kPGN127501}, {0,0} };
// List here messages your device will transmit.
const unsigned long TransmitMessages[] PROGMEM = { 65284L,65283L,65290L,0 };
// Global variables
tN2kBinaryStatus BankStatus; uint8_t CzSwitchState1; uint8_t CzSwitchState2; uint8_t CzMfdDisplaySyncState1; uint8_t CzMfdDisplaySyncState2;
#define CzDipSwitch1 0x01 #define CzDipSwitch2 0x02 #define CzPGN12501DeviceInstance 0x00
using namespace std;
int main(void) { cout << "N2k CZone Switch test" << endl; CzSwitchState1 = 0; // Starting switch state, should be saved to non volitile memory CzSwitchState2 = 0; CzMfdDisplaySyncState1 = 0; // Starting MFD Switch display State CzMfdDisplaySyncState2 = 1 ; // Starting MFD Switch display State N2kResetBinaryStatus(BankStatus); setvbuf (stdout, NULL, _IONBF, 0); // No buffering on stdout, just send chars as they come. NMEA2000.SetN2kCANReceiveFrameBufSize(50); //Set Product information NMEA2000.SetProductInformation("00000001",// Manufacturer's Model serial code 8212, // Manufacturer's product code "YDCC-04", // Manufacturer's Model ID "1.06 12/12/2019", // Manufacturer's Software version code "Circuit Control/YACHTD.COM"// Manufacturer's Model version );
// Set device information
NMEA2000.SetDeviceInformation(000000001, //unique number. Use e.g. Serial number.
140, // Device function=Atmospheric. See codes on http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
30, // Device class=External Environment. See codes on http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
717 // 437 YD BEP 295 empirbus 304Just choosen free from code list on http://www.nmea.org/Assets/20121020%20nmea%202000%20registration%20list.pdf
);
NMEA2000.SetForwardStream(&serStream); // Connect bridge function for streaming output.
NMEA2000.SetForwardType(tNMEA2000::fwdt_Text); // Show in clear text (for now)
NMEA2000.SetMode(tNMEA2000::N2km_ListenAndNode, 169);
NMEA2000.EnableForward(false);
NMEA2000.ExtendTransmitMessages(TransmitMessages);
NMEA2000.SetMsgHandler(HandleNMEA2000Msg);
if (!NMEA2000.Open())
{
cout << "Failed to open CAN port" << endl;
return 1;
}
delay(500);
cout << endl << "CAN Bus started, going to watch it now" << endl;
//SetCZoneN2kPGN65290(0x1d);
//SetCZoneN2kPGN65290(0x1e);
while(1){
NMEA2000.ParseMessages();
SendN2k(); // Will send out CAN messages in open text
}
return 0;
}
void SendN2k(void) { static unsigned long CzUpdated1 = millis(); static unsigned long CzUpdated2 = millis(); static unsigned long CzUpdated3 = millis();
if (CzUpdated1 + CzUpdatePeriod1 < millis()) {
CzUpdated1 = millis();
SetCZoneN2kPGN65284(CzDipSwitch1);
SetCZoneN2kPGN65284(CzDipSwitch2);
}
if (CzUpdated2 + CzUpdatePeriod2 < millis()) {
CzUpdated2 = millis();
SetCZoneN2kPGN65283(CzDipSwitch1);
SetCZoneN2kPGN65283(CzDipSwitch2);
}
if (CzUpdated3 + CzUpdatePeriod3 < millis()) {
CzUpdated3 = millis();
CzSendPNG12501(0);
}
}
//NMEA 2000 message handler
void HandleNMEA2000Msg(const tN2kMsg& N2kMsg) { int iHandler; for (iHandler = 0; NMEA2000Handlers[iHandler].PGN != 0 && !(N2kMsg.PGN == NMEA2000Handlers[iHandler].PGN); iHandler++); if (NMEA2000Handlers[iHandler].PGN != 0) {
NMEA2000Handlers[iHandler].Handler(N2kMsg);
}
}
// ******************************************************************************************** // PGN65284 CZone Switch Bank Command message. This needs to be sent every 2 seconds as well as on MFD switch // change event. Used to "authenticate" via the CZone dip switch // Universal commands to multiple banks of two - state devices
void SetCZoneN2kPGN65284(unsigned char CzDipSw ) {
tN2kMsg N2kMsg;
// N2kMsg.Destination=0;
N2kMsg.SetPGN(65284L);
N2kMsg.Priority = 7;
N2kMsg.AddByte(0x27);
N2kMsg.AddByte(0x99);
N2kMsg.AddByte(CzDipSw);
N2kMsg.AddByte(0x0f);
if (CzDipSw == CzDipSwitch1)
N2kMsg.AddByte(CzSwitchState1);
else N2kMsg.AddByte(CzSwitchState2);
N2kMsg.AddByte(0x00);
N2kMsg.AddByte(0x00);
N2kMsg.AddByte(0x00);
// printf("PGN65283 Switch state = %x %x\n",CzSwitchState,CzMfdDisplaySyncState); NMEA2000.SendMsg(N2kMsg); }
// ******************************************************************************************** // 65283 MFD Display sync, sent as a resonse to a switch key press // Called as a response to a PGN 65280 "Switch Control" from MFD // PGN 65280 commands a toggle of the state of the switch inicated by // " SwitchToChange "
void SetCZoneN2kPGN65283(unsigned char CzDipSw ) { tN2kMsg N2kMsg; N2kMsg.SetPGN(65283L); N2kMsg.Destination =0; N2kMsg.Priority = 7; N2kMsg.AddByte(0x27); N2kMsg.AddByte(0x99); N2kMsg.AddByte(CzDipSw);
if (CzDipSw == CzDipSwitch1) {
N2kMsg.AddByte(CzMfdDisplaySyncState1);
// printf("PGN65283 Switch state1 = %02x %02x\n",CzSwitchState1,CzMfdDisplaySyncState1);
} else
{
N2kMsg.AddByte(CzMfdDisplaySyncState2);
// printf("PGN65283 Switch state2 = %02x %02x\n",CzSwitchState2,CzMfdDisplaySyncState2);
}
N2kMsg.AddByte(0x00);
N2kMsg.AddByte(0x00);
N2kMsg.AddByte(0x00);
N2kMsg.AddByte(0x10);
NMEA2000.SendMsg(N2kMsg);
}
// ******************************************************************************************** // PGN65280 sent from controlling MFD to request a change of state of a single switch // The command Byte 2 = Bit to toggle 0x05 = Sw 1, 0x06 = Sw 2, 0x07 = Sw 3, 0x08 = Sw // Byte 6 will = 0xF2 to indicate the start of the change and initiates the sending of the following: // PGN65283, PGN65284 (also a PGN12501, PGN12502 for compatability With NMEA2000 switching) // After the response has been sent, the MFD sends a PNG65280 with Byte 6 will = 0x40 to indicate success
void ParseCZoneN2kPGN65280(const tN2kMsg& N2kMsg) { int idx = 0; int len = N2kMsg.DataLen; uint16_t Id = N2kMsg.Get2ByteUInt(idx); if (len != 8) return; // right length if (Id != 0x9927) return; // is Czone msg idx=6; // if byte6 == 0x04 toggle the state of a switch uint8_t iState = N2kMsg.GetByte(idx);
if ( iState == 0x40)
{
cout << "Key Pressed ";
idx = 2;
iState = N2kMsg.GetByte(idx);
cout << iState << endl;
switch (iState) {
case 0x05: CzSwitchState1 ^= 0x01; // state of the four switchs
CzMfdDisplaySyncState1 ^= 0x01; // for MDF display sync
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,1, CzSwitchState1 && 0x01); // send the change out
cout << "Switch 1 changed Switch State " << endl;
break;
case 0x06: CzSwitchState1 ^= 0x02;
CzMfdDisplaySyncState1 ^= 0x04;
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,2, CzSwitchState1 && 0x02); // send the change out
cout << "Switch 2 changed Switch State " << endl;
break;
case 0x07: CzSwitchState1 ^= 0x04;
CzMfdDisplaySyncState1 ^= 0x10;
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,3, CzSwitchState1 && 0x04); // send the change out
cout << "Switch 3 changed Switch State " << endl;
break;
case 0x08: CzSwitchState1 ^= 0x08;
CzMfdDisplaySyncState1 ^= 0x40;
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,4, CzSwitchState1 && 0x08); // send the change out
cout << "Switch 4 changed Switch State " << endl;
break;
case 0x09: CzSwitchState2 ^= 0x01; // state of the four switchs
CzMfdDisplaySyncState2 ^= 0x01; // for MDF display sync
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,5, CzSwitchState2 && 0x01); // send the change out
cout << "Switch 5 changed Switch State " << endl;
break;
case 0x0a: CzSwitchState2 ^= 0x02;
CzMfdDisplaySyncState2 ^= 0x04;
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,6, CzSwitchState2 && 0x04); // send the change out
cout << "Switch 6 changed Switch State " << endl;
break;
case 0x0b: CzSwitchState2 ^= 0x04;
CzMfdDisplaySyncState2 ^= 0x10;
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,7, CzSwitchState2 && 0x10); // send the change out
cout << "Switch 7 changed Switch State " << endl;
break;
case 0x0c: CzSwitchState2 ^= 0x08;
CzMfdDisplaySyncState2 ^= 0x40;
CzSendPNG12501(CzPGN12501DeviceInstance);
SetSwitch(CzPGN12501DeviceInstance,8, CzSwitchState2 && 0x40); // send the change out
cout << "Switch 8 changed Switch State " << endl;
}
} else return; // if not 0x04 must be end of change message
idx = 0;
uint32_t PGN = N2kMsg.PGN;
if (len ==0) return;
else
cout << "length : " << len << " ";
cout << PGN << " ";
for (int i =0; i<len; i++)
{
uint8_t x = N2kMsg.GetByte(idx);
printf ("%0X ",x);
}
printf("\n");
if (iState > 0x08) SetCZoneN2kPGN65283(CzDipSwitch2);
else SetCZoneN2kPGN65283(CzDipSwitch1);
//SetCZoneN2kPGN65284(0x1d);
}
void CzSendPNG12501(unsigned char DeviceInstance){ tN2kMsg N2kMsg; tN2kBinaryStatus BankStatus; N2kResetBinaryStatus(BankStatus); BankStatus = (BankStatus & CzMfdDisplaySyncState2) << 8; // BankStatus = BankStatus | CzMfdDisplaySyncState1; SetN2kPGN127501(N2kMsg,DeviceInstance, BankStatus); NMEA2000.SendMsg(N2kMsg); }
//********************************************************************************************************
// 127502 Switch Bank Control // Universal commands to multiple banks of two - state devices // Field # Field Description // 1 Switch bank instance // 2 Switch 1 // -- // 29 Switch 28 // void SetN2kPGN127502(tN2kMsg& N2kMsg, unsigned char DeviceBankInstance, tN2kBinaryStatus BankStatus) { N2kMsg.SetPGN(127502L); N2kMsg.Priority = 3; BankStatus = (BankStatus << 8) | DeviceBankInstance; N2kMsg.AddUInt64(BankStatus); }
//***************************************************************************** inline void SetN2kSwitchBankCommand(tN2kMsg& N2kMsg, unsigned char DeviceBankInstance, tN2kBinaryStatus BankStatus) { SetN2kPGN127502(N2kMsg, DeviceBankInstance, BankStatus); }
//*****************************************************************************
void SetSwitch(unsigned char DeviceBankInstance, uint8_t SwitchIndex, bool ItemStatus){
// tN2kBinaryStatus BankStatus;
tN2kMsg N2kMsg;
N2kResetBinaryStatus(BankStatus);
N2kSetStatusBinaryOnStatus(BankStatus, ItemStatus ? N2kOnOff_On : N2kOnOff_Off, SwitchIndex);
SetN2kSwitchBankCommand(N2kMsg, DeviceBankInstance, BankStatus);
NMEA2000.SendMsg(N2kMsg);
}
void ParseYDN2kPGN127501 (const tN2kMsg& N2kMsg){
cout << "YD PGN127501" << endl;
unsigned char Dbi =0;
int SwitchState;
int ItemIndex;
tN2kBinaryStatus BankStatus = 0;
ParseN2kPGN127501(N2kMsg,Dbi,BankStatus);
for(ItemIndex=1; ItemIndex < 9 ; ItemIndex++)
{
SwitchState = N2kGetStatusOnBinaryStatus(BankStatus,ItemIndex);
switch (SwitchState)
{
case 0:
cout << "Switch " << ItemIndex << " Status = Off" << endl;
break;
case 1:
cout << "Switch " << ItemIndex << " Status = On" << endl;
break;
case 2:
cout << "Switch " << ItemIndex << " Status = Error" <<endl;
break;
case 3:
cout << "Switch " << ItemIndex << " Status = Unavailable" <<endl;
break;
}
}
}
void ParseCZoneN2kPGN65290(const tN2kMsg& N2kMsg){
printf("PGN65290 recieved \n");
int idx = 0;
int len = N2kMsg.DataLen;
uint16_t Id = N2kMsg.Get2ByteUInt(idx);
if (len != 8) return; // right length
if (Id != 0x9927) return; // is Czone msg
//SetCZoneN2kPGN65290(0x1d); //SetCZoneN2kPGN65290(0x1e); }
void SetCZoneN2kPGN65290(unsigned char CzDipSw ){
tN2kMsg N2kMsg;
N2kMsg.SetPGN(65290L);
N2kMsg.Destination = 255;
N2kMsg.Priority = 7;
N2kMsg.AddByte(0x27);
N2kMsg.AddByte(0x99);
N2kMsg.AddByte(0x3b);
N2kMsg.AddByte(0x52);
N2kMsg.AddByte(0x0f);
N2kMsg.AddByte(0x00);
N2kMsg.AddByte(0x40);
N2kMsg.AddByte(CzDipSw);
NMEA2000.SendMsg(N2kMsg);
}
Thanks Paul. Did you attach a new file, or did you refer to the snippet in the previous message? I can't see any attachment.
Reverse engineering is hard and requires working device pair.
I expect that config file will be provided with 130816, since that is fast packet message and allows up to 221 bytes in one block. 65291 is single frame message and can carry only 6 bytes, sice two will be used for proprieatary info. So I expect this is only some king of handshake or maybe definition for 130816 content.
[N2KCanTest2.zip](https://github.com/ttlappalainen/NMEA2000/files/6174733/N2KCanTest2.zip)
Hi Christian,
The link is to a modified file that I have removed the attempt of an 8 bit switch bank, I tested on the axiom and Rpi and it works. Remember the code was a experimental prototype and there is a lot of debug and redundant code but seems to work. The YD config file should use the serial code "00260126" which then matches the variable CzDipSwitch1 0x1b. to calculate the CzDipSwitch1 value, it is the last to 2 digits of the serial code plus one.
Hi Paul,
Thanks a lot! I can confirm I can get that to work on an ESP32 as well. I will see if I can enhance it and get to two devices to work as well.
Wil this work on a teensy as wel ?
Van: christianolsman @.> Verzonden: maandag 22 maart 2021 18:14 Aan: ttlappalainen/NMEA2000 @.> CC: sprokkie @.>; Comment @.> Onderwerp: Re: [ttlappalainen/NMEA2000] Binary Switch Control (#128)
Hi Paul,
Thanks a lot! I can confirm I can get that to work on an ESP32 as well. I will see if I can enhance it and get to two devices to work as well.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/ttlappalainen/NMEA2000/issues/128#issuecomment-804242014 , or unsubscribe https://github.com/notifications/unsubscribe-auth/AG7UQCIQDHUAXTLRYKC7NLLTE53GVANCNFSM4GDXXGOQ . https://github.com/notifications/beacon/AG7UQCKZBFMKCJW6NQJEY23TE53GVA5CNFSM4GDXXGO2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOF7X4EXQ.gif
Yes, Sprokkie.
But Paul's code will not run out of box in an arduino environment. I will brush up my code and distribute it (hopefully) within the coming week.
Have you tested with your MFD (Axiom or what ever), does they work with way Maretron Claims as standard? See conversation on issue #210 . There MFD requests and controls 127501 with 126208.
Christian, Timo I have done some more work on the Raymarine CZone/YD switching. It now includes options for 4 or 8 switches, the version linked is setup for 8 switches and seems to work very reliably. I have also ported to Arduino and implemented using a Teensy 3.2 including pin to relay mapping, also tested on a Teensy 4 and a MEGA. I've tried to clean the code up and use better coding practice based on some suggestions from Timo. This version more reliably authenticates with the MFD using the 65290 / 65284 messages. regards Paul A CzRaymarineMFDSwitches.zip
Hi,
Thanks Paul. I did some further work as well on this and I could not see that 65283 made any difference in my case. Are you sure that is actually needed? I removed it and it worked fine. I also managed to get all 8 switches to work. Unfortunately, I have my head burried in other projects right now and the MFD has been put back in the boat. I will probably return to this once the boat is on the hard again.
Been watching this now and then for a while now. I would really like to get this sorted out. @Timo Would it be silly if I started a gofundme project for this? If you got the funding to buy a NMEA 2000 binary switch or two, sure you would tinker with it, and later on share your findings with the community? :)
I have table full of devices from others so for sure I will not buy anything. If you like to send me one or two for free, I may test them, but do not promise anything.
go fu
Been watching this now and then for a while now. I would really like to get this sorted out. @timo Would it be silly if I started a gofundme project for this? If you got the funding to buy a NMEA 2000 binary switch or two, sure you would tinker with it, and later on share your findings with the community? :)
Go funde me for devices sounds like a good idea. Some one has to arrange the go fund me, and is willing to buy the hardware and send to timo. Or timo can buy the items after he got the go fund me money.
I've managed to make the Naviop panel work on the Navico MFDs. The plugin is in nodejs for SignalK and the code is ugly :) Naviop expects a device to be present on the network and I can't figure out the logic behind the mapping of relays/fuses, so I just did as is without trying to understand the logic. Package is here: https://www.npmjs.com/package/signalk-naviop-plugin