feature: in EvseMode, control the power supply via special homeplug message

This commit is contained in:
uhi22 2024-05-26 21:47:02 +02:00
parent 7e86812032
commit a5991434e1
4 changed files with 66 additions and 5 deletions

View file

@ -263,6 +263,8 @@ class fsmEvse():
if (self.simulatedPresentVoltage<uTarget): if (self.simulatedPresentVoltage<uTarget):
self.simulatedPresentVoltage += 5 self.simulatedPresentVoltage += 5
strPresentVoltage = str(self.simulatedPresentVoltage) # "345" strPresentVoltage = str(self.simulatedPresentVoltage) # "345"
# in case we control a real power supply: give the precharge target to it
self.hardwareInterface.setPowerSupplyVoltageAndCurrent(uTarget, 1)
self.callbackShowStatus(strPresentVoltage, "EVSEPresentVoltage") self.callbackShowStatus(strPresentVoltage, "EVSEPresentVoltage")
msg = addV2GTPHeader(exiEncode("E"+self.schemaSelection+"g_"+strPresentVoltage)) # EDg for Encode, Din, PreChargeResponse msg = addV2GTPHeader(exiEncode("E"+self.schemaSelection+"g_"+strPresentVoltage)) # EDg for Encode, Din, PreChargeResponse
if (testsuite_faultinjection_is_triggered(TC_EVSE_Shutdown_during_PreCharge)): if (testsuite_faultinjection_is_triggered(TC_EVSE_Shutdown_during_PreCharge)):
@ -434,7 +436,7 @@ class fsmEvse():
self.callbackShowStatus = callbackShowStatus self.callbackShowStatus = callbackShowStatus
self.callbackSoCStatus = callbackSoCStatus self.callbackSoCStatus = callbackSoCStatus
#todo self.addressManager = addressManager #todo self.addressManager = addressManager
#todo self.hardwareInterface = hardwareInterface self.hardwareInterface = hardwareInterface
self.addToTrace("initializing fsmEvse") self.addToTrace("initializing fsmEvse")
self.faultInjectionDelayUntilSocketOpen_s = 0 self.faultInjectionDelayUntilSocketOpen_s = 0
if (self.faultInjectionDelayUntilSocketOpen_s>0): if (self.faultInjectionDelayUntilSocketOpen_s>0):

View file

@ -156,6 +156,10 @@ class hardwareInterface():
self.chargerVoltage = int(voltageNow) self.chargerVoltage = int(voltageNow)
self.chargerCurrent = int(currentNow) self.chargerCurrent = int(currentNow)
def setPowerSupplyVoltageAndCurrent(self, targetVoltage, targetCurrent):
# if we are the charger, and have a real power supply which we want to control, we do it here
self.homeplughandler.sendSpecialMessageToControlThePowerSupply(targetVoltage, targetCurrent)
def getInletVoltage(self): def getInletVoltage(self):
# uncomment this line, to take the simulated inlet voltage instead of the really measured # uncomment this line, to take the simulated inlet voltage instead of the really measured
# self.inletVoltage = self.simulatedInletVoltage # self.inletVoltage = self.simulatedInletVoltage
@ -224,9 +228,10 @@ class hardwareInterface():
GPIO.setup(PinPowerRelay, GPIO.OUT) #output for port relays GPIO.setup(PinPowerRelay, GPIO.OUT) #output for port relays
GPIO.setup(PinCp, GPIO.OUT) #output for CP GPIO.setup(PinCp, GPIO.OUT) #output for CP
def __init__(self, callbackAddToTrace=None, callbackShowStatus=None): def __init__(self, callbackAddToTrace=None, callbackShowStatus=None, homeplughandler=None):
self.callbackAddToTrace = callbackAddToTrace self.callbackAddToTrace = callbackAddToTrace
self.callbackShowStatus = callbackShowStatus self.callbackShowStatus = callbackShowStatus
self.homeplughandler = homeplughandler
self.loopcounter = 0 self.loopcounter = 0
self.outvalue = 0 self.outvalue = 0

