Skip to content

Commit

Permalink
Memory optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
ttlappalainen committed Mar 12, 2016
1 parent 37a23b2 commit 00004be
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 44 deletions.
18 changes: 10 additions & 8 deletions N2kMsg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ Author: Timo Lappalainen
*/

#include <N2kMsg.h>
//#include <MemoryFree.h> // For testing used memory

#define Escape 0x10
#define StartOfText 0x02
#define EndOfText 0x03
#define MsgTypeN2k 0x93

#define MaxActisenseMsgBuf 400
unsigned char ActisenseMsgBuf[MaxActisenseMsgBuf];

//*****************************************************************************
tN2kMsg::tN2kMsg(unsigned char _Source) {
Expand Down Expand Up @@ -282,21 +282,21 @@ void SetBuf4ByteDouble(double v, double precision, int &index, unsigned char *bu
int32_t *vi=(int32_t *)(&buf[index]);
index+=4;

(*vi)=(int32_t)(v/precision);
(*vi)=(int32_t)round(v/precision);
}

//*****************************************************************************
void SetBuf4ByteUDouble(double v, double precision, int &index, unsigned char *buf) {
uint32_t *vi=(uint32_t *)(&buf[index]);
index+=4;

(*vi)=(uint32_t)(v/precision);
(*vi)=(uint32_t)round(v/precision);
}

//*****************************************************************************
void SetBuf3ByteDouble(double v, double precision, int &index, unsigned char *buf) {
long vl;
vl=(long)(v/precision);
vl=(long)round(v/precision);
SetBuf3ByteInt(vl,index,buf);
}

Expand Down Expand Up @@ -423,31 +423,31 @@ void SetBuf2ByteDouble(double v, double precision, int &index, unsigned char *bu
int16_t *vi=(int16_t *)(&buf[index]);
index+=2;

(*vi)=(int16_t)(v/precision);
(*vi)=(int16_t)round(v/precision);
}

//*****************************************************************************
void SetBuf2ByteUDouble(double v, double precision, int &index, unsigned char *buf) {
uint16_t *vi=(uint16_t *)(&buf[index]);
index+=2;

(*vi)=(uint16_t)(v/precision);
(*vi)=(uint16_t)round(v/precision);
}

//*****************************************************************************
void SetBuf1ByteDouble(double v, double precision, int &index, unsigned char *buf) {
int8_t *vi=(int8_t *)(&buf[index]);
index+=1;

(*vi)=(int8_t)(v/precision);
(*vi)=(int8_t)round(v/precision);
}

//*****************************************************************************
void SetBuf1ByteUDouble(double v, double precision, int &index, unsigned char *buf) {
uint8_t *vi=(uint8_t *)(&buf[index]);
index+=1;

(*vi)=(uint8_t)(v/precision);
(*vi)=(uint8_t)round(v/precision);
}

//*****************************************************************************
Expand Down Expand Up @@ -543,8 +543,10 @@ void tN2kMsg::SendInActisenseFormat(Stream *port) const {
byte msgIdx=0;
int byteSum = 0;
byte CheckSum;
unsigned char ActisenseMsgBuf[MaxActisenseMsgBuf];

if (!IsValid()) return;
// Serial.print("freeMemory()="); Serial.println(freeMemory());

ActisenseMsgBuf[msgIdx++]=Escape;
ActisenseMsgBuf[msgIdx++]=StartOfText;
Expand Down
2 changes: 1 addition & 1 deletion N2kMsg.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ double GetBuf8ByteDouble(double precision, int &index, const unsigned char *buf,
class tN2kMsg
{
public:
static const int MaxDataLen=255;
static const int MaxDataLen=223; // with fast packet 1. frame can have 6 byte and rest 31 frames 7 bytes
unsigned char Priority;
unsigned long PGN;
mutable unsigned char Source;
Expand Down
78 changes: 61 additions & 17 deletions NMEA2000.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,25 @@ Author: Timo Lappalainen

#define N2kAddressClaimTimeout 250

unsigned long SingleFrameSystemMessages[]={59392L /*ISO Acknowledgement*/, 59904L /*ISO Request*/, 60928L /*ISO Address Claim*/,
const unsigned long SingleFrameSystemMessages[] PROGMEM={59392L /*ISO Acknowledgement*/, 59904L /*ISO Request*/, 60928L /*ISO Address Claim*/,
0};
unsigned long FastPacketSystemMessages[]={126208L,
const unsigned long FastPacketSystemMessages[] PROGMEM={126208L,
0};
unsigned long DefSingleFrameMessages[]={126992L, 127245L, 127250L, 127488L, 127505L, 127508L, 128259L, 128267L , 129026L, 130311L,
const unsigned long DefSingleFrameMessages[] PROGMEM={126992L, 127245L, 127250L, 127488L, 127505L, 127508L, 128259L, 128267L , 129026L, 130311L,
0};
unsigned long DefFastPacketMessages[]={126996L,126998L,127489L,127506L,128275L,129029L,
const unsigned long DefFastPacketMessages[] PROGMEM={126996L,126998L,127489L,127506L,128275L,129029L,
0};

const tProductInformation DefProductInformation PROGMEM={
1300, // N2kVersion
666, // ProductCode
"Arduino N2k->PC", //N2kModelID
"1.0.0.0", //N2kSwCode
"1.0.0", // N2kModelVersion
"00000001", // N2kModelSerialCode
0, // SertificationLevel
1 // LoadEquivalency
};
//*****************************************************************************
void SetCharBuf(const char *str, int MaxLen, char *buf) {
int i=0;
Expand All @@ -49,8 +59,16 @@ void SetMsgBuf(const unsigned long *msgs, int MaxLen, unsigned long* buf) {

//*****************************************************************************
tNMEA2000::tNMEA2000() {
SetMsgBuf(DefSingleFrameMessages,Max_N2kSingleFrameMessages_len,SingleFrameMessages);
SetMsgBuf(DefFastPacketMessages,Max_N2kFastPacketMessages_len,FastPacketMessages);
// Point for message code buffers was that developer can dynamically change them.
// Currentrly there has not been any need for that, so due to ram optmization do it later.
SingleFrameMessages=DefSingleFrameMessages;
FastPacketMessages=DefFastPacketMessages;
//SetMsgBuf(DefSingleFrameMessages,Max_N2kSingleFrameMessages_len,SingleFrameMessages);
//SetMsgBuf(DefFastPacketMessages,Max_N2kFastPacketMessages_len,FastPacketMessages);

N2kCANMsgBuf=0;
MaxN2kCANMsgs=0;

MsgHandler=0;
ForwardStream=&Serial;
N2kSource[0]=0;
Expand All @@ -64,7 +82,9 @@ tNMEA2000::tNMEA2000() {
EnableForward();
SetForwardSystemMessages();
SetForwardOwnMessages();
for (int i=0; i<MaxN2kCANMsgs; i++) N2kCANMsgBuf[i].FreeMessage();
LocalProductInformation=0;
ProductInformation=&DefProductInformation;
/*
SetProductInformation("00000001", // Manufacturer's Model serial code
666, // Manufacturer's product code
"Arduino N2k->PC", // Manufacturer's Model ID
Expand All @@ -74,6 +94,7 @@ tNMEA2000::tNMEA2000() {
1310, // NMEA2000 version used
0 // Sertification level ??
);
*/
SetDeviceInformation(1, // 21 bit resolution, max 2097151. Each device from same manufacturer should have unique number.
130, // PC Gateway. See codes on http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
25, // Inter/Intranetwork Device. See codes on http://www.nmea.org/Assets/20120726%20nmea%202000%20class%20&%20function%20codes%20v%202.00.pdf
Expand All @@ -83,6 +104,13 @@ tNMEA2000::tNMEA2000() {
DeviceCount=1;
}

//*****************************************************************************
void tNMEA2000::SetProductInformation(const tProductInformation *_ProductInformation) {
ProductInformation=_ProductInformation;
if (ProductInformation==0) ProductInformation=LocalProductInformation;
if (ProductInformation==0) ProductInformation=&DefProductInformation;
}

//*****************************************************************************
void tNMEA2000::SetProductInformation(const char *_ModelSerialCode,
unsigned int _ProductCode,
Expand All @@ -92,14 +120,18 @@ void tNMEA2000::SetProductInformation(const char *_ModelSerialCode,
unsigned char _LoadEquivalency,
unsigned int _N2kVersion,
unsigned char _SertificationLevel) {
if (_N2kVersion!=0xffff) N2kVersion=_N2kVersion;
if (_ProductCode!=0xffff) ProductCode=_ProductCode;
if (_ModelID) SetCharBuf(_ModelID,sizeof(N2kModelID),N2kModelID);
if (_SwCode) SetCharBuf(_SwCode,sizeof(N2kSwCode),N2kSwCode);
if (_ModelVersion) SetCharBuf(_ModelVersion,sizeof(N2kModelVersion),N2kModelVersion);
if (_ModelSerialCode) SetCharBuf(_ModelSerialCode,sizeof(N2kModelSerialCode),N2kModelSerialCode);
if (_SertificationLevel!=0xff) SertificationLevel=_SertificationLevel;
if (_LoadEquivalency!=0xff) LoadEquivalency=_LoadEquivalency;
if (LocalProductInformation==0) {
LocalProductInformation=new tProductInformation();
}
ProductInformation=LocalProductInformation;
if (_N2kVersion!=0xffff) LocalProductInformation->N2kVersion=_N2kVersion;
if (_ProductCode!=0xffff) LocalProductInformation->ProductCode=_ProductCode;
if (_ModelID) SetCharBuf(_ModelID,sizeof(LocalProductInformation->N2kModelID),LocalProductInformation->N2kModelID);
if (_SwCode) SetCharBuf(_SwCode,sizeof(LocalProductInformation->N2kSwCode),LocalProductInformation->N2kSwCode);
if (_ModelVersion) SetCharBuf(_ModelVersion,sizeof(LocalProductInformation->N2kModelVersion),LocalProductInformation->N2kModelVersion);
if (_ModelSerialCode) SetCharBuf(_ModelSerialCode,sizeof(LocalProductInformation->N2kModelSerialCode),LocalProductInformation->N2kModelSerialCode);
if (_SertificationLevel!=0xff) LocalProductInformation->SertificationLevel=_SertificationLevel;
if (_LoadEquivalency!=0xff) LocalProductInformation->LoadEquivalency=_LoadEquivalency;
}

//*****************************************************************************
Expand All @@ -126,8 +158,13 @@ void tNMEA2000::SetMode(tN2kMode _N2kMode, unsigned long _N2kSource) {
//*****************************************************************************
bool tNMEA2000::Open() {
if (!DeviceReady) {
if ( N2kCANMsgBuf==0 ) {
if ( MaxN2kCANMsgs==0 ) MaxN2kCANMsgs=5;
N2kCANMsgBuf = new tN2kCANMsg[MaxN2kCANMsgs];
for (int i=0; i<MaxN2kCANMsgs; i++) N2kCANMsgBuf[i].FreeMessage();
}

DeviceReady=CANOpen();

if ( ForwardType==tNMEA2000::fwdt_Text) {
if ( DeviceReady ) { ForwardStream->println("CAN device ready"); } else { ForwardStream->println("CAN device failed to open"); }
}
Expand Down Expand Up @@ -403,7 +440,14 @@ void tNMEA2000::SendProductInformation(int DeviceIndex) {
if ( DeviceIndex<0 || DeviceIndex>=DeviceCount) return;
tN2kMsg RespondMsg(N2kSource[DeviceIndex]);

SetN2kProductInformation(RespondMsg,N2kVersion,ProductCode,N2kModelID,N2kSwCode,N2kModelVersion,N2kModelSerialCode,SertificationLevel,LoadEquivalency);
SetN2kProductInformation(RespondMsg,ProductInformation->N2kVersion,
ProductInformation->ProductCode,
ProductInformation->N2kModelID,
ProductInformation->N2kSwCode,
ProductInformation->N2kModelVersion,
ProductInformation->N2kModelSerialCode,
ProductInformation->SertificationLevel,
ProductInformation->LoadEquivalency);
SendMsg(RespondMsg,DeviceIndex);
}

Expand Down
46 changes: 28 additions & 18 deletions NMEA2000.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ address anymore. See also method ReadResetAddressChanged().
#define Max_N2kModelVersion_len 32
#define Max_N2kModelSerialCode_len 32

#define Max_N2kSingleFrameMessages_len 50
#define Max_N2kFastPacketMessages_len 50
//#define Max_N2kSingleFrameMessages_len 50
//#define Max_N2kFastPacketMessages_len 50

#define Max_N2kDevices 4
#define Max_N2kDevices 1

#define Max_N2kMsgBuf_Time 100

Expand Down Expand Up @@ -98,6 +98,18 @@ class tDeviceInformation {
uint64_t GetName() { return DeviceInformation.Name; }
};

struct tProductInformation {
unsigned int N2kVersion;
unsigned int ProductCode;
// Note that we reserve one extra char for null termination
char N2kModelID[Max_N2kModelID_len+1];
char N2kSwCode[Max_N2kSwCode_len+1];
char N2kModelVersion[Max_N2kModelVersion_len+1];
char N2kModelSerialCode[Max_N2kModelSerialCode_len+1];
unsigned char SertificationLevel;
unsigned char LoadEquivalency;
};

class tNMEA2000
{
public:
Expand Down Expand Up @@ -146,23 +158,16 @@ class tNMEA2000
unsigned long N2kSource[Max_N2kDevices];

// Product information
unsigned int N2kVersion;
unsigned int ProductCode;
// Note that we reserve one extra char for null termination
char N2kModelID[Max_N2kModelID_len+1];
char N2kSwCode[Max_N2kSwCode_len+1];
char N2kModelVersion[Max_N2kModelVersion_len+1];
char N2kModelSerialCode[Max_N2kModelSerialCode_len+1];
unsigned char SertificationLevel;
unsigned char LoadEquivalency;
const tProductInformation *ProductInformation;
tProductInformation *LocalProductInformation;

unsigned long SingleFrameMessages[Max_N2kSingleFrameMessages_len];
unsigned long FastPacketMessages[Max_N2kFastPacketMessages_len];
const unsigned long *SingleFrameMessages;
const unsigned long *FastPacketMessages;

protected:
// Buffer for received messages.
#define MaxN2kCANMsgs 5
tN2kCANMsg N2kCANMsgBuf[MaxN2kCANMsgs];
tN2kCANMsg *N2kCANMsgBuf;
unsigned char MaxN2kCANMsgs;

// Handler callback
void (*MsgHandler)(const tN2kMsg &N2kMsg);
Expand Down Expand Up @@ -192,6 +197,10 @@ class tNMEA2000
public:
tNMEA2000();

// As default there are reservation for 5 messages. If it is not critical to handle all fast packet messages like with N2km_NodeOnly
// you can set buffer size smaller like 3 or 2 by calling this before open.
void SetN2kCANMsgBufSize(const unsigned char _MaxN2kCANMsgs) { if (N2kCANMsgBuf==0) { MaxN2kCANMsgs=_MaxN2kCANMsgs; }; }

// Define your product information. Defaults will be set on initialization.
// For keeping defaults use 0xffff/0xff for int/char values and nul ptr for pointers.
// LoadEquivalency is multiplication of 50 mA, what your device will take power from
Expand All @@ -207,6 +216,7 @@ class tNMEA2000
unsigned int _N2kVersion=0xffff, // Default=1300
unsigned char _SertificationLevel=0xff // Default=1
);
void SetProductInformation(const tProductInformation *_ProductInformation); // You can save ram by defining tProductInformation to PROGMEM

// Set default device information.
// For keeping defaults use 0xffff/0xff for int/char values and nul ptr for pointers.
Expand All @@ -231,8 +241,8 @@ class tNMEA2000

// Class handles automatically address claiming and tell to the bus about itself.
void SendIsoAddressClaim(unsigned char Destination=0xff, int DeviceIndex=0);
void SendProductInformation(int DeviceIndex);
void SendConfigurationInformation(int DeviceIndex);
void SendProductInformation(int DeviceIndex=0);
void SendConfigurationInformation(int DeviceIndex=0);

// Set this before open. You can not change mode after Open().
// Note that other than N2km_ListenOnly modes will automatically start initialization and address claim procedure.
Expand Down
7 changes: 7 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ To use avr processors internal CAN you need also NMEA2000_avr library and avr_ca
which you can download from https://github.com/thomasonw/avr_can

== Changes ==
12.03.2016 Memory tuning. Currently multi device and user definable message filters has not been implemented, so I changed buffer sizes to minimum.

There is also new function void tNMEA2000::SetN2kCANMsgBufSize(const unsigned char _MaxN2kCANMsgs); to define buffer size for received N2k messages.
Note that library has to collect fast packet frames, which may arrive fragmented from different devices, so as default this buffer size has been set to 5.
If your device is only sending some data (mode is tNMEA2000::N2km_NodeOnly), you do not need to catch all fast packet messages (if any), so you can set
buffer size smaller.

09.03.2016 Additional PGN 127250, PGN 128275 Support by adwuk.

08.03.2016 AVR CAN support by thomasonw.
Expand Down

0 comments on commit 00004be

Please sign in to comment.