Teensy 4.0 / High Voltage Heater

Hi Jordan, all fine here, hope you're doing well too.
Had to read back the thread for a refresh...
Anyway, this chinese heater looks to better documented although I see some unclearities.

View attachment 35078
ID 0x22 represents the signal sent by VDDM to HVCH, with a byte length of 4 bytes. If I wanted to place the maximum values for allowable power, target water temp, water flow signal and enable signal, would it look like this:

uint8_t lindata[] = {0xff, oxff,0x3ff,0x1};
lin.order(0x22, lindata, 4, lin2x);
This is a bit strange; for ID22 the spec states Byte 2 is 10 bits long? If you want to write 0x3FF, you need to split that value over 2 LIN data bytes like so:
1720902713969.png


Which translates to:
uint8_t lindata[] = {0xFF, 0xFF, 0xFF, 0xD0};
lin.order(0x22, lindata, 4, lin2x);


For ID 0x33, the length in 3 bytes, but the info only goes into Byte 0 and Byte 1 above. I am trying to understand if there are still 3 separate pieces of information and how they are placed. If I wanted to place zeros for the E2E checksum, E2E counter and HVCH enable signal, would it look like this:

uint8_t lindata[] = {0x0, 0x0, 0x0};
lin.order(0x33, lindata, 3, lin2x);

View attachment 35079

ID33 is actually 2 bytes like so:
1720902743872.png


Which translates to:
uint8_t lindata[] = {0x00, 0x08};
lin.order(0x33, lindata, 2, lin2x);

Note 1: LIN messages are only 2, 4 or 8 bytes long, not 3.
Note 2: does the spec state something about how to calculate the E2E checksum and what the E2E counter means?

ID 0x17 shows what we should be getting on the lin.response, correct?

Would that look like:
lin.response (0x17, data, 3, lin2x)
I was not sure if LSB and MSB come into play in any of this.

Thanks!
To request ID17 you need to request 4 bytes: lin.response (0x17, data, 4, lin2x)
The response will look like this:
1720903294064.png


Hope this bit-fiddling makes sense...

Paul
 
Thanks again Paul. I do not believe I have any info on calculating the checksum. I will ask the seller and look for anything in what they sent to me.

Just so I understand better, on ID22, is the 0xD0 (208) because in byte 3, there are 2 1s and then a 0 to finish out that command then one more 1 for that entire byte? 2 0 8 ?

If so, on ID33, byte 1 has 4 0s and only the 1 at position 5 (which is a full on or full off signal) so on equals (0x08) 08?
 
Just so I understand better, on ID22, is the 0xD0 (208) because in byte 3, there are 2 1s and then a 0 to finish out that command then one more 1 for that entire byte? 2 0 8 ?
Correct, I set the bits in byte 3 that are not defined in the datasheet (bits 26, 28-31), to '0'.
If so, on ID33, byte 1 has 4 0s and only the 1 at position 5 (which is a full on or full off signal) so on equals (0x08) 08?
Those 4 0-bits are the value of E2E counter. Perhaps we need to write a certain value there? Hopefully the seller returns your question.

Paul
 
Hi Paul. The new heater arrives today and I plan to try to get this one working, since we were provided with valuable information we did not have on the other heaters, such as baud rate (19200) and the IDs that are supposed to send and receive the signals. Also, I was able to obtain the LDF file for this heater, which I assume will help us out. I edited a bunch of stuff out of this LDF file since it included other parts not related to the heater. Not sure what this information reveals to you:


Code:
/* ************************************************************************** */
/*                                                                            */
/*  Description:   LIN Description file                                       */
/*  System:        SDB23100                                                   */
/*  Variant:       E171_VCU_ADCU3                                             */
/*  Cluster:       ECM_LIN1                                                   */
/*  Master:        ECM_LIN1                                                   */
/*  Generated at:  2023-06-29 09:00                                           */
/*                                                                            */
/* ************************************************************************** */

LIN_description_file;
LIN_protocol_version = "2.1";
LIN_language_version = "2.1";
LIN_speed = 19.2 kbps;

Channel_name = "";

Nodes {
    Master: VDDM, 5.000 ms, 1.000 ms;
    Slaves: HVCH;
}

Signals {
    HVCHPartNo10CmplEndSgn1: 8, 0, HVCH, VDDM;
    HVCHPartNo10CmplEndSgn2: 8, 0, HVCH, VDDM;
    HVCHPartNo10CmplEndSgn3: 8, 0, HVCH, VDDM;
    HVCHPartNo10CmplNr1: 8, 0, HVCH, VDDM;
    HVCHPartNo10CmplNr2: 8, 0, HVCH, VDDM;
    HVCHPartNo10CmplNr3: 8, 0, HVCH, VDDM;
    HVCHPartNo10CmplNr4: 8, 0, HVCH, VDDM;
    HVCHPartNo10CmplNr5: 8, 0, HVCH, VDDM;
    HVCHPartNoCmplEndSgn1: 8, 0, HVCH, VDDM;
    HVCHPartNoCmplEndSgn2: 8, 0, HVCH, VDDM;
    HVCHPartNoCmplEndSgn3: 8, 0, HVCH, VDDM;
    HVCHPartNoCmplNr1: 8, 0, HVCH, VDDM;
    HVCHPartNoCmplNr2: 8, 0, HVCH, VDDM;
    HVCHPartNoCmplNr3: 8, 0, HVCH, VDDM;
    HVCHPartNoCmplNr4: 8, 0, HVCH, VDDM;
    HVCHSerNoNr1: 8, 0, HVCH, VDDM;
    HVCHSerNoNr2: 8, 0, HVCH, VDDM;
    HVCHSerNoNr3: 8, 0, HVCH, VDDM;
    HVCHSerNoNr4: 8, 0, HVCH, VDDM;
    HvCooltHeatrEnad: 1, 0, VDDM;
    HvCooltHeatrEnadWhE2EChks: 8, 0, VDDM, HVCH;
    HvCooltHeatrEnadWhE2ECntr: 4, 0, VDDM, HVCH;
    HvCooltHeatrEnadWhE2EHvchEnad: 1, 0, VDDM, HVCH;
    HvCooltHeatrICnsSig: 8, 0, HVCH, VDDM;
    HvCooltHeatrInfoCompProtn: 1, 0, HVCH, VDDM;
    HvCooltHeatrProtnOfSelfTmpSigHwProtn: 1, 0, HVCH, VDDM;
    HvCooltHeatrProtnOfSelfTmpSigOvrheatg: 1, 0, HVCH, VDDM;
    HvCooltHeatrProtnOfSelfTmpSigProtnOfSelfTmp: 1, 0, HVCH, VDDM;
    HvCooltHeatrProtnOfSelfTmpSigProtnOfSelfTmpResd: 1, 0, HVCH, VDDM;
    HvCooltHeatrSnsrFltSigCooltTInSnsrFlt: 1, 0, HVCH, VDDM;
    HvCooltHeatrSnsrFltSigCooltTOutSnsrFlt: 1, 0, HVCH, VDDM;
    HvCooltHeatrSnsrFltSigResdForSnsrFlt: 1, 0, HVCH, VDDM;
    HvCooltHeatrSnsrFltSigTInMtrlSnsrFlt: 1, 0, HVCH, VDDM;
    HvCooltHeatrSplyHighU: 10, 0, HVCH;
    HvCooltHeatrSplyUForCtrlUnitValSig: 8, 0, HVCH, VDDM;
    HvCooltHeatrSrvRqrdSigCircForDrvrShoOrOpen: 1, 0, HVCH, VDDM;
    HvCooltHeatrSrvRqrdSigICnsOutOfRng: 1, 0, HVCH, VDDM;
    HvCooltHeatrSrvRqrdSigMemErr: 1, 0, HVCH, VDDM;
    HvCooltHeatrSrvRqrdSigSrvRqrd: 1, 0, HVCH, VDDM;
    HvCooltHeatrSrvRqrdSigSrvRqrdResd: 1, 0, HVCH, VDDM;
    HvCooltHeatrStsSig: 3, 0, HVCH, VDDM;
    HvCooltHeatrStsWhE2EChks: 8, 0, HVCH, VDDM;
    HvCooltHeatrStsWhE2ECntr: 4, 0, HVCH, VDDM;
    HvCooltHeatrStsWhE2EHvchSts: 3, 0, HVCH, VDDM;
    HvCooltHeatrWarnSigCooltTOutOfRng: 1, 0, HVCH, VDDM;
    HvCooltHeatrWarnSigFltInCom: 1, 0, HVCH, VDDM;
    HvCooltHeatrWarnSigFltPrsnt: 1, 0, HVCH, VDDM;
    HvCooltHeatrWarnSigFltPrsntResd: 1, 0, HVCH, VDDM;
    HvCooltHeatrWarnSigHvOutOfRng: 1, 0, HVCH, VDDM;
    HvCooltHeatrWarnSigULoOutOfRng: 1, 0, HVCH, VDDM;
    HvCooltWtrHeatrWtrTInIntk: 8, 40, HVCH, VDDM;
    HvCooltWtrHeatrWtrTInOutl: 8, 40, HVCH, VDDM;
    HvHeatrPwrCns: 10, 0, HVCH, VDDM;
    HvHeatrPwrCnsDes: 10, 0, HVCH, VDDM;
    HvWtrHeatrPwrCnsAllwd: 8, 0, VDDM, HVCH;
    HvWtrHeatrWtrTDes: 8, 40, VDDM, HVCH;
    ErrRespHVCH: 1, 0, HVCH, VDDM;
}