View file

@ -358,6 +358,37 @@ class pyPlcHomeplug():
self.fillRunId(36) # 36 to 43 runid 8 bytes self.fillRunId(36) # 36 to 43 runid 8 bytes
# rest is 00 # rest is 00
def composeSpecialMessage(self):
# special "homeplug" message, to control a hardware device.
# We re-purpose the ATTEN_CHAR.IND, because a AR4720 PEV modem is transparent for it also in unpaired state,
# and it contains a lot of space which can be used to transfer data. Also it is not expected to disturb the
# normal traffic, because it may be also caused by cross-coupling from an other charger, and the normal
# communication should be immune to such things.
self.mytransmitbuffer = bytearray(129)
self.cleanTransmitBuffer()
# Destination MAC
self.fillDestinationMac(MAC_BROADCAST)
# Source MAC
self.fillSourceMac(self.myMAC)
# Protocol
self.mytransmitbuffer[12]=0x88 # Protocol HomeplugAV
self.mytransmitbuffer[13]=0xE1
self.mytransmitbuffer[14]=0x01 # version
self.mytransmitbuffer[15]=0x6E # ATTEN_CHAR.IND
self.mytransmitbuffer[16]=0x60 #
self.mytransmitbuffer[17]=0x00 # 2 bytes fragmentation information. 0000 means: unfragmented.
self.mytransmitbuffer[18]=0x00 #
self.mytransmitbuffer[19]=0x00 # apptype
self.mytransmitbuffer[20]=0x00 # security
self.fillDestinationMac(MAC_BROADCAST, 21) # The wireshark calls it source_mac, but alpitronic fills it with PEV mac.
self.fillRunId(27) # runid 8 bytes
self.mytransmitbuffer[35]=0x00 # 35 - 51 source_id, 17 bytes. The alpitronic fills it with 00
self.mytransmitbuffer[52]=0x00 # 52 - 68 response_id, 17 bytes. The alpitronic fills it with 00.
self.mytransmitbuffer[69]=0x0A # Number of sounds. 10 in normal case.
self.mytransmitbuffer[70]=0x3A # Number of groups = 58.
for i in range(71, 129): # 71 to 128: 58 special-purpose-bytes
self.mytransmitbuffer[i]=self.specialMessageTransmitBuffer[i-71]
def composeStartAttenCharInd(self): def composeStartAttenCharInd(self):
# reference: see wireshark interpreted frame from ioniq # reference: see wireshark interpreted frame from ioniq
self.mytransmitbuffer = bytearray(60) self.mytransmitbuffer = bytearray(60)
@ -555,6 +586,10 @@ class pyPlcHomeplug():
self.composeGetSwWithRamdomMac() self.composeGetSwWithRamdomMac()
self.addToTrace("transmitting GetSwWithRamdomMac") self.addToTrace("transmitting GetSwWithRamdomMac")
self.transmit(self.mytransmitbuffer) self.transmit(self.mytransmitbuffer)
if (selection=="9"):
self.composeSpecialMessage()
self.addToTrace("transmitting SpecialMessage")
self.transmit(self.mytransmitbuffer)
def transmit(self, pkt): def transmit(self, pkt):
self.sniffer.sendpacket(bytes(pkt)) self.sniffer.sendpacket(bytes(pkt))
@ -1069,6 +1104,24 @@ class pyPlcHomeplug():
self.strInterfaceName=getConfigValue("eth_interface") self.strInterfaceName=getConfigValue("eth_interface")
print("Linux interface is " + self.strInterfaceName) print("Linux interface is " + self.strInterfaceName)
def sendSpecialMessageToControlThePowerSupply(self, targetVoltage, targetCurrent):
u = int(targetVoltage*10) # resolution: 0.1 volt
i = int(targetCurrent*10) # resolution: 0.1 ampere
self.specialMessageTransmitBuffer[0] = 0xAF # Header 3 byte
self.specialMessageTransmitBuffer[1] = 0xFE #
self.specialMessageTransmitBuffer[2] = 0xDC #
self.specialMessageTransmitBuffer[3] = u >> 8 # target voltage, MSB first
self.specialMessageTransmitBuffer[4] = u & 0xFF # target voltage, LSB
self.specialMessageTransmitBuffer[5] = u >> 8 # same again, for plausibilization
self.specialMessageTransmitBuffer[6] = u & 0xFF
self.specialMessageTransmitBuffer[7] = i >> 8 # target current, MSB first
self.specialMessageTransmitBuffer[8] = i & 0xFF # target current, LSB
self.specialMessageTransmitBuffer[9] = i >> 8 # same again
self.specialMessageTransmitBuffer[10] = i & 0xFF
self.composeSpecialMessage()
self.addToTrace("transmitting SpecialMessage to control the power supply")
self.transmit(self.mytransmitbuffer)
def enterPevMode(self): def enterPevMode(self):
self.iAmEvse = 0 # not emulating a charging station self.iAmEvse = 0 # not emulating a charging station
self.iAmPev = 1 # emulating a vehicle self.iAmPev = 1 # emulating a vehicle
@ -1134,6 +1187,7 @@ class pyPlcHomeplug():
# a default pev RunId. Will be overwritten later, if we are evse. If we are the pev, we are free to choose a # a default pev RunId. Will be overwritten later, if we are evse. If we are the pev, we are free to choose a
# RunID, e.g. the Ioniq uses the MAC plus 0x00 0x00 padding, the Tesla uses "TESLA EV". # RunID, e.g. the Ioniq uses the MAC plus 0x00 0x00 padding, the Tesla uses "TESLA EV".
self.pevRunId = [0xDC, 0x0E, 0xA1, 0xDE, 0xAD, 0xBE, 0xEF, 0x55 ] self.pevRunId = [0xDC, 0x0E, 0xA1, 0xDE, 0xAD, 0xBE, 0xEF, 0x55 ]
self.specialMessageTransmitBuffer = bytearray(58)
self.myMAC = self.addressManager.getLocalMacAddress() self.myMAC = self.addressManager.getLocalMacAddress()
self.runningCounter=0 self.runningCounter=0
self.ipv6 = pyPlcIpv6.ipv6handler(self.transmit, self.addressManager, self.connMgr, self.callbackShowStatus) self.ipv6 = pyPlcIpv6.ipv6handler(self.transmit, self.addressManager, self.connMgr, self.callbackShowStatus)

