NMEA 2000 Shield

Hey Timo,

Thank's for the detailed answer.

I skale from -5 to due to the example I saw. Thats all.

If I connect the hall sensor to analog and judt do the 5/1023 I get the value 40 to 980. The sensor is giving 0.5 to 4.5 v by 90 degree rotation.

Of course it would be better to have a higner resulution as I need this value as well for the autopilot later on.

Regards..

But if your sensor is giving values 40..980 on 90 deg rotation, it means it turns from -45° .. 45°. Then you should have do scaling: double rudderValue = map(rudderSens, 40, 980, -pi/4, pi/4); or double rudderValue = DegToRad(map(rudderSens, 40, 980, -45, 45));

I am still learning a lot..

yes, would make of course sens to do so.

Resulution is at the 90 degree version 0.022. Which is 2045 per 45 degrees of turn if I got that right. But this is o much as the resolution is 10 bit (1024 steps at 5v) on the analog input right?

Details from the sensor:

BI TECHNOLOGIES / TT ELECTRONICS 6127V1A90L.5 - Hall Effect Sensor, Position, 90°

Output Voltage 0.2 Vdc (4%) to 4.8 Vdc (96%) Typical (see Feature Codes table) Output Overvoltage Limits 10 Vdc to -0.3 Vdc; output may be shorted to ground or supply without damage Output Current ±8 mA Max. Output Load 1 kΩ Min., 10 kΩ Typical Input Voltage 4.5 to 5.5 Vdc Supply Voltage Absolute Limits 20 Vdc Max., -10 Vdc Min. Independent Linearity1 ±0.5% (0.25% Available) Hysteresis 0.2% Max. Resolution 0.088° for 360° travel, 0.011° for 45° travel Supply Current 8.5 mA Typical, 12 mA Max. Dielectric Strength 750 V rms Insulation Resistance 1,000 MegΩ Min. Electrostatic Discharge (ESD) Passes 2 kV human body model and 15 kV air discharge Bulk Current Injection (BCI) Passes 2-500 MHz at 200 mA Actual Electrical Travel 360° Typical (see Ordering Information)

Is there someone who has examined the can bus in volvo penta engines?

Hello Timo,

I am really sorry to bother you with the rudder position thing.. I do try it now for the last 6 days and get stuck every time with something else.

That is killing me soon... Back to analog instruments! :grin:

This code:

void RudderPostion() {
  double dMap(double x, double in_min = 40, double in_max = 1023, double out_min = -45, double out_max = 45){
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
  }
}

is producing this error:

RudderPosition_NMEA2K_v2:42: error: a function-definition is not allowed here before '{' token

exit status 1
a function-definition is not allowed here before '{' token

This is the hole sketch I thought to use:

// Demo: NMEA2000 library. Send main wind data to the bus.

#include 
#include   // This will automatically choose right CAN library and create suitable NMEA2000 object
#include 

int rudderSens = 0xA0;        // value read from the pot
double rudderValue = 0;
const unsigned long TransmitMessages[] PROGMEM = {127245L, 0};

void setup() {
  // Set Product information
  NMEA2000.SetProductInformation("00000002", // Manufacturer's Model serial code
                                 100, // Manufacturer's product code
                                 "Rudder Position monitor",  // Manufacturer's Model ID
                                 "1.1.0.22 (2016-12-31)",  // Manufacturer's Software version code
                                 "1.1.0.0 (2016-12-31)" // Manufacturer's Model version
                                );
  // Set device information
  NMEA2000.SetDeviceInformation(1, // Unique number. Use e.g. Serial number.
                                155, // Device function=Atmospheric. See codes on http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
                                40, // Device class=External Environment. See codes on  http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
                                1851 // Just choosen free from code list on http://www.nmea.org/Assets/20121020%20nmea%202000%20registration%20list.pdf
                               );
  // Uncomment 2 rows below to see, what device will send to bus. Use e.g. OpenSkipper or Actisense NMEA Reader
  Serial.begin(115200);
  NMEA2000.SetForwardStream(&Serial);
  // If you want to use simple ascii monitor like Arduino Serial Monitor, uncomment next line
  //NMEA2000.SetForwardType(tNMEA2000::fwdt_Text); // Show in clear text. Leave uncommented for default Actisense format.

  // If you also want to see all traffic on the bus use N2km_ListenAndNode instead of N2km_NodeOnly below
  NMEA2000.SetMode(tNMEA2000::N2km_NodeOnly, 23);

  // NMEA2000.SetDebugMode(tNMEA2000::dm_Actisense); // Uncomment this, so you can test code without CAN bus chips on Arduino Mega

  NMEA2000.EnableForward(false);
  NMEA2000.ExtendTransmitMessages(TransmitMessages);
  NMEA2000.Open();
}