Diagnostic_signals {
    MasterReqB0: 8, 0;
    MasterReqB1: 8, 0;
    MasterReqB2: 8, 0;
    MasterReqB3: 8, 0;
    MasterReqB4: 8, 0;
    MasterReqB5: 8, 0;
    MasterReqB6: 8, 0;
    MasterReqB7: 8, 0;
    SlaveRespB0: 8, 0;
    SlaveRespB1: 8, 0;
    SlaveRespB2: 8, 0;
    SlaveRespB3: 8, 0;
    SlaveRespB4: 8, 0;
    SlaveRespB5: 8, 0;
    SlaveRespB6: 8, 0;
    SlaveRespB7: 8, 0;
}

Frames {

    }
    EcmEcm_Lin1Fr03: 0x22, VDDM, 4 {
        HvWtrHeatrPwrCnsAllwd, 0;
        HvWtrHeatrWtrTDes, 8;
        CooltFlowInCmptmtCirc, 16;
        HvCooltHeatrEnad, 27;
    }
    EcmEcm_Lin1Fr05: 0x33, VDDM, 3 {
        HvCooltHeatrEnadWhE2EChks, 0;
        HvCooltHeatrEnadWhE2ECntr, 8;
        HvCooltHeatrEnadWhE2EHvchEnad, 12;
    }
    HvchEcm_Lin1Fr02: 0x17, HVCH, 3 {
        HvHeatrPwrCns, 0;
        HvHeatrPwrCnsDes, 10;
    }
    HvchEcm_Lin1PartNr10Fr04: 0x20, HVCH, 8 {
        HVCHPartNo10CmplEndSgn1, 0;
        HVCHPartNo10CmplEndSgn2, 8;
        HVCHPartNo10CmplEndSgn3, 16;
        HVCHPartNo10CmplNr1, 24;
        HVCHPartNo10CmplNr2, 32;
        HVCHPartNo10CmplNr3, 40;
        HVCHPartNo10CmplNr4, 48;
        HVCHPartNo10CmplNr5, 56;
    }
            HvchEcm_Lin1SerNrFr01: 0x25, HVCH, 4 {
        HVCHSerNoNr1, 0;
        HVCHSerNoNr2, 8;
        HVCHSerNoNr3, 16;
        HVCHSerNoNr4, 24;
    }
            HvchEcm_Lin1PartNr10Fr08: 0x26, HVCH, 7 {
        HVCHPartNoCmplEndSgn1, 0;
        HVCHPartNoCmplEndSgn2, 8;
        HVCHPartNoCmplEndSgn3, 16;
        HVCHPartNoCmplNr1, 24;
        HVCHPartNoCmplNr2, 32;
        HVCHPartNoCmplNr3, 40;
        HVCHPartNoCmplNr4, 48;
    }
        HvchEcm_Lin1Fr01: 0x27, HVCH, 7 {
        HvCooltHeatrICnsSig, 16;
        HvCooltHeatrInfoCompProtn, 46;
        HvCooltHeatrProtnOfSelfTmpSigHwProtn, 48;
        HvCooltHeatrProtnOfSelfTmpSigOvrheatg, 49;
        HvCooltHeatrProtnOfSelfTmpSigProtnOfSelfTmp, 50;
        HvCooltHeatrProtnOfSelfTmpSigProtnOfSelfTmpResd, 51;
        HvCooltHeatrSnsrFltSigCooltTInSnsrFlt, 52;
        HvCooltHeatrSnsrFltSigCooltTOutSnsrFlt, 53;
        HvCooltHeatrSnsrFltSigResdForSnsrFlt, 54;
        HvCooltHeatrSnsrFltSigTInMtrlSnsrFlt, 55;
        HvCooltHeatrSplyUForCtrlUnitValSig, 24;
        HvCooltHeatrSrvRqrdSigCircForDrvrShoOrOpen, 0;
        HvCooltHeatrSrvRqrdSigICnsOutOfRng, 1;
        HvCooltHeatrSrvRqrdSigMemErr, 2;
        HvCooltHeatrSrvRqrdSigSrvRqrd, 3;
        HvCooltHeatrSrvRqrdSigSrvRqrdResd, 4;
        HvCooltHeatrStsSig, 5;
        HvCooltHeatrWarnSigCooltTOutOfRng, 40;
        HvCooltHeatrWarnSigFltInCom, 41;
        HvCooltHeatrWarnSigFltPrsnt, 42;
        HvCooltHeatrWarnSigFltPrsntResd, 43;
        HvCooltHeatrWarnSigHvOutOfRng, 44;
        HvCooltHeatrWarnSigULoOutOfRng, 45;
        HvCooltWtrHeatrWtrTInIntk, 32;
        HvCooltWtrHeatrWtrTInOutl, 8;
        ErrRespHVCH, 47;
    }
            HvchEcm_Lin1Fr03: 0x34, HVCH, 3 {
        HvCooltHeatrStsWhE2EChks, 0;
        HvCooltHeatrStsWhE2ECntr, 8;
        HvCooltHeatrStsWhE2EHvchSts, 12;
    }
            HvchEcm_Lin1Fr04: 0x1E, HVCH, 8 {
        HvCooltHeatrSplyHighU, 0;
    }

