strncpy(config->luaScript, R"( AIRBAG = 0x050 -- 1088 TCU_1 = 0x440 -- 1344 TCU_2 = 0x540 -- 1440 BRAKE_2 = 0x5A0 VWTP_OUT = 0x200 VWTP_IN = 0x202 VWTP_TESTER = 0x300 -- 640 MOTOR_1 = 0x280 -- 644 MOTOR_BRE = 0x284 -- 648 MOTOR_2 = 0x288 -- 896 MOTOR_3 = 0x380 -- 1152 MOTOR_5 = 0x480 -- 1160 MOTOR_6 = 0x488 -- 1386 ACC_GRA = 0x56A -- 1408 the one with variable payload MOTOR_INFO = 0x580 -- 1416 MOTOR_7 = 0x588 TCU_BUS = 1 fakeTorque = 0 hexstr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "A", "B", "C", "D", "E", "F" } function toHexString(num) if num == 0 then return '0' end local result = "" while num > 0 do local n = num % 16 result = hexstr[n + 1] ..result num = math.floor(num / 16) end return result end function arrayToString(arr) local str = "" local index = 1 while arr[index] ~= nil do str = str.." "..toHexString(arr[index]) index = index + 1 end return str end function onTcu1(bus, id, dlc, data) -- print("onTcu1") end function onTcu2(bus, id, dlc, data) -- print("onTcu2") end motor5FuelCounter = 0 function setBitRange(data, totalBitIndex, bitWidth, value) local byteIndex = totalBitIndex >> 3 local bitInByteIndex = totalBitIndex - byteIndex * 8 if (bitInByteIndex + bitWidth > 8) then bitsToHandleNow = 8 - bitInByteIndex setBitRange(data, totalBitIndex + bitsToHandleNow, bitWidth - bitsToHandleNow, value >> bitsToHandleNow) bitWidth = bitsToHandleNow end mask = (1 << bitWidth) - 1 data[1 + byteIndex] = data[1 + byteIndex] & (~(mask << bitInByteIndex)) maskedValue = value & mask shiftedValue = maskedValue << bitInByteIndex data[1 + byteIndex] = data[1 + byteIndex] | shiftedValue end function setTwoBytes(data, offset, value) data[offset + 1] = value % 255 data[offset + 2] = (value >> 8) % 255 end shallSleep = Timer.new() -- we want to turn on with hardware switch while ignition key is off hadIgnitionEvent = false function onAirBag(bus, id, dlc, data) -- looks like we have ignition key do not sleep! shallSleep : reset() hadIgnitionEvent = true end function setTwoBytes(data, offset, value) value = math.floor(value) data[offset + 2] = value >> 8 data[offset + 1] = value & 0xff end function xorChecksum(data, targetIndex) local index = 1 local result = 0 while data[index] ~= nil do if index ~= targetIndex then result = result ~ data[index] end index = index + 1 end data[targetIndex] = result return result end function getBitRange(data, bitIndex, bitWidth) byteIndex = bitIndex >> 3 shift = bitIndex - byteIndex * 8 value = data[1 + byteIndex] if (shift + bitWidth > 8) then value = value + data[2 + byteIndex] * 256 end mask = (1 << bitWidth) - 1 return (value >> shift) & mask end motor1Data = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } motorBreData = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } motor2Data = { 0x8A, 0x8D, 0x10, 0x04, 0x00, 0x4C, 0xDC, 0x87 } motor2mux = {0x8A, 0xE8, 0x2C, 0x64} canMotorInfo = { 0x00, 0x00, 0x00, 0x14, 0x1C, 0x93, 0x48, 0x14 } canMotorInfo1= { 0x99, 0x14, 0x00, 0x7F, 0x00, 0xF0, 0x47, 0x01 } canMotorInfo3= { 0x9B, 0x14, 0x00, 0x11, 0x1F, 0xE0, 0x0C, 0x46 } canMotor3 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } motor5Data = { 0x1C, 0x08, 0xF3, 0x55, 0x19, 0x00, 0x00, 0xAD } motor6Data = { 0x00, 0x00, 0x00, 0x7E, 0xFE, 0xFF, 0xFF, 0x00 } motor7Data = { 0x1A, 0x66, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00 } accGraData = { 0x00, 0x00, 0x08, 0x00, 0x1A, 0x00, 0x02, 0x01 } setTickRate(100) everySecondTimer = Timer.new() canMotorInfoCounter = 0 motorBreCounter = 0 --accGraCounter = 0 counter16 = 0 motor2counter = 0 mafSensor = Sensor.new("maf") mafCalibrationIndex = findCurveIndex("mafcurve") canMotorInfoTotalCounter = 0 function onAccGra(bus, id, dlc, data) print("onAccGra") end canRxAdd(AIRBAG, onAirBag) canRxAdd(TCU_1, onTcu1) canRxAdd(TCU_2, onTcu2) -- canRxAdd(BRAKE_2) -- canRxAdd(1, ACC_GRA, onAccGra) function onMotor1(bus, id, dlc, data) rpm = getSensor("RPM") or 0 tps = getSensor("TPS1") or 0 fakeTorque = interpolate(0, 6, 100, 60, tps) engineTorque = fakeTorque * 0.9 innerTorqWithoutExt = fakeTorque torqueLoss = 20 requestedTorque = fakeTorque motor1Data[2] = engineTorque / 0.39 setTwoBytes(motor1Data, 2, rpm / 0.25) motor1Data[5] = innerTorqWithoutExt / 0.4 motor1Data[6] = tps / 0.4 motor1Data[7] = torqueLoss / 0.39 motor1Data[8] = requestedTorque / 0.39 -- print ('MOTOR_1 fakeTorque ' ..fakeTorque) -- print ('MOTOR_1 engineTorque ' ..engineTorque ..' RPM ' ..rpm) -- print ('MOTOR_1 innerTorqWithoutExt ' ..innerTorqWithoutExt ..' tps ' ..tps) -- print ('MOTOR_1 torqueLoss ' ..torqueLoss ..' requestedTorque ' ..requestedTorque) txCan(TCU_BUS, MOTOR_1, 0, motor1Data) end function onMotor3(bus, id, dlc, data) iat = getSensor("IAT") or 0 tps = getSensor("TPS1") or 0 desired_wheel_torque = fakeTorque canMotor3[2] = (iat + 48) / 0.75 canMotor3[3] = tps / 0.4 canMotor3[5] = 0x20 setBitRange(canMotor3, 24, 12, math.floor(desired_wheel_torque / 0.39)) canMotor3[8] = tps / 0.4 txCan(TCU_BUS, MOTOR_3, 0, canMotor3) end motorBreCounter = 0 function onMotorBre(bus, id, dlc, data) motorBreCounter = (motorBreCounter + 1) % 16 setBitRange(motorBreData, 8, 4, motorBreCounter) xorChecksum(motorBreData, 1) txCan(TCU_BUS, MOTOR_BRE, 0, motorBreData) end motor2counter = 0 function onMotor2(bus, id, dlc, data) motor2counter = (motor2counter + 1) % 16 minTorque = fakeTorque / 2 -- todo: add CLT motor2Data[7] = math.floor(minTorque / 0.39) --print ( "brake " .. getBitRange(data, 16, 2) .. " " .. rpm) brakeBit = rpm < 2000 and 1 or 0 setBitRange(motor2Data, 16, 1, brakeBit) index = math.floor(motor2counter / 4) motor2Data[1] = motor2mux[1 + index] txCan(TCU_BUS, MOTOR_2, 0, motor2Data) end motor5FuelCounter = 0 function onMotor5(bus, id, dlc, data) setBitRange(motor5Data, 5, 9, motor5FuelCounter) xorChecksum(motor5Data, 8) txCan(TCU_BUS, MOTOR_5, 0, motor5Data) end counter16 = 0 function onMotor6(bus, id, dlc, data) counter16 = (counter16 + 1) % 16 -- engineTorque = getBitRange(data, 8, 8) * 0.39 -- actualTorque = getBitRange(data, 16, 8) * 0.39 -- feedbackGearbox = getBitRange(data, 40, 8) * 0.39 engineTorque = fakeTorque * 0.9 actualTorque = fakeTorque feedbackGearbox = 255 motor6Data[2] = math.floor(engineTorque / 0.39) motor6Data[3] = math.floor(actualTorque / 0.39) motor6Data[6] = math.floor(feedbackGearbox / 0.39) setBitRange(motor6Data, 60, 4, counter16) xorChecksum(motor6Data, 1) txCan(TCU_BUS, MOTOR_6, 0, motor6Data) end accGraCounter = 0 function onAccGra(bus, id, dlc, data) accGraCounter = (accGraCounter + 1) % 16 setBitRange(accGraData, 60, 4, accGraCounter) xorChecksum(accGraData, 1) txCan(TCU_BUS, id, 0, accGraData) -- txCan(TCU_BUS, id, 0, data) end canMotorInfoCounter = 0 function onMotorInfo(bus, id, dlc, data) canMotorInfoTotalCounter = canMotorInfoTotalCounter + 1 canMotorInfoCounter = (canMotorInfoCounter + 1) % 16 -- canMotorInfoCounter = getBitRange(data, 0, 4) baseByte = canMotorInfoTotalCounter < 6 and 0x80 or 0x90 canMotorInfo[1] = baseByte + (canMotorInfoCounter) canMotorInfo1[1] = baseByte + (canMotorInfoCounter) canMotorInfo3[1] = baseByte + (canMotorInfoCounter) mod4 = canMotorInfoCounter % 4 if (mod4 == 0 or mod4 == 2) then txCan(TCU_BUS, MOTOR_INFO, 0, canMotorInfo) elseif (mod4 == 1) then txCan(TCU_BUS, MOTOR_INFO, 0, canMotorInfo1) else txCan(TCU_BUS, MOTOR_INFO, 0, canMotorInfo3) end end function onMotor7(bus, id, dlc, data) txCan(TCU_BUS, MOTOR_7, 0, motor7Data) end local tcuId = 0 function onCanHello(bus, id, dlc, data) -- here we handle 201 packets print('Got Hello Response ' ..arrayToString(data)) tcuId = data[6] * 256 + data[5] print('From TCU ' ..tcuId) txCan(1, tcuId, 0, { 0xA0, 0x0F, 0x8A, 0xFF, 0x32, 0xFF }) end local sendCounter = 2 local packetCounter = 1 local payLoadIndex = 0 local groups = { 10 } -- todo: smarter array size calculation? local groupsSize = 1 local groupIndex = 1 function onCanTester(bus, id, dlc, data) -- here we handle 300 packets -- print('Got from tester ' ..arrayToString(data)) if data[1] == 0xA3 then -- print ("Keep-alive") txCan(1, tcuId, 0, { 0xA1, 0x0F, 0x8A, 0xFF, 0x4A, 0xFF }) groupIndex = groupIndex + 1 if groupIndex > groupsSize then groupIndex = 1 end groupId = groups[groupIndex] print (groupIndex .." " ..groupId) reqFirst = 0x10 + sendCounter print("Requesting next group " ..groupId .." with counter " ..sendCounter) txCan(1, tcuId, 0, { reqFirst, 0x00, 0x02, 0x21, groupId }) sendCounter = sendCounter + 1 if sendCounter == 16 then sendCounter = 0 end return end if data[1] == 0xA1 then print ("Happy 300 packet") txCan(1, tcuId, 0, { 0x10, 0x00, 0x02, 0x10, 0x89 }) return end if data[1] == 0xA8 then print ("They said Bye-Bye") return end if data[1] == 0x10 and dlc == 5 then ackPacket = 0xB0 + packetCounter print ("Sending ACK B1 " ..ackPacket) txCan(1, tcuId, 0, { ackPacket }) -- request first group from array txCan(1, tcuId, 0, { 0x11, 0x00, 0x02, 0x21, groups[1] }) return end top4 = math.floor(data[1] / 16) if top4 == 0xB then -- print("Got ACK") return end if top4 == 2 or top4 == 1 then print ("Looks like payload index " ..payLoadIndex ..": " ..arrayToString(data)) if groupId == 2 and payLoadIndex == 0 then L7 = data[7] H9 = data[8] V = 256 * H9 + L7 print("V 0 " ..V) end if groupId == 2 and payLoadIndex == 1 then L3 = data[3] H4 = data[4] V = 256 * H4 + L3 print("V 1 " ..V) end if groupId == 2 and payLoadIndex == 2 then L2 = data[2] H3 = data[3] V = 256 * H3 + L2 print("V 2 " ..V) end payLoadIndex = payLoadIndex + 1 packetCounter = packetCounter + 1 if packetCounter > 15 then packetCounter = 0 end if top4 == 1 then ackPacket = 0xB0 + packetCounter print ("Sending payload ACK " ..ackPacket) txCan(1, tcuId, 0, { ackPacket }) payLoadIndex = 0 end return end print('Got unexpected ' ..arrayToString(data)) end canRxAdd(VWTP_IN, onCanHello) txCan(1, VWTP_OUT, 0, { 0x02, 0xC0, 0x00, 0x10, 0x00, 0x03, 0x01 }) function onTick() freqValue = getSensor("AuxSpeed1") or 0 mafValue = curve(mafCalibrationIndex, 5) -- print(freqValue .. " mafValue=" .. mafValue) mafSensor : set(mafValue) counter16 = (counter16 + 1) % 16 rpm = getSensor("RPM") or 0 vbat = getSensor("BatteryVoltage") or 0 if rpm == 0 then canMotorInfoTotalCounter = 0 end onMotor1(0, 0, 0, nil) onMotor3(0, 0, 0, nil) onMotorBre(0, 0, 0, nil) onMotor2(0, 0, 0, nil) onMotor5(0, 0, 0, nil) onMotor6(0, 0, 0, nil) onMotor7(0, 0, 0, nil) onAccGra(0, 0, 0, nil) local timeToTurnOff = shallSleep : getElapsedSeconds() > 2 local connectedToUsb = vbat < 4 if hadIgnitionEvent and timeToTurnOff then -- looks like ignition key was removed -- mcu_standby() end if everySecondTimer : getElapsedSeconds() > 1 then everySecondTimer : reset() motor5FuelCounter = motor5FuelCounter + 20 onMotorInfo(0, 0, 0, nil) end end )", efi::size(config->luaScript));