View file

@ -31,7 +31,7 @@ class pyPlcWorker():
self.isSimulationMode = isSimulationMode self.isSimulationMode = isSimulationMode
self.connMgr = connMgr.connMgr(self.workerAddToTrace, self.showStatus) self.connMgr = connMgr.connMgr(self.workerAddToTrace, self.showStatus)
self.hp = pyPlcHomeplug.pyPlcHomeplug(self.workerAddToTrace, self.showStatus, self.mode, self.addressManager, self.connMgr, self.isSimulationMode) self.hp = pyPlcHomeplug.pyPlcHomeplug(self.workerAddToTrace, self.showStatus, self.mode, self.addressManager, self.connMgr, self.isSimulationMode)
self.hardwareInterface = hardwareInterface.hardwareInterface(self.workerAddToTrace, self.showStatus) self.hardwareInterface = hardwareInterface.hardwareInterface(self.workerAddToTrace, self.showStatus, self.hp)
self.hp.printToUdp("pyPlcWorker init") self.hp.printToUdp("pyPlcWorker init")
# Find out the version number, using git. # Find out the version number, using git.
# see https://stackoverflow.com/questions/14989858/get-the-current-git-hash-in-a-python-script # see https://stackoverflow.com/questions/14989858/get-the-current-git-hash-in-a-python-script