Diagnostic_frames {
    MasterReq: 60 {
        MasterReqB0, 0;
        MasterReqB1, 8;
        MasterReqB2, 16;
        MasterReqB3, 24;
        MasterReqB4, 32;
        MasterReqB5, 40;
        MasterReqB6, 48;
        MasterReqB7, 56;
    }
    SlaveResp: 61 {
        SlaveRespB0, 0;
        SlaveRespB1, 8;
        SlaveRespB2, 16;
        SlaveRespB3, 24;
        SlaveRespB4, 32;
        SlaveRespB5, 40;
        SlaveRespB6, 48;
        SlaveRespB7, 56;
    }
}

Node_attributes {
        HVCH {
        LIN_protocol = "2.1";
        configured_NAD = 0x7A;
        initial_NAD = 0x7A;
        product_id = 0x00, 0x00, 0;
        response_error = ErrRespHVCH;
        configurable_frames {
            EcmEcm_Lin1Fr03;
            EcmEcm_Lin1Fr05;
            HvchEcm_Lin1Fr01;
            HvchEcm_Lin1Fr02;
            HvchEcm_Lin1Fr03;
            HvchEcm_Lin1PartNr10Fr04;
            HvchEcm_Lin1PartNr10Fr08;
            HvchEcm_Lin1SerNrFr01;
            HvchEcm_Lin1Fr04;
        }
    }
}