#define RudderSendPeriod 100 // ms
void SendRudder() {
  static unsigned long RudderSendTime = millis() + RudderSendPeriod;
  tN2kMsg N2kMsg;
  if ( RudderSendTime < millis() ) {
    RudderSendTime += RudderSendPeriod;
    double dMap(double x, double in_min = 40, double in_max = 1023, double out_min = -45, double out_max = 45) {
      return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    }
    SetN2kRudder(N2kMsg, rudderValue);
    NMEA2000.SendMsg(N2kMsg);
  }
}

void loop() {
  NMEA2000.ParseMessages();
  SendRudder();
}

It took also some time until I have recognized that the usual map function will not work here. I thought your post which showed the userdefiend map function is not mandetorry for this case and I got always wrong values. The strange thing is that I used the standart map function to test the sensor in a simple sketch and it was working just fine like this one:

int rudderSens= A0;  // Analog input pin that the potentiometer is attached to
int sensorValue = 0;        // value read from the pot
int RudderPosition;

void setup() {
   Serial.begin(115200); 
}

void loop() {
  // read the analog in value:
  sensorValue = analogRead(rudderSens);            
  // map it to the range of the analog out:
  RudderPosition= map(sensorValue, 54, 1023, -45,45);  
     

  // print the results to the serial monitor:
     

  Serial.println(sensorValue);  
}

In case I got the sketch running than I always got a constant rudderposition send which had nothing to do with the sensor value.

If I get this one working fine than I think I get almost every other I thought to implement also working.

Would be gorgeous to get this running with the help of you, without I think I have to buy the commercial version of this sensor for rudder position.

Thousends of thanks in advance!

Your questions actually belongs to some basic c/c++ syntax forum. That you should study first.

One problem is that you just copied my code sample inside on other function. The new function dMap should be defined alone as own function: .. double dMap(...) { ... } ... void SendRudder() { ... } ...

That is basic syntax.

Then you have set default values for dMap function parameters: ... double dMap(double x, double in_min = 40...) ... Do not use default values, before you understand the meaning of them. You should then call dMap inside your code: ... void SendRudder() { ... double rudderValue=dMap(sensorValue, 54, 1023, -45,45); ... }

Like you do with map, which is just function as dMap, but defined in some Arduino standard library.

Then note that in your code you defined int RudderPosition. This means that you can have 90 difference values between -45 45. If1 degree accuracy is enough for you, that is fine and then also standard map is fine. If you like to have better accuracy, you have to use double of float.

Then you have defined analog pin as int rudderSens= A0; So on board it says A0..A15 printed, but if you read definition of analogRead:

analogRead(pin) Parameters

pin: the number of the analog input pin to read from (0 to 5 on most boards, 0 to 7 on the Mini and Nano, 0 to 15 on the Mega)

So you should simply have: int rudderSens=0;

Modified:

Hey Timo,

sure you are right that this is basic. The code I posted was ment that I wanted to use that one as base. Not that this was finnished. I worte a lot of versions and I didn´t wanted to post all of the garbage I produced here.

->****** History now Would be better to have a more accurracy of the rudder angle for sure. As soon as I add dMap to a line I get that it is not declared. Do I declare it I get as double for example I get "dMap can not be used as a function. In other posts I could found exactly the same like you posted it and I compared it to many different sources. I used the user dMap as a single function and also used double or float (void blabla... analogRead(reudderSens).. , return dmap(....) ) with descriped result. The point is the error appears also as single function and that was than the point where I didn´t came any further. <-****** History now

