From a5991434e1a9bd9b5998ef415508d7f239f334b8 Mon Sep 17 00:00:00 2001 From: uhi22 Date: Sun, 26 May 2024 21:47:02 +0200 Subject: [PATCH] feature: in EvseMode, control the power supply via special homeplug message --- fsmEvse.py | 4 ++- hardwareInterface.py | 7 +++++- pyPlcHomeplug.py | 58 ++++++++++++++++++++++++++++++++++++++++++-- pyPlcWorker.py | 2 +- 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/fsmEvse.py b/fsmEvse.py index 910a513..a802cd1 100644 --- a/fsmEvse.py +++ b/fsmEvse.py @@ -263,6 +263,8 @@ class fsmEvse(): if (self.simulatedPresentVoltage0): diff --git a/hardwareInterface.py b/hardwareInterface.py index 534fa19..793bd5e 100644 --- a/hardwareInterface.py +++ b/hardwareInterface.py @@ -155,6 +155,10 @@ class hardwareInterface(): def setChargerVoltageAndCurrent(self, voltageNow, currentNow): self.chargerVoltage = int(voltageNow) 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): # uncomment this line, to take the simulated inlet voltage instead of the really measured @@ -224,9 +228,10 @@ class hardwareInterface(): GPIO.setup(PinPowerRelay, GPIO.OUT) #output for port relays 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.callbackShowStatus = callbackShowStatus + self.homeplughandler = homeplughandler self.loopcounter = 0 self.outvalue = 0 diff --git a/pyPlcHomeplug.py b/pyPlcHomeplug.py index a1e4273..f751a4e 100644 --- a/pyPlcHomeplug.py +++ b/pyPlcHomeplug.py @@ -357,7 +357,38 @@ class pyPlcHomeplug(): self.mytransmitbuffer[35]=0x00 # self.fillRunId(36) # 36 to 43 runid 8 bytes # 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): # reference: see wireshark interpreted frame from ioniq self.mytransmitbuffer = bytearray(60) @@ -555,6 +586,10 @@ class pyPlcHomeplug(): self.composeGetSwWithRamdomMac() self.addToTrace("transmitting GetSwWithRamdomMac") self.transmit(self.mytransmitbuffer) + if (selection=="9"): + self.composeSpecialMessage() + self.addToTrace("transmitting SpecialMessage") + self.transmit(self.mytransmitbuffer) def transmit(self, pkt): self.sniffer.sendpacket(bytes(pkt)) @@ -1068,7 +1103,25 @@ class pyPlcHomeplug(): # Take the interface name from the ini file. For Linux, this is all we need. self.strInterfaceName=getConfigValue("eth_interface") 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): self.iAmEvse = 0 # not emulating a charging station 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 # 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.specialMessageTransmitBuffer = bytearray(58) self.myMAC = self.addressManager.getLocalMacAddress() self.runningCounter=0 self.ipv6 = pyPlcIpv6.ipv6handler(self.transmit, self.addressManager, self.connMgr, self.callbackShowStatus) diff --git a/pyPlcWorker.py b/pyPlcWorker.py index c2761c9..00efbdf 100644 --- a/pyPlcWorker.py +++ b/pyPlcWorker.py @@ -31,7 +31,7 @@ class pyPlcWorker(): self.isSimulationMode = isSimulationMode 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.hardwareInterface = hardwareInterface.hardwareInterface(self.workerAddToTrace, self.showStatus) + self.hardwareInterface = hardwareInterface.hardwareInterface(self.workerAddToTrace, self.showStatus, self.hp) self.hp.printToUdp("pyPlcWorker init") # Find out the version number, using git. # see https://stackoverflow.com/questions/14989858/get-the-current-git-hash-in-a-python-script