Schedule_tables {
    Ecm_Lin1DiagRequestSchedule {
        MasterReq delay 15.000 ms;
    }
    Ecm_Lin1DiagResponseSchedule {
        SlaveResp delay 15.000 ms;
    }
    Ecm_Lin1LinScheduleSerNrTable {
        HvchEcm_Lin1PartNr10Fr04 delay 15.000 ms;
        HvchEcm_Lin1PartNr10Fr08 delay 10.000 ms;
        HvchEcm_Lin1SerNrFr01 delay 10.000 ms;
    }

Signal_encoding_types {
    ActiveSaveReq {
        logical_value, 0, "ActiveSaveReq_NoReq";
        logical_value, 1, "ActiveSaveReq_ActiveSave_Req";
        logical_value, 2, "ActiveSaveReq_Reserved";
        logical_value, 3, "ActiveSaveReq_SNA";
    }
    Boolean {
        logical_value, 0, "Boolean_FALSE";
        logical_value, 1, "Boolean_TRUE";
    }
    FaultSts {
        logical_value, 0, "FaultSts_NoError";
        logical_value, 1, "FaultSts_PositionUnknown";
        logical_value, 2, "FaultSts_PCTR_Error";
        logical_value, 3, "FaultSts_Blocking_Err";
        logical_value, 4, "FaultSts_SetPoint_Err";
        logical_value, 5, "FaultSts_Learning_Err";
        logical_value, 6, "FaultSts_MotorCoilShort";
        logical_value, 7, "FaultSts_MotorCoilOpen";
        logical_value, 8, "FaultSts_MotorDrvOvertemperatureShutdown";
        logical_value, 9, "FaultSts_Indeterminate";
        logical_value, 10, "FaultSts_Reserved";
        logical_value, 15, "FaultSts_SNA";
    }
    Flt {
        logical_value, 0, "Flt_NoFault";
        logical_value, 1, "Flt_Fault";
    }
    GenQf1 {
        logical_value, 0, "GenQf1_UndefindDataAccur";
        logical_value, 1, "GenQf1_TmpUndefdData";
        logical_value, 2, "GenQf1_DataAccurNotWithinSpcn";
        logical_value, 3, "GenQf1_AccurData";
    }
    HvCooltHeatrICns {
        physical_value, 0, 255, 0.25, 0.0, "Unitless";
    }
    HvCooltHeatrSplyUForCtrlUnitVal {
        physical_value, 0, 255, 0.1, 0.0, "Unitless";
    }
    HvCooltHeatrSts {
        logical_value, 0, "HvCooltHeatrSts_Off";
        logical_value, 1, "HvCooltHeatrSts_LockedUntilNextStart";
        logical_value, 2, "HvCooltHeatrSts_LockedUntilService";
        logical_value, 3, "HvCooltHeatrSts_LockedPermanent";
        logical_value, 4, "HvCooltHeatrSts_Operation";
        logical_value, 5, "HvCooltHeatrSts_Reserved1";
        logical_value, 6, "HvCooltHeatrSts_Reserved2";
        logical_value, 7, "HvCooltHeatrSts_Reserved3";
    }
    HvHeatrPwrLim {
        physical_value, 0, 255, 40.0, 0.0, "W";
    }
    OnOff1 {
        logical_value, 0, "OnOff1_Off";
        logical_value, 1, "OnOff1_On";
        }
    PTCModeReq {
        logical_value, 0, "PTCModeReq_Power";
        logical_value, 1, "PTCModeReq_Duty";
        logical_value, 2, "PTCModeReq_Direct";
    }
    Pwr3 {
        physical_value, 0, 1023, 20.0, 0.0, "W";
    }
    Pwr5 {
        physical_value, 0, 255, 20.0, 0.0, "W";
    }
    RefDrvReq {
        logical_value, 0, "RefDrvReq_NoReq";
        logical_value, 1, "RefDrvReq_REFDRV_Req";
        logical_value, 2, "RefDrvReq_Reserved";
        logical_value, 3, "RefDrvReq_SNA";
    }
    SpdLvlReq {
        logical_value, 0, "SpdLvlReq_Level0_Slow";
        logical_value, 1, "SpdLvlReq_Level1_Normal";
        logical_value, 2, "SpdLvlReq_Level2_Fast";
        logical_value, 3, "SpdLvlReq_SNA";
    }
    SpeedLvl {
        logical_value, 0, "SpeedLvl_SpeedLevelLow";
        logical_value, 1, "SpeedLvl_SpeedLevelNormal";
        logical_value, 2, "SpeedLvl_SpeedLevelFast";
        logical_value, 3, "SpeedLvl_SNA";
    }
    TempSts {
        logical_value, 0, "TempSts_Temperatureok";
        logical_value, 1, "TempSts_OvertemperatureWarning";
        logical_value, 2, "TempSts_PCBOvertemperature";
        logical_value, 3, "TempSts_LowTemperature";
        logical_value, 4, "TempSts_notused";
        logical_value, 7, "TempSts_SNA";
    }
    Validity {
        logical_value, 0, "Validity_NotValid";
        logical_value, 1, "Validity_Valid";
    }
    VoltSts {
        logical_value, 0, "VoltSts_VoltageOK";
        logical_value, 1, "VoltSts_OverVoltage";
        logical_value, 2, "VoltSts_UnderVoltage";
        logical_value, 3, "VoltSts_Reserved";
    }
}

Signal_representation {
   
    ActiveSaveReq : HpBattFwvActvSaveReq, HpBattVlvActvSaveReq;
    Boolean : AirPtcEnad, HvCooltHeatrEnad, HvCooltHeatrEnadWhE2EHvchEnad, HvCooltHeatrProtnOfSelfTmpSigHwProtn,
        HvCooltHeatrProtnOfSelfTmpSigOvrheatg, HvCooltHeatrProtnOfSelfTmpSigProtnOfSelfTmp,
        HvCooltHeatrProtnOfSelfTmpSigProtnOfSelfTmpResd, HvCooltHeatrSnsrFltSigCooltTInSnsrFlt,
        HvCooltHeatrSnsrFltSigCooltTOutSnsrFlt, HvCooltHeatrSnsrFltSigResdForSnsrFlt,
        HvCooltHeatrSnsrFltSigTInMtrlSnsrFlt, HvCooltHeatrSrvRqrdSigCircForDrvrShoOrOpen,
        HvCooltHeatrSrvRqrdSigICnsOutOfRng, HvCooltHeatrSrvRqrdSigMemErr, HvCooltHeatrSrvRqrdSigSrvRqrd,
        HvCooltHeatrSrvRqrdSigSrvRqrdResd, HvCooltHeatrWarnSigCooltTOutOfRng, HvCooltHeatrWarnSigFltInCom,
        HvCooltHeatrWarnSigFltPrsnt, HvCooltHeatrWarnSigFltPrsntResd, HvCooltHeatrWarnSigHvOutOfRng,
        HvCooltHeatrWarnSigULoOutOfRng;
        CmprDiagcFbCmprDrvrCircSnsrPlaus, HVCHPartNo10CmplEndSgn3, HVCHPartNo10CmplNr1,
        HVCHPartNo10CmplNr2, HVCHPartNo10CmplNr3, HVCHPartNo10CmplNr4, HVCHPartNo10CmplNr5, HVCHPartNoCmplEndSgn1,
        HVCHPartNoCmplEndSgn2, HVCHPartNoCmplEndSgn3, HVCHPartNoCmplNr1, HVCHPartNoCmplNr2, HVCHPartNoCmplNr3,
        HVCHPartNoCmplNr4, HVCHSerNoNr1, HVCHSerNoNr2, HVCHSerNoNr3, HVCHSerNoNr4, HvCooltHeatrEnadWhE2EChks,
        HvCooltHeatrStsWhE2EChks, MvDcDcModReqChks;
}

I started to re-write our code for this new heater using the data provided and your decoding of the bits and bytes. Here it is for your review:

Code:
#include "lin_bus.h"

// Create an IntervalTimer object
IntervalTimer myTimer;

int ledState = LOW;                // ledState used to set the LED
unsigned long interval = 200000;   // interval at which to blinkLED to run every 0.2 seconds

LIN lin;

int lin_cs = 32; // cs and serial port set for skpang LIN / FDCAN board
int led1 = 23;
int lin_fault = 28;

//uint8_t  Power = 0xFF; // set to required power changed from uint16_t
//uint8_t  Temperature = 0xFF; //set to required temperature
//uint8_t  Flow = 0x3FF; //set to required water flow
//uint16_t heateractualpower = 0;
//uint16_t heaterpowerrequested = 0;


uint8_t CRC = 0;
uint8_t CRC2 = 0;
uint8_t CRC3 = 0;
uint8_t orderID = 0x22;
uint8_t orderID2 = 0x33;
uint8_t responseID = 0x17;
uint8_t responseID2 = 0x27;
uint8_t responseID3 = 0x34;

//uint8_t data[8];

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(lin_fault,INPUT);
  pinMode(lin_cs, OUTPUT);
  digitalWrite(lin_cs, HIGH); // enable MCP2004 LIN transceiver
  digitalWrite(LED_BUILTIN, HIGH);
 
  Serial.begin(115200);
  //Serial.print("Heater demo");
 
  lin.begin(&Serial3, 19200);
  delay(100); // wait 100ms
  pinMode(led1,OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
 
  myTimer.begin(blinkLED, interval);
}

void loop() {
  // heater
  SendLin();
  delay(100);
  //Serial.print(" Heater test\n");

}

void blinkLED() {
  ledState = !ledState;
 
  digitalWrite(LED_BUILTIN, ledState);
  digitalWrite(led1, ledState);
}

  static void SendLin()
{
   //for (orderID = 0; orderID < 64; orderID++) {

   static bool read = true;
   uint8_t data[8];
   delay(100);
   uint8_t lindata[] = {0xFF, 0xFF, 0xFF, 0xD0};
   lin.order(0x22, lindata, 4, lin2x);
   delay(100);
   uint8_t lindata2[] = {0x00, 0x08};
   lin.order(0x33, lindata2, 2, lin2x);;
   delay(100);

   lin.response (0x17, data, 4, lin2x) // -1 indicates crc error
     {
     //heateractualpower = data[0];
     //heaterpowerrequested = data[1];
     Serial.print("responseID 0x17:");
     Serial.print(0x17);
     Serial.print(",");
     Serial.print("Response Data:");
     Serial.print(data);
     Serial.print(",");
     Serial.print("CRC:");
     Serial.print(CRC);
     Serial.print(",");
     }

    lin.response(0x27, data2, 8, lin2x) // -1 indicates crc error
     {
     Serial.print("responseID 0x27:");
     Serial.print(0x27);
     Serial.print(",");
     Serial.print("Response Data:");
     Serial.print(data2);
     Serial.print(",");
     Serial.print("CRC2:");
     Serial.print(CRC3);
     Serial.print(",");
   }
    lin.response(0x34, data3, 4, lin2x) // -1 indicates crc error
     {
     Serial.print("responseID 0x34:");
     Serial.print(0x34);
     Serial.print(",");
     Serial.print("Response Data:");
     Serial.print(data3);
     Serial.print(",");
     Serial.print("CRC3:");
     Serial.print(CRC3);
     Serial.print(",");
   }

   read = !read;
  }
}

I am not sure if you would want me to start with one of our simpler programs to make sure everything pings as it should.

Hope this all makes sense to you.

THANKS!
 
Hi Jordan,

I tidied up your code and added some lines so that it compiles without error now.
Had a quick look at the LDF file but for the finer details it takes some time to wrap ones head around it...

C++:
#include "lin_bus.h"

// Create an IntervalTimer object
IntervalTimer myTimer;

int ledState = LOW;               // ledState used to set the LED
unsigned long interval = 200000;  // interval at which to blinkLED to run every 0.2 seconds

LIN lin;

int lin_cs = 32;  // cs and serial port set for skpang LIN / FDCAN board
int led1 = 23;
int lin_fault = 28;

//uint8_t  Power = 0xFF; // set to required power changed from uint16_t
//uint8_t  Temperature = 0xFF; //set to required temperature
//uint8_t  Flow = 0x3FF; //set to required water flow
//uint16_t heateractualpower = 0;
//uint16_t heaterpowerrequested = 0;

uint8_t data[8];
uint8_t data2[8];
uint8_t data3[8];
uint8_t CRC = 0;
uint8_t CRC2 = 0;
uint8_t CRC3 = 0;
uint8_t orderID = 0x22;
uint8_t orderID2 = 0x33;
uint8_t responseID = 0x17;
uint8_t responseID2 = 0x27;
uint8_t responseID3 = 0x34;
bool read = false;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(lin_fault, INPUT);
  pinMode(lin_cs, OUTPUT);
  digitalWrite(lin_cs, HIGH);  // enable MCP2004 LIN transceiver
  digitalWrite(LED_BUILTIN, HIGH);

  Serial.begin(115200);
  //Serial.print("Heater demo");

  lin.begin(&Serial3, 19200);
  delay(100);  // wait 100ms
  pinMode(led1, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  myTimer.begin(blinkLED, interval);
}

void loop() {
  SendLin();
  delay(100);
}

void blinkLED() {
  ledState = !ledState;
  digitalWrite(LED_BUILTIN, ledState);
  digitalWrite(led1, ledState);
}

void SendLin() {
  read = true;

  uint8_t lindata[] = { 0xFF, 0xFF, 0xFF, 0xD0 };
  lin.order(0x22, lindata, 4, lin2x);
  delay(100);

  uint8_t lindata2[] = { 0x00, 0x08 };
  lin.order(0x33, lindata2, 2, lin2x);
  delay(100);

  CRC = lin.response(0x17, data, 4, lin2x);  // -1 indicates crc error
  Serial.print("responseID:");
  Serial.print(0x17, HEX);
  Serial.print(",");

  Serial.print("Response Data:");
  for (int i = 0; i < 8; i++) {  // display received data
    Serial.printf("0x%.2X ", data[i]);
  }
  Serial.print(",");

  Serial.print("CRC:");
  Serial.println(CRC);

  CRC2 = lin.response(0x27, data2, 8, lin2x);  // -1 indicates crc error
  Serial.print("responseID:");
  Serial.print(0x27, HEX);
  Serial.print(",");

  Serial.print("Response Data:");
  for (int i = 0; i < 8; i++) {  // display received data
    Serial.printf("0x%.2X ", data2[i]);
  }
  Serial.print(",");

  Serial.print("CRC2:");
  Serial.println(CRC2);

  CRC3 = lin.response(0x34, data3, 4, lin2x);  // -1 indicates crc error
  Serial.print("responseID:");
  Serial.print(0x34, HEX);
  Serial.print(",");

  Serial.print("Response Data:");
  for (int i = 0; i < 8; i++) {  // display received data
    Serial.printf("0x%.2X ", data3[i]);
  }
  Serial.print(",");

  Serial.print("CRC3:");
  Serial.println(CRC3);

  read = !read;
}

Paul
 
Paul, thank you so much for doing this. The heater just arrived at my office, so I will take it home and hook it up to the test bench I was using before and fire it up and see what happens with this code. Maybe we will get lucky on the first pass.

I really appreciate your time and commitment to helping me on this project. If anything pops out from the LDF file, let me know. I was hoping there might be some clues in there about the checksum and such.
 
Hi Paul. I hooked up everything this morning. Had to rewire connectors for the new heater and get my test bench back up and running. When I turned things on, the CRCs all went from 255 to 0, which is good. No heat though. Here is the feedback I was getting from the system. Hopefully we can decode this for some answers. I am certain we can crack this now that we have the manual, know we have the right baud rate and know the IDs that send and receive data.

Code:
responseID:17,Response Data:0x00 0x00 0x7F 0x2A 0x00 0x00 0x00 0x00 ,CRC:255
responseID:27,Response Data:0x00 0x00 0x1C 0x23 0x00 0x20 0x08 0x00 ,CRC2:255
responseID:34,Response Data:0x00 0x00 0x7F 0x2A 0x00 0x00 0x00 0x00 ,CRC3:255

responseID:17,Response Data:0x00 0x00 0x00 0x68 0x00 0x00 0x00 0x00 ,CRC:0
responseID:27,Response Data:0x20 0x3F 0x00 0x83 0x3F 0x80 0x00 0x75 ,CRC2:0
responseID:34,Response Data:0xFF 0x00 0x00 0x4B 0x00 0x00 0x00 0x00 ,CRC3:0

responseID:17,Response Data:0x00 0x00 0x00 0x68 0x00 0x00 0x00 0x00 ,CRC:0
responseID:27,Response Data:0x20 0x3F 0x00 0x83 0x3F 0x80 0x00 0x75 ,CRC2:0
responseID:34,Response Data:0xFF 0x11 0x00 0x3A 0x00 0x00 0x00 0x00 ,CRC3:0

responseID:17,Response Data:0x00 0x00 0x00 0x68 0x00 0x00 0x00 0x00 ,CRC:0
responseID:27,Response Data:0x20 0x3F 0x00 0x83 0x3F 0x80 0x00 0x75 ,CRC2:0
responseID:34,Response Data:0xFF 0x00 0x00 0x4B 0x00 0x00 0x00 0x00 ,CRC3:0

responseID:17,Response Data:0x00 0x00 0x00 0x68 0x00 0x00 0x00 0x00 ,CRC:0
responseID:27,Response Data:0x20 0x3F 0x00 0x83 0x3F 0x80 0x00 0x75 ,CRC2:0
responseID:34,Response Data:0xFF 0x01 0x00 0x4A 0x00 0x00 0x00 0x00 ,CRC3:0

It also just occurred to me that I installed IDE on my new laptop and reinstalled the teensy software, but do I need to add those other files again with altered cpp file?
 
Last edited:
It also just occurred to me that I installed IDE on my new laptop and reinstalled the teensy software, but do I need to add those other files again with altered cpp file?
Apparently you installed the LIN bus library already otherwise I wouldn't compile. But indeed you need to comment out line 463 of lin-bus.cpp like so: //_stream->read();.

I rechecked my modified code from message #155 and there are some faults with respect to serial printing the response data.
ID17 is 4 bytes long so 4 bytes need to be printed, not 8.
ID27 is 8 bytes long so indeed print 8 bytes.
ID33 is also 4 bytes long so 4 bytes need to be printed, not 8.
Updated code below.

