NMEA2000 icon indicating copy to clipboard operation
NMEA2000 copied to clipboard

Disable frame sending for power saving mode

Open schwinn opened this issue 4 years ago • 14 comments

Hi Timo,

I'm currenty building a device, which should run on battery for serveral weeks and want to put the can transceiver in power saving mode (disables sending to the bus) during some periods. Is there a easy way to disable sending frames temporarily on the NMEA2000 library. When I enable the can transceiver after the power saving mode, I will receive all the queued heartbeat packets at once.

Thanks, Thorsten

schwinn avatar Apr 02 '20 13:04 schwinn

If you do not call ParseMessages, library will not do anything.

I do not know your hw, but in some hw can tranceiver will not go to sleep. This means that they will still collect frames to controller internal hw buffer. Naturally the interrupt should not collect frames to ram. So when you wake system up and call ParseMessages, it may read frames from hw buffer and show them up. Normally these internal buffers are only few frames.

ttlappalainen avatar Apr 03 '20 03:04 ttlappalainen

I will try this. Thanks

schwinn avatar Apr 03 '20 09:04 schwinn

Hi Timo,

I've one more question...

I'm using a Teensy 3.2 at 4Mhz and now I'm able to put Flexcan to Freeze mode and disable the sending part of the CAN transceiver. For this I have added a NMEA2000.Close() routine to your library, where I set DeviceReady to false. Because of this, I'm able to do a NMEA2000.Open() again after reenabling the canbus and my device is doing a new address claim.

The device uses 2.4mA from 12V when flexcan is disabled and about 4,5mA with active canbus.

Are you planing to add a Close function to your library in future?

Thanks, Thorsten

schwinn avatar Apr 06 '20 14:04 schwinn

Please send me the close code, I'll study it. I thought 24MHz was minimum for working CAN controller.

ttlappalainen avatar Apr 06 '20 15:04 ttlappalainen

The Tennsy is running fine on 4Mhz. According to documentation the lowest clockrate is 2Mhz for 250kbps bitrate.

It's only a hack to set DeviceReady to false to be able to call Open again.

NMEA2000.cpp //***************************************************************************** bool tNMEA2000::Close() { DeviceReady = false; return DeviceReady; }

NMEA2000.h bool Close();

schwinn avatar Apr 06 '20 19:04 schwinn

It is not that easy. That will cause e.g. creating new group function handlers. So slowly your device will run out of memory. There may be some other recreated structures or effects with can drivers. So I have to carefully look through all code where does it effect.

Close method is not either good name. I think better would be Pause or Sleep. Some CAN controllers has sleep feature, which stops their functions.

ttlappalainen avatar Apr 07 '20 03:04 ttlappalainen

Fix, it does not recreate those handlers, but still need to check other can drivers.

For future compatibility we need other name for Close(). For close I have feeling that it would be called just befor descructor to free things. Name could be e.g. Pause(), Sleep() or Stop().

ttlappalainen avatar Apr 07 '20 03:04 ttlappalainen

Thanks for your input. I'm currenty testing a new version with a Stop() and Continue() function. Then the handlers will not be recreated, but all missed Heartbeats will be send out at once. But I was able to solve it.

You can see the changes here: https://github.com/ttlappalainen/NMEA2000/compare/master...schwinn:master

I'll get back when I have more information.

schwinn avatar Apr 07 '20 14:04 schwinn

I did not understood:

      DeviceReady = true;
      if ( (ForwardStream!=0) && (ForwardType==tNMEA2000::fwdt_Text) ) {
        if ( DeviceReady ) { ForwardStream->println(F("CAN device ready")); } else { ForwardStream->println(F("CAN device failed to open")); }
      }

Your if ( DeviceReady ) will be always true?

Do we really need StopActive and Continue. If you just modify

void tNMEA2000::SendHeartbeat(bool force) {
...
if ( Devices[iDev].NextHeartbeatSentTime==0 || Devices[iDev].NextHeartbeatSentTime+Devices[iDev].HeartbeatInterval<millis() ) { Devices[iDev].NextHeartbeatSentTime=millis(); }

That fixes HB sending also for cases someone just stops calling ParseMessages. For now it would be enough just have

void tNMEA2000::Stop() {
    DeviceReady = false;
}

Keep it void, since we expect it always to do stop. If there will be later need for result, it can be added. But in compatibility view you can not remove return type.

Also now next call to ParseMessages will automatically continue.

ttlappalainen avatar Apr 08 '20 03:04 ttlappalainen

With the StopActive in the library it is not necessary to rewrite the whole program to suppress the execution of ParseMessage() and SendMsg(). These routines call Open() and with DeviceReady=false the routine would be executed again. I'm testing it with this logic right now, and it works fine.

The following was just copied from Open() and I will remove it.

  DeviceReady = true;
  if ( (ForwardStream!=0) && (ForwardType==tNMEA2000::fwdt_Text) ) {
    if ( DeviceReady ) { ForwardStream->println(F("CAN device ready")); } else { ForwardStream->println(F("CAN device failed to open")); }
  }

I'll do the change of SendHeartbeat() and I will test it.

Do we really need StopActive and Continue?

I like the idea to have a new AddressClaim after joining the bus again after having been offline. In case there's a new device on the bus, which is using our old address.

What do you think about that?

schwinn avatar Apr 09 '20 06:04 schwinn

It depends do you want to have possibility to call ParseMessages and SendMsg so that they does not do anything. That would actually mean Enable/Disable feature. Stop could act as function for stopping CAN now so that next ParseMessages or SendMsg will start it automatically. After Stop system will initialize device and controllers as they would have been stopped and also start address claim. So you do not need Continue(). Note that your SendMsg will fail during address claim period (250 ms).

If the device is not listening the bus even short period or if you somehow flush received messages, it should do address claiming again. Currently with just DeviceReady=false it will do it now. At quick check Teensy driver does not need anything special - just CANOpen. Just noticed that you have to add to Open() if ( N2kCANMsgBuf==0 ) InitCANFrameBuffers(); I have to check other drivers, does CANOpen() cause some harm, it it will be called again.

ttlappalainen avatar Apr 09 '20 07:04 ttlappalainen

I have removed the Continue() function and I'm testing it now.

I have tested a similar variant in the past, but the teensy has frozen after a few hours. Maybe it ran out of memory because of calling InitCANFrameBuffers() always after stopping the library.

The changes are commited to my fork.

schwinn avatar Apr 09 '20 09:04 schwinn

Hi Timo,

the Teensy was running about one week with the new code and then it stopped sending CAN frames. The rest of my code was still running, but without CAN.

Can this happen because CANOpen() is called every time, when I use the bus again after DeviceReady was set to false?

schwinn avatar Apr 16 '20 14:04 schwinn

Can not say for sure. Putting system to sleep has different effects for CAN controller on any device. Also sleep mode effects. So you should study what sleep does for Teensy CAN controller and how it should be put to sleep state and opened from there. Just now I do not have time to do that. I can consult what I know.

ttlappalainen avatar Apr 17 '20 04:04 ttlappalainen