I simply forgot that I create my own map function which I have to call than later in the program. Stupid failure... Sorry

So differences between float double int and other main functions of the reference library I do know about. I saw that analogRead was missing and used it before like I got temp monitor to work like it should. I know not so much, yet. But I know about the simple basics.

I will post thoose questions next time in the forum which belong to this type of questions.

Sorry my a bit rude answer. I meant that you should really study code syntax basics and do some simple trainings. Also play with code by commenting things, moving code to different location or what ever to learn what effects to where and what kind of errors you get. On code below try e.g. comment { at the end of line "double dMap(..." to see how error message leads you totally wrong. Also study to read error messages - it is not sometimes easy with c++.

Below is ready code for rudder, which I may add as example some day. Study it and try to learn.

// Demo: NMEA2000 library. Read rudder angle from the Analog sensor

#include 
//#define N2k_CAN_INT_PIN 21 // Use interrupt  and it is connected to pin 21. Use this with with e.g. Mega board. Check where your interrupt has been connected.
//#define USE_MCP_CAN_CLOCK_SET 8 // Uncomment this, if your CAN shield uses 8 MHz clock instead of 16 Mhz like e.g. on seeedstudio can bus shield 
#include   // This will automatically choose right CAN library and create suitable NMEA2000 object
#include 

// List here messages your device will transmit.
const unsigned long TransmitMessages[] PROGMEM = {130306L, 0};

// *****************************************************************************
void setup() {
  // Set Product information
  NMEA2000.SetProductInformation("00000002", // Manufacturer's Model serial code
                                 100, // Manufacturer's product code
                                 "Rudder Position",  // Manufacturer's Model ID
                                 "1.1.0.22 (2018-01-05)",  // Manufacturer's Software version code
                                 "1.1.0.0 (2018-01-05)" // Manufacturer's Model version
                                 );
  // Set device information
  NMEA2000.SetDeviceInformation(1, // Unique number. Use e.g. Serial number.
                                155, // Device function=Rudder. See codes on http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
                                40, // Device class=Steering and Control Surfaces. See codes on  http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
                                2046 // Just choosen free from code list on http://www.nmea.org/Assets/20121020%20nmea%202000%20registration%20list.pdf                               
                               );
  // Uncomment 2 rows below to see, what device will send to bus. Use e.g. OpenSkipper or Actisense NMEA Reader                           
  Serial.begin(115200);
  NMEA2000.SetForwardStream(&Serial);
  // If you want to use simple ascii monitor like Arduino Serial Monitor, uncomment next line
  NMEA2000.SetForwardType(tNMEA2000::fwdt_Text); // Show in clear text. Comment for default Actisense format.

  // If you also want to see all traffic on the bus use N2km_ListenAndNode instead of N2km_NodeOnly below
  NMEA2000.SetMode(tNMEA2000::N2km_NodeOnly,23);
  // NMEA2000.SetDebugMode(tNMEA2000::dm_Actisense); // Uncomment this, so you can test code without CAN bus chips on Arduino Mega
  NMEA2000.EnableForward(false); // Comment this, if you want to see bus traffic on your serial.
  NMEA2000.ExtendTransmitMessages(TransmitMessages);
  NMEA2000.Open();
}

// *****************************************************************************
void loop() {
  NMEA2000.ParseMessages();
  SendRudder();
}

// *****************************************************************************
// Double replacement for standard map function
double dMap(double x, double in_min, double in_max, double out_min, double out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

// *****************************************************************************
// Function reads rudder postion from A0 input and scales it to angle in degrees
double ReadRudderPosition() {
  double rudderValue = dMap(analogRead(A0), 40, 980, -45, 45);
  // We actually would need here some filter function to prevent noise on analog.
  return rudderValue; 
}

#define RudderSendPeriod 100

// *****************************************************************************
void SendRudder() {
  static unsigned long RudderSendTime = millis() + RudderSendPeriod;
  tN2kMsg N2kMsg;
  if ( RudderSendTime < millis() ) {
    RudderSendTime += RudderSendPeriod;
    double rudderValue=ReadRudderPosition();
    Serial.print("Rudder value: "); Serial.println(rudderValue);
    SetN2kRudder(N2kMsg, DegToRad(rudderValue));
    NMEA2000.SendMsg(N2kMsg);
   }
}

Hey Timo,

nothing to appologize for. I expacted almost a little harder answer already as I posted a lot of times about it and got afraid. That´s why I appologized always already in the question for that post. ;) Stupid questions, special if they appear so often, deserve sometimes harder answer to reset the brain. And it worked also yesturday evening as I wrote almost the same like you posted now:

double dMap(double val, double in_min, double in_max, double out_min, double out_max) {
    return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

int RudderRead() {
analogRead(rudderSens);
int rudderValue = dMap(rudderSens, 40, 980, -45, 45);
  return rudderValue; // Read here the true temperature e.g. from analog input 
Serial.println(rudderValue);
}


#define RudderSendPeriod 100

void SendRudder() {
  static unsigned long RudderSendTime = millis() + RudderSendPeriod;
  tN2kMsg N2kMsg;
  if ( RudderSendTime < millis() ) {
    RudderSendTime += RudderSendPeriod;
    SetN2kRudder(N2kMsg, rudderValue);
    NMEA2000.SendMsg(N2kMsg);
   }
}

void loop() {
  NMEA2000.ParseMessages();
  SendRudder();
}

I will compare it carefully to learn the things I do wrong and not just use it as template, promissed!

The debug console from OpenSkipper showed up that it was expacting 6 bit and recieved 8 bit. Not sure if that have something todo with my problem. So I still have 0 as value for the angle. (used my version, not the one you posted now).

I don´t give up so fast. It is now "just" 7-8 days for the rudderfunction I spent. :-)

What is responsible for the 8 bit or 6 bit in the message?

A nother thing I couldn´t figure out the meaning of it is this one:

tN2kMsg N2kMsg;

First time I got hit because I tried the rudder and got that tN2kMsg was not defiend in this scope. So I just copied this from other examples from you without knowing what it is. ok so far i know it says that tN2kMsg is the same like N2kMsg. I couldn´t exactly find the point in the library files where this is defiend to figure out what this is. I think at least it is the definition of the message to be sent due to "NMEA2000.SendMsg(N2kMsg);.

Could you may reffer to a point in the library files where I can take a look to get more known with the messages which are sent?

Thanks a lot!

PS:

The Teensy´s arrived today. I ordered 3.2´s and one 3.5. The 3.5 is also 5 volt compatible and have the sd card slot onboard. Thought cool to use for the planned GPS tracker I lke to build in the future. One difference to arduino´s is that I have to use the FlexCAN library, something else I have to be carefull with? They are not so cheap as Arduino and I don´t want to burn any. I also got the Linx RXM-GNSS-GM delivered. High performance/ precision GPS (and all other sattelites). Looking forward t see the differences comparing to the "cheaper" once.

I noticed that you have to change on the beginning of my example:

const unsigned long TransmitMessages[] PROGMEM = {127245L, 0};

This gives right information to other devices on the bus.

In your code int ReadRudder you have to defined it to return integer value. Remember that then compiler will do automatic typecasting from double to int. And there is no warning about that, since Arduino IDE does not have good option to turn all warnings as error. So define double ReadRudder(...) Also inside function define double rudderValue=dMap(...)

Have you checked that you have latest 1.6B version of OpenSkipper? And I think you mean 6 bytes.

tN2kMsg N2kMsg; does say that tN2kMsg is N2kMsg. It says that you define variable N2kMsg, which is type of tN2kMsg. It is as you write double val; you define variable val, which is type of double.

It is OpenSkipper 1.6.2016.1217.

I ment 6 vytes not bit yes.

During the test I used allover double, also inside the RudderRead function. The "int rudderValue = dMap(rudderSens, 40, 980, -45, 45);" was just a test.

I did changed the PROGMEM number to 127245L. I found this number from the N2KMessage.h after I compared the number from the Windmonitor with N2KMessages.h. So I use this now all the time. 127245 is the number in the first row of rudder "void SetN2kPGN127245". The "L" behind I just left there, couldn´t find out if that is right there or not. So in your last posted version of the rudder sketch is still the number used from the windmonitor if I understand this correct.

Later I start all over again to be 100% sure.

Hi,

I am trying to hack together some new electronics capabilities for my sailboat. I currently have a basic NMEA 2000 bus on the boat. I'd like to add low-cost custom sensors and logging capability to the system. My current idea is to install Signal K on a Raspberry Pi Zero W, and cobble the NMEA 2000 bus to the RasPi with an Arduino. Then it would be relatively simple to create "micro-sensors" with an ESP8266 or such. My ideas for sensors would include temperature/humidity/barometer in different locations, engine tachometer (from the RPM gauge signal), electronic compass, gyrometer etc, and so on. Having all those directly on NMEA 2000 would increase the bus complexity and cost quite a bit, hence the wish for simple wireless devices.

I've successfully built the connecting hardware with an Arduino Due and MCP2562 with Timo Lappalainen's excellent instructions. I'm able to eavesdrop to N2k data with ActisenseListener. Similarly, my B&G Vulcan 7 MFD can see the device and receive data when I load MessageSender.ino to the Arduino. Hence, I believe the hardware connectivity is working.

What I don't get is how to use the Arduino as a simple gateway. Did I understand correctly that ActisenseListenerSender should basically act as an Actisense NGT-1 replacement? The code doesn't set any N2k product information, and at least the B&G plotter doesn't see any transmitted data. Should I add the product information data myself? What else might I be missing from a functional gateway?

Of course, I'm not above opening my wallet and simply buying an Actisense NGW-1 or similar, but I quite like the DIY aspect and the challenge of not just throwing some off-the-shelf hardware on the problem. :-)

Cheers,

Matti

You do not need NGT-1, exept if you would like to have nice box. With same money you can buy several Teensys. My library does all, what you need.

Also I would not think having devices directly on N2k bus increases costs. You do not need to use expencive N2k connectors. You can simply make your own hub box by using cheap connectors used on DeviceNet (see. e.g. Farnell codes 1717025 and 1717013). Pin order is on Teensy_Actisense_listener_sender_schematics.pdf. With 20 € you can make 5 pos hub with all connectors. I currently have 4 hubs on by boat. The complexity will increase a bit, but then your devices can get power from bus.

ActisenseListenerSender has been designed mostly for testing and logging purposes. It passes messages sent as they are e.g. from my NMEA Simulator (see. kave.fi/Apps). There you can select to send or not the device information. NGT-1 works a bit different way, since it allways shows messages sent from NGT-1, not from Simulator. I can add Gateway example, or you can make it by yourself by combining TemperaturMonitor and ActisenseReader used on ActisenseListenerSender. The other thing I wonder that how did you test that ActisenseListenerSender does not send anything? How did you transmitted data?

Then if you are going to use RPi, it should be possible to integrate library with it by using PiCan2. I have been thinking that, but had no time. Library should compile on Pi and there is NMEA2000_socketCAN for that. As far as I know, SignalK are listen only, so you need anyway code to generate messages. Library has most common message generators on N2kMessages.

Hi all,

Love the work you are doing. I was wondering if the PGN for MOB (I think it is PGN 127233) can be included the library since I want to make a MOB node.

Also i made a PCB design for an atmega 328 (the chip on an Arduino uno) to work with a standard MCP2515 CAN Bus Module. This PCB has I2C or 2 analog connections and a digital connection.

I have used this PCB in 3 applications (a wind monitor, a digital compass and a barometer).

Frederik

NMEA2kPCB.zip (576 KB)

WindMonitor.zip (2.28 KB)

elcompass.ino (4.71 KB)

baro.ino (2.79 KB)

I have warned that library does not fit well to Arduino Uno . You have use defines to disable all rarely used features and set buffers to minimum. I prefer at minimum Teensy 3.2

Unfortunately I have not found description for PGN 127233. The list of fields does not tell enough - we need bit by bit desrciption. I would use that too. On the other hand, have you checked that some device would listen that message? Older MFD versions does not listen it, so e.g. there is no help for my Garmin GMI 20.

Hey,

which CAN IC is generally best to use with the Teensy 3.2 one?

I have currently MCP2551, MCP2562, MCP25515 and the SN65HVD234.

I try with the MCP2551 at VUSB or 3.3V but I don´t get any luck. I have set the bandwidth first to default 125K and than I tried 500K but got no traffic.

Is there any "finished" can shield wich works fine with the teensy´s?

Regards..

I prefer MCP2562. It uses 5V power, but has pin 5 for defining IO-level. Check my example schematics https://github.com/ttlappalainen/NMEA2000/blob/master/Examples/TeensyActisenseListenerSender/Documents/Teensy_Actisense_listener_sender_schematics.pdf

Which badwidth you mean? NMEA2000 library has fixed bandwidth to NMEA 2000 bus speed 250kps.

Check this one: http://skpang.co.uk/catalog/teensy-canbus-breakout-board-include-teensy-32-p-1507.html

Hi,

I think I was being hasty. I dragged out an old Windows laptop and tried your NMEA-Simulator and was able to get values to the MFD just fine. For a while - after some 15-20 seconds (guesstimate) traffic stops and the devices disappear. I wonder is that a feature or an issue in my hardware? I saw a mention that some Dues have problems sending data. Might that be related? If so, I also have an idle Teensy that I can repurpose for this project.

Now that I understand how the ActisenseListenerSender differs from the actual Actisense hardware, it's fine. I might still do the gateway change because I believe the Signal K plugin and canboat expect actual Actisense style operation. I'll be happy to make a PR for that.

There's now a rudimentary signalk-to-nmea2000 plugin available. Only very few PGNs are supported at the moment, but it doesn't seem to complicated to add any I might need. And I still believe I could get a simpler end result that way. I should get by with a simple ESP8266, a cheapo buck converter, a fuse holder and a piece of perf board as well as any sensor device I might be using. If I went to native N2k, I'd need at least a Teensy+tranceiver for each sensor (or sensor group), right?

Thanks for the quick response!

Cheers,

Matti

Using RPi directly would be tempting, but I'm also perfectly happy if I can get the Arduino working. :-D

I got now the MCP2551 working. I don´t know if this have a reason or if this is neccessary, but it didn´t worked until I added the CS pin to the configuration with #define N2k_SPI_CS_PIN 53  // Pin for SPI Can Select. As soon this was part of the config beside the Integer pin 21 and the clock set to 8 mhz it looked like everything is just fine.

By bandwith i ment the setting from the FlexCAN.h FlexCAN(uint32_t baud = 1000000, .... in the public part of the file. If this have no meaning than I guess it is like you said 250K. By default the setting from the flexcan is set to 500K. I just tested a bit with that number since I didn´t got any communication.

Finnally I am now at that point where I can say that I get a bit more familiar to the coding here. That was a long and also expensive journey:

DON´T RUN 12 V ON THE BUS!

I was so unlucky that I plugged the 12 V into the CAN bus connection so 1 SN65HVD234, 3 MCP2515 shields, 1 MCP2562 and finally one Geniu DUE went to sleep for ever. Was a nice firework. ;D

Another thing:

TrimTabs

There exists an automatic steering for the TrimTabs from Bennett and another which also is NMEA2K comapatible to show the position of the tabs.
I am very interessted to build something like this as well as this part was very expensive even without NMEA2k and I have no idicator at all on my hydraulic tab at all.

I thought a bit about it, to indicate the position is no big deel and the orgin sensors are not so expensive as well. But the automasation will be a bit harder I guess. A lot of filtering due to waves and motions on the water which shuld not affect the position like making a turn for example.

Did anybody had something like this in mind also?

define N2k_SPI_CS_PIN 53 is the default setting even you do not use it.

DON´T RUN 12 V ON THE BUS! This is wrong. You can run 12 V. If you look my schema https://github.com/ttlappalainen/NMEA2000/blob/master/Documents/ArduinoDUE_CAN_with_MCP2562.pdf you will see there must be diode from 12 V.