Would it be possible to share the whole heater manual that you copied parts of into message #150? Any additional info is welcome.

Paul

C++:
#include "lin_bus.h"

// Create an IntervalTimer object
IntervalTimer myTimer;

int ledState = LOW;               // ledState used to set the LED
unsigned long interval = 200000;  // interval at which to blinkLED to run every 0.2 seconds

LIN lin;

int lin_cs = 32;  // cs and serial port set for skpang LIN / FDCAN board
int led1 = 23;
int lin_fault = 28;

//uint8_t  Power = 0xFF; // set to required power changed from uint16_t
//uint8_t  Temperature = 0xFF; //set to required temperature
//uint8_t  Flow = 0x3FF; //set to required water flow
//uint16_t heateractualpower = 0;
//uint16_t heaterpowerrequested = 0;

uint8_t data[4];
uint8_t data2[8];
uint8_t data3[4];
uint8_t CRC = 0;
uint8_t CRC2 = 0;
uint8_t CRC3 = 0;
uint8_t orderID = 0x22;
uint8_t orderID2 = 0x33;
uint8_t responseID = 0x17;
uint8_t responseID2 = 0x27;
uint8_t responseID3 = 0x34;
bool read = false;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(lin_fault, INPUT);
  pinMode(lin_cs, OUTPUT);
  digitalWrite(lin_cs, HIGH);  // enable MCP2004 LIN transceiver
  digitalWrite(LED_BUILTIN, HIGH);

  Serial.begin(115200);
  //Serial.print("Heater demo");

  lin.begin(&Serial3, 19200);
  delay(100);  // wait 100ms
  pinMode(led1, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  myTimer.begin(blinkLED, interval);
}

void loop() {
  SendLin();
  delay(100);
}

void blinkLED() {
  ledState = !ledState;
  digitalWrite(LED_BUILTIN, ledState);
  digitalWrite(led1, ledState);
}

void SendLin() {
  read = true;
 
  // write ID22 register
  uint8_t lindata[] = { 0xFF, 0xFF, 0xFF, 0xD0 };
  lin.order(0x22, lindata, 4, lin2x);
  delay(100);

  // write ID33 register
  uint8_t lindata2[] = { 0x00, 0x08 };
  lin.order(0x33, lindata2, 2, lin2x);
  delay(100);

  // read ID17 register
  CRC = lin.response(0x17, data, 4, lin2x);  // -1 indicates crc error
  Serial.print("responseID:");
  Serial.print(0x17, HEX);
  Serial.print(",");
  Serial.print("Response Data:");
  for (int i = 0; i < 4; i++) {  // display received data
    Serial.printf("0x%.2X ", data[i]);
  }
  Serial.print(",");
  Serial.print("CRC:");
  Serial.println(CRC);

  // read ID27 register
  CRC2 = lin.response(0x27, data2, 8, lin2x);  // -1 indicates crc error
  Serial.print("responseID:");
  Serial.print(0x27, HEX);
  Serial.print(",");
  Serial.print("Response Data:");
  for (int i = 0; i < 8; i++) {  // display received data
    Serial.printf("0x%.2X ", data2[i]);
  }
  Serial.print(",");
  Serial.print("CRC2:");
  Serial.println(CRC2);

  // read ID34 register
  CRC3 = lin.response(0x34, data3, 4, lin2x);  // -1 indicates crc error
  Serial.print("responseID:");
  Serial.print(0x34, HEX);
  Serial.print(",");
  Serial.print("Response Data:");
  for (int i = 0; i < 4; i++) {  // display received data
    Serial.printf("0x%.2X ", data3[i]);
  }
  Serial.print(",");
  Serial.print("CRC3:");
  Serial.println(CRC3);

  read = !read;
}
 
Thanks again Paul. I will make sur line 463 is commented out. Thanks also for the updated code. I will give it a whirl. I will try to get you this entire manual as well.

In the meantime, I have asked about the checksum and no response yet, but here is an explanation of the checksum and a calculator if you think we can do this ourselves:

Checksum Calculator

Bus Communication​


Transactions on the LIN bus are broken into master and slave processes. The master process is the first part of the message. It is always initiated by the master node on the LIN bus, and is comprised of the header followed by a response period where it waits for the slave period. The header contains the following sections:
  • Break
  • Delimiter
  • Sync byte
  • PID/Identifier byte
The Break is compromised of a level transition on the line from recessive to dominant, and persists for a total of 13 bit times. Immediately following the Break is a delimiter bit, which is at least one bit time of the recessive level. Following the delimiter is a Sync byte, in which the master sends 0x55 on the LIN line, allowing for bit rate synchronization between the master and the slave devices on the bus. Finally, the master process finishes with an identifier byte for the message. Identifiers are determined by the design of each specific LIN bus and outlined in the LDF.
Figure 1. LIN Header

GUID-7158FDCB-F23C-4BC4-A433-434AF68863E9-low.png

After the master process completes, it allows the slave process to take over the LIN bus. The slave process consists of two portions: data and checksum. The data can be sent by any of the nodes (including the master). The ID field of the header determines which specific node is transmitting, how much is being transmitted, and what data is sent. After the data has finished transmitting, the final piece of the slave process is the checksum. There are two methods of calculating checksums in the LIN protocol. LIN 1.3 and older devices calculated the checksum only on the data bytes of the message, while newer LIN standards also include the PID byte in the checksum calculation. In both, the checksum is calculated by adding together the bytes and subtracting 255 every time the sum is greater than 256. An example calculation is shown below.
Example Data Frame:
PID: 0x15
Data bytes:
0x34, 0x55, 0x67, 0x97
Checksum type: Includes PID
0x34+0x55=0x89
0x89+0x67=0xF0
0xF0+0x97=0x187
this is greater than 256(0x100), so 255 (0xFF) is subtracted.
0x187-0xFF=0x88
0x88+0x15=0x9D
Final Checksum value is 0x9D.
 
Manual attached, along with other specs provided. Hope this is helpful to you.
 

Attachments

  • SR02-12 specification.pdf
    999.7 KB · Views: 60
  • Copy of E171_400VMatrix Protocol Protection Strategy(ENGLISH)20230809.pdf
    246.4 KB · Views: 43
In the meantime, I have asked about the checksum and no response yet, but here is an explanation of the checksum and a calculator if you think we can do this ourselves:
Hi Jordan, the whole LIN-bus checksum stuff is clear and handled properly by the library.

The checksum I was looking for is the one in register ID33. In message #151 I wrote:
ID33 is actually 2 bytes like so:
1720902743872.png



Which translates to:
uint8_t lindata[] = {0x00, 0x08};
lin.order(0x33, lindata, 2, lin2x);

