AngusMcDoon
Well-Known Member
Time for a new YAPP. All my previous efforts have used Seatalk 1 and NMEA0183, because that's what I had on the boat I owned at the time. That boat has gone now, and the new one is coming with a NMEA2000 set of instruments, so I've been having a play.
NMEA2000 is a closed standard and is very expensive to buy - far too much for home tinkerers. However, some Finns have reverse engineered it (which is legal) and created an open source library which they have put in the public domain license free on Github here...
GitHub - ttlappalainen/NMEA2000: NMEA2000 library for Arduino
The library is aimed at various Arduinos for hardware (including the Espressif ESP32 DevKitC board) and the Arduino IDE for building the software. I don't do Arduino of any type, but it was pretty easy to modify it slightly to build using the native ESP-IDF software development kit. This first project uses a BMP280 atmospheric pressure sensor breakout board from Pimoroni. From this the pressure is read periodically, packaged up in a NMEA2000 message and sent out using a Microchip MCP2551 Canbus driver chip. The whole circuit is only 2 small boards and 3 passive components. Currently it's powered by the USB connector on the DevKitC board but a simple voltage regulator will be sufficient to power the circuit from the NMEA2000 power lines. This is the state of the circuit at the moment...

The device is recognised on the network by a MFD on the same network shown here...

and here's the pressure shown on an instrument head display...

The code to get the pressure from the BMP280 pressure sensor via I2C I've lifted from a previous YAPP, modifying it slightly to use the ESP-IDF I2C library rather than the STM32 one. Similarly I did the same to save the device address in non-volatile storage. Other than minor changes to the NMEA2000 library to get it to build using ESP-IDF nothing needed to be done to that library. The code that I wrote new for this project that links these 3 bits together is less than 100 lines long and so small I can stick it here...
The ESP32 has built in Bluetooth, so I might extend the code to send the pressure in a NMEA0183 format message via that interface so that OpenCPN running on a phone or tablet can grab the data and show it.
I need to get hold of a proper NMEA2000 connector and do a proper circuit board - and find a box to put it in. Then my intention is to replicate my previous Anchor Watcher YAPP to use NMEA2000 rather than Seatalk 1 which had an atmospheric pressure sensor as one of its features.
NMEA2000 is a closed standard and is very expensive to buy - far too much for home tinkerers. However, some Finns have reverse engineered it (which is legal) and created an open source library which they have put in the public domain license free on Github here...
GitHub - ttlappalainen/NMEA2000: NMEA2000 library for Arduino
The library is aimed at various Arduinos for hardware (including the Espressif ESP32 DevKitC board) and the Arduino IDE for building the software. I don't do Arduino of any type, but it was pretty easy to modify it slightly to build using the native ESP-IDF software development kit. This first project uses a BMP280 atmospheric pressure sensor breakout board from Pimoroni. From this the pressure is read periodically, packaged up in a NMEA2000 message and sent out using a Microchip MCP2551 Canbus driver chip. The whole circuit is only 2 small boards and 3 passive components. Currently it's powered by the USB connector on the DevKitC board but a simple voltage regulator will be sufficient to power the circuit from the NMEA2000 power lines. This is the state of the circuit at the moment...

The device is recognised on the network by a MFD on the same network shown here...

and here's the pressure shown on an instrument head display...

The code to get the pressure from the BMP280 pressure sensor via I2C I've lifted from a previous YAPP, modifying it slightly to use the ESP-IDF I2C library rather than the STM32 one. Similarly I did the same to save the device address in non-volatile storage. Other than minor changes to the NMEA2000 library to get it to build using ESP-IDF nothing needed to be done to that library. The code that I wrote new for this project that links these 3 bits together is less than 100 lines long and so small I can stick it here...
Code:
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "NMEA2000_CAN.h"
#include "N2kMessages.h"
#include "hal_non_vol.h"
#include "pressure_sensor.h"
#include "main.h"
#define PRESSURE_SENSOR_TASK_STACK_SIZE 8096U
const unsigned long transmit_messages[] = {130310UL, 0UL};
static TaskHandle_t main_task_handle;
static StaticQueue_t pressure_sensor_queue;
static uint8_t pressure_sensor_queue_buffer[sizeof(float)];
static QueueHandle_t pressure_sensor_queue_handle;
TaskHandle_t get_main_task_handle(void)
{
return main_task_handle;
}
static struct non_vol_t
{
uint32_t signature;
uint8_t deviceAddress;
} non_vol;
extern "C" void app_main(void)
{
uint8_t task_started_count = 0U;
main_task_handle = xTaskGetCurrentTaskHandle();
mw_hal_non_vol_init();
pressure_sensor_init();
pressure_sensor_queue_handle = xQueueCreateStatic((UBaseType_t)1, (UBaseType_t)(sizeof(float)), pressure_sensor_queue_buffer, &pressure_sensor_queue);
(void)xTaskCreate(pressure_sensor_task, "pressure sensor task", PRESSURE_SENSOR_TASK_STACK_SIZE, &pressure_sensor_queue_handle, (UBaseType_t)1, NULL);
// wait until all server tasks have started
do
{
(void)ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
task_started_count++;
}
while (task_started_count < 1U);
// load previous N2K device address from non vol
mw_hal_non_vol_load((uint8_t *)&non_vol, sizeof(non_vol_t));
if (non_vol.signature != 0xdeadbeef)
{
memset(&non_vol, 0, sizeof(non_vol_t));
non_vol.signature = 0xdeadbeef;
non_vol.deviceAddress = 22U;
mw_hal_non_vol_save((uint8_t *)&non_vol, sizeof(non_vol_t));
}
// set up N2K
NMEA2000.SetN2kCANMsgBufSize(16);
NMEA2000.SetProductInformation("00000001", 1, "Simple pressure monitor", "1.0", "1.0");
NMEA2000.SetDeviceInformation(1, 140, 75, 2040);
NMEA2000.SetMode(tNMEA2000::N2km_NodeOnly, non_vol.deviceAddress);
NMEA2000.EnableForward(false);
NMEA2000.ExtendTransmitMessages(transmit_messages);
NMEA2000.Open();
while (true)
{
// check if a pressure reading is available
float pressure_data;
if (xQueueReceive(pressure_sensor_queue_handle, (void *)&pressure_data, (TickType_t)0) == pdTRUE)
{
tN2kMsg N2kMsg;
SetN2kOutsideEnvironmentalParameters(N2kMsg, 1, N2kDoubleNA, N2kDoubleNA, mBarToPascal((double)pressure_data));
NMEA2000.SendMsg(N2kMsg);
}
// do N2K routine stuff
NMEA2000.ParseMessages();
if (NMEA2000.ReadResetAddressChanged())
{
non_vol.deviceAddress = NMEA2000.GetN2kSource();
mw_hal_non_vol_save((uint8_t *)&non_vol, sizeof(non_vol_t));
}
vTaskDelay(1);
}
}
The ESP32 has built in Bluetooth, so I might extend the code to send the pressure in a NMEA0183 format message via that interface so that OpenCPN running on a phone or tablet can grab the data and show it.
I need to get hold of a proper NMEA2000 connector and do a proper circuit board - and find a box to put it in. Then my intention is to replicate my previous Anchor Watcher YAPP to use NMEA2000 rather than Seatalk 1 which had an atmospheric pressure sensor as one of its features.
Last edited: