We have been reluctant to share more information because we don’t want to be inundated with emails requesting support for projects copying our systems. I am not a software engineer and this has just been a hobby for me during the evenings. The systems are constantly evolving and there are some software bugs. A professional software engineer would certainly laugh at the way the code is written, but it has worked so far for us. And my programming knowledge is getting better all the time!
Following on from the Cuthbert’s Arduino Computer Network page, this is a more in-depth description of the Fuse Box Arduino. This Arduino captures the following information and shares it on the CAN:
- Victron BMV battery monitor serial data.
- PWM solar charger output current.
Victron BMV battery monitor serial data
We have a Victron BMV-602S battery monitor system. This system outputs a 3.3V serial data feed from its VE.Direct interface port. Full information about this interface is provided in the Victron document “VE.Direct Protocol” available from the Victron Energy website so will not go into more detail here.
The bases of the code I have used to receive and decode the serial data from the BMV-602S was download from: https://www.victronenergy.com/live/open_source:start However, the code was found to cause stack over-runs and various other issues. These issues caused the Arduino Mega computers to ‘lock-up’ and the Due to generate spurious data. I therefore re-wrote the code to clear these issues.
The BMV-602S VE.Direct serial data is connected to Serial 1. It should be noted that although this signal is 3.3V it is a high enough voltage to work with 5V systems like the Arduino Uno and Mega without voltage level changing.
PWM solar charger output current
The PWM solar charger does not have any data output. I have used an Arduino current measuring module in the PWM solar charger’s output to monitor the current output. This modules output is read by the Arduino on analogue port one. As the module has a 5V maximum output its voltage level needed to be reduced so that it is compatible with the Due 3.3V maximum input voltage. This is done using 4.7K and 10K resistor network.
Fresh and Grey water level
Cuthbert’s water level display is quite simple: move a switch (one way for fresh water and the other way for grey) and get a reading showing empty, ¼, ½, ¾ or full on a needle gauge. The Fuse Box Arduino uses two relays (K3 for grey water and K4 for fresh water) which when energised, make the electrical circuit as if the water level switch had been used. The Arduino’s analogue zero port is used to measure the voltage of the needle gauge. As the needle gauge voltage is quite low, about 0.5V for a ‘full’ reading, no additional circuitry is required.
Smoke/LPG/Carbon Monoxide detection (work in progress).
I’m using two gas sensors for this alarm system:
- MQ-6
- Highly sensitive to Propane (LPG) and Butane.
- Small sensitivity to Alcohol and smoke.
- MQ-7
- Highly sensitive to Carbon Monoxide
The MQ-6 is quite easy to use, apply a 5V heating voltage and read its output on analogue 2. As the sensor is powered from 5V the output is reduced using a 4.7K and 10K resistor network.
The MQ-7 is a little more complicated as it requires the heater voltage to be switched between 5V (for 30 seconds) and 1.4V (for 90 seconds). The sensor’s output during the high voltage heating is compared to the output during the low voltage heating. If the output during low voltage heating is higher than the output during high voltage heating, the Carbon Monoxide level is above 100ppm and the alarm is sounded. As the sensor is powered from 5V, the output is reduced using a 4.7K and 10K resistor network and fed to analogue 3. The MQ-7 heater is powered through two 150-ohm resistors (in parallel to handle the current) which results in 1.4V at the heater. To increase the heating voltage, digital pin 2 is switched high. When pin 4 is high, the BC547 transistor is turned on, which turns on the IRF24G FET. With the FET switched on, the two 150-ohm resistors are effectively bypassed and 5V is applied to the heater.
There is a yellow LED connected to digital pin 9 and used to indicate a MQ-6 alarm – gas/smoke. There is a red LED connected to digital pin 10 and used to indicate a MQ-7 alarm – Carbon Monoxide. There is also an alarm cancel button attached to digital pin 11. In the event of an alarm relay K1 is energised to power a siren.
This alarm function is a work in progress and has not been tested or the software refined yet!
GPS monitoring
The GPS module occasionally ‘hangs up’ and stops outputting data. The Fuse Box Arduino monitors the GPS CAN messages and if they are not received after a few minutes, energises relay K4 to reset the GPS module.
4D Display
To ‘future proof’ the interface design there are a couple of connectors to attach a 4D Display system to this Arduino. At present this is not used.
CAN Interface
The CAN high-speed buffer chip used (MCP2551) is a 5V device, so the communications with it must have the voltage levels adjusted for use with the 3.3V Due. This is done using a small ‘level converter’ module (cheaper than buying the individual components).
Serial 3
As there were two spare channels left on the ‘level converter’, serial 3 was also fed through to provide a couple of 5V compatible digital pins or serial port for future use.
Circuits and Software
Below are the circuit diagram and PCB design for the interface.
Below is the software. I have tried to comment it as much as possible so hopefully it can be understood by others. The libraries were downloaded from Copperhill Technology and have been modified slightly for my use.
This Arduino program is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
Fusebox_V2.6
// ------------------------------------------------------------------------ // ARD1939 - SAE J1939 Protocol Stack for Arduino Due // ------------------------------------------------------------------------ // // This Arduino program is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. int TestDataOn = 1; // Set to 1 for data stream to laptop, 2 to stream gas sensor data only float SoftwareVersion = 2.6; // Software Version Number #include <stdlib.h> #include <inttypes.h> #include <SPI.h> #include "Cuthbert_Due_ARD1939.h" ARD1939 j1939; #define BMV Serial1 // BMV Rx only #define BT Serial3 // BlueTooth interface const byte EN = 21; // Bluetooth enable pin const byte State = 23; // Bluetooth State pin //**********BMV definitions**************************** #define BV 0 // mV Main (battery) voltage #define VS 1 // mV Auxiliary (starter) voltage #define BI 2 // L mA Battery current #define CE 3 // L mAh Consumed Amp Hours #define SOC 4 // % State-of-charge #define TTG 5 // Minutes, Time-to-go #define Alarm 6 // Alarm condition active #define Relay 7 // Relay state #define AR 8 // Alarm reason #define H1 9 // L mAh Depth of the deepest discharge #define H2 10 // L mAh Depth of the last discharge #define H3 11 // L mAh Depth of the average discharge #define H4 12 // Number of charge cycles #define H5 13 // Number of full discharges #define H6 14 // L mAh Cumulative Amp Hours drawn #define H7 15 // mV Minimum main (battery) voltage #define H8 16 // mV Maximum main (battery) voltage #define H9 17 // L Seconds Number of seconds since last full charge #define H10 18 // Number of automatic synchronizations #define H11 19 // Number of low main voltage alarms #define H12 20 // Number of high main voltage alarms #define H13 21 // Number of low auxiliary voltage alarms #define H14 22 // Number of high auxiliary voltage alarms #define H15 23 // mV Minimum auxiliary (battery) voltage #define H16 24 // mV Maximum auxiliary (battery) voltage #define BMVb 25 // Model description (deprecated) #define FWb 26 // Firmware version #define CHECKSUMb 27 //***********Relay definitions************************* const byte K1 = 3; // Step inhibit const byte K2 = 4; // GPS power const byte K3 = 6; // waste water *** fresh and waste water switched until new relay installed *** const byte K4 = 5; // fresh water unsigned long pwm_millis; unsigned long send_millis; const byte WaterLevel = A0; // water level sensor pin const byte buffsize = 32; const byte value_bytes = 33; const byte label_bytes = 9; const byte bmv_num_keywords = 28; char bmv_keywords[bmv_num_keywords][label_bytes] = {"V", "VS", "I", "CE", "SOC", "TTG", "Alarm", "Relay", "AR", "H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8", "H9", "H10", "H11", "H12", "H13", "H14", "H15", "H16", "BMV", "FW", "Checksum" }; char bmv_receivedChars[buffsize]; // an array to store the received bmv data char bmv_tempChars[buffsize]; // an array to manipulate the received data char bmv_recv_label[bmv_num_keywords][label_bytes] = {0}; // {0} tells the compiler to initalize it with 0. char bmv_recv_value[bmv_num_keywords][value_bytes] = {0}; // That does not mean it is filled with 0's long bmv_value[bmv_num_keywords] = {0}; // The array that holds the verified data static byte bmv_blockindex = 0; bool bmv_new_data = false; bool bmv_blockend = false; //***************PWM data****************************** byte const PWM_current_pin = A1; // current sensor analogue pin int PWM_I = 0; // mA PWM output current byte PWM_PPV = 0; // W panel power byte PWM_H20 = 0; // 0.01 kWh yield today byte PWM_H21 = 0; // W max power today byte PWM_H22 = 0; // 0.01 kWh yield yesturday byte PWM_H23 = 0; // W max power yesturday long PWM_Ws = 0; // used for yield calculations yield in W a sec int old_day = 0; // used to detect night to day change int current0 = 0; // used to average the current readings int current1 = 0; // used to average the current readings int current2 = 0; // used to average the current readings int current3 = 0; // used to average the current readings int current4 = 0; // used to average the current readings int mppt_V = 0; int mppt_HSDS = 0; byte BMVdata[8] = {0}; byte BMVdata2[8] = {0}; byte BMVdata3[8] = {0}; byte BMVdata4[8] = {0}; byte BMVdata5[8] = {0}; byte BMVdata6[8] = {0}; byte BMVdata7[8] = {0}; byte PWMdata[7] = {0}; int guageReading[6] = {0}; byte guageSend[2] = {0}; byte BMVcounter = 0; byte GASdata[8] = {0}; byte fwFlag = 0; byte wwFlag = 0; byte GPSoverride = 0; unsigned long freshGuageTime = 0; unsigned long wasteGuageTime = 0; unsigned long RelayTimer = 0; byte RelayFlag = 0; byte sendWaterLevels = 0; int GPSmodule = 0; // used for timing GPS CAN messages // J1939 Variables byte nMsgId; byte nDestAddr; byte nSrcAddr; byte nPriority; byte nJ1939Status; int nMsgLen; long lPGN; byte pMsg[J1939_MSGLEN]; byte sequence = 0; byte temp = 0; //used to combine bits into a byte for transmission const byte CoPower = 2; // controls the CO sensors power const byte CoSensor = A3; // CO sensor input const byte SmokeSensor = A2; // Smoke sensor input const byte DisplayReset = 8; // 4D display reset pin const byte SmokeLED = 9; // Smoke LED const byte CoLED = 10; // CO LED const byte AlarmCx = 11; // Alarm cancel button unsigned long CoHeaterTimer = 0; // Timer for CO sensor heater unsigned long AlarmTime = 0; // Timer used for the alarm byte CoHeaterFlag = 0; // used to control heater timing int CoValueHigh = 0; // CO sensor value during high heating int CoValueLow = 0; // CO sensor value during low heating byte AlarmSilance = 0; // Alarm silencing flag int SmokeValue = 0; // Smoke sensor value int Smoke1 = 0; // Smoke sensor value used to adverage readings int Smoke2 = 0; // Smoke sensor value used to adverage readings int Smoke3 = 0; // Smoke sensor value used to adverage readings int Smoke4 = 0; // Smoke sensor value used to adverage readings byte COpercent = 0; // CO ratio between high and low values byte COpercentMax = 150; // maximum CO ratio recorded int SmokeMax = 0; // maximum smoke signal recorded bool SmokeAlarm = false; // smaoke alarm flag bool COalarm = false; // CO alarm flag // Time byte LocalHour = 0; byte LocalMin = 0; byte LocalSec = 0; int UTCoffset = 0; // Bluetooth byte master = 0; // master on/off switch in BT app byte waterValve = 1; // water valve control int fillLevel = 0; // fill by x litres byte fillGuage = 0; // fill to guage reading byte fillReset = 0; // used to reset the fill litre counter byte usedReset = 0; // used to reset the used water counter byte Flow[3] = {0}; // array for CAN flow message unsigned long flowPeriod = 0; // timing for CAN message tx byte Decimal = 0; // Lat Long decimals // ------------------------------------------------------------------------ // Setup routine runs on power-up or reset // ------------------------------------------------------------------------ void setup() { analogReadResolution(12); // sets the analog read to 12 bit = 4095 for (int i = K1; i < (K4 + 1); i++) { // Sets up relays as outputs and turns them all off pinMode(i, OUTPUT); digitalWrite(i, HIGH); } pinMode(CoPower, OUTPUT); pinMode(DisplayReset, OUTPUT); pinMode(SmokeLED, OUTPUT); pinMode(CoLED, OUTPUT); pinMode(AlarmCx, INPUT_PULLUP); pinMode(EN, OUTPUT); pinMode(State, INPUT); digitalWrite(EN, HIGH); // Open serial communications BMV.begin(19200); // connection to BMV BT.begin(115200); // connection to BlueTooth /*if (EEPROM.read(1) != 255) { old_day = EEPROMReadlong(2); // Old day PWM_H20 = EEPROMReadlong(6); // 0.01 kWh yield today PWM_H21 = EEPROMReadlong(10); // W max power today PWM_H22 = EEPROMReadlong(14); // 0.01 kWh yield yesturday PWM_H23 = EEPROMReadlong(18); // W max power yesturday EEPROM.update(1, 0); }*/ // Set the serial interface baud rate if (TestDataOn > 0) { Serial.begin(MONITOR_BAUD_RATE); Serial.print("Software Version "); Serial.println(SoftwareVersion); } // Initialize the J1939 protocol including CAN settings if (j1939.Init(SYSTEM_TIME) == 0) if (TestDataOn == 1) Serial.println("CAN Controller Init OK"); else if (TestDataOn == 1) Serial.println("CAN Controller Init Failed"); // Set the preferred address and address range j1939.SetPreferredAddress(SA_PREFERRED_FUSE); j1939.SetAddressRange(ADDRESSRANGEBOTTOM, ADDRESSRANGETOP); // Set the message filter j1939.SetMessageFilter(59999); // Transport Protocol j1939.SetMessageFilter(65500); // MPPT j1939.SetMessageFilter(65501); // BMV j1939.SetMessageFilter(65502); // PWM j1939.SetMessageFilter(65503); // GPS j1939.SetMessageFilter(65504); // Tie Current j1939.SetMessageFilter(65505); // Flow Meter j1939.SetMessageFilter(65506); // Flow Meter and valve control j1939.SetMessageFilter(65507); // Water Level j1939.SetMessageFilter(65508); // Relay Control j1939.SetMessageFilter(65509); // Water Use //j1939.SetMessageFilter(65510); // Power Calculations j1939.SetMessageFilter(65511); // Water level request j1939.SetMessageFilter(65512); // Time //j1939.SetMessageFilter(65513); // UTC update //j1939.SetMessageFilter(65514); // Engine shut down //j1939.SetMessageFilter(65515); // Engine armed //j1939.SetMessageFilter(65516); // Engine shut down status j1939.SetMessageFilter(65517); // MPPT2 j1939.SetMessageFilter(65518); // BMV2 j1939.SetMessageFilter(65519); // GPS2 //j1939.SetMessageFilter(65520); // j1939.SetMessageFilter(65521); // BMV4 j1939.SetMessageFilter(65522); // BMV5 j1939.SetMessageFilter(65523); // BMV6 j1939.SetMessageFilter(65524); // BMV7 j1939.SetMessageFilter(65525); // Gas alarm //j1939.SetMessageFilter(65526); // AFAM data output 1 //j1939.SetMessageFilter(65527); // AFAM data output 2 //j1939.SetMessageFilter(65528); // AFAM data output 3 //j1939.SetMessageFilter(65529); // AFAM data output 4 //j1939.SetMessageFilter(65530); // AFAM Control // Set the NAME j1939.SetNAME(NAME_IDENTITY_NUMBER, NAME_MANUFACTURER_CODE, NAME_FUNCTION_INSTANCE, NAME_ECU_INSTANCE_FUSE, NAME_FUNCTION, NAME_VEHICLE_SYSTEM, NAME_VEHICLE_SYSTEM_INSTANCE, NAME_INDUSTRY_GROUP, NAME_ARBITRARY_ADDRESS_CAPABLE); }// end setup // ------------------------------------------------------------------------ // Main Loop - Arduino Entry Point // ------------------------------------------------------------------------ void loop() { BmvRecvWithEndMarker(); // Receive information on Serial from BMV if (RelayFlag == 1) guageReadings(); // runs the guageReading route again if its in the process of taking a reading //******************************* gas alarms********************************************** if ((COpercent < COpercentMax) && (COpercent > 0)) COpercentMax = COpercent; if (SmokeValue > SmokeMax) SmokeMax = SmokeValue; if ((millis() - CoHeaterTimer) >= 90000) { digitalWrite(CoPower, HIGH); // set heater to high for 30 seconds CoHeaterTimer = millis(); CoHeaterFlag = 1; } if (((millis() - CoHeaterTimer) >= 30000) && (CoHeaterFlag == 1)) { digitalWrite(CoPower, LOW); // set heater to low for 90 seconds CoHeaterTimer = millis(); CoHeaterFlag = 0; } if (CoHeaterFlag == 1) { CoValueHigh = (analogRead(CoSensor) + CoValueHigh) / 2; } else { CoValueLow = (analogRead(CoSensor) + CoValueLow) / 2; } COpercent = (CoValueLow * 100) / CoValueHigh; /*if ((COpercent < 90) && (millis() > 540000)) { // Alarm situation! (after a 9 min warm up) digitalWrite(CoLED, HIGH); COalarm = true; AlarmTime = millis(); if (AlarmSilance == 0) { digitalWrite(alarm, LOW); // Sound the alarm } else { digitalWrite(alarm, HIGH); // cancel alarm sound } }*/ if ((COpercent > 100) && ((millis() - AlarmTime) > 60000)) { // Alarm cancel after 60 seconds below alarm threshold //digitalWrite(alarm, HIGH); digitalWrite(CoLED, LOW); AlarmTime = 0; AlarmSilance = 0; COalarm = false; } Smoke4 = Smoke3; Smoke3 = Smoke2; Smoke2 = Smoke1; Smoke1 = analogRead(SmokeSensor); SmokeValue = (Smoke1 + Smoke2 + Smoke3 + Smoke4) / 4; /*if (SmokeValue > 300) { // Alarm situation! digitalWrite(SmokeLED, HIGH); SmokeAlarm = true; AlarmTime = millis(); if (AlarmSilance == 0) { digitalWrite(alarm, LOW); // Sound the alarm } else { digitalWrite(alarm, HIGH); // cancel alarm sound } }*/ if ((SmokeValue < 200) && ((millis() - AlarmTime) > 60000)) { // Alarm cancel after 60 seconds below alarm threshold //digitalWrite(alarm, HIGH); digitalWrite(SmokeLED, LOW); AlarmTime = 0; AlarmSilance = 0; SmokeAlarm = false; } if (digitalRead(AlarmCx) == LOW) { COpercentMax = 150; SmokeMax = 0; AlarmSilance = 1; } //************************** PWM Current Averaging *************************************** current4 = current3; current3 = current2; current2 = current1; current1 = current0; current0 = analogRead(PWM_current_pin); current0 = (current0 + current1 + current2 + current3 + current4) / 5; //**************************************************************************************** if ((millis() - pwm_millis) > 3000) { PWM_calculations(); pwm_millis = millis(); Serial.println(analogRead(WaterLevel)); } if ((millis() - send_millis) > 500) { CANsend(); // send CAN messages (and test data if enabled) if (sendWaterLevels > 0) { guageReadings(); sendWaterLevels++; if (sendWaterLevels > 4) sendWaterLevels = 0; } send_millis = millis(); GPSmodule--; // used for timing GPS CAN messages if (TestDataOn == 2) { Serial.print(CoValueHigh); Serial.print(" CO high, "); Serial.print(CoValueLow); Serial.print(" CO low, "); Serial.print(SmokeValue); Serial.print(" smoke, "); Serial.print(COpercent); Serial.print(" CO %, "); Serial.print(COpercentMax); Serial.print(" CO% max, "); Serial.print(SmokeMax); Serial.println(" smoke max"); } } //************************************** Serial monitor control ****************************************************** if (Serial.available() > 0) { // Serial monitor controls for changing the used, remaining water and charge 100% values byte ByteReceived = Serial.read(); if ((ByteReceived == 'g') || (ByteReceived == 'G')) { // gets water level guage readings freshGuageTime = millis() + 11000; guageReadings(); } } //************************************** J1939 main loop code ******************************************************** char sString[80]; // Establish the timer base in units of milliseconds delay(SYSTEM_TIME); // Call the J1939 protocol stack nJ1939Status = j1939.Operate(&nMsgId, &lPGN, &pMsg[0], &nMsgLen, &nDestAddr, &nSrcAddr, &nPriority); if (lPGN == 65500) { // MPPT mppt_V = DataReadInt(0); // mV main battery voltage int MI = DataReadInt(2); // mA output current int VPV = pMsg[4]; // V panel voltage byte PPV = pMsg[5]; // W panel power //Bvalue[CS] = pMsg[6]; // state of operation //Bvalue[ERR] = pMsg[7]; // error messages BT.print("*h" + String(PPV) + "W MPPT*"); BT.print("*i" + String(VPV) + "V Panel*"); BT.print("*j" + String((MI / 1000.0)) + "A MPPT*"); if (TestDataOn == 1) { Serial.print("MPPT V = "); Serial.println(mppt_V); } } if (lPGN == 65504) { // Tie Current int TieI = DataReadInt(0) - 800; // Tie Current BT.print("*g" + String(TieI) + "A Tie*"); } if (lPGN == 65517) { // MPPT2 mppt_HSDS = DataReadInt(6); // day sequence number if (TestDataOn == 1) { Serial.print("HSDS = "); Serial.println(mppt_HSDS); } } if (lPGN == 65508) { // relay control if (((pMsg[0] >> 6) & 1) == 1) digitalWrite(K1, LOW); else digitalWrite(K1, HIGH); // controls the K1 relay - Step inhibit if (((pMsg[0] >> 7) & 1) == 1) { // controls the K2 relay - GPS power GPSoverride = 1; } else { GPSoverride = 0; GPSmodule = -450; } } if (lPGN == 65511) { // water guage reading request sendWaterLevels = 1; if (TestDataOn == 1) { Serial.println("Water reading request received"); } } if (lPGN == 65512) { // Time LocalHour = pMsg[0]; LocalMin = pMsg[1]; LocalSec = pMsg[2]; if (pMsg[4] == 0) { UTCoffset = pMsg[3]; } else { UTCoffset = pMsg[3] * -1; // UTC offset } String a = ""; String b = ":"; String c = ":"; if (LocalHour < 10) a = "0"; if (LocalMin < 10) b = ":0"; if (LocalSec < 10) c = ":0"; BT.print("*k" + a + String(LocalHour) + b + String(LocalMin) + c + String(LocalSec) + "L Time*"); } if (lPGN == 65503) { // GPS String LaH = ""; String LoH = ""; byte LaDD = pMsg[0]; // deg Latitude byte LaMM = pMsg[1]; // min Latitude if (LaMM > 99) { LaMM = LaMM - 100; LaH = "S"; // South } else { LaH = "N"; // North } byte LaSS = pMsg[2]; // sec Latitude byte LoDD = pMsg[3]; // deg Longitude byte LoMM = pMsg[4]; // min Longitude if (LoMM > 99) { LoMM = LoMM - 100; LoH = "W"; // West } else { LoH = "E"; // East } byte LoSS = pMsg[5]; // sec Longitude byte FixAge = pMsg[6]; // Fix Age byte Sats = pMsg[7]; // Sats GPSmodule = 0; // GPS and GSM Arduino message received int longdec = Decimal / 10; // split out the Lat and Long decimals int latdec = Decimal - (longdec * 10); String a = ""; // add leading zeros and formatting String b = " "; String c = "' "; String d = ""; String e = " "; String f = "' "; if (LaDD < 10) a = "0"; if (LaMM < 10) b = " 0"; if (LaSS < 10) c = "' 0"; if ((LoDD < 100) && (LoDD > 9)) d = "0"; if (LoDD < 10) d = "00"; if (LoMM < 10) e = " 0"; if (LoSS < 10) f = "' 0"; BT.print("*l" + LaH + a + String(LaDD) + b + String(LaMM) + c + String(LaSS + (latdec / 10.0)) + "''*"); BT.print("*m" + LoH + d + String(LoDD) + e + String(LoMM) + f + String(LoSS + (longdec / 10.0)) + "''*"); BT.print("*n" + String(FixAge) + "ms fix age*"); BT.print("*o" + String(Sats) + " satellites*"); } if (lPGN == 65519) { // GPS2 int Alt = DataReadInt(0); // Altitude int HDOP = DataReadInt(2); // HDOP byte kph = pMsg[4]; // kph int Course = DataReadInt(5); // Course Decimal = pMsg[7]; // Lat/Long decimal BT.print("*p" + String(Alt) + "m alt*"); int feet = Alt * 3.281; BT.print("*q" + String(feet) + "ft alt*"); BT.print("*r" + String(HDOP) + " HDOP*"); } if (((GPSmodule < -400) && (GPSmodule > -405)) || (GPSoverride == 1)) { // reset GPS if no CAN message received for 5 min digitalWrite(K2, LOW); // turn GPS module off } if ((GPSmodule < -405) && (GPSoverride == 0)) { digitalWrite(K2, HIGH); // turn GPS module on } if (GPSmodule == -1200) { GPSmodule = 0; } //********************************* Bluetooth Code ************************************************* if (BT.available() > 0) { byte ByteReceived = BT.read(); if (ByteReceived == 'M') master = 1; // Master on if (master == 1) { if (ByteReceived == 'A') { // Fill to 1/4 fillGuage = 1; BT.print("*UFilling to 1/4*");; BT.print("*VFill to 1/2*"); BT.print("*WFill to 3/4*"); BT.print("*XFill to full*"); fillLevel = 0; BT.print("*J0L to fill*"); } if (ByteReceived == 'B') { // Fill to 1/2 fillGuage = 2; BT.print("*VFilling to 1/2*"); BT.print("*UFill to 1/4*"); BT.print("*WFill to 3/4*"); BT.print("*XFill to full*"); fillLevel = 0; BT.print("*J0L to fill*"); } if (ByteReceived == 'C') { // Fill to 3/4 fillGuage = 3; BT.print("*WFilling to 3/4*"); BT.print("*UFill to 1/4*"); BT.print("*VFill to 1/2*"); BT.print("*XFill to full*"); fillLevel = 0; BT.print("*J0L to fill*"); } if (ByteReceived == 'D') { // Fill to full fillGuage = 4; BT.print("*XFilling to full*"); BT.print("*UFill to 1/4*"); BT.print("*VFill to 1/2*"); BT.print("*WFill to 3/4*"); fillLevel = 0; BT.print("*J0L to fill*"); } if (ByteReceived == 'G') { // Fill slider char data[5] = {0}; static byte ndx = 0; char endMarker = 'H'; char rc; bool endMessage = false; while (BT.available() > 0 && endMessage == false) { // get the slider value rc = BT.read(); if (rc != endMarker) { data[ndx] = rc; ndx++; } else { data[ndx] = '\0'; // terminate the string ndx = 0; endMessage = true; } } fillLevel = atoi(data); BT.print("*J" + String(data) + "L to fill*"); fillGuage = 0; BT.print("*UFill to 1/4*"); BT.print("*VFill to 1/2*"); BT.print("*WFill to 3/4*"); BT.print("*XFill to full*"); } if (ByteReceived == 'K') waterValve = 0; // Water valve on if (ByteReceived == 'L') waterValve = 1; // water valve off if (ByteReceived == 'N') { // Master off master = 2; fillReset = 1; fillGuage = 0; fillLevel = 0; waterValve = 1; BT.print("*A0*"); BT.print("*B0*"); } if (ByteReceived == 'R') { // Fill reset fillReset = 1; fillGuage = 0; fillLevel = 0; waterValve = 1; BT.print("*UFill to 1/4*"); BT.print("*VFill to 1/2*"); BT.print("*WFill to 3/4*"); BT.print("*XFill to full*"); } if (ByteReceived == 'U') usedReset = 1; // Used reset } } if (nJ1939Status == NORMALDATATRAFFIC) { // Flow Meter and valve control (sending this message triggers the water level message) if ((master > 0) && (millis() > flowPeriod)) { nSrcAddr = j1939.GetSourceAddress(); temp = 1 | (waterValve << 1) | (fillReset << 2) | (usedReset << 3); Flow[0] = temp; // Battery tie, watervalve, ml reset and used ml reset Flow[1] = fillLevel; // fill quantity in L, 0 = fill by guage level Flow[2] = fillGuage; // fill level by guage, 0 = fill by ml, 1 = 1/4, 2 = 1/2, 3 = 3/4, 4 = full j1939.Transmit(3, 65506, nSrcAddr, 132, Flow, 3); // Tx the data to the Battery Arduino 132 fillReset = 0; usedReset = 0; if (master == 2) master = 0; flowPeriod = millis() + 1000; // re-run every second } } if (lPGN == 65505) { // Flow meter byte lpm = pMsg[0]; // lpm flow rate long ml = DataReadLong(1); // ml loaded byte waterValveState = pMsg[5]; // status of the water valve byte fillComplete = pMsg[6]; BT.print("*S" + String(lpm) + "Lpm fill*"); BT.print("*T" + String((ml / 10.0)) + "L filled*"); if (waterValveState == 1) { BT.print("*FR0G0B0*"); // turn light off } else { BT.print("*FR0G255B0*"); // turn light on green } if (fillComplete == 1) { fillGuage = 0; fillLevel = 0; waterValve = 1; BT.print("*P100*"); // sound bleep and vibrate to warn that filling is complete } else { guageReadings(); // if fill is not complete take water readings } } if (lPGN == 65509) { // Flow meter int usedlpm = pMsg[0]; // lpm used flow rate int usedml = DataReadInt(1); // litres to one decimal place used int remainml = DataReadInt(3); // litres to one decimal place used BT.print("*z" + String(usedlpm) + "Lpm use*"); BT.print("*E" + String((usedml / 10.0)) + "L used*"); BT.print("*R" + String((remainml / 10.0)) + "L total*"); } } void guageReadings() { if ((millis() - freshGuageTime) > 5000) { // takes a fresh water reading every 5 sec if (RelayFlag == 0) { digitalWrite(K4, LOW); // turns on fresh water level guage RelayTimer = millis(); RelayFlag = 1; } if (((millis() - RelayTimer) > 1000) && (RelayFlag == 1)) { // 1500ms delay allows the needle to stabalise before taking a reading guageReading[3] = guageReading[0]; guageReading[0] = analogRead(WaterLevel); digitalWrite(K4, HIGH); // turns off fresh water level guage if (TestDataOn == 1) { Serial.println(); Serial.print("Fresh water: analogue water reading = "); Serial.println(guageReading[0]); } if (guageReading[0] <= 65) guageReading[0] = 0; if ((guageReading[0] > 65) && (guageReading[0] <= 195)) guageReading[0] = 1; //130 if ((guageReading[0] > 195) && (guageReading[0] <= 325)) guageReading[0] = 2; //260 if ((guageReading[0] > 325) && (guageReading[0] <= 455)) guageReading[0] = 3; //390 if (guageReading[0] > 455) guageReading[0] = 4; if ((guageReading[0] == guageReading[3]) || (fwFlag == 1)) { // ignores a fluctuating reading guageReading[5] = guageReading[0]; BT.print("*A" + String(guageReading[5]) + "*"); if (TestDataOn == 1) { Serial.print("Fresh water: Gauge = "); Serial.println(guageReading[5]); } fwFlag = 0; } if (guageReading[0] != guageReading[3]) { fwFlag = 1; } RelayFlag = 0; freshGuageTime = millis(); } } if ((millis() - wasteGuageTime) > 6000) { // takes a waste water reading every 6 sec if (RelayFlag == 0) { digitalWrite(K3, LOW); // turns on waste water level guage RelayTimer = millis(); RelayFlag = 1; } if (((millis() - RelayTimer) > 1500) && (RelayFlag == 1)) { // 1500ms delay allows the needle to stabalise before taking a reading guageReading[4] = guageReading[1]; guageReading[1] = analogRead(WaterLevel); digitalWrite(K3, HIGH); // turns off waste water level guage if (TestDataOn == 1) { Serial.println(); Serial.print("Waste water: analogue water reading = "); Serial.println(guageReading[1]); } if (guageReading[1] <= 65) guageReading[1] = 0; if ((guageReading[1] > 65) && (guageReading[1] <= 195)) guageReading[1] = 1; if ((guageReading[1] > 195) && (guageReading[1] <= 325)) guageReading[1] = 2; if ((guageReading[1] > 325) && (guageReading[1] <= 455)) guageReading[1] = 3; if (guageReading[1] > 455) guageReading[1] = 4; if ((guageReading[1] == guageReading[4]) || (wwFlag == 1)) { guageReading[6] = guageReading[1]; BT.print("*B" + String(guageReading[6]) + "*"); if (TestDataOn == 1) { Serial.print("Waste water: Gauge = "); Serial.println(guageReading[6]); } wwFlag = 0; } if (guageReading[1] != guageReading[4]) { wwFlag = 1; } RelayFlag = 0; wasteGuageTime = millis(); } } if ((nJ1939Status == NORMALDATATRAFFIC) && (RelayFlag == 0)) { // Send out a message nSrcAddr = j1939.GetSourceAddress(); guageSend[0] = guageReading[5]; guageSend[1] = guageReading[6]; j1939.Transmit(3, 65507, nSrcAddr, 0xFF, guageSend, 2); // Water Level messages } } void PWM_calculations() { // analgue set to 12 bit = 4095 - 2056 center bias, 81.8999 A/D units per amp PWM_I = (current0 - 2056) / 0.0818999; // mA PWM output current (514 center bias value) 66 mV per Amp = 13.5168 A/D units per amp if (PWM_I < 75) PWM_I = 0; // ignores values below 75 mA < 1W if ((bmv_value[BV] < 9) && (mppt_V < 9)) { // error trap if the bmv_V and mppt_V feeds are lost PWM_PPV = (PWM_I * 13000) / 1000000; // W power calculated using 13V } if ((bmv_value[BV] < 9) && (mppt_V > 9)) { PWM_PPV = (PWM_I * mppt_V) / 1000000; // W power calculated using mppt_V } else { PWM_PPV = (PWM_I * bmv_value[BV]) / 1000000; // W power calculated using bmv_V } if (PWM_PPV > PWM_H21) PWM_H21 = PWM_PPV; // updated H21, max PWM W today PWM_Ws = PWM_Ws + PWM_PPV; // Ws yield today, watts per 3 seconds PWM_H20 = PWM_Ws / 12000; // 0.01 kWh yield today (PWM_Ws divided by 1200 to convert from 3 seconds updates to hours and then divided by 10 to convert from Wh to 0.01 kWh) if (old_day < mppt_HSDS) { // end of day if (old_day != 0) { // stops an update on first data capture from MPPT PWM_H22 = PWM_H20; // 0.01 kWh yield yesturday PWM_H23 = PWM_H21; // W max power yesturday PWM_H20 = 0; // reset 0.01 kWh yield today PWM_H21 = 0; // reset W max power today } old_day = mppt_HSDS; } } void BmvRecvWithEndMarker() { static byte ndx = 0; char endMarker = '\n'; char rc; while (BMV.available() > 0 && bmv_new_data == false) { rc = BMV.read(); if (rc != endMarker) { bmv_receivedChars[ndx] = rc; ndx++; if (ndx >= buffsize) { ndx = buffsize - 1; } } else { bmv_receivedChars[ndx] = '\0'; // terminate the string ndx = 0; bmv_new_data = true; } } if (bmv_new_data == true) { //Copy it to the temp array because parseData will alter it. strcpy(bmv_tempChars, bmv_receivedChars); char * strtokLabel; // this is used by strtok() to store the label char * strtokValue; // this is used by strtok() to store the value strtokLabel = strtok(bmv_tempChars, "\t"); // get the first part - the label strtokValue = strtok(NULL, "\r"); // get the second part - the value int start = 0; for (int j = start; (j - start) < bmv_num_keywords; j++) { if (strcmp(strtokLabel, bmv_keywords[j % bmv_num_keywords]) == 0) { // found the label, copy it to the value array bmv_value[j] = atol(strtokValue); start = (j + 1) % bmv_num_keywords; // start searching the next one at this hit +1 break; } } bmv_new_data = false; } } void CANsend() { if (nJ1939Status == NORMALDATATRAFFIC) // Send out messages { // BMV CE, H1, H2, H3 and H6 are negative numbers so * -1 to make positive. I divide by 100 to keep as 2 byte int and 500 amps added to keep positive nSrcAddr = j1939.GetSourceAddress(); if (sequence == 0) { BMVdata[0] = bmv_value[0] & 0xFF; // V BMVdata[1] = (bmv_value[0] >> 8) & 0xFF; BMVdata[2] = bmv_value[1] & 0xFF; // VS BMVdata[3] = (bmv_value[1] >> 8) & 0xFF; BMVdata[4] = ((bmv_value[2] / 100) + 5000) & 0xFF; // I (0.1's A + 500 amps) BMVdata[5] = (((bmv_value[2] / 100) + 5000) >> 8) & 0xFF; BMVdata[6] = bmv_value[4] & 0xFF; // SOC BMVdata[7] = (bmv_value[4] >> 8) & 0xFF; j1939.Transmit(5, 65501, nSrcAddr, 0xFF, BMVdata, 8); BT.print("*a" + String((bmv_value[4] / 10.0)) + "% SOC*"); BT.print("*b" + String((bmv_value[0] / 1000.0)) + "V Leisure*"); BT.print("*c" + String((bmv_value[1] / 1000.0)) + "V Starter*"); BT.print("*d" + String((bmv_value[2] / 1000.0)) + "A Leisure*"); if (BMVcounter == 1) { BMVdata2[0] = ((bmv_value[3] * -1) / 100) & 0xFF; // CE L mAh Consumed Amp Hours BMVdata2[1] = (((bmv_value[3] * -1) / 100) >> 8) & 0xFF; BMVdata2[2] = bmv_value[5] & 0xFF; // TTG BMVdata2[3] = (bmv_value[5] >> 8) & 0xFF; BMVdata2[4] = bmv_value[6]; // Alarm BMVdata2[5] = bmv_value[8]; // Alarm reason BMVdata2[6] = ((bmv_value[10] / 100) * -1) & 0xFF; // L mAh Depth of the last discharge H2 BMVdata2[7] = (((bmv_value[10] / 100) * -1) >> 8) & 0xFF; j1939.Transmit(7, 65518, nSrcAddr, 0xFF, BMVdata2, 8); } if (BMVcounter == 2) { BMVdata4[0] = ((bmv_value[11] / 100) * -1) & 0xFF; // L mAh Depth of the average discharge H3 BMVdata4[1] = (((bmv_value[11] / 100) * -1) >> 8) & 0xFF; BMVdata4[2] = bmv_value[12] & 0xFF; // Number of charge cycles BMVdata4[3] = (bmv_value[12] >> 8) & 0xFF; BMVdata4[4] = bmv_value[13] & 0xFF; // Number of full discharges BMVdata4[5] = (bmv_value[13] >> 8) & 0xFF; j1939.Transmit(7, 65521, nSrcAddr, 0xFF, BMVdata4, 6); } if (BMVcounter == 3) { BMVdata5[0] = (bmv_value[14] * -1) & 0xFF; // L mAh Cumulative Amp Hours drawn BMVdata5[1] = ((bmv_value[14] * -1) >> 8) & 0xFF; BMVdata5[2] = ((bmv_value[14] * -1) >> 16) & 0xFF; BMVdata5[3] = ((bmv_value[14] * -1) >> 24) & 0xFF; BMVdata5[4] = bmv_value[19]; // Number of low main voltage alarms BMVdata5[5] = bmv_value[21]; // Number of low auxiliary voltage alarms BMVdata5[6] = bmv_value[20]; // Number of high main voltage alarms BMVdata5[7] = bmv_value[22]; // Number of high auxiliary voltage alarms j1939.Transmit(7, 65522, nSrcAddr, 0xFF, BMVdata5, 8); } if (BMVcounter == 4) { BMVdata6[0] = (bmv_value[17] / 864) & 0xFF; // Number of days since last full charge to 2 decimal places H9 BMVdata6[1] = ((bmv_value[17] / 864) >> 8) & 0xFF; BMVdata6[2] = bmv_value[15] & 0xFF; // mV Minimum main (battery) voltage BMVdata6[3] = (bmv_value[15] >> 8) & 0xFF; BMVdata6[4] = bmv_value[16] & 0xFF; // mV Maximum main (battery) voltage BMVdata6[5] = (bmv_value[16] >> 8) & 0xFF; j1939.Transmit(7, 65523, nSrcAddr, 0xFF, BMVdata6, 6); } if (BMVcounter == 5) { BMVdata7[0] = bmv_value[23] & 0xFF; // mV Minimum auxiliary (battery) voltage BMVdata7[1] = (bmv_value[23] >> 8) & 0xFF; BMVdata7[2] = bmv_value[24] & 0xFF; // mV Maximum auxiliary (battery) voltage BMVdata7[3] = (bmv_value[24] >> 8) & 0xFF; BMVdata7[4] = bmv_value[18] & 0xFF; // Number of automatic synchronizations BMVdata7[5] = (bmv_value[18] >> 8) & 0xFF; BMVdata7[6] = bmv_value[7]; // Relay j1939.Transmit(7, 65524, nSrcAddr, 0xFF, BMVdata7, 7); BMVcounter = 0; } BMVcounter++; } if (sequence == 1) { PWMdata[0] = (PWM_I & 0xFF); PWMdata[1] = ((PWM_I >> 8) & 0xFF); PWMdata[2] = PWM_PPV; PWMdata[3] = PWM_H20; PWMdata[4] = PWM_H21; PWMdata[5] = PWM_H22; PWMdata[6] = PWM_H23; // 7 bytes j1939.Transmit(5, 65502, nSrcAddr, 0xFF, PWMdata, 7); BT.print("*e" + String(PWM_PPV) + "W PWM*"); BT.print("*f" + String((PWM_I / 1000.0)) + "A PWM*"); } if (sequence == 2) { GASdata[0] = (CoValueHigh & 0xFF); GASdata[1] = ((CoValueHigh >> 8) & 0xFF); GASdata[2] = (CoValueLow & 0xFF); GASdata[3] = ((CoValueLow >> 8) & 0xFF); GASdata[4] = SmokeValue; GASdata[5] = COpercent; GASdata[6] = SmokeAlarm; GASdata[7] = COalarm; j1939.Transmit(2, 65525, nSrcAddr, 0xFF, GASdata, 8); } if ((sequence == 1) && (TestDataOn == 1)) TestData(); // Sends test data to laptop sequence++; if (sequence > 2) sequence = 0; } } int DataReadInt(int address) { //Read the 2 bytes from CAN message. byte two = pMsg[address]; byte one = pMsg[address + 1]; //Return the recomposed int by using bitshift. return ((two << 0) & 0xFF) + ((one << 8) & 0xFFFF); } long DataReadLong(int address) { //Read the 4 bytes from CAN message. byte four = pMsg[address]; byte three = pMsg[address + 1]; byte two = pMsg[address + 2]; byte one = pMsg[address + 3]; //Return the recomposed long by using bitshift. return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF); } void TestData() { /* Serial.print("CAN address = "); Serial.println(nSrcAddr); Serial.print("BMV 65501 0xFFDD = "); for (int i = 0; i < 8; i++) { Serial.print(BMVdata[i]); Serial.print(", "); } Serial.println(); Serial.print("BMV2 65518 0xFFDD = "); for (int i = 0; i < 8; i++) { Serial.print(BMVdata2[i]); Serial.print(", "); } Serial.println(); Serial.print("BMV3 65520 0xFFDD = "); for (int i = 0; i < 8; i++) { Serial.print(BMVdata3[i]); Serial.print(", "); } Serial.println(); Serial.print("BMV4 65521 0xFFDD = "); for (int i = 0; i < 8; i++) { Serial.print(BMVdata4[i]); Serial.print(", "); } Serial.println(); Serial.print("BMV5 65522 0xFFDD = "); for (int i = 0; i < 8; i++) { Serial.print(BMVdata5[i]); Serial.print(", "); } Serial.println(); Serial.print("BMV6 65523 0xFFDD = "); for (int i = 0; i < 8; i++) { Serial.print(BMVdata6[i]); Serial.print(", "); } Serial.println(); Serial.print("BMV7 65524 0xFFDD = "); for (int i = 0; i < 7; i++) { Serial.print(BMVdata7[i]); Serial.print(", "); } Serial.println(); Serial.print("PWM 65502 0xFFDE = "); for (int i = 0; i < 12; i++) { Serial.print(PWMdata[i]); Serial.print(", "); } Serial.println(); Serial.print("Current raw = "); Serial.print(current0); Serial.print(", PWM_I = "); Serial.print(PWM_I); Serial.print("mA, PWM_PPV = "); Serial.print(PWM_PPV); Serial.print("W, PWM_H20 = "); Serial.print(PWM_H20); Serial.print("kWh, PWM_H21 = "); Serial.print(PWM_H21); Serial.print("W, PWM_H22 = "); Serial.print(PWM_H22); Serial.print("kWH, PWM_H23 = "); Serial.print(PWM_H23); Serial.println("W"); Serial.println(); Serial.print("CO heater flag = "); Serial.print(CoHeaterFlag); Serial.print(", CO sensor out put: Heater high = "); Serial.print(CoValueHigh); Serial.print(", Heater low = "); Serial.print(CoValueLow); Serial.print(", Smoke sensor = "); Serial.println(SmokeValue); for (int i = 0; i < bmv_num_keywords; i++) { Serial.print(bmv_keywords[i]); Serial.print(" = "); Serial.print(bmv_value[i]); Serial.print(", "); if (i == 14) Serial.println(); } Serial.println();*/ }
In addition to the above code you will need the CAN libraries:
Cuthbert_Due_ARD1939.h
//downloaded from: http://ard1939.com #ifndef ARD1939_H #define ARD1939_H // Arduino Definitions #define MONITOR_BAUD_RATE 115200 #define CAN_PORT 0 #define CAN_OK 0 #define CAN_ERROR 1 // System Settings #define SYSTEM_TIME 1 // Milliseconds #define TRANSPORT_PROTOCOL 1 #define J1939_MSGLEN 64 // adjusted for Cuthbert net #define MSGFILTERS 40 // adjusted for Cuthbert net #define SA_PREFERRED 130 #define SA_PREFERRED_FUSE 131 #define SA_PREFERRED_BATTERY 132 #define SA_PREFERRED_GSM 133 #define SA_PREFERRED_DISPLAY 134 #define SA_PREFERRED_MONITOR 135 #define SA_PREFERRED_AFAM 136 #define ADDRESSRANGEBOTTOM 128 #define ADDRESSRANGETOP 247 #define GLOBALADDRESS 255 #define NULLADDRESS 254 // NAME Fields Default #define NAME_IDENTITY_NUMBER 0xFFFFFF #define NAME_MANUFACTURER_CODE 0xFFF #define NAME_FUNCTION_INSTANCE 0 #define NAME_ECU_INSTANCE 0x00 #define NAME_ECU_INSTANCE_FUSE 0x01 #define NAME_ECU_INSTANCE_BATTERY 0x02 #define NAME_ECU_INSTANCE_GSM 0x03 #define NAME_ECU_INSTANCE_DISPLAY 0x04 #define NAME_ECU_INSTANCE_MONITOR 0x05 #define NAME_ECU_INSTANCE_AFAM 0x06 #define NAME_FUNCTION 0xFF #define NAME_RESERVED 0 #define NAME_VEHICLE_SYSTEM 0x7F #define NAME_VEHICLE_SYSTEM_INSTANCE 0 #define NAME_INDUSTRY_GROUP 0x00 #define NAME_ARBITRARY_ADDRESS_CAPABLE 0x01 // 1 = address adjustable, 0 = fixed address // Return Codes #define ADDRESSCLAIM_INIT 0 #define ADDRESSCLAIM_INPROGRESS 1 #define ADDRESSCLAIM_FINISHED 2 #define NORMALDATATRAFFIC 2 #define ADDRESSCLAIM_FAILED 3 #define J1939_MSG_NONE 0 #define J1939_MSG_PROTOCOL 1 #define J1939_MSG_NETWORKDATA 2 #define J1939_MSG_APP 3 // Compiler Settings #define OK 0 #define ERR 1 // Debugger Settings #define DEBUG 1 #if DEBUG == 1 #define DEBUG_INIT() char sDebug[128]; #define DEBUG_PRINTHEX(T, v) Serial.print(T); sprintf(sDebug, "%x\n\r", v); Serial.print(sDebug); #define DEBUG_PRINTDEC(T, v) Serial.print(T); sprintf(sDebug, "%d\n\r", v); Serial.print(sDebug); #define DEBUG_PRINTARRAYHEX(T, a, l) Serial.print(T); if(l == 0) Serial.print("Empty.\n\r"); else {for(int x=0; x<l; x++){sprintf(sDebug, "%x ", a[x]); Serial.print(sDebug);} Serial.print("\n\r");} #define DEBUG_PRINTARRAYDEC(T, a, l) Serial.print(T); if(l == 0) Serial.print("Empty.\n\r"); else {for(int x=0; x<l; x++){sprintf(sDebug, "%d ", a[x]); Serial.print(sDebug);} Serial.print("\n\r");} #define DEBUG_HALT() while(Serial.available() == 0); Serial.setTimeout(1); Serial.readBytes(sDebug, 1); #endif struct v35 { int v36; bool v21; bool v37; }; class ARD1939 { public: // Initialization byte Init(int nSystemTime); void SetPreferredAddress(byte nAddr); void SetAddressRange(byte nAddrBottom, byte nAddrTop); void SetNAME(long lIdentityNumber, int nManufacturerCode, byte nFunctionInstance, byte nECUInstance, byte nFunction, byte nVehicleSystem, byte nVehicleSystemInstance, byte nIndustryGroup, byte nArbitraryAddressCapable); byte SetMessageFilter(long lPGN); // Read/Write - Check Status byte Operate(byte* nMsgId, long* lPGN, byte* pMsg, int* nMsgLen, byte* nDestAddr, byte* nSrcAddr, byte* nPriority); byte Transmit(byte nPriority, long lPGN, byte nSourceAddress, byte nDestAddress, byte* pData, int nDataLen); // Other Application Functions void Terminate(void); byte GetSourceAddress(void); void DeleteMessageFilter(long lPGN); private: byte f01(byte, byte*); bool f02(void); byte f03(byte*, byte*); byte f04(long*, byte*, int*, byte*, byte*, byte*); void f05(void); void f06(struct v35*); bool f07(long*, byte*); bool f08(long); bool f09(long); #if TRANSPORT_PROTOCOL == 1 byte f10(byte, long, byte, byte, byte*, int); void f11(byte); void f12(byte); byte f13(long, byte*, int, byte, byte, byte); #endif }; // end class ARD1939 #endif
Cuthbert_Due_can.cpp
// ------------------------------------------------------------------------ // J1939 CAN Connection // ------------------------------------------------------------------------ #include <inttypes.h> #include "Cuthbert_Due_due_can.h" #include "Cuthbert_Due_ARD1939.h" // ------------------------------------------------------------------------ // CAN message ring buffer setup // ------------------------------------------------------------------------ #define CANMSGBUFFERSIZE 10 struct CANMsg { long lID; byte pData[8]; int nDataLen; }; CANMsg CANMsgBuffer[CANMSGBUFFERSIZE]; int nWritePointer; int nReadPointer; #define ENABLE_PIN_CAN0 67 // changed from 62 to 67 for AFAM compatability, hardware modified too. #define ENABLE_PIN_CAN1 66 // changed from 65 to 66 for AFAM compatability, hardware modified too. // ------------------------------------------------------------------------ // Initialize the CAN controller // ------------------------------------------------------------------------ byte canInit(void) { // Default settings nReadPointer = 0; nWritePointer = 0; byte cRetCode = CAN_ERROR; // Initialize the desired CAN port #if CAN_PORT == 0 if(Can0.begin(CAN_BPS_250K, ENABLE_PIN_CAN0) == 1) cRetCode = CAN_OK; #endif #if CAN_PORT == 1 if(Can1.begin(CAN_BPS_250K, ENABLE_PIN_CAN1) == 1) cRetCode = CAN_OK; #endif // Allow all filters after Init was successful if(cRetCode == CAN_OK) { #if CAN_PORT == 0 Can0.watchFor(); #endif #if CAN_PORT == 1 Can1.watchFor(); #endif }// end if return cRetCode; }// end canInitialize // ------------------------------------------------------------------------ // Check CAN controller for error // ------------------------------------------------------------------------ byte canCheckError(void) { if(Can0.check_error() == 0) return CAN_OK; else return CAN_ERROR; }// end canCheckError // ------------------------------------------------------------------------ // Transmit CAN message // ------------------------------------------------------------------------ byte canTransmit(long lID, unsigned char* pData, int nDataLen) { // Declarations byte cRetCode = CAN_ERROR; CAN_FRAME frameCANMsg; // Port the message frameCANMsg.id = lID; frameCANMsg.extended = true; frameCANMsg.length = nDataLen; // Copy the data, making sure there are 8 data bytes total byte cDataCopy[8]; for(byte cIndex = 0; cIndex < 8; cIndex++) { if(cIndex < nDataLen) cDataCopy[cIndex] = pData[cIndex]; else cDataCopy[cIndex] = 0x00; }// end for frameCANMsg.data.low = (long)cDataCopy[0] + ((long)cDataCopy[1] << 8) + ((long)cDataCopy[2] << 16) + ((long)cDataCopy[3] << 24); frameCANMsg.data.high = (long)cDataCopy[4] + ((long)cDataCopy[5] << 8) + ((long)cDataCopy[6] << 16) + ((long)cDataCopy[7] << 24); // Send the message #if CAN_PORT == 0 cRetCode = Can0.sendFrame(frameCANMsg); #endif #if CAN_PORT == 1 cRetCode = Can1.sendFrame(frameCANMsg); #endif return cRetCode; }// end canTransmit // ------------------------------------------------------------------------ // Receive CAN message // ------------------------------------------------------------------------ byte canReceive(long* lID, unsigned char* pData, int* nDataLen) { // Declarations byte cLen = 0; CAN_FRAME frameIncoming; // Check the desired port #if CAN_PORT == 0 cLen = Can0.available(); if(cLen > 0) Can0.read(frameIncoming); #endif #if CAN_PORT == 1 cLen = Can1.available(); if(cLen > 0) Can1.read(frameIncoming); #endif if(cLen > 0 && frameIncoming.extended == true) { // Copy the data CANMsgBuffer[nWritePointer].pData[0] = (byte)(frameIncoming.data.low & 0x000000FF); CANMsgBuffer[nWritePointer].pData[1] = (byte)((frameIncoming.data.low & 0x0000FF00) >> 8); CANMsgBuffer[nWritePointer].pData[2] = (byte)((frameIncoming.data.low & 0x00FF0000) >> 16); CANMsgBuffer[nWritePointer].pData[3] = (byte)((frameIncoming.data.low & 0xFF000000) >> 24); CANMsgBuffer[nWritePointer].pData[4] = (byte)(frameIncoming.data.high & 0x000000FF); CANMsgBuffer[nWritePointer].pData[5] = (byte)((frameIncoming.data.high & 0x0000FF00) >> 8); CANMsgBuffer[nWritePointer].pData[6] = (byte)((frameIncoming.data.high & 0x00FF0000) >> 16); CANMsgBuffer[nWritePointer].pData[7] = (byte)((frameIncoming.data.high & 0xFF000000) >> 24); // Read the message buffer CANMsgBuffer[nWritePointer].nDataLen = (int)frameIncoming.length; CANMsgBuffer[nWritePointer].lID = frameIncoming.id; if(++nWritePointer == CANMSGBUFFERSIZE) nWritePointer = 0; }// end if // Check ring buffer for a message if(nReadPointer != nWritePointer) { // Read the next message buffer entry *nDataLen = CANMsgBuffer[nReadPointer].nDataLen; *lID = CANMsgBuffer[nReadPointer].lID; for(int nIdx = 0; nIdx < *nDataLen; nIdx++) pData[nIdx] = CANMsgBuffer[nReadPointer].pData[nIdx]; if(++nReadPointer == CANMSGBUFFERSIZE) nReadPointer = 0; return CAN_OK; } else return CAN_ERROR; }// end canReceive
Cuthbert_Due_due_can.cpp
/* Copyright (c) 2013 Arduino. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "Cuthbert_Due_due_can.h" /** * \brief constructor for the class * * \param pCan Which canbus hardware to use (CAN0 or CAN1) * \param Rs pin to use for transceiver Rs control * \param En pin to use for transceiver enable */ CANRaw::CANRaw(Can* pCan, uint32_t En ) { m_pCan = pCan; enablePin = En; bigEndian = false; } /** * \brief Configure CAN baudrate. * * \param ul_baudrate Baudrate value (kB/s), allowed values: * 1000, 800, 500, 250, 125, 50, 25, 10, 5. * * \retval Set the baudrate successfully or not. */ uint32_t CANRaw::set_baudrate(uint32_t ul_baudrate) { uint8_t uc_tq; uint8_t uc_prescale; uint32_t ul_mod; uint32_t ul_cur_mod; can_bit_timing_t *p_bit_time; static uint32_t ul_mck = SystemCoreClock; /* Check whether the baudrate prescale will be greater than the max divide value. */ if (((ul_mck + (ul_baudrate * CAN_MAX_TQ_NUM - 1)) / (ul_baudrate * CAN_MAX_TQ_NUM)) > CAN_BAUDRATE_MAX_DIV) { return 0; } /* Check whether the input MCK is too small. */ if (ul_mck < ul_baudrate * CAN_MIN_TQ_NUM) { return 0; } /* Initialize it as the minimum Time Quantum. */ uc_tq = CAN_MIN_TQ_NUM; /* Initialize the remainder as the max value. When the remainder is 0, get the right TQ number. */ ul_mod = 0xffffffff; /* Find out the approximate Time Quantum according to the baudrate. */ for (uint8_t i = CAN_MIN_TQ_NUM; i <= CAN_MAX_TQ_NUM; i++) { if ((ul_mck / (ul_baudrate * i)) <= CAN_BAUDRATE_MAX_DIV) { ul_cur_mod = ul_mck % (ul_baudrate * i); if (ul_cur_mod < ul_mod){ ul_mod = ul_cur_mod; uc_tq = i; if (!ul_mod) { break; } } } } /* Calculate the baudrate prescale value. */ uc_prescale = ul_mck / (ul_baudrate * uc_tq); /* Get the right CAN BIT Timing group. */ p_bit_time = (can_bit_timing_t *)&can_bit_time[uc_tq - CAN_MIN_TQ_NUM]; /* Before modifying the CANBR register, disable the CAN controller. */ //can_disable(m_pCan); m_pCan->CAN_MR &= ~CAN_MR_CANEN; /* Write into the CAN baudrate register. */ m_pCan->CAN_BR = CAN_BR_PHASE2(p_bit_time->uc_phase2 - 1) | CAN_BR_PHASE1(p_bit_time->uc_phase1 - 1) | CAN_BR_PROPAG(p_bit_time->uc_prog - 1) | CAN_BR_SJW(p_bit_time->uc_sjw - 1) | CAN_BR_BRP(uc_prescale - 1); return 1; } uint32_t CANRaw::begin() { return init(CAN_DEFAULT_BAUD); } uint32_t CANRaw::begin(uint32_t baudrate) { return init(baudrate); } uint32_t CANRaw::begin(uint32_t baudrate, uint8_t enablePin) { this->enablePin = enablePin; return init(baudrate); } /** * \brief Initialize CAN controller. * * \param ul_mck CAN module input clock. * \param ul_baudrate CAN communication baudrate in kbs. * * \retval 0 If failed to initialize the CAN module; otherwise successful. * * \note PMC clock for CAN peripheral should be enabled before calling this function. */ uint32_t CANRaw::init(uint32_t ul_baudrate) { uint32_t ul_flag; uint32_t ul_tick; //initialize all function pointers to null for (int i = 0; i < 9; i++) cbCANFrame[i] = 0; //arduino 1.5.2 doesn't init canbus so make sure to do it here. #ifdef ARDUINO152 PIO_Configure(PIOA,PIO_PERIPH_A, PIO_PA1A_CANRX0|PIO_PA0A_CANTX0, PIO_DEFAULT); PIO_Configure(PIOB,PIO_PERIPH_A, PIO_PB15A_CANRX1|PIO_PB14A_CANTX1, PIO_DEFAULT); #endif if (m_pCan == CAN0) pmc_enable_periph_clk(ID_CAN0); if (m_pCan == CAN1) pmc_enable_periph_clk(ID_CAN1); if (enablePin != 255) { pinMode(enablePin, OUTPUT); digitalWrite(enablePin, HIGH); } /* Initialize the baudrate for CAN module. */ ul_flag = set_baudrate(ul_baudrate); if (ul_flag == 0) { return 0; } /* Reset the CAN eight message mailbox. */ reset_all_mailbox(); //Also disable all interrupts by default disable_interrupt(CAN_DISABLE_ALL_INTERRUPT_MASK); //By default use one mailbox for TX setNumTXBoxes(1); /* Enable the CAN controller. */ enable(); /* Wait until the CAN is synchronized with the bus activity. */ ul_flag = 0; ul_tick = 0; while (!(ul_flag & CAN_SR_WAKEUP) && (ul_tick < CAN_TIMEOUT)) { ul_flag = m_pCan->CAN_SR; ul_tick++; } NVIC_SetPriority(m_pCan == CAN0 ? CAN0_IRQn : CAN1_IRQn, 12); //set a fairly low priority so almost anything can preempt NVIC_EnableIRQ(m_pCan == CAN0 ? CAN0_IRQn : CAN1_IRQn); //tell the nested interrupt controller to turn on our interrupt /* Timeout or the CAN module has been synchronized with the bus. */ if (CAN_TIMEOUT == ul_tick) { return 0; } else { return 1; } } /* \brief Initializes mailboxes to the requested mix of RX and TX boxes * * \param txboxes How many of the 8 boxes should be used for TX * */ void CANRaw::setNumTXBoxes(int txboxes) { int c; if (txboxes > 8) txboxes = 8; if (txboxes < 0) txboxes = 0; numTXBoxes = txboxes; //Inialize RX boxen for (c = 0; c < 8 - numTXBoxes; c++) { mailbox_set_mode(c, CAN_MB_RX_MODE); mailbox_set_id(c, 0x0, false); mailbox_set_accept_mask(c, 0x7FF, false); } //Initialize TX boxen for (c = 8 - numTXBoxes; c < 8; c++) { mailbox_set_mode(c, CAN_MB_TX_MODE); mailbox_set_priority(c, 10); mailbox_set_accept_mask(c, 0x7FF, false); } } /** * \brief Set up a callback function for given mailbox * * \param mailbox Which mailbox (0-7) to assign callback to. * \param cb A function pointer to a function with prototype "void functionname(CAN_FRAME *frame);" * */ void CANRaw::setCallback(int mailbox, void (*cb)(CAN_FRAME *)) { if ((mailbox < 0) || (mailbox > 7)) return; cbCANFrame[mailbox] = cb; } /** * \brief Set up a general callback that will be used if no callback was registered for receiving mailbox * * \param cb A function pointer to a function with prototype "void functionname(CAN_FRAME *frame);" * * \note If this function is used to set up a callback then no buffering of frames will ever take place. */ void CANRaw::setGeneralCallback(void (*cb)(CAN_FRAME *)) { cbCANFrame[8] = cb; } void CANRaw::attachCANInterrupt(void (*cb)(CAN_FRAME *)) { setGeneralCallback(cb); } void CANRaw::attachCANInterrupt(uint8_t mailBox, void (*cb)(CAN_FRAME *)) { setCallback(mailBox, cb); } void CANRaw::detachCANInterrupt(uint8_t mailBox) { if ((mailBox < 0) || (mailBox > 7)) return; cbCANFrame[mailBox] = 0; } /** * \brief Enable CAN Controller. * */ void CANRaw::enable() { m_pCan->CAN_MR |= CAN_MR_CANEN; if (enablePin != 255) digitalWrite(enablePin, HIGH); } /** * \brief Disable CAN Controller. * */ void CANRaw::disable() { m_pCan->CAN_MR &= ~CAN_MR_CANEN; if (enablePin != 255) digitalWrite(enablePin, LOW); } /** * \brief Disable CAN Controller low power mode. * */ void CANRaw::disable_low_power_mode() { m_pCan->CAN_MR &= ~CAN_MR_LPM; } /** * \brief Enable CAN Controller low power mode. * */ void CANRaw::enable_low_power_mode() { m_pCan->CAN_MR |= CAN_MR_LPM; } /** * \brief Disable CAN Controller autobaud/listen mode. * */ void CANRaw::disable_autobaud_listen_mode() { m_pCan->CAN_MR &= ~CAN_MR_ABM; } /** * \brief Enable CAN Controller autobaud/listen mode. * */ void CANRaw::enable_autobaud_listen_mode() { m_pCan->CAN_MR |= CAN_MR_ABM; } /** * \brief CAN Controller won't generate overload frame. * */ void CANRaw::disable_overload_frame() { m_pCan->CAN_MR &= ~CAN_MR_OVL; } /** * \brief CAN Controller will generate an overload frame after each successful * reception for mailboxes configured in Receive mode, Producer and Consumer. * */ void CANRaw::enable_overload_frame() { m_pCan->CAN_MR |= CAN_MR_OVL; } /** * \brief Configure the timestamp capture point, at the start or the end of frame. * * \param m_pCan Pointer to a CAN peripheral instance. * \param ul_flag 0: Timestamp is captured at each start of frame; * 1: Timestamp is captured at each end of frame. */ void CANRaw::set_timestamp_capture_point(uint32_t ul_flag) { if (ul_flag) { m_pCan->CAN_MR |= CAN_MR_TEOF; } else { m_pCan->CAN_MR &= ~CAN_MR_TEOF; } } /** * \brief Disable CAN Controller time triggered mode. * */ void CANRaw::disable_time_triggered_mode() { m_pCan->CAN_MR &= ~CAN_MR_TTM; } /** * \brief Enable CAN Controller time triggered mode. * */ void CANRaw::enable_time_triggered_mode() { m_pCan->CAN_MR |= CAN_MR_TTM; } /** * \brief Disable CAN Controller timer freeze. * */ void CANRaw::disable_timer_freeze() { m_pCan->CAN_MR &= ~CAN_MR_TIMFRZ; } /** * \brief Enable CAN Controller timer freeze. * */ void CANRaw::enable_timer_freeze() { m_pCan->CAN_MR |= CAN_MR_TIMFRZ; } /** * \brief Disable CAN Controller transmit repeat function. * */ void CANRaw::disable_tx_repeat() { m_pCan->CAN_MR |= CAN_MR_DRPT; } /** * \brief Enable CAN Controller transmit repeat function. * */ void CANRaw::enable_tx_repeat() { m_pCan->CAN_MR &= ~CAN_MR_DRPT; } /** * \brief Configure CAN Controller reception synchronization stage. * * \param ul_stage The reception stage to be configured. * * \note This is just for debug purpose only. */ void CANRaw::set_rx_sync_stage(uint32_t ul_stage) { m_pCan->CAN_MR = (m_pCan->CAN_MR & ~CAN_MR_RXSYNC_Msk) | ul_stage; } /** * \brief Enable CAN interrupt. * * \param dw_mask Interrupt to be enabled. */ void CANRaw::enable_interrupt(uint32_t dw_mask) { m_pCan->CAN_IER = dw_mask; } /** * \brief Disable CAN interrupt. * * \param dw_mask Interrupt to be disabled. */ void CANRaw::disable_interrupt(uint32_t dw_mask) { m_pCan->CAN_IDR = dw_mask; } /** * \brief Get CAN Interrupt Mask. * * * \retval CAN interrupt mask. */ uint32_t CANRaw::get_interrupt_mask() { return (m_pCan->CAN_IMR); } /** * \brief Get CAN status. * * * \retval CAN status. */ uint32_t CANRaw::get_status() { return (m_pCan->CAN_SR); } uint8_t CANRaw::check_error(void) { if((m_pCan->CAN_SR & 0b00000000110000101100001011000010)==0) return 1; else return 0; } /** * \brief Get the 16-bit free-running internal timer count. * * * \retval The internal CAN free-running timer counter. */ uint32_t CANRaw::get_internal_timer_value() { return (m_pCan->CAN_TIM); } /** * \brief Get CAN timestamp register value. * * * \retval The timestamp value. */ uint32_t CANRaw::get_timestamp_value() { return (m_pCan->CAN_TIMESTP); } /** * \brief Get CAN transmit error counter. * * * \retval Transmit error counter. */ uint8_t CANRaw::get_tx_error_cnt() { return (uint8_t) (m_pCan->CAN_ECR >> CAN_ECR_TEC_Pos); } /** * \brief Get CAN receive error counter. * * * \retval Receive error counter. */ uint8_t CANRaw::get_rx_error_cnt() { return (uint8_t) (m_pCan->CAN_ECR >> CAN_ECR_REC_Pos); } /** * \brief Reset the internal free-running 16-bit timer. * * * \note If the internal timer counter is frozen, this function automatically * re-enables it. */ void CANRaw::reset_internal_timer() { m_pCan->CAN_TCR |= CAN_TCR_TIMRST; } /** * \brief Send global transfer request. * * \param uc_mask Mask for mailboxes that are requested to transfer. */ void CANRaw::global_send_transfer_cmd(uint8_t uc_mask) { uint32_t ul_reg; ul_reg = m_pCan->CAN_TCR & ((uint32_t)~GLOBAL_MAILBOX_MASK); m_pCan->CAN_TCR = ul_reg | uc_mask; } /** * \brief Send global abort request. * * \param uc_mask Mask for mailboxes that are requested to abort. */ void CANRaw::global_send_abort_cmd(uint8_t uc_mask) { uint32_t ul_reg; ul_reg = m_pCan->CAN_ACR & ((uint32_t)~GLOBAL_MAILBOX_MASK); m_pCan->CAN_ACR = ul_reg | uc_mask; } /** * \brief Configure the timemark for the mailbox. * * \param uc_index Indicate which mailbox is to be configured. * \param us_cnt The timemark to be set. * * \note The timemark is active in Time Triggered mode only. */ void CANRaw::mailbox_set_timemark(uint8_t uc_index, uint16_t us_cnt) { uint32_t ul_reg; if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; ul_reg = m_pCan->CAN_MB[uc_index].CAN_MMR & ((uint32_t)~TIMEMARK_MASK); m_pCan->CAN_MB[uc_index].CAN_MMR = ul_reg | us_cnt; } /** * \brief Get status of the mailbox. * * \param uc_index Indicate which mailbox is to be read. * * \retval The mailbox status. */ uint32_t CANRaw::mailbox_get_status(uint8_t uc_index) { if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; return (m_pCan->CAN_MB[uc_index].CAN_MSR); } /** * \brief Send single mailbox transfer request. * * \param uc_index Indicate which mailbox is to be configured. */ void CANRaw::mailbox_send_transfer_cmd(uint8_t uc_index) { if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; m_pCan->CAN_MB[uc_index].CAN_MCR |= CAN_MCR_MTCR; } /** * \brief Send single mailbox abort request. * * \param uc_index Indicate which mailbox is to be configured. */ void CANRaw::mailbox_send_abort_cmd(uint8_t uc_index) { if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; m_pCan->CAN_MB[uc_index].CAN_MCR |= CAN_MCR_MACR; } /** * \brief Initialize the mailbox to a default, known state. * * \param p_mailbox Pointer to a CAN mailbox instance. */ void CANRaw::mailbox_init(uint8_t uc_index) { if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; m_pCan->CAN_MB[uc_index].CAN_MMR = 0; m_pCan->CAN_MB[uc_index].CAN_MAM = 0; m_pCan->CAN_MB[uc_index].CAN_MID = 0; m_pCan->CAN_MB[uc_index].CAN_MDL = 0; m_pCan->CAN_MB[uc_index].CAN_MDH = 0; m_pCan->CAN_MB[uc_index].CAN_MCR = 0; } /** * \brief Reset the eight mailboxes. * * \param m_pCan Pointer to a CAN peripheral instance. */ void CANRaw::reset_all_mailbox() { for (uint8_t i = 0; i < CANMB_NUMBER; i++) { mailbox_init(i); } } void CANRaw::setBigEndian(bool end) { bigEndian = end; } void CANRaw::setWriteID(uint32_t id) { write_id = id; } template <typename t> void CANRaw::write(t inputValue) { CAN_FRAME tempFrame; uint8_t *buff = (uint8_t *)inputValue; int thisSize = sizeof(t); if (thisSize > 8) thisSize = 8; if (!bigEndian) { for (int i = 0; i < thisSize; i++) { tempFrame.data.bytes[i] = buff[i]; } } else //reverse byte order. The M3 is in little endian so this causes big endian order { for (int i = 0; i < thisSize; i++) { tempFrame.data.bytes[i] = buff[thisSize - i - 1]; } } tempFrame.id = this->write_id; tempFrame.length = thisSize; if (this->write_id > 0x7FF) tempFrame.extended = true; else tempFrame.extended = false; sendFrame(tempFrame); } /** * \brief Send a frame out of this canbus port * * \param txFrame The filled out frame structure to use for sending * * \note Will do one of two things - 1. Send the given frame out of the first available mailbox * or 2. queue the frame for sending later via interrupt. Automatically turns on TX interrupt * if necessary. * * Returns whether sending/queueing succeeded. Will not smash the queue if it gets full. */ bool CANRaw::sendFrame(CAN_FRAME& txFrame) { for (int i = 0; i < 8; i++) { if (((m_pCan->CAN_MB[i].CAN_MMR >> 24) & 7) == CAN_MB_TX_MODE) {//is this mailbox set up as a TX box? if (m_pCan->CAN_MB[i].CAN_MSR & CAN_MSR_MRDY) {//is it also available (not sending anything?) mailbox_set_id(i, txFrame.id, txFrame.extended); mailbox_set_datalen(i, txFrame.length); mailbox_set_priority(i, txFrame.priority); for (uint8_t cnt = 0; cnt < 8; cnt++) { mailbox_set_databyte(i, cnt, txFrame.data.bytes[cnt]); } enable_interrupt(0x01u << i); //enable the TX interrupt for this box global_send_transfer_cmd((0x1u << i)); return true; //we've sent it. mission accomplished. } } } //if execution got to this point then no free mailbox was found above //so, queue the frame if possible. But, don't increment the //tail if it would smash into the head and kill the queue. uint8_t temp; temp = (tx_buffer_tail + 1) % SIZE_TX_BUFFER; if (temp == tx_buffer_head) return false; tx_frame_buff[tx_buffer_tail].id = txFrame.id; tx_frame_buff[tx_buffer_tail].extended = txFrame.extended; tx_frame_buff[tx_buffer_tail].length = txFrame.length; tx_frame_buff[tx_buffer_tail].data.value = txFrame.data.value; tx_buffer_tail = temp; return true; } /** * \brief Read a frame from out of the mailbox and into a software buffer * * \param uc_index which mailbox to read * \param rxframe Pointer to a receive frame structure which we'll fill out * * \retval Different CAN mailbox transfer status. * */ uint32_t CANRaw::mailbox_read(uint8_t uc_index, volatile CAN_FRAME *rxframe) { uint32_t ul_status; uint32_t ul_retval; uint32_t ul_id; uint32_t ul_datal, ul_datah; if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; ul_retval = 0; ul_status = m_pCan->CAN_MB[uc_index].CAN_MSR; /* Check whether there is overwriting happening in Receive with Overwrite mode, or there're messages lost in Receive mode. */ if ((ul_status & CAN_MSR_MRDY) && (ul_status & CAN_MSR_MMI)) { ul_retval = CAN_MAILBOX_RX_OVER; } ul_id = m_pCan->CAN_MB[uc_index].CAN_MID; if ((ul_id & CAN_MID_MIDE) == CAN_MID_MIDE) { //extended id rxframe->id = ul_id & 0x1FFFFFFFu; rxframe->extended = true; } else { //standard ID rxframe->id = (ul_id >> CAN_MID_MIDvA_Pos) & 0x7ffu; rxframe->extended = false; } rxframe->fid = m_pCan->CAN_MB[uc_index].CAN_MFID; rxframe->length = (ul_status & CAN_MSR_MDLC_Msk) >> CAN_MSR_MDLC_Pos; ul_datal = m_pCan->CAN_MB[uc_index].CAN_MDL; ul_datah = m_pCan->CAN_MB[uc_index].CAN_MDH; rxframe->data.high = ul_datah; rxframe->data.low = ul_datal; /* Read the mailbox status again to check whether the software needs to re-read mailbox data register. */ ul_status = m_pCan->CAN_MB[uc_index].CAN_MSR; if (ul_status & CAN_MSR_MMI) { ul_retval |= CAN_MAILBOX_RX_NEED_RD_AGAIN; } else { ul_retval |= CAN_MAILBOX_TRANSFER_OK; } /* Enable next receive process. */ mailbox_send_transfer_cmd(uc_index); return ul_retval; } /** * \brief Sets the ID portion of the given mailbox * * \param uc_index The mailbox to set (0-7) * \param id The ID to set (11 or 29 bit) * \param extended Boolean indicating if this ID should be designated as extended * */ void CANRaw::mailbox_set_id(uint8_t uc_index, uint32_t id, bool extended) { if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; if (extended) { m_pCan->CAN_MB[uc_index].CAN_MID = id | CAN_MID_MIDE; } else { m_pCan->CAN_MB[uc_index].CAN_MID = CAN_MID_MIDvA(id); } } /** * \brief Get ID currently associated with a given mailbox * * \param uc_index The mailbox to get the ID from (0-7) * * \retval The ID associated with the mailbox * */ uint32_t CANRaw::mailbox_get_id(uint8_t uc_index) { if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; if (m_pCan->CAN_MB[uc_index].CAN_MID & CAN_MID_MIDE) { return m_pCan->CAN_MB[uc_index].CAN_MID; } else { return (m_pCan->CAN_MB[uc_index].CAN_MID >> CAN_MID_MIDvA_Pos) & 0x7ffu; } } /** * \brief Set the transmission priority for given mailbox * * \param uc_index The mailbox to use * \param pri The priority to set (0-15 in ascending priority) * */ void CANRaw::mailbox_set_priority(uint8_t uc_index, uint8_t pri) { if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; m_pCan->CAN_MB[uc_index].CAN_MMR = (m_pCan->CAN_MB[uc_index].CAN_MMR & ~CAN_MMR_PRIOR_Msk) | (pri << CAN_MMR_PRIOR_Pos); } /** * \brief Set mask for RX on the given mailbox * * \param uc_index The mailbox to use * \param mask The mask to set * \param ext Whether this should be an extended mask or not * */ void CANRaw::mailbox_set_accept_mask(uint8_t uc_index, uint32_t mask, bool ext) { if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; if (ext) { m_pCan->CAN_MB[uc_index].CAN_MAM = mask | CAN_MAM_MIDE; m_pCan->CAN_MB[uc_index].CAN_MID |= CAN_MAM_MIDE; } else { m_pCan->CAN_MB[uc_index].CAN_MAM = CAN_MAM_MIDvA(mask); m_pCan->CAN_MB[uc_index].CAN_MID &= ~CAN_MAM_MIDE; } } /** * \brief Set the mode of the given mailbox * * \param uc_index Which mailbox to set (0-7) * \param mode The mode to set mailbox to * * \Note Modes: 0 = Disabled, 1 = RX, 2 = RX with overwrite * 3 = TX, 4 = consumer 5 = producer */ void CANRaw::mailbox_set_mode(uint8_t uc_index, uint8_t mode) { if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; if (mode > 5) mode = 0; //set disabled on invalid mode m_pCan->CAN_MB[uc_index].CAN_MMR = (m_pCan->CAN_MB[uc_index].CAN_MMR & ~CAN_MMR_MOT_Msk) | (mode << CAN_MMR_MOT_Pos); } /** * \brief Get current mode of given mailbox * * \param uc_index Which mailbox to retrieve mode from (0-7) * * \retval Mode of mailbox * */ uint8_t CANRaw::mailbox_get_mode(uint8_t uc_index) { if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; return (uint8_t)(m_pCan->CAN_MB[uc_index].CAN_MMR >> CAN_MMR_MOT_Pos) & 0x7; } /** * \brief Set value of one byte of data for mailbox * * \param uc_index Which mailbox (0-7) * \param bytepos Which byte to set (0-7) * \param val The byte value to set * */ void CANRaw::mailbox_set_databyte(uint8_t uc_index, uint8_t bytepos, uint8_t val) { uint8_t shift; //how many bits to shift uint32_t working; //working copy of the relevant data int if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; if (bytepos > 7) bytepos = 7; shift = 8 * (bytepos & 3); //how many bytes to shift up into position if (bytepos < 4) { //low data block working = m_pCan->CAN_MB[uc_index].CAN_MDL & ~(255 << shift); //mask out where we have to be working |= (val << shift); m_pCan->CAN_MB[uc_index].CAN_MDL = working; } else { //high data block working = m_pCan->CAN_MB[uc_index].CAN_MDH & ~(255 << shift); //mask out where we have to be working |= (val << shift); m_pCan->CAN_MB[uc_index].CAN_MDH = working; } } /** * \brief Set the lower 32 bits of the mailbox's data in one shot * * \param uc_index Which mailbox? (0-7) * \param val The 32 bit value to use * */ void CANRaw::mailbox_set_datal(uint8_t uc_index, uint32_t val) { if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; m_pCan->CAN_MB[uc_index].CAN_MDL = val; } /** * \brief Set the upper 32 bits of the mailbox's data in one shot * * \param uc_index Which mailbox? (0-7) * \param val The 32 bit value to use * */ void CANRaw::mailbox_set_datah(uint8_t uc_index, uint32_t val) { if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; m_pCan->CAN_MB[uc_index].CAN_MDH = val; } /** * \brief Set # of data bytes for given mailbox * * \param uc_index Which mailbox? (0-7) * \param dlen The number of data bytes to use (0-8) * */ void CANRaw::mailbox_set_datalen(uint8_t uc_index, uint8_t dlen) { if (uc_index > CANMB_NUMBER-1) uc_index = CANMB_NUMBER-1; if (dlen > 8) dlen = 8; m_pCan->CAN_MB[uc_index].CAN_MCR = (m_pCan->CAN_MB[uc_index].CAN_MCR & ~CAN_MCR_MDLC_Msk) | CAN_MCR_MDLC(dlen); } /** * \brief Command a mailbox to send the frame stored in it * * \param uc_index which mailbox to send frame. Load it up first * * \retval CAN_MAILBOX_NOT_READY: Failed because mailbox isn't ready for transmitting message. * CAN_MAILBOX_TRANSFER_OK: Successfully send out a frame. */ uint32_t CANRaw::mailbox_tx_frame(uint8_t uc_index) { uint32_t ul_status; /* Read the mailbox status firstly to check whether the mailbox is ready or not. */ ul_status = m_pCan->CAN_MB[uc_index].CAN_MSR; if (!(ul_status & CAN_MSR_MRDY)) { return CAN_MAILBOX_NOT_READY; } /* Set the MBx bit in the Transfer Command Register to send out the remote frame. */ global_send_transfer_cmd((1 << uc_index)); return CAN_MAILBOX_TRANSFER_OK; } int CANRaw::available() { int val; if (rx_avail()) { val = rx_buffer_head - rx_buffer_tail; //Now, because this is a cyclic buffer it is possible that the ordering was reversed //So, handle that case if (val < 0) val += SIZE_RX_BUFFER; return val; } else return 0; } /** * \brief Check whether there are received canbus frames in the buffer * * \retval true if there are frames waiting in buffer, otherwise false */ bool CANRaw::rx_avail() { return (rx_buffer_head != rx_buffer_tail)?true:false; } //wrapper for syntactic sugar reasons - could have just been a #define uint32_t CANRaw::read(CAN_FRAME & buffer) { return get_rx_buff(buffer); } /** * \brief Retrieve a frame from the RX buffer * * \param buffer Reference to the frame structure to fill out * * \retval 0 no frames waiting to be received, 1 if a frame was returned */ uint32_t CANRaw::get_rx_buff(CAN_FRAME& buffer) { if (rx_buffer_head == rx_buffer_tail) return 0; buffer.id = rx_frame_buff[rx_buffer_tail].id; buffer.extended = rx_frame_buff[rx_buffer_tail].extended; buffer.length = rx_frame_buff[rx_buffer_tail].length; buffer.data.value = rx_frame_buff[rx_buffer_tail].data.value; rx_buffer_tail = (rx_buffer_tail + 1) % SIZE_RX_BUFFER; return 1; } /** * \brief Handle all interrupt reasons */ void CANRaw::interruptHandler() { uint32_t ul_status = m_pCan->CAN_SR; //get status of interrupts if (ul_status & CAN_SR_MB0) { //mailbox 0 event mailbox_int_handler(0, ul_status); } if (ul_status & CAN_SR_MB1) { //mailbox 1 event mailbox_int_handler(1, ul_status); } if (ul_status & CAN_SR_MB2) { //mailbox 2 event mailbox_int_handler(2, ul_status); } if (ul_status & CAN_SR_MB3) { //mailbox 3 event mailbox_int_handler(3, ul_status); } if (ul_status & CAN_SR_MB4) { //mailbox 4 event mailbox_int_handler(4, ul_status); } if (ul_status & CAN_SR_MB5) { //mailbox 5 event mailbox_int_handler(5, ul_status); } if (ul_status & CAN_SR_MB6) { //mailbox 6 event mailbox_int_handler(6, ul_status); } if (ul_status & CAN_SR_MB7) { //mailbox 7 event mailbox_int_handler(7, ul_status); } if (ul_status & CAN_SR_ERRA) { //error active } if (ul_status & CAN_SR_WARN) { //warning limit } if (ul_status & CAN_SR_ERRP) { //error passive } if (ul_status & CAN_SR_BOFF) { //bus off } if (ul_status & CAN_SR_SLEEP) { //controller in sleep mode } if (ul_status & CAN_SR_WAKEUP) { //controller woke up } if (ul_status & CAN_SR_TOVF) { //timer overflow } if (ul_status & CAN_SR_TSTP) { //timestamp - start or end of frame } if (ul_status & CAN_SR_CERR) { //CRC error in mailbox } if (ul_status & CAN_SR_SERR) { //stuffing error in mailbox } if (ul_status & CAN_SR_AERR) { //ack error } if (ul_status & CAN_SR_FERR) { //form error } if (ul_status & CAN_SR_BERR) { //bit error } } /** * \brief Find unused RX mailbox and return its number */ int CANRaw::findFreeRXMailbox() { for (int c = 0; c < 8; c++) { if (mailbox_get_mode(c) == CAN_MB_RX_MODE) { if (mailbox_get_id(c) == 0) { return c; } } } return -1; } /** * \brief Set up an RX mailbox (first free) for the given parameters. * * \param id - the post mask ID to match against * \param mask - the mask to use for this filter * \param extended - whether to use 29 bit filter * * \ret number of mailbox we just used (or -1 if there are no free boxes to use) */ int CANRaw::setRXFilter(uint32_t id, uint32_t mask, bool extended) { int c = findFreeRXMailbox(); if (c < 0) return -1; mailbox_set_accept_mask(c, mask, extended); mailbox_set_id(c, id, extended); enable_interrupt(getMailboxIer(c)); return c; } /** * \brief Set up an RX mailbox (given MB number) filter * * \param mailbox Which mailbox to use (0-7) * \param id The ID to match against * \param mask The mask to apply before ID matching * \param extended Whether this should be extended mask or not * * \retval Mailbox number if successful or -1 on failure */ int CANRaw::setRXFilter(uint8_t mailbox, uint32_t id, uint32_t mask, bool extended) { if (mailbox > 7) return -1; mailbox_set_accept_mask(mailbox, mask, extended); mailbox_set_id(mailbox, id, extended); enable_interrupt(getMailboxIer(mailbox)); return mailbox; } //set up to allow everything through. Sets up two mailboxes and returns the first one //Not a terribly good idea to call because it totally ignores most everything and just //quickly sets things up for all in. But, it's perfect for the novice getting started. int CANRaw::watchFor() { int retVal = setRXFilter(0, 0, false); enable_interrupt(getMailboxIer(0)); setRXFilter(0, 0, true); enable_interrupt(getMailboxIer(1)); return retVal; } //Let a single frame ID through. Automatic determination of extended. Also automatically sets mask int CANRaw::watchFor(uint32_t id) { if (id > 0x7FF) return setRXFilter(id, 0x1FFFFFFF, true); else return setRXFilter(id, 0x7FF, false); } //Allow a range of IDs through based on mask. Still auto determines extended. int CANRaw::watchFor(uint32_t id, uint32_t mask) { if (id > 0x7FF) return setRXFilter(id, mask, true); else return setRXFilter(id, mask, false); } //A bit more complicated. Makes sure that the range from id1 to id2 is let through. This might open //the floodgates if you aren't careful. int CANRaw::watchForRange(uint32_t id1, uint32_t id2) { unsigned long id = 0; int mask = 0; unsigned long temp; if (id1 > id2) { //looks funny I know. In place swap with no temporary storage. Neato! id1 = id1 ^ id2; id2 = id1 ^ id2; //note difference here. id1 = id1 ^ id2; } id = id1; if (id2 <= 0x7FF) mask = 0x7FF; else mask = 0x1FFFFFFF; /* Here is a quick overview of the theory behind these calculations. We start with mask set to 11 or 29 set bits (all 1's) and id set to the lowest ID in the range. From there we go through every single ID possible in the range. For each ID we AND with the current ID. At the end only bits that never changed and were 1's will still be 1's. This yields the ID we can match against to let these frames through The mask is calculated by finding the bitfield difference between the lowest ID and the current ID. This calculation will be 1 anywhere the bits were different. We invert this so that it is 1 anywhere the bits where the same. Then we AND with the current Mask. At the end the mask will be 1 anywhere the bits never changed. This is the perfect mask. */ for (unsigned long c = id1; c <= id2; c++) { id &= c; temp = (~(id1 ^ c)) & 0x1FFFFFFF; mask &= temp; } //output of the above crazy loop is actually the end result. if (id > 0x7FF) return setRXFilter(id, mask, true); else return setRXFilter(id, mask, false); } /* * Get the IER (interrupt mask) for the specified mailbox index. * * \param mailbox - the index of the mailbox to get the IER for * \retval the IER of the specified mailbox */ uint32_t CANRaw::getMailboxIer(int8_t mailbox) { switch (mailbox) { case 0: return CAN_IER_MB0; case 1: return CAN_IER_MB1; case 2: return CAN_IER_MB2; case 3: return CAN_IER_MB3; case 4: return CAN_IER_MB4; case 5: return CAN_IER_MB5; case 6: return CAN_IER_MB6; case 7: return CAN_IER_MB7; } return 0; } /** * \brief Handle a mailbox interrupt event * \param mb which mailbox generated this event * \param ul_status The status register of the canbus hardware */ void CANRaw::mailbox_int_handler(uint8_t mb, uint32_t ul_status) { CAN_FRAME tempFrame; if (mb > 7) mb = 7; if (m_pCan->CAN_MB[mb].CAN_MSR & CAN_MSR_MRDY) { //mailbox signals it is ready switch(((m_pCan->CAN_MB[mb].CAN_MMR >> 24) & 7)) { //what sort of mailbox is it? case 1: //receive case 2: //receive w/ overwrite case 4: //consumer - technically still a receive buffer mailbox_read(mb, &tempFrame); //First, try to send a callback. If no callback registered then buffer the frame. if (cbCANFrame[mb]) (*cbCANFrame[mb])(&tempFrame); else if (cbCANFrame[8]) (*cbCANFrame[8])(&tempFrame); else { uint8_t temp = (rx_buffer_head + 1) % SIZE_RX_BUFFER; if (temp != rx_buffer_tail) { memcpy((void *)&rx_frame_buff[rx_buffer_head], &tempFrame, sizeof(CAN_FRAME)); rx_buffer_head = temp; } } break; case 3: //transmit if (tx_buffer_head != tx_buffer_tail) { //if there is a frame in the queue to send mailbox_set_id(mb, tx_frame_buff[tx_buffer_head].id, tx_frame_buff[tx_buffer_head].extended); mailbox_set_datalen(mb, tx_frame_buff[tx_buffer_head].length); mailbox_set_priority(mb, tx_frame_buff[tx_buffer_head].priority); for (uint8_t cnt = 0; cnt < 8; cnt++) mailbox_set_databyte(mb, cnt, tx_frame_buff[tx_buffer_head].data.bytes[cnt]); global_send_transfer_cmd((0x1u << mb)); tx_buffer_head = (tx_buffer_head + 1) % SIZE_TX_BUFFER; } else { disable_interrupt(0x01 << mb); } break; case 5: //producer - technically still a transmit buffer break; } } } /** * \brief Interrupt dispatchers - Never directly call these * * \note These two functions needed because interrupt handlers cannot be part of a class */ void CAN0_Handler(void) { Can0.interruptHandler(); } void CAN1_Handler(void) { Can1.interruptHandler(); } /// instantiate the two canbus adapters CANRaw Can0(CAN0, CAN0_EN); CANRaw Can1(CAN1, CAN1_EN);
Cuthbert_Due_due_can.h
/* Copyright (c) 2013 Arduino. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _CAN_LIBRARY_ #define _CAN_LIBRARY_ #include <Arduino.h> //add some extra stuff that is needed for Arduino 1.5.2 #ifndef PINS_CAN0 static const uint8_t CAN1RX = 88; static const uint8_t CAN1TX = 89; // CAN0 #define PINS_CAN0 (90u) // CAN1 #define PINS_CAN1 (91u) #define ARDUINO152 #endif #define CAN Can0 #define CAN2 Can1 #define CAN0_EN 67 // change from 62 to 67 for AFAM, hardware modified to use DAC1 instead of A11 #define CAN1_EN 66 // change from 65 to 66 for AFAM, hardware modified to use DAC0 instead of A8 /** Define the Mailbox mask for eight mailboxes. */ #define GLOBAL_MAILBOX_MASK 0x000000ff /** Disable all interrupt mask */ #define CAN_DISABLE_ALL_INTERRUPT_MASK 0xffffffff /** Define the typical baudrate for CAN communication. */ #ifdef CAN_BPS_500K #undef CAN_BPS_1000K #undef CAN_BPS_800K #undef CAN_BPS_500K #undef CAN_BPS_250K #undef CAN_BPS_125K #undef CAN_BPS_50K #undef CAN_BPS_33333 #undef CAN_BPS_25K #undef CAN_BPS_10K #undef CAN_BPS_5K #endif /** Define the typical baudrate for CAN communication in KHz. */ #define CAN_BPS_1000K 1000000 #define CAN_BPS_800K 800000 #define CAN_BPS_500K 500000 #define CAN_BPS_250K 250000 #define CAN_BPS_125K 125000 #define CAN_BPS_50K 50000 #define CAN_BPS_33333 33333 #define CAN_BPS_25K 25000 #define CAN_BPS_10K 10000 #define CAN_BPS_5K 5000 #define CAN_DEFAULT_BAUD CAN_BPS_250K /** Define the mailbox mode. */ #define CAN_MB_DISABLE_MODE 0 #define CAN_MB_RX_MODE 1 #define CAN_MB_RX_OVER_WR_MODE 2 #define CAN_MB_TX_MODE 3 #define CAN_MB_CONSUMER_MODE 4 #define CAN_MB_PRODUCER_MODE 5 /** Define CAN mailbox transfer status code. */ #define CAN_MAILBOX_TRANSFER_OK 0 //! Read from or write into mailbox successfully. #define CAN_MAILBOX_NOT_READY 0x01 //! Receiver is empty or transmitter is busy. #define CAN_MAILBOX_RX_OVER 0x02 //! Message overwriting happens or there're messages lost in different receive modes. #define CAN_MAILBOX_RX_NEED_RD_AGAIN 0x04 //! Application needs to re-read the data register in Receive with Overwrite mode. #define SIZE_RX_BUFFER 32 //RX incoming ring buffer is this big #define SIZE_TX_BUFFER 16 //TX ring buffer is this big /** Define the timemark mask. */ #define TIMEMARK_MASK 0x0000ffff /* CAN timeout for synchronization. */ #define CAN_TIMEOUT 100000 /** The max value for CAN baudrate prescale. */ #define CAN_BAUDRATE_MAX_DIV 128 /** Define the scope for TQ. */ #define CAN_MIN_TQ_NUM 8 #define CAN_MAX_TQ_NUM 25 /** Define the fixed bit time value. */ #define CAN_BIT_SYNC 1 #define CAN_BIT_IPT 2 typedef struct { uint8_t uc_tq; //! CAN_BIT_SYNC + uc_prog + uc_phase1 + uc_phase2 = uc_tq, 8 <= uc_tq <= 25. uint8_t uc_prog; //! Propagation segment, (3-bits + 1), 1~8; uint8_t uc_phase1; //! Phase segment 1, (3-bits + 1), 1~8; uint8_t uc_phase2; //! Phase segment 2, (3-bits + 1), 1~8, CAN_BIT_IPT <= uc_phase2; uint8_t uc_sjw; //! Resynchronization jump width, (2-bits + 1), min(uc_phase1, 4); uint8_t uc_sp; //! Sample point value, 0~100 in percent. } can_bit_timing_t; /** Values of bit time register for different baudrates, Sample point = ((1 + uc_prog + uc_phase1) / uc_tq) * 100%. */ const can_bit_timing_t can_bit_time[] = { {8, (2 + 1), (1 + 1), (1 + 1), (2 + 1), 75}, {9, (1 + 1), (2 + 1), (2 + 1), (1 + 1), 67}, {10, (2 + 1), (2 + 1), (2 + 1), (2 + 1), 70}, {11, (3 + 1), (2 + 1), (2 + 1), (3 + 1), 72}, {12, (2 + 1), (3 + 1), (3 + 1), (3 + 1), 67}, {13, (3 + 1), (3 + 1), (3 + 1), (3 + 1), 77}, {14, (3 + 1), (3 + 1), (4 + 1), (3 + 1), 64}, {15, (3 + 1), (4 + 1), (4 + 1), (3 + 1), 67}, {16, (4 + 1), (4 + 1), (4 + 1), (3 + 1), 69}, {17, (5 + 1), (4 + 1), (4 + 1), (3 + 1), 71}, {18, (4 + 1), (5 + 1), (5 + 1), (3 + 1), 67}, {19, (5 + 1), (5 + 1), (5 + 1), (3 + 1), 68}, {20, (6 + 1), (5 + 1), (5 + 1), (3 + 1), 70}, {21, (7 + 1), (5 + 1), (5 + 1), (3 + 1), 71}, {22, (6 + 1), (6 + 1), (6 + 1), (3 + 1), 68}, {23, (7 + 1), (7 + 1), (6 + 1), (3 + 1), 70}, {24, (6 + 1), (7 + 1), (7 + 1), (3 + 1), 67}, {25, (7 + 1), (7 + 1), (7 + 1), (3 + 1), 68} }; //This is architecture specific. DO NOT USE THIS UNION ON ANYTHING OTHER THAN THE CORTEX M3 / Arduino Due //UNLESS YOU DOUBLE CHECK THINGS! typedef union { uint64_t value; struct { uint32_t low; uint32_t high; }; struct { uint16_t s0; uint16_t s1; uint16_t s2; uint16_t s3; }; uint8_t bytes[8]; uint8_t byte[8]; //alternate name so you can omit the s if you feel it makes more sense } BytesUnion; typedef struct { uint32_t id; // EID if ide set, SID otherwise uint32_t fid; // family ID uint8_t rtr; // Remote Transmission Request uint8_t priority; // Priority but only important for TX frames and then only for special uses. uint8_t extended; // Extended ID flag uint8_t length; // Number of data bytes BytesUnion data; // 64 bits - lots of ways to access it. } CAN_FRAME; class CANRaw { protected: int numTXBoxes; //There are 8 mailboxes, anything not TX will be set RX private: /* CAN peripheral, set by constructor */ Can* m_pCan ; volatile CAN_FRAME rx_frame_buff[SIZE_RX_BUFFER]; volatile CAN_FRAME tx_frame_buff[SIZE_TX_BUFFER]; volatile uint16_t rx_buffer_head, rx_buffer_tail; volatile uint16_t tx_buffer_head, tx_buffer_tail; void mailbox_int_handler(uint8_t mb, uint32_t ul_status); uint8_t enablePin; uint32_t write_id; //public storage for an id. Will be used by the write function to set which ID to send to. bool bigEndian; void (*cbCANFrame[9])(CAN_FRAME *); //8 mailboxes plus an optional catch all public: // Constructor CANRaw( Can* pCan, uint32_t En); int setRXFilter(uint32_t id, uint32_t mask, bool extended); int setRXFilter(uint8_t mailbox, uint32_t id, uint32_t mask, bool extended); int watchFor(); //allow anything through int watchFor(uint32_t id); //allow just this ID through (automatic determination of extended status) int watchFor(uint32_t id, uint32_t mask); //allow a range of ids through int watchForRange(uint32_t id1, uint32_t id2); //try to allow the range from id1 to id2 - automatically determine base ID and mask void setNumTXBoxes(int txboxes); int findFreeRXMailbox(); uint8_t mailbox_get_mode(uint8_t uc_index); uint32_t mailbox_get_id(uint8_t uc_index); uint32_t getMailboxIer(int8_t mailbox); uint32_t set_baudrate(uint32_t ul_baudrate); uint32_t init(uint32_t ul_baudrate); uint32_t begin(); uint32_t begin(uint32_t baudrate); uint32_t begin(uint32_t baudrate, uint8_t enablePin); void enable(); void disable(); void disable_low_power_mode(); void enable_low_power_mode(); void disable_autobaud_listen_mode(); void enable_autobaud_listen_mode(); void disable_overload_frame(); void enable_overload_frame(); void set_timestamp_capture_point(uint32_t ul_flag); void disable_time_triggered_mode(); void enable_time_triggered_mode(); void disable_timer_freeze(); void enable_timer_freeze(); void disable_tx_repeat(); void enable_tx_repeat(); void set_rx_sync_stage(uint32_t ul_stage); void enable_interrupt(uint32_t dw_mask); void disable_interrupt(uint32_t dw_mask); uint32_t get_interrupt_mask(); uint32_t get_status(); uint8_t check_error(); uint32_t get_internal_timer_value(); uint32_t get_timestamp_value(); uint8_t get_tx_error_cnt(); uint8_t get_rx_error_cnt(); void reset_internal_timer(); void global_send_transfer_cmd(uint8_t uc_mask); void global_send_abort_cmd(uint8_t uc_mask); void mailbox_set_timemark(uint8_t uc_index, uint16_t us_cnt); uint32_t mailbox_get_status(uint8_t uc_index); void mailbox_send_transfer_cmd(uint8_t uc_index); void mailbox_send_abort_cmd(uint8_t uc_index); void mailbox_init(uint8_t uc_index); uint32_t mailbox_read(uint8_t uc_index, volatile CAN_FRAME *rxframe); uint32_t mailbox_tx_frame(uint8_t uc_index); void mailbox_set_id(uint8_t uc_index, uint32_t id, bool extended); void mailbox_set_priority(uint8_t uc_index, uint8_t pri); void mailbox_set_accept_mask(uint8_t uc_index, uint32_t mask, bool ext); void mailbox_set_mode(uint8_t uc_index, uint8_t mode); void mailbox_set_databyte(uint8_t uc_index, uint8_t bytepos, uint8_t val); void mailbox_set_datalen(uint8_t uc_index, uint8_t dlen); void mailbox_set_datal(uint8_t uc_index, uint32_t val); void mailbox_set_datah(uint8_t uc_index, uint32_t val); bool sendFrame(CAN_FRAME& txFrame); void setWriteID(uint32_t id); template <typename t> void write(t inputValue); //write a variable # of bytes out in a frame. Uses id as the ID. void setBigEndian(bool); void setCallback(int mailbox, void (*cb)(CAN_FRAME *)); void setGeneralCallback(void (*cb)(CAN_FRAME *)); //note that these below versions still use mailbox number. There isn't a good way around this. void attachCANInterrupt(void (*cb)(CAN_FRAME *)); //alternative callname for setGeneralCallback void attachCANInterrupt(uint8_t mailBox, void (*cb)(CAN_FRAME *)); void detachCANInterrupt(uint8_t mailBox); void reset_all_mailbox(); void interruptHandler(); bool rx_avail(); int available(); //like rx_avail but returns the number of waiting frames uint32_t get_rx_buff(CAN_FRAME &); uint32_t read(CAN_FRAME &); }; extern CANRaw Can0; extern CANRaw Can1; #endif // _CAN_LIBRARY_
Cuthbert_Due_J1939.cpp
#include <stdlib.h> #include <inttypes.h> #include <SPI.h> #include "Cuthbert_Due_ARD1939.h" #define d49 10 #define d50 8 #define d31 0x00EA00 #define d32 0x00EAFF #define d46 0x00 #define d47 0xEA #define d48 0x00 #define d29 0x00EE00 #define d30 0x00EEFF #define d25 0x00 #define d26 0xEE #define d27 0x00 #define d28 6 #define d33 0x00EE00 #define d34 0x00FED8 #define d35 0x00ECFF #define d36 0x00EC00 #define d37 0xEC #define d38 7 #define d39 0x00EB00 #define d40 7 #define d41 32 #define d42 16 #define d43 17 #define d44 19 #define d45 255 #define d51 255 struct v01 { bool bActive; long v25; }; v01 v02[MSGFILTERS]; unsigned char v03[] = { (byte)(NAME_IDENTITY_NUMBER & 0xFF), (byte)((NAME_IDENTITY_NUMBER >> 8) & 0xFF), (byte)(((NAME_MANUFACTURER_CODE << 5) & 0xFF) | (NAME_IDENTITY_NUMBER >> 16)), (byte)(NAME_MANUFACTURER_CODE >> 3), (byte)((NAME_FUNCTION_INSTANCE << 3) | NAME_ECU_INSTANCE), (byte)(NAME_FUNCTION), (byte)(NAME_VEHICLE_SYSTEM << 1), (byte)((NAME_ARBITRARY_ADDRESS_CAPABLE << 7) | (NAME_INDUSTRY_GROUP << 4) | (NAME_VEHICLE_SYSTEM_INSTANCE)) }; #define v04 4 long v05[] = { d29, d34, d36, d39 }; struct v06 { bool v07; byte v08; bool v09; bool v10; bool v11; byte v12; byte v13; byte v14; bool v15; byte v16; byte v17; }; struct v06 v18; #if TRANSPORT_PROTOCOL == 1 #define d01 0 #define d02 1 #define d03 2 struct v19 { byte v20; bool v21; bool v22; bool v23; bool v24; long v25; byte v26; byte v27; byte v28; byte v29; byte v30[J1939_MSGLEN]; int v31; byte v32; }; v19 v33; v19 v34; #define d04 32 #define d05 16 #define d06 17 #define d07 19 #define d08 255 #define d09 1 #define d10 2 #define d11 3 #define d12 0 #define d13 1 byte v62[] = {0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}; byte v63[8]; #endif struct v35 v38; struct v35 v39; #if TRANSPORT_PROTOCOL == 1 struct v35 v40; struct v35 v41; struct v35 v42; struct v35 v43; struct v35 v44; struct v35 v45; struct v35 v46; struct v35 v47; struct v35 v48; #endif int v49; int v50; #if TRANSPORT_PROTOCOL == 1 int v51; int v52; int v53; int v54; int v55; int v56; int v57; int v58; int v59; #endif #define d14 250 #define d15 100 #if TRANSPORT_PROTOCOL == 1 #define d16 50 #define d17 750 #define d18 50 #define d19 200 #define d20 500 #define d21 750 #define d22 1250 #define d23 1250 #define d24 1050 #endif int v60[] = {8, 9, 10, 12, 15}; byte v61; byte v64; extern byte canInit(void); extern byte canCheckError(void); extern byte canTransmit(long, unsigned char*, int); extern byte canReceive(long*, unsigned char*, int*); byte ARD1939::Init(int v80) { int v65; f06(&v38); f06(&v39); v49 = d14 / v80; v50 = d15 / v80; v61 = 0; v60[0] = (int)8 / v80; v60[1] = (int)9 / v80; v60[2] = (int)10 / v80; v60[3] = (int)12 / v80; v60[4] = (int)15 / v80; v18.v07 = false; v18.v08 = false; v18.v09 = false; v18.v10 = false; v18.v11 = false; v18.v12 = SA_PREFERRED; v18.v13 = NULLADDRESS; v18.v14 = NULLADDRESS; v18.v15 = false; v18.v16 = NULLADDRESS; v18.v17 = NULLADDRESS; for(v65 = 0; v65 < MSGFILTERS; v65++) { v02[v65].bActive = false; v02[v65].v25 = 0; } #if TRANSPORT_PROTOCOL == 1 v51 = d16 / v80; v52 = d17 / v80; v53 = d18 / v80; v56 = d21 / v80; v54 = d19 / v80; v55 = d20 / v80; v56 = d21 / v80; v57 = d22 / v80; v58 = d23 / v80; v59 = d24 / v80; f11(d12); f12(d12); #endif v64 = 0; return canInit(); } void ARD1939::Terminate(void) { Init(SYSTEM_TIME); } byte ARD1939::Operate(byte* v70, long* v25, byte* v71, int* v31, byte* v67, byte* v66, byte* v76) { byte v68; byte v82; int v65; f05(); *v70 = f04(v25, &v71[0], v31, v67, v66, v76); if(*v70 == J1939_MSG_APP) { if(*v67 != v18.v17 && *v67 != GLOBALADDRESS) *v70 = J1939_MSG_NETWORKDATA; } if(v18.v08 == true) { v68 = NORMALDATATRAFFIC; switch(*v25) { case d31: if(*v70 == J1939_MSG_PROTOCOL) { if(v71[0] == d27 && v71[1] == d26 && v71[2] == d25) { if(*v67 == GLOBALADDRESS) { Transmit(d28, d29, v18.v17, GLOBALADDRESS, &v03[0], 8); } else if(*v67 == v18.v17) { Transmit(d28, d29, v18.v17, *v66, &v03[0], 8); } } } break; case d29: if(*v66 == v18.v17) { v82 = f03(&v71[0], &v03[0]); switch(v82) { case 0: #if TRANSPORT_PROTOCOL == 1 f11(d12); f12(d12); #endif Transmit(d28, d29, NULLADDRESS, GLOBALADDRESS, &v03[0], 8); v18.v08 = false; v18.v11 = true; v68 = ADDRESSCLAIM_FAILED; break; case 1: #if TRANSPORT_PROTOCOL == 1 f11(d12); f12(d12); #endif v18.v07 = false; v18.v08 = false; f01(*v66, &v03[0]); break; case 2: Transmit(d28, d29, v18.v17, GLOBALADDRESS, &v03[0], 8); break; } } break; case d34: break; } #if TRANSPORT_PROTOCOL == 1 f13(*v25, v71, *v31, *v67, *v66, *v76); if(*v70 == J1939_MSG_NONE) { if(v33.v32 == true) { *v25 = v33.v25; *v31 = v33.v31; *v67 = v33.v27; *v66 = v33.v26; *v76 = d51; for(v65 = 0; v65 < v33.v31; v65++) v71[v65] = v33.v30[v65]; f11(d12); *v70 = J1939_MSG_APP; } else if(v34.v32 == true) { *v25 = v34.v25; *v31 = v34.v31; *v67 = v34.v27; *v66 = v34.v26; *v76 = d51; for(v65 = 0; v65 < v34.v31; v65++) v71[v65] = v34.v30[v65]; f12(d12); *v70 = J1939_MSG_APP; } } #endif } else if(v18.v11 == true) { v68 = ADDRESSCLAIM_FAILED; switch(*v25) { case d31: if(*v70 == J1939_MSG_PROTOCOL) { if(v71[0] == d27 && v71[1] == d26 && v71[2] == d25) { if(*v67 == GLOBALADDRESS) { v39.v36 = v60[v61++]; v39.v21 = true; if(v61 > 4) v61 = 0; } } } break; case d29: break; case d34: break; } if(v39.v37 == true) { f06(&v39); Transmit(d28, d29, NULLADDRESS, GLOBALADDRESS, &v03[0], 8); } } else { v68 = f01(*v66, &v71[0]); } return v68; } byte ARD1939::f01(byte v16, byte* v91) { byte v68; byte v82; v68 = ADDRESSCLAIM_INPROGRESS; if(v18.v11 == true) v68 = ADDRESSCLAIM_FAILED; else if(v18.v08 == true) v68 = ADDRESSCLAIM_FINISHED; else if(v18.v10 == true) { if(v39.v37 == true) { f06(&v39); Transmit(d28, d29, v18.v16, GLOBALADDRESS, &v03[0], 8); v38.v36 = v49; v38.v21 = true; v18.v10 = false; } } else { if(v18.v07 == false) { if(f02() == true) { Transmit(d28, d29, v18.v16, GLOBALADDRESS, &v03[0], 8); v38.v36 = v49; v38.v21 = true; v18.v07 = true; } else { Transmit(d28, d29, NULLADDRESS, GLOBALADDRESS, &v03[0], 8); v18.v08 = false; v18.v11 = true; v68 = ADDRESSCLAIM_FAILED; } } else { if(v38.v37 == true) { f06(&v38); v18.v17 = v18.v16; v18.v08 = true; v68 = ADDRESSCLAIM_FINISHED; } else { if(canCheckError() == 1) { f06(&v38); if(++v64 == d49) { v18.v08 = false; v18.v11 = true; v68 = ADDRESSCLAIM_FAILED; } else { canInit(); v39.v36 = v60[v61++]; v39.v21 = true; if(v61 > 4) v61 = 0; v18.v10 = true; } } else v64 = 0; if(v16 == v18.v16) { v82 = f03(&v91[0], &v03[0]); switch(v82) { case 0: #if TRANSPORT_PROTOCOL == 1 f11(d12); f12(d12); #endif Transmit(d28, d29, NULLADDRESS, GLOBALADDRESS, &v03[0], 8); v18.v08 = false; v18.v11 = true; v68 = ADDRESSCLAIM_FAILED; break; case 1: #if TRANSPORT_PROTOCOL == 1 f11(d12); f12(d12); #endif if(f02() == true) { Transmit(d28, d29, v18.v16, GLOBALADDRESS, &v03[0], 8); v38.v36 = v49; v38.v21 = true; } else { Transmit(d28, d29, NULLADDRESS, GLOBALADDRESS, &v03[0], 8); v18.v08 = false; v18.v11 = true; v68 = ADDRESSCLAIM_FAILED; } break; case 2: Transmit(d28, d29, v18.v16, GLOBALADDRESS, &v03[0], 8); v38.v36 = v49; v38.v21 = true; break; } } } } } return v68; } bool ARD1939::f02(void) { bool v72; v72 = true; if(v18.v12 == NULLADDRESS) v18.v09 = true; if(v18.v09 == false) { v18.v16 = v18.v12; v18.v09 = true; } else { if(v18.v13 == NULLADDRESS || v18.v14 == NULLADDRESS) { v72 = false; } else { if(v18.v16 == NULLADDRESS || v18.v15 == false) { v18.v16 = v18.v13; v18.v15 = true; } else { if(v18.v16 < v18.v14) v18.v16++; else v72 = false; } if(v18.v16 == v18.v12) { if(v18.v16 < v18.v14) v18.v16++; else v72 = false; } } } return v72; } byte ARD1939::f03(byte* v92, byte* v93) { byte v65; for(v65 = 8; v65 > 0; v65--) { if(v92[v65-1] != v93[v65-1]) { if(v92[v65-1] < v93[v65-1]) return 1; else return 2; } } return 0; } byte ARD1939::f04(long* v25, byte* v71, int* v31, byte* v67, byte* v66, byte* v76) { long v78; long v74; *v25 = 0; *v31 = 0; *v67 = NULLADDRESS; *v66 = NULLADDRESS; *v76 = 0; if(canReceive(&v74, &v71[0], v31) == 0) { v78 = v74 & 0x1C000000; *v76 = (byte)(v78 >> 26); *v25 = v74 & 0x01FFFF00; *v25 = *v25 >> 8; *v66 = (byte)(v74 & 0x000000FF); *v67 = GLOBALADDRESS; if(f08(*v25) == true) { *v67 = (byte)(*v25 & 0xFF); *v25 = *v25 & 0x01FF00; } if(f07(v25, &v71[0]) == true) return J1939_MSG_PROTOCOL; else return J1939_MSG_APP; } else return J1939_MSG_NONE; } byte ARD1939::Transmit(byte v76, long v25, byte v17, byte v67ess, byte* v79, int v73) { long v74; if(v73 > J1939_MSGLEN) return ERR; v74 = ((long)v76 << 26) + (v25 << 8) + (long)v17; if(f08(v25) == true) v74 = v74 | ((long)v67ess << 8); if(v73 > 8) #if TRANSPORT_PROTOCOL == 1 return f10(v76, v25, v17, v67ess, v79, v73); #else return ERR; #endif else return canTransmit(v74, v79, v73); } void ARD1939::f05(void) { if(v38.v21 == true && v38.v37 == false) { if(--v38.v36 == 0) v38.v37 = true; } if(v39.v21 == true && v39.v37 == false) if(--v39.v36 == 0) v39.v37 = true; #if TRANSPORT_PROTOCOL == 1 if(v40.v21 == true && v40.v37 == false) if(--v40.v36 == 0) v40.v37 = true; if(v41.v21 == true && v41.v37 == false) if(--v41.v36 == 0) v41.v37 = true; if(v42.v21 == true && v42.v37 == false) if(--v42.v36 == 0) v42.v37 = true; if(v43.v21 == true && v43.v37 == false) if(--v43.v36 == 0) v43.v37 = true; if(v44.v21 == true && v44.v37 == false) if(--v44.v36 == 0) v44.v37 = true; if(v45.v21 == true && v45.v37 == false) if(--v45.v36 == 0) v45.v37 = true; if(v46.v21 == true && v46.v37 == false) if(--v46.v36 == 0) v46.v37 = true; if(v47.v21 == true && v47.v37 == false) if(--v47.v36 == 0) v47.v37 = true; if(v48.v21 == true && v48.v37 == false) if(--v48.v36 == 0) v48.v37 = true; #endif } void ARD1939::f06(struct v35* v75) { v75->v36 = 0; v75->v21 = false; v75->v37 = false; } bool ARD1939::f07(long* v25, byte* v83) { bool v69; byte v65; byte v84; byte v85; v69 = false; v84 = (byte)((*v25 & 0x00FF00) >> 8); v85 = (byte)(*v25 & 0x0000FF); switch(v84) { case d47: if(v85 == GLOBALADDRESS) { if(v83[0] == d27 && v83[1] == d26 && v83[2] == d25) v69 = true; } else { *v25 = *v25 & 0x00FF00; if(v83[0] == d27 && v83[1] == d26 && v83[2] == d25) v69 = true; } break; default: for(v65 = 0; v65 < v04 - 1; v65++) { if(*v25 == v05[v65]) { v69 = true; break; } } break; } return v69; } bool ARD1939::f08(long v25) { if(v25 > 0 && v25 <= 0xEFFF) return true; if(v25 > 0x10000 && v25 <= 0x1EFFF) return true; return false; } byte ARD1939::GetSourceAddress(void) { return v18.v17; } void ARD1939::SetPreferredAddress(byte v86) { v18.v12 = v86; } void ARD1939::SetAddressRange(byte v87, byte v88) { v18.v13 = v87; v18.v14 = v88; } void ARD1939::SetNAME(long v81, int nManufacturerCode, byte nFunctionInstance, byte nECUInstance, byte nFunction, byte nVehicleSystem, byte nVehicleSystemInstance, byte nIndustryGroup, byte nArbitraryAddressCapable) { v03[0] = (byte)(v81 & 0xFF); v03[1] = (byte)((v81 >> 8) & 0xFF); v03[2] = (byte)(((nManufacturerCode << 5) & 0xFF) | (v81 >> 16)); v03[3] = (byte)(nManufacturerCode >> 3); v03[4] = (byte)((nFunctionInstance << 3) | nECUInstance); v03[5] = (byte)(nFunction); v03[6] = (byte)(nVehicleSystem << 1); v03[7] = (byte)((nArbitraryAddressCapable << 7) | (nIndustryGroup << 4) | (nVehicleSystemInstance)); } byte ARD1939::SetMessageFilter(long v25) { byte v94; int v65; v94 = ERR; if((v25 & 0x00FF00) == d31) v25 = d31; if(f09(v25) == true) v94 = OK; else { for(v65 = 0; v65 < MSGFILTERS; v65++) { if(v02[v65].bActive == false) { v02[v65].bActive = true; v02[v65].v25 = v25; v94 = OK; break; } } } return v94; } void ARD1939::DeleteMessageFilter(long v25) { int v65; if((v25 & 0x00FF00) == d31) v25 = d31; for(v65 = 0; v65 < MSGFILTERS; v65++) { if(v02[v65].v25 == v25) { v02[v65].bActive = false; v02[v65].v25 = 0; break; } } } bool ARD1939::f09(long v25) { bool v69; int v65; v69 = false; if((v25 & 0x00FF00) == d31) v25 = d31; for(v65 = 0; v65 < MSGFILTERS; v65++) { if(v02[v65].bActive == true && v02[v65].v25 == v25) { v69 = true; break; } } return v69; } #if TRANSPORT_PROTOCOL == 1 byte ARD1939::f13(long v25, byte* v71, int v31, byte v67, byte v66, byte v76) { byte v94; int nPointer; int v65; v94 = OK; if(v33.v20 == d03 && v33.v21 == true) { if(v33.v22 == false) { v62[0] = d04; v62[1] = (byte)(v33.v31 & 0xFF); v62[2] = (byte)(v33.v31 >> 8); v62[3] = v33.v28; v62[4] = 0xFF; v62[5] = (byte)(v33.v25 & 0x0000FF); v62[6] = (byte)((v33.v25 & 0x00FF00) >> 8); v62[7] = (byte)(v33.v25 >> 16); v94 = Transmit(d38, d36, v33.v26, GLOBALADDRESS, &v62[0], 8); v40.v36 = v51; v40.v21 = true; v33.v22 = true; } else { if(v40.v37 == true ) { nPointer = v33.v29 * 7; v63[0] = ++v33.v29; for(v65 = 0; v65 < 7; v65++) v63[v65+1] = v33.v30[nPointer + v65]; v94 = Transmit(d40, d39, v33.v26, GLOBALADDRESS, &v63[0], 8); if(v33.v29 == v33.v28) { f11(d12); } else { v40.v36 = v51; v40.v21 = true; v40.v37 = false; } } } } if(v25 == d36 && v71[0] == d04 && v33.v20 == d01 && v33.v32 == false) { v33.v25 = (((long)v71[7]) << 16) + (((long)v71[6]) << 8) + (long)v71[5]; if(f09(v33.v25) == true) { v33.v31 = (int)v71[1] + ((int)(v71[2]) << 8); if(v33.v31 > J1939_MSGLEN) { f11(d12); } else { v33.v20 = d02; v33.v21 = true; v33.v26 = v66; v33.v27 = v67; v33.v28 = v71[3]; v33.v29 = 0; v41.v36 = v52; v41.v21 = true; } } else v33.v25 = 0; } if(v33.v20 == d02 && v41.v37 == true) { f11(d12); } if(v33.v20 == d02 && v33.v21 == true && v25 == d39 && v66 == v33.v26 && v67 == v33.v27) { nPointer = ((int)v71[0] - 1) * 7; for(v65 = 1; v65 < 8; v65++) v33.v30[nPointer++] = v71[v65]; if(++v33.v29 == v33.v28) { f11(d13); v33.v32 = true; } } if(v34.v20 == d03 && v34.v21 == true && v25 == d36 && v71[0] == d45) { f12(d12); } if(v34.v20 == d03 && v34.v21 == true && v25 == d36 && v71[0] == d07) { f12(d12); } if(v34.v20 == d03 && v34.v21 == true) { if(v34.v23 == false) { v62[0] = d05; v62[1] = (byte)(v34.v31 & 0xFF); v62[2] = (byte)(v34.v31 >> 8); v62[3] = v34.v28; v62[4] = 0xFF; v62[5] = (byte)(v34.v25 & 0x0000FF); v62[6] = (byte)((v34.v25 & 0x00FF00) >> 8); v62[7] = (byte)(v34.v25 >> 16); v94 = Transmit(d38, d36, v34.v26, v34.v27, &v62[0], 8); v43.v36 = v54; v43.v21 = true; v34.v23 = true; } else { if(v43.v37 == true) { v62[0] = d08; v62[1] = d11; v62[2] = 0xFF; v62[3] = 0xFF; v62[4] = 0xFF; v62[5] = (byte)(v34.v25 & 0x0000FF); v62[6] = (byte)((v34.v25 & 0x00FF00) >> 8); v62[7] = (byte)(v34.v25 >> 16); v94 = Transmit(d38, d36, v34.v26, v34.v27, &v62[0], 8); f12(d12); } if(v25 == d36 && v67 == v34.v26 && v71[0] == d06) { f06(&v43); v42.v36 = v53; v42.v21 = true; v34.v24 = true; } if(v34.v24 == true && v42.v37 == true) { nPointer = v34.v29 * 7; v63[0] = ++v34.v29; for(v65 = 0; v65 < 7; v65++) v63[v65+1] = v34.v30[nPointer + v65]; v94 = Transmit(d40, d39, v34.v26, v34.v27, &v63[0], 8); if(v34.v29 == v34.v28) { f06(&v42); v47.v36 = v58; v47.v21 = true; } else { v42.v36 = v51; v42.v21 = true; v42.v37 = false; } } if(v47.v37 == true) { v62[0] = d08; v62[1] = d11; v62[2] = 0xFF; v62[3] = 0xFF; v62[4] = 0xFF; v62[5] = (byte)(v34.v25 & 0x0000FF); v62[6] = (byte)((v34.v25 & 0x00FF00) >> 8); v62[7] = (byte)(v34.v25 >> 16); v94 = Transmit(d38, d36, v34.v26, v34.v27, &v62[0], 8); f12(d12); } } } if(v25 == d36 && v67 == v18.v17 && v71[0] == d05) { int v77; v77 = (int)v71[1] + ((int)(v71[2]) << 8); if(v34.v20 != d01 || v77 > J1939_MSGLEN || v34.v32 == true) { v62[0] = d08; if(v34.v31 > J1939_MSGLEN) v62[1] = d10; else v62[1] = d09; v62[2] = 0xFF; v62[3] = 0xFF; v62[4] = 0xFF; v62[5] = v71[5]; v62[6] = v71[6]; v62[7] = v71[7]; v94 = Transmit(d38, d36, v67, v66, &v62[0], 8); } else { v34.v25 = (((long)v71[7]) << 16) + (((long)v71[6]) << 8) + (long)v71[5]; if(f09(v34.v25) == true) { v34.v20 = d02; v34.v21 = true; v34.v24 = true; v34.v26 = v66; v34.v27 = v67; v34.v28 = v71[3]; v34.v29 = 0; v34.v31 = (int)v71[1] + ((int)(v71[2]) << 8); for(v65 = 0; v65 < 8; v65++) v62[v65] = v71[v65]; v62[0] = d06; v62[1] = v62[3]; v62[2] = 1; v62[3] = 0xFF; v94 = Transmit(d38, d36, v67, v66, &v62[0], 8); v45.v36 = v56; v45.v21 = true; } else { v62[0] = d08; v62[1] = d10; v62[2] = 0xFF; v62[3] = 0xFF; v62[4] = 0xFF; v62[5] = v71[5]; v62[6] = v71[6]; v62[7] = v71[7]; v94 = Transmit(d38, d36, v67, v66, &v62[0], 8); } } } if(v34.v20 == d02 && v34.v21 == true) { if(v45.v37 == true) { f12(d12); v62[0] = d08; v62[1] = d11; v62[2] = 0xFF; v62[3] = 0xFF; v62[4] = 0xFF; v62[5] = (byte)(v34.v25 & 0x0000FF); v62[6] = (byte)((v34.v25 & 0x00FF00) >> 8); v62[7] = (byte)(v34.v25 >> 16); v94 = Transmit(d38, d36, v34.v27, v34.v26, &v62[0], 8); } if(v25 == d39 && v67 == v34.v27 && v66 == v34.v26) { nPointer = ((int)v71[0] - 1) * 7; for(v65 = 1; v65 < 8; v65++) v34.v30[nPointer++] = v71[v65]; if(++v34.v29 == v34.v28) { v62[0] = d07; v62[1] = (byte)(v34.v31 & 0x00FF); v62[2] = (byte)((v34.v31 & 0x00FF) >> 8); v62[3] = v34.v28; v62[4] = 0xFF; v62[5] = (byte)(v34.v25 & 0x0000FF); v62[6] = (byte)((v34.v25 & 0x00FF00) >> 8); v62[7] = (byte)(v34.v25 >> 16); v94 = Transmit(d38, d36, v34.v27, v34.v26, &v62[0], 8); f12(d13); v34.v32 = true; } } } return v94; } byte ARD1939::f10(byte v76, long v25, byte v17, byte v67ess, byte* v79, int v77) { byte v68; int v65; struct v19* v89; v68 = OK; if(v67ess != GLOBALADDRESS) v89 = &v34; else v89 = &v33; if(v89->v20 != d01 || v77 > J1939_MSGLEN) v68 = ERR; else { for(v65 = 0; v65 < v77; v65++) v89->v30[v65] = v79[v65]; for(v65 = v77; v65 < (v77 + 7); v65++) { if(v65 >= J1939_MSGLEN) break; v89->v30[v65] = 0xFF; } v89->v25 = v25; v89->v31 = v77; v89->v26 = v17; v89->v27 = v67ess; v65 = v77; v89->v28 = 0; while(v65 > 0) { v65 = v65 - 7; v89->v28++; } v89->v29 = 0; v89->v20 = d03; v89->v21 = true; } return v68; } void ARD1939::f11(byte v90) { if(v90 == d12) { v33.v20 = d01; v33.v21 = false; v33.v22 = false; v33.v25 = 0; v33.v26 = GLOBALADDRESS; v33.v27 = GLOBALADDRESS; v33.v28 = 0; v33.v29 = 0; v33.v31 = 0; v33.v32 = false; } f06(&v40); f06(&v41); } void ARD1939::f12(byte v90) { if(v90 == d12) { v34.v20 = d01; v34.v21 = false; v34.v23 = false; v34.v24 = false; v34.v25 = 0; v34.v26 = GLOBALADDRESS; v34.v27 = GLOBALADDRESS; v34.v28 = 0; v34.v29 = 0; v34.v31 = 0; v34.v32 = false; } f06(&v42); f06(&v43); f06(&v44); f06(&v45); f06(&v46); f06(&v47); f06(&v48); } #endif
Hey… was this useful? If so, you might like to support our website costs with a donation below. Thanks 😊👍