Note 1: LIN messages are only 2, 4 or 8 bytes long, not 3.
Note 2: does the spec state something about how to calculate the E2E checksum and what the E2E counter means?
Unfortunately both documents do not shine any additional light on the E2E checksum and E2E counter values, both used in write register ID33 and read register ID34...perhaps we should just ignore these values and keep them zero when writing.

Thanks for the documents anyway!

Paul
 
Thanks Paul. Sorry I didn't understand the issue with checksum. I am wondering if the key again will be the "enable" command in 0x33 and confirming if it worked.

I will say that one item I was able to decode from the received data that made sense to me was in responseID:27, byte 3 ( Heater low side voltage measurement). When off and just connected to my computer, it read 0x23 (3.5V) and when on it read 0x83 (13.1V) so that feels good that something made sense :).
 
Hi Paul. I heard back from the engineer in China. Hope this information is helpful.

The E2E checksum and E2E counter are included in the 0x34 message, and their detailed definitions are as follows:

E2E checksum: This field is accumulated by all other fields, taking the lower 8 bits

E2E counter: The count carried by each frame is reported as 0, 1, 0, 1

In this project, only the 0x34 message has the above two fields. Regarding the definition of the above two fields in this message, Byte0 is the E2E checksum, and the lower 4 bits of Byte1 are the E2E counter




For example, receiving the following frame sequence:

Frame 1:

ID : 0x34

data :0x40 0x40 0x00

E2E checksum: Byte0=(Byte1+Byte2)&0xFF=0x40

E2E counter: cnt=(Byte1&0xF)=0

Frame 2:

Received frame ID 0x34: 0x41 0x41 0x00

E2E checksum: Byte0=(Byte1+Byte2)&0xFF=0x41

E2E counter: cnt=(Byte1&0xF)=1

Frame 3:

Received frame ID 0x34: 0x2A 0x40 0xEA

E2E checksum: Byte0=(Byte1+Byte2)&0xFF=0x2A

E2E counter: cnt=(Byte1&0xF)=0

Additionally, on our code we are using, they had this feedback:

The length of the 0x33 message is defined as 3, but the actual sending length is 2, which does not meet the definition
Similarly:
The length of the 0x17 message is defined as 3
0x27 message length is defined as 7
0x34 message length is defined as 3

Of course, the problem is that when I make the above changes back to 3s and 7, it compiles, but the CRCs stay at 255 and never kick into 0 like they do with 4s and 2s and 8s.
 
Last edited:
Thanks for forwarding the response from the engineer.
On the E2E checksum and E2E counter values, since he is talking only about read-register 0x34 ["In this project, only the 0x34 message has the above two fields"], I think we're fine with leaving these 2 values in write-register 0x33 empty.

About the message lengths:
The length of the 0x33 message is defined as 3, but the actual sending length is 2, which does not meet the definition
Well, I have to disagree with that statement, since the spec states:

1723271070181.png

It's 3 registers but 2 LIN data bytes.
Three bytes would also violate the LIN bus spec which states only 2, 4 or 8 data bytes.

0x27 message length is defined as 7
Actually, ID27 is 26 registers in 6 data bytes according to SR02-12 specification.pdf.
1723271693271.png

So that should be sent as 8 LIN data bytes.

Hmm, this does not really bring us further.
What you could ask them is whether they can supply the initialisation sequence for this heater, either in flowchart format, SFC, pseudo code or C++ code. I checked both specifications again but that info is not present.

Paul
 
Thanks again Paul. I have reached out once more with the above response to see if they can help us understand their logic and also if they can provide us with the initialization sequence. Will report back.

In the meantime, I wondered if we should try the PWM route to see if that works better, as a test. I know you have some experience in coding PWM over the teensy board from other posts I read on here. I had asked them if PWM was capable over PIN 3 of the connector, which was the PWM wire for the other heaters we were trying to crack into and the wiring diagram and connector on this heater appears to be same. Here is the info they provided to me about PWM:

We execute PWM control through software, not through external low-voltage wire for PWM signal control; The meaning of a slash on a pin is undefined. It can be understood as follows: the software inside the heater can automatically execute PWM control.

1. The PWM working frequency is 20Hz, and the PWM signal is implemented through software.
2. Full heating requires 100 duty cycles
3. The duty cycle is proportional to power, and under normal circumstances, power is proportional to heat
4. Close when the actual duty cycle is less than 1%

So is it possible to send PWM commands through the LIN wire/pin to the heater using my same board without modifying anything, if this is a software based PWM sequence?
 
From the answer of the engineer and re-reading the spec, my understanding is as follows:
1. We can not drive the heater by an external PWM signal. There is no analog or PWM pin available on the Low voltage connector.
2. Internally, the heater uses PWM to drive the IGBT's (power transistors).
3. The internal PWM signal's dutycycle is determined by the target temperature, the actual temperature and the flow of water.
4. We set, using ID22, the max power that we can deliver, the target temp and the waterflow. We enable the heater operation by ID33 Enable Heater.
5. The internal heater control circuitry tries to match target temp to actual temp, by varying the dutycycle of the internal PWM signal.

Well, let's hope they come back with more info. I'm still confused about their odd number of LIN data bytes.

Paul
 
Thanks Paul. I am confused about PWM. Can we bypass the LIN BUS roadblock and just change our programming to act like PWM or are you saying the LIN and PWM are interconnected and we cannot program for PWM as an alternative to PWM? I understood them to say either was available.

If we can try PWM independent of Lin, can I use my current board or would we have modify it?

Also, would the coding be from scratch? I would love to experiment with this to see if it is a solution.

I run the TESLA AC compressor over PWM (external PWM wire with analog input and control).

Sorry if I am being dense . . .
 
Can we bypass the LIN BUS roadblock and just change our programming to act like PWM or are you saying the LIN and PWM are interconnected and we cannot program for PWM as an alternative to PWM? I understood them to say either was available.
Hi Jordan, sorry, we cannot bypass the LIN roadblock. The heater only accepts LIN data for control...

1723488202434.png

What the engineer stated is: "We execute PWM control through software, not through external low-voltage wire for PWM signal control". And then he writes out 4 points how the internal PWM signal control works.
That internal PWM signal is generated by the MCU block and sent to the IGBT driver block.

To be honest: you do not really want to code your own external PWM-based temp/power control. To do so, you also need will need to read-out the internal temp sensors, the current sensor and all the protection circuits.
Yes, it's a pity that the heater does not have an analog control input.

Paul
 
Ah. That makes sense. Well, darn. I think my next step might be to go back to the testing you and I did for the other heaters, but knowing we have the right IDs and baud rate. I wonder if we work to see if we can get the heater feedback to show something like power input by running through all 255 number against each of the bytes in ID 33.
 
Paul, can you help me understand how you de-code the HEX feedback on on byte consisting of various bits?

For this response:

Code:
responseID:27,Response Data:0x20 0x3F 0x00 0x83 0x3F 0x80 0x00 0x75 ,CRC2:0

I am curious how to understand how Byte 0 reading 0x20 gives me the answer to how each of the bits/registers recorded its data.

