ttlappalainen / NMEA2000

NMEA2000 library for Arduino
508 stars 211 forks source link

BMS CAN - NMEA2000 converter #290

Open minoseigenheer opened 1 year ago

minoseigenheer commented 1 year ago

I have a BMS (Battery Management System) which broadcasts with a custom CAN protocol. To display the battery info on a multifunctional display I need a NMEA2000 converter. For testing I would like to use a Leonardo CAN bus board with single CAN controller MCP2515 + MCP2551 transceiver. https://www.hobbytronics.co.uk/leonardo-canbus How can I parse the CAN messages on the same CAN bus as I communicate with NMEA2000 devices? Is there a check if the received message is a NMEA2000 message where I could add an else try parsing BMS messages? Finally I would like to make a board with two isolated transceivers.

ttlappalainen commented 1 year ago

You can run library with that board. ATmega32U4 has only 2.5 MB RAM and I prefer 8 MB. I prefer to use Teensy 3.5, 3.6 or 4.x. They also has 2 CAN controllers so that you need only tranceivers. Teensy 3.2 or ESP32 are also good, but for second CAN you need external CAN controller.

Currently library core reads only CAN messages with 29 bit ID. Messages with 11 bit ID will be discarded.

Each message has own PGN. Proprietary messages has also manufacturer number and industry code to separate messages with same PGN from each other. If there is overlapping with CANopen and NMEA2000 PGN definitions for non proprietary messages, you may need to separate messages also with sender device information. So if proprietary message industry code or sender device industry code is marine, it should be NMEA2000 message. To keep track of devices on the bus, there is rather automatic N2kDeviceList module in the library.

minoseigenheer commented 1 year ago

Thanks for the great library and your feedback. A Teensy 3.6 or 4 with two isolated CAN transceivers would be an easy solution but they are barely available at the moment. I can still order custom assembled boards with STM32F105 for example.

I found this NMEA2000 CAN driver for STM32F103 which works with STM32duino.

Has anyone experience with NMEA2000 on STM32?

ttlappalainen commented 1 year ago

With quick check it does not look currently very good. Problem is that for me it look it does not use interrupt, which causes process stop until whole message has been sent. With e.g., 25 frame fastpacket that means minimum 10 ms process stop.

minoseigenheer commented 1 year ago

Yes it looks like jiauka started multi buffer and interrupt support but never finished it.

//TODO
#if 0
    CANbus->setNumTXBoxes(NumTxMailBoxes);

// With this support system can have different buffers for high and low priority and fast packet messages.
// After message has been sent to driver, it buffers it automatically and sends it by interrupt.
// We may need to make these possible to set.
    uint16_t HighPriorityBufferSize=CANGlobalBufSize / 10;
    HighPriorityBufferSize=(HighPriorityBufferSize<15?HighPriorityBufferSize:15);// just guessing
    CANGlobalBufSize-=HighPriorityBufferSize;
    uint16_t FastPacketBufferSize= (CANGlobalBufSize * 9 / 10);
    CANGlobalBufSize-=FastPacketBufferSize;

    CANbus->setMailBoxTxBufferSize(CANbus->getFirstTxBox(),HighPriorityBufferSize);// Highest priority buffer
    CANbus->setMailBoxTxBufferSize(CANbus->getLastTxBox(),FastPacketBufferSize);// Fastpacket buffer
    STM32F1_CAN::getInstance().setTxBufferSize(CANGlobalBufSize);
#endif
#endif

    tNMEA2000::InitCANFrameBuffers(); // call main initialization
}

#if 0
extern "C" void CAN1_TX_IRQHandler(void) {
    CanTxMsgTypeDef frame;

    if (popTX(&frame) == true) {
        memcpy(CanHandle.pTxMsg,&frame,sizeof(CanTxMsgTypeDef));
        HAL_CAN_Transmit_IT(&CanHandle);  // Rearm transmit
    }
    HAL_CAN_IRQHandler(&CanHandle);
}
#if 0
void HAL_CAN_TxCpltCallback(CAN_HandleTypeDef* _canHandle)
{
    HAL_CAN_Transmit_IT(_canHandle); // Rearm transmit
}
#endif
minoseigenheer commented 1 year ago

I looked at the STM32 LL CAN driver again and it looks like "NEW_LIB" is actually using interrupts if wait_sent == false https://github.com/jiauka/STM32F1_CAN/blob/master/STM32F1_CAN.cpp I will try to get a test setup working next weekend.

ttlappalainen commented 1 year ago

But unfortunately biggest problem appears with fastpackets. And wait_sent should be is_fast_packet.

minoseigenheer commented 1 year ago

Sorry I try to understand the changes necessary. Could you explain the problem or missing part with fast packets?

ttlappalainen commented 1 year ago

There is two ways to solve it.

  1. Each CAN controller mailbox has own buffer. In this method when mailbox is empty interrupt simply takes frames out from its own buffer and writes it to specific mailbox.

  2. You have common buffer for interrups. In this method buffer frames must also include information to which mailbox should be used.

As far as I understood there is 3 out mailbox. Then it is easy to deal frames according to interrupt to mailbox buffers. E.g., <=2 to box 1, 3-4 to box 2, >=5 to box 3. Before decicion of deal order you have to check from CAN controller document box priority order. I expect that also that CAN controller has priority oder for boxes. So if box 1 has highest priority, above works. If box 3 has highest priority, you have to switch aboce order.

Case 1 is used e.g., on NMEA2000_teensy. Case 2 is used on e.g., NMEA2000_Teensyx, where I have used tPriorityRingBuffer for frame buffering.

In library logic whent it sends frame, it tries first to send it directly with driver. If that fails, it puts frame to wait to internal buffer and tries next time to send it to driver. This is also difference between NMEA2000_teensy and NMEA2000_Teensyx. In driver inherited InitCANFrameBuffers() NMEA2000_teensy calls finally tNMEA2000::InitCANFrameBuffers(); to iniatialize small library buffer. NMEA2000_Teensyx does not do that and so whole buffer is holded on driver tPriorityRingBuffer txRing.

I also prefer to think would it be best to include STM31F_CAN to NMEA2000_stm32f1. In this way you avoid problem that someone has wrong version of STM31F_CAN installed. I used that method with NMEA2000_Teensyx and NMEA2000_ESP32. When I started to work with embedded and these open libraries I thought that this is nice to have common library and a big mass coders fixing possible problems. Soon I noticed that e.g., for FLEX_CAN they did not implement my change requests or it finally took loooong time. Meanwhile there were lot of mesh with NMEA2000_Teensy, since people had default old version installed, which did not work properly with N2k. Also there is sometimes problem that someone just pushed changes, which are not backward compatible.

I think it is best to take a look to NMEA2000_Teensyx and possibly used similar method with STM driver.