0x27 Byte 0 Bit 0 Length 1 = (0 or 1)
0x27 Byte 0 Bit 1 Length 1 = (0 or 1)
0x27 Byte 0 Bit 2 Length 1 = (0 or 1)
0x27 Byte 0 Bit 3 Length 1 = (0 or 1)
0x27 Byte 0 Bit 4 Length 1 = (0 or 1)
0x27 Byte 0 Bit 5 Length 3 = Heater Status (0-7)

So with a response of 0x20, how do I know the responses to each of the above items?

Let me know if I did this correctly. 0x20 converts to binary of 00100000 in the calculator, but I need to flip this for the LSB to be first and MSB to be second, so it would convert to 00000001. If this is correct, then my answers would be:

0x27 Byte 0 Bit 0 Length 1 = 0
0x27 Byte 0 Bit 1 Length 1 = 0
0x27 Byte 0 Bit 2 Length 1 = 0
0x27 Byte 0 Bit 3 Length 1 = 0
0x27 Byte 0 Bit 4 Length 1 = 0
0x27 Byte 0 Bit 5 Length 3 = 1 (001)

Did I do this correctly?
 
Last edited:
@jsimonkeller:

I'll jump in, in case you're looking for a quick turn-around on an answer.

You correctly converted (hex) 0x20 to (binary) 00100000. In the binary representation of any hex number, no bits need to either be rearranged and/or flipped. A number represented in binary format would be read from left-to-right as individual bits, where any individual bit can take on one of two values (0 or 1).

With that in mind, when writing about individual bit positions and values, by convention, the bits are named/referenced as Bit 0 (right-most bit) to Bit 7 (left-most bit).

So, looking at the binary representation of 0x20 again, you would interpret the binary number 00100000 as follows:

Bit 0 = 0 (the right-most bit)
Bit 1 = 0
Bit 2 = 0
Bit 3 = 0
Bit 4 = 0
Bit 5 = 1
Bit 6 = 0
Bit 7 = 0 (the left-most bit)

For the heater status (contained in Bit 5, Bit 6, and Bit 7), to determine the actual value, you would read those three bits left-to-right as a 3-bit number. Again, using the same 00100000 binary representation, this value in the upper three bits would be read as 001. So, even though you did arrive at the same/right answer, my primary intent is to make sure that you understand how the bits are named, and their positions within the binary representation (with emphasis on "without flipping and/or rearrangement").

Does that make sense ?? Hopefully, that helps to understand how to read the individual bits from the binary representation to aid in interpreting the status returned by your heater.

Mark J Culross
KD5RXT
 
Thank You! That makes total sense and I really appreciate the thorough answer and explanation, as my way of doing it, while correct in this instance, would not have been in other instances. Reading Left (7) to Right (0) makes the whole process much easier for me to not mess it up.

I really appreciate forums like this one and the kind people who share of their knowledge for a better informed community.
 
Hi Jordan,
Looked further into the odd/even number of databytes.
Several sources [incl Wikipedia] state that the number of bytes must be 2, 4 or 8.
However when studying the LIN 2.1 spec, no such requirement is there.
So last night I contacted CSS Electronics about this and they replied this morning.
This '2, 4 or 8 bytes' requirement is from the LIN 2.0 spec:

1723536175645.png


For LIN 2.1, there isn't such a requirement: the number of bytes can be 1 up to 8. So the engineer may be right after all.
Long time ago when I worked for NCR, the then CEO Charles Exley had this saying: "check your references"...he was right.

Since the heater is supposed to be LIN2.1 compliant, I went over the heater spec again and modified your latest sketch above to the bare minimum in order for you to give it a try. Put some reasonable values in for power and temp.
The code below only writes ID22 & ID33 and reads ID17. That should be enough to kick-off the heater and read back the "Measured powerconsumption" and "Desired power consumption". If "Measured powerconsumption" is zero than we know the heater is not on.

By the way, don't know whether we ever discussed it but what is your HV power source? An actual car or?

Paul

C++:
#include "lin_bus.h"

LIN lin;

int lin_cs = 32;  // cs and serial port set for skpang LIN / FDCAN board
int led1 = 23;
int lin_fault = 28;

uint8_t data[4];
uint8_t CRC = 0;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(lin_fault, INPUT);
  pinMode(lin_cs, OUTPUT);
  digitalWrite(lin_cs, HIGH);  // enable MCP2004 LIN transceiver
  digitalWrite(LED_BUILTIN, HIGH);

  Serial.begin(115200);
  //Serial.print("Heater demo");

  lin.begin(&Serial3, 19200);
  delay(100);  // wait 100ms
  pinMode(led1, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
}

void loop() {
  // write ID22 register
  uint8_t lindata[] = { 0x19, 0x78, 0xFF, 0xD0 };  // 1000W power, 80C target temp, max flow, enable
  lin.order(0x22, lindata, 4, lin2x);
  delay(100);

  // write ID33 register
  uint8_t lindata2[] = { 0x00, 0x08 };  // chechksum 0, counter 0, enable
  lin.order(0x33, lindata2, 2, lin2x);
  delay(100);

  // read ID17 register
  CRC = lin.response(0x17, data, 3, lin2x);  // we need 10+10 bits according to spec, so request 3 bytes
  Serial.print("responseID:");
  Serial.print(0x17, HEX);
  Serial.print(",");
  Serial.print("Response Data:");
  for (int i = 0; i < 3; i++) {  // display received data
    Serial.printf("0x%.2X ", data[i]);
  }
  Serial.print(",");
  Serial.print("CRC:");
  Serial.println(CRC); // -1 indicates crc error

  delay(5000);
}
 
Hi Paul. Thanks for the update. I love the folks at CSS. They answered a lot of questions for me when I was converting my brake booster analog signal to CAN so I could control the creep function on the car through the Open Inverter can bus.


I will try this out and let you know, BUT since ID33 is also a 3 byte message, shouldn't we also modify ID 33 to:

Code:
lin.order(0x33, lindata2, 3, lin2x);

If we do this, do we need to put a number into BYTE 3 as well?

Code:
 uint8_t lindata2[] = { 0x00, 0x08, ????? };  // checksum 0, counter 0, enable

I am using the car as the power source. Pushing out around 315V right now (range is 250-350V).

Powering LV with my portable 13V power source until am ready to put it into the car.


Car Heater Test.jpg
 
I imported the above code and here was the response without 12V (along with HV ready):

responseID:17,Response Data:0x00 0x00 0x1B ,CRC:255
responseID:17,Response Data:0x00 0x00 0x1B ,CRC:255
responseID:17,Response Data:0x00 0x00 0x1B ,CRC:255

Once the 12V was applied to the heater (along with HV ready):

responseID:17,Response Data:0x00 0x00 0x00 ,CRC:255
responseID:17,Response Data:0x00 0x00 0x00 ,CRC:255
responseID:17,Response Data:0x00 0x00 0x00 ,CRC:255
 
Back
Top