mirror of
https://github.com/uhi22/pyPLC.git
synced 2024-12-20 01:31:52 +00:00
Merge branch 'master' of https://github.com/uhi22/pyPLC
This commit is contained in:
commit
7908def225
7 changed files with 186 additions and 26 deletions
|
@ -36,7 +36,7 @@ The open-plc-utils report the version MAC-QCA7500-2.8.0.30-01-20190707-CS.
|
||||||
Contra:
|
Contra:
|
||||||
- Even with the modified configuration for pev (according to https://openinverter.org/forum/viewtopic.php?p=55120#p55120), the QCA7500 does not send SLAC parameter requests. This means: Cannot be used as pev.
|
- Even with the modified configuration for pev (according to https://openinverter.org/forum/viewtopic.php?p=55120#p55120), the QCA7500 does not send SLAC parameter requests. This means: Cannot be used as pev.
|
||||||
|
|
||||||
### TPlink TL-PA4010P
|
### TPlink TL-PA4010P v5.0
|
||||||
This adaptor was suggested by https://openinverter.org/forum/viewtopic.php?p=37085#p37085 and there,
|
This adaptor was suggested by https://openinverter.org/forum/viewtopic.php?p=37085#p37085 and there,
|
||||||
successfully used to establish a communication to the CCS charger.
|
successfully used to establish a communication to the CCS charger.
|
||||||
|
|
||||||
|
@ -67,6 +67,12 @@ How to modify:
|
||||||
- connect cables to supply the device. Works with 12V, also works with 5V from an USB power bank.
|
- connect cables to supply the device. Works with 12V, also works with 5V from an USB power bank.
|
||||||
- connect cables and circuit (1nF and 150ohms in series) for connecting to the pilot line.
|
- connect cables and circuit (1nF and 150ohms in series) for connecting to the pilot line.
|
||||||
|
|
||||||
|
#### TPlink TL-PA4010P v2.3
|
||||||
|
![image](https://github.com/user-attachments/assets/c98d100c-f7fc-4e41-a183-1b5eeebd43fa)
|
||||||
|
![image](https://github.com/user-attachments/assets/6fef8096-4373-42d9-9688-7d284f587940)
|
||||||
|
|
||||||
|
Confirmed to be working as EVSE (needs patching via open-plc-utils) and supplied with 5V.
|
||||||
|
|
||||||
## Controller for the PEV
|
## Controller for the PEV
|
||||||
|
|
||||||
Besides the homeplug modem, there are additional parts necessary for a vehicle to perform CCS charging. Two of these are: 1. The inlet voltage measurement. 2. The control of CP state and relays.
|
Besides the homeplug modem, there are additional parts necessary for a vehicle to perform CCS charging. Two of these are: 1. The inlet voltage measurement. 2. The control of CP state and relays.
|
||||||
|
|
|
@ -44,6 +44,10 @@ light_bulb_demo = no
|
||||||
# Possible values: yes or no
|
# Possible values: yes or no
|
||||||
soc_simulation = yes
|
soc_simulation = yes
|
||||||
|
|
||||||
|
# Precharge simulation
|
||||||
|
# In EVSE mode simulate precharging rather than using physical value
|
||||||
|
evse_simulate_precharge = yes
|
||||||
|
|
||||||
# Device selection for the digital outputs, for CP state and power relays
|
# Device selection for the digital outputs, for CP state and power relays
|
||||||
# Possible options:
|
# Possible options:
|
||||||
# dieter: Serial controlled device, which controls the digital outputs. E.g. arduino from https://github.com/uhi22/dieter
|
# dieter: Serial controlled device, which controls the digital outputs. E.g. arduino from https://github.com/uhi22/dieter
|
||||||
|
@ -52,6 +56,7 @@ soc_simulation = yes
|
||||||
digital_output_device = dieter
|
digital_output_device = dieter
|
||||||
#digital_output_device = beaglebone
|
#digital_output_device = beaglebone
|
||||||
#digital_output_device = celeron55device
|
#digital_output_device = celeron55device
|
||||||
|
#digital_output_device = mqtt
|
||||||
|
|
||||||
|
|
||||||
# Device to read the physically measured inlet voltage in PevMode
|
# Device to read the physically measured inlet voltage in PevMode
|
||||||
|
@ -62,6 +67,7 @@ digital_output_device = dieter
|
||||||
#analog_input_device = dieter
|
#analog_input_device = dieter
|
||||||
analog_input_device = none
|
analog_input_device = none
|
||||||
#analog_input_device = celeron55device
|
#analog_input_device = celeron55device
|
||||||
|
#analog_input_device = mqtt
|
||||||
|
|
||||||
|
|
||||||
# Criteria for ending the PreCharge phase in PevMode
|
# Criteria for ending the PreCharge phase in PevMode
|
||||||
|
@ -89,6 +95,9 @@ u_delta_max_for_end_of_precharge = 10
|
||||||
# auto
|
# auto
|
||||||
serial_port = auto
|
serial_port = auto
|
||||||
serial_baud = 19200
|
serial_baud = 19200
|
||||||
|
# MQTT broker and base topic in case MQTT is used
|
||||||
|
mqtt_broker = localhost
|
||||||
|
mqtt_topic = pyPlc
|
||||||
|
|
||||||
# The target voltage used in the CurrentDemandRequest.
|
# The target voltage used in the CurrentDemandRequest.
|
||||||
# This is a value for first try-outs. Better would
|
# This is a value for first try-outs. Better would
|
||||||
|
@ -131,10 +140,13 @@ udp_syslog_enable = Yes
|
||||||
# Set backend for obtaining charging parameters, we start with CHAdeMO CAN for now
|
# Set backend for obtaining charging parameters, we start with CHAdeMO CAN for now
|
||||||
# Need to make a simulator device and maybe a celeron device?
|
# Need to make a simulator device and maybe a celeron device?
|
||||||
# Possible values:
|
# Possible values:
|
||||||
|
# mqtt: pyPLC is used as a bridge between an MQTT broker and the car or EVSE
|
||||||
# chademo: pyPLC is used as bridge between a CCS charger and a CHAdeMO* car.
|
# chademo: pyPLC is used as bridge between a CCS charger and a CHAdeMO* car.
|
||||||
# Limitations/explanations here: https://openinverter.org/forum/viewtopic.php?p=57894#p57894 and
|
# Limitations/explanations here: https://openinverter.org/forum/viewtopic.php?p=57894#p57894 and
|
||||||
# https://openinverter.org/forum/viewtopic.php?t=1063 (Is it possible to make a CCS to CHAdeMO adapter?)
|
# https://openinverter.org/forum/viewtopic.php?t=1063 (Is it possible to make a CCS to CHAdeMO adapter?)
|
||||||
# none: all other use cases
|
# none: all other use cases
|
||||||
|
#charge_parameter_backend = chademo
|
||||||
|
#charge_parameter_backend = mqtt
|
||||||
charge_parameter_backend = none
|
charge_parameter_backend = none
|
||||||
|
|
||||||
# REST callback for SoC states. Comment out to disable. Do not leave a trailing slash
|
# REST callback for SoC states. Comment out to disable. Do not leave a trailing slash
|
||||||
|
|
18
fsmEvse.py
18
fsmEvse.py
|
@ -27,8 +27,10 @@ class fsmEvse():
|
||||||
|
|
||||||
def publishStatus(self, s):
|
def publishStatus(self, s):
|
||||||
self.callbackShowStatus(s, "evseState")
|
self.callbackShowStatus(s, "evseState")
|
||||||
|
self.hardwareInterface.displayState(s)
|
||||||
|
|
||||||
def publishSoCs(self, current_soc: int, full_soc: int = -1, energy_capacity: int = -1, energy_request: int = -1, evccid: str = "", origin: str = ""):
|
def publishSoCs(self, current_soc: int, full_soc: int = -1, energy_capacity: int = -1, energy_request: int = -1, evccid: str = "", origin: str = ""):
|
||||||
|
self.hardwareInterface.displaySoc(current_soc)
|
||||||
if self.callbackSoCStatus is not None:
|
if self.callbackSoCStatus is not None:
|
||||||
self.callbackSoCStatus(current_soc, full_soc, energy_capacity, energy_request, self.evccid, origin)
|
self.callbackSoCStatus(current_soc, full_soc, energy_capacity, energy_request, self.evccid, origin)
|
||||||
|
|
||||||
|
@ -262,7 +264,12 @@ class fsmEvse():
|
||||||
self.simulatedPresentVoltage += 20
|
self.simulatedPresentVoltage += 20
|
||||||
if (self.simulatedPresentVoltage<uTarget):
|
if (self.simulatedPresentVoltage<uTarget):
|
||||||
self.simulatedPresentVoltage += 5
|
self.simulatedPresentVoltage += 5
|
||||||
strPresentVoltage = str(int(self.simulatedPresentVoltage*10)/10) # "345"
|
|
||||||
|
if getConfigValueBool('evse_simulate_precharge'):
|
||||||
|
strPresentVoltage = str(int(self.simulatedPresentVoltage*10)/10) # "345"
|
||||||
|
else:
|
||||||
|
strPresentVoltage = str(self.hardwareInterface.getInletVoltage())
|
||||||
|
|
||||||
# in case we control a real power supply: give the precharge target to it
|
# in case we control a real power supply: give the precharge target to it
|
||||||
self.hardwareInterface.setPowerSupplyVoltageAndCurrent(uTarget, 1)
|
self.hardwareInterface.setPowerSupplyVoltageAndCurrent(uTarget, 1)
|
||||||
self.callbackShowStatus(strPresentVoltage, "EVSEPresentVoltage")
|
self.callbackShowStatus(strPresentVoltage, "EVSEPresentVoltage")
|
||||||
|
@ -322,11 +329,12 @@ class fsmEvse():
|
||||||
strEVTargetCurrentValue = jsondict["EVTargetCurrent.Value"]
|
strEVTargetCurrentValue = jsondict["EVTargetCurrent.Value"]
|
||||||
strEVTargetCurrentMultiplier = jsondict["EVTargetCurrent.Multiplier"]
|
strEVTargetCurrentMultiplier = jsondict["EVTargetCurrent.Multiplier"]
|
||||||
iTarget = combineValueAndMultiplier(strEVTargetCurrentValue, strEVTargetCurrentMultiplier)
|
iTarget = combineValueAndMultiplier(strEVTargetCurrentValue, strEVTargetCurrentMultiplier)
|
||||||
self.addToTrace("EV wants EVTargetVoltage " + str(uTarget))
|
self.addToTrace("EV wants EVTargetVoltage " + str(uTarget) + " and EVTargetCurrent " + str(iTarget))
|
||||||
current_soc = int(jsondict.get("DC_EVStatus.EVRESSSOC", -1))
|
current_soc = int(jsondict.get("DC_EVStatus.EVRESSSOC", -1))
|
||||||
full_soc = int(jsondict.get("FullSOC", -1))
|
full_soc = int(jsondict.get("FullSOC", -1))
|
||||||
energy_capacity = int(jsondict.get("EVEnergyCapacity.Value", -1))
|
energy_capacity = int(jsondict.get("EVEnergyCapacity.Value", -1))
|
||||||
energy_request = int(jsondict.get("EVEnergyRequest.Value", -1))
|
energy_request = int(jsondict.get("EVEnergyRequest.Value", -1))
|
||||||
|
self.hardwareInterface.setPowerSupplyVoltageAndCurrent(uTarget, iTarget)
|
||||||
|
|
||||||
self.publishSoCs(current_soc, full_soc, energy_capacity, energy_request, origin="CurrentDemandReq")
|
self.publishSoCs(current_soc, full_soc, energy_capacity, energy_request, origin="CurrentDemandReq")
|
||||||
|
|
||||||
|
@ -336,10 +344,10 @@ class fsmEvse():
|
||||||
except:
|
except:
|
||||||
self.addToTrace("ERROR: Could not decode the CurrentDemandReq")
|
self.addToTrace("ERROR: Could not decode the CurrentDemandReq")
|
||||||
self.simulatedPresentVoltage = uTarget + 3*random() # The charger provides the voltage which is demanded by the car.
|
self.simulatedPresentVoltage = uTarget + 3*random() # The charger provides the voltage which is demanded by the car.
|
||||||
strPresentVoltage = str(int(self.simulatedPresentVoltage*10)/10) # "345"
|
strPresentVoltage = str(self.hardwareInterface.getInletVoltage()) #str(self.simulatedPresentVoltage)
|
||||||
self.callbackShowStatus(strPresentVoltage, "EVSEPresentVoltage")
|
self.callbackShowStatus(strPresentVoltage, "EVSEPresentVoltage")
|
||||||
strEVSEPresentCurrent = "1" # Just as a dummy current
|
strEVSEPresentCurrent = str(self.hardwareInterface.getAccuMaxCurrent()) #"1" # Just as a dummy current
|
||||||
if (self.blChargeStopTrigger == 1):
|
if (self.blChargeStopTrigger == 1 or self.hardwareInterface.stopRequest()):
|
||||||
# User pressed the STOP button on the charger. Send EVSE_Shutdown.
|
# User pressed the STOP button on the charger. Send EVSE_Shutdown.
|
||||||
self.addToTrace("User pressed the STOP button on the charger. Sending EVSE_Shutdown.")
|
self.addToTrace("User pressed the STOP button on the charger. Sending EVSE_Shutdown.")
|
||||||
strEVSEStatus = "2" # 2=EVSE_Shutdown, means the user stopped the session on the charger.
|
strEVSEStatus = "2" # 2=EVSE_Shutdown, means the user stopped the session on the charger.
|
||||||
|
|
|
@ -286,6 +286,7 @@ class fsmPev():
|
||||||
self.sessionId = strSessionId
|
self.sessionId = strSessionId
|
||||||
except:
|
except:
|
||||||
self.addToTrace("ERROR: Could not decode the sessionID")
|
self.addToTrace("ERROR: Could not decode the sessionID")
|
||||||
|
|
||||||
if ((strResponseCode!="OK_NewSessionEstablished") and (strResponseCode!="OK")):
|
if ((strResponseCode!="OK_NewSessionEstablished") and (strResponseCode!="OK")):
|
||||||
# According to the standard, the only valid response code is OK_NewSessionEstablished.
|
# According to the standard, the only valid response code is OK_NewSessionEstablished.
|
||||||
# But the ABB chargers use "OK", so we need to accept this, too. Discussed
|
# But the ABB chargers use "OK", so we need to accept this, too. Discussed
|
||||||
|
|
|
@ -7,13 +7,18 @@
|
||||||
# python -m serial.tools.list_ports
|
# python -m serial.tools.list_ports
|
||||||
|
|
||||||
import serial # the pyserial
|
import serial # the pyserial
|
||||||
|
from pyPlcModes import *
|
||||||
from serial.tools.list_ports import comports
|
from serial.tools.list_ports import comports
|
||||||
from time import sleep, time
|
from time import sleep, time
|
||||||
from configmodule import getConfigValue, getConfigValueBool
|
from configmodule import getConfigValue, getConfigValueBool
|
||||||
import sys # For exit_on_session_end hack
|
import sys # For exit_on_session_end hack
|
||||||
|
|
||||||
PinCp = "P8_18"
|
PinCp = "P9_41"
|
||||||
PinPowerRelay = "P8_16"
|
PinPowerRelay = "P9_17"
|
||||||
|
|
||||||
|
if (getConfigValue("digital_output_device")=="mqtt"):
|
||||||
|
# If we use MQTT as a hardware interface import it here
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
|
||||||
if (getConfigValue("digital_output_device")=="beaglebone"):
|
if (getConfigValue("digital_output_device")=="beaglebone"):
|
||||||
# In case we run on beaglebone, we want to use GPIO ports.
|
# In case we run on beaglebone, we want to use GPIO ports.
|
||||||
|
@ -85,12 +90,22 @@ class hardwareInterface():
|
||||||
def addToTrace(self, s):
|
def addToTrace(self, s):
|
||||||
self.callbackAddToTrace("[HARDWAREINTERFACE] " + s)
|
self.callbackAddToTrace("[HARDWAREINTERFACE] " + s)
|
||||||
|
|
||||||
|
def displayState(self, state):
|
||||||
|
if (getConfigValue("digital_output_device")=="mqtt"):
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/fsm_state", state)
|
||||||
|
|
||||||
|
def displaySoc(self, soc):
|
||||||
|
if getConfigValue("charge_parameter_backend") == "mqtt":
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/soc", str(soc))
|
||||||
|
|
||||||
def setStateB(self):
|
def setStateB(self):
|
||||||
self.addToTrace("Setting CP line into state B.")
|
self.addToTrace("Setting CP line into state B.")
|
||||||
if (getConfigValue("digital_output_device")=="beaglebone"):
|
if (getConfigValue("digital_output_device")=="beaglebone"):
|
||||||
GPIO.output(PinCp, GPIO.LOW)
|
GPIO.output(PinCp, GPIO.LOW)
|
||||||
if (getConfigValue("digital_output_device")=="celeron55device"):
|
if (getConfigValue("digital_output_device")=="celeron55device"):
|
||||||
self.ser.write(bytes("cp=0\n", "utf-8"))
|
self.ser.write(bytes("cp=0\n", "utf-8"))
|
||||||
|
if (getConfigValue("digital_output_device")=="mqtt"):
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/cpstate", "B")
|
||||||
self.outvalue &= ~1
|
self.outvalue &= ~1
|
||||||
|
|
||||||
def setStateC(self):
|
def setStateC(self):
|
||||||
|
@ -99,6 +114,8 @@ class hardwareInterface():
|
||||||
GPIO.output(PinCp, GPIO.HIGH)
|
GPIO.output(PinCp, GPIO.HIGH)
|
||||||
if (getConfigValue("digital_output_device")=="celeron55device"):
|
if (getConfigValue("digital_output_device")=="celeron55device"):
|
||||||
self.ser.write(bytes("cp=1\n", "utf-8"))
|
self.ser.write(bytes("cp=1\n", "utf-8"))
|
||||||
|
if (getConfigValue("digital_output_device")=="mqtt"):
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/cpstate", "C")
|
||||||
self.outvalue |= 1
|
self.outvalue |= 1
|
||||||
|
|
||||||
def setPowerRelayOn(self):
|
def setPowerRelayOn(self):
|
||||||
|
@ -107,6 +124,8 @@ class hardwareInterface():
|
||||||
GPIO.output(PinPowerRelay, GPIO.HIGH)
|
GPIO.output(PinPowerRelay, GPIO.HIGH)
|
||||||
if (getConfigValue("digital_output_device")=="celeron55device"):
|
if (getConfigValue("digital_output_device")=="celeron55device"):
|
||||||
self.ser.write(bytes("contactor=1\n", "utf-8"))
|
self.ser.write(bytes("contactor=1\n", "utf-8"))
|
||||||
|
if (getConfigValue("digital_output_device")=="mqtt"):
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/relay_request", "on")
|
||||||
self.outvalue |= 2
|
self.outvalue |= 2
|
||||||
|
|
||||||
def setPowerRelayOff(self):
|
def setPowerRelayOff(self):
|
||||||
|
@ -115,6 +134,8 @@ class hardwareInterface():
|
||||||
GPIO.output(PinPowerRelay, GPIO.LOW)
|
GPIO.output(PinPowerRelay, GPIO.LOW)
|
||||||
if (getConfigValue("digital_output_device")=="celeron55device"):
|
if (getConfigValue("digital_output_device")=="celeron55device"):
|
||||||
self.ser.write(bytes("contactor=0\n", "utf-8"))
|
self.ser.write(bytes("contactor=0\n", "utf-8"))
|
||||||
|
if (getConfigValue("digital_output_device")=="mqtt"):
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/relay_request", "off")
|
||||||
self.outvalue &= ~2
|
self.outvalue &= ~2
|
||||||
|
|
||||||
def setRelay2On(self):
|
def setRelay2On(self):
|
||||||
|
@ -134,12 +155,16 @@ class hardwareInterface():
|
||||||
self.addToTrace("Locking the connector")
|
self.addToTrace("Locking the connector")
|
||||||
if (getConfigValue("digital_output_device")=="celeron55device"):
|
if (getConfigValue("digital_output_device")=="celeron55device"):
|
||||||
self.ser.write(bytes("lock\n", "utf-8"))
|
self.ser.write(bytes("lock\n", "utf-8"))
|
||||||
|
if (getConfigValue("digital_output_device")=="mqtt"):
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/lock_request", "lock")
|
||||||
# todo control the lock motor into lock direction until the end (time based or current based stopping?)
|
# todo control the lock motor into lock direction until the end (time based or current based stopping?)
|
||||||
|
|
||||||
def triggerConnectorUnlocking(self):
|
def triggerConnectorUnlocking(self):
|
||||||
self.addToTrace("Unocking the connector")
|
self.addToTrace("Unlocking the connector")
|
||||||
if (getConfigValue("digital_output_device")=="celeron55device"):
|
if (getConfigValue("digital_output_device")=="celeron55device"):
|
||||||
self.ser.write(bytes("unlock\n", "utf-8"))
|
self.ser.write(bytes("unlock\n", "utf-8"))
|
||||||
|
if (getConfigValue("digital_output_device")=="mqtt"):
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/lock_request", "unlock")
|
||||||
# todo control the lock motor into unlock direction until the end (time based or current based stopping?)
|
# todo control the lock motor into unlock direction until the end (time based or current based stopping?)
|
||||||
|
|
||||||
def isConnectorLocked(self):
|
def isConnectorLocked(self):
|
||||||
|
@ -149,16 +174,29 @@ class hardwareInterface():
|
||||||
return 1 # todo: use the real connector lock feedback
|
return 1 # todo: use the real connector lock feedback
|
||||||
|
|
||||||
def setChargerParameters(self, maxVoltage, maxCurrent):
|
def setChargerParameters(self, maxVoltage, maxCurrent):
|
||||||
|
self.addToTrace("Setting charger parameters maxVoltage=%d V, maxCurrent=%d A" % (maxVoltage, maxCurrent))
|
||||||
self.maxChargerVoltage = int(maxVoltage)
|
self.maxChargerVoltage = int(maxVoltage)
|
||||||
self.maxChargerCurrent = int(maxCurrent)
|
self.maxChargerCurrent = int(maxCurrent)
|
||||||
|
if getConfigValue("charge_parameter_backend") == "mqtt":
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/charger_max_voltage", str(maxVoltage))
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/charger_max_current", str(maxCurrent))
|
||||||
|
|
||||||
def setChargerVoltageAndCurrent(self, voltageNow, currentNow):
|
def setChargerVoltageAndCurrent(self, voltageNow, currentNow):
|
||||||
|
self.addToTrace("Setting charger present values Voltage=%d V, Current=%d A" % (voltageNow, currentNow))
|
||||||
self.chargerVoltage = int(voltageNow)
|
self.chargerVoltage = int(voltageNow)
|
||||||
self.chargerCurrent = int(currentNow)
|
self.chargerCurrent = int(currentNow)
|
||||||
|
|
||||||
|
if getConfigValue("charge_parameter_backend") == "mqtt":
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/charger_voltage", voltageNow)
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/charger_current", currentNow)
|
||||||
|
|
||||||
def setPowerSupplyVoltageAndCurrent(self, targetVoltage, targetCurrent):
|
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
|
# 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)
|
self.homeplughandler.sendSpecialMessageToControlThePowerSupply(targetVoltage, targetCurrent)
|
||||||
|
#here we can publish the voltage and current requests received from the PEV side
|
||||||
|
if getConfigValue("charge_parameter_backend") == "mqtt":
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/target_voltage", str(targetVoltage))
|
||||||
|
self.mqttclient.publish(getConfigValue("mqtt_topic") + "/target_current", str(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
|
||||||
|
@ -166,10 +204,8 @@ class hardwareInterface():
|
||||||
return self.inletVoltage
|
return self.inletVoltage
|
||||||
|
|
||||||
def getAccuVoltage(self):
|
def getAccuVoltage(self):
|
||||||
if (getConfigValue("digital_output_device")=="celeron55device"):
|
if getConfigValue("charge_parameter_backend") in ["chademo", "mqtt", "celeron55device"]:
|
||||||
return self.accuVoltage
|
return self.accuVoltage
|
||||||
elif getConfigValue("charge_parameter_backend")=="chademo":
|
|
||||||
return self.accuVoltage
|
|
||||||
#todo: get real measured voltage from the accu
|
#todo: get real measured voltage from the accu
|
||||||
self.accuVoltage = 230
|
self.accuVoltage = 230
|
||||||
return self.accuVoltage
|
return self.accuVoltage
|
||||||
|
@ -182,15 +218,15 @@ class hardwareInterface():
|
||||||
if self.accuMaxCurrent >= EVMaximumCurrentLimit:
|
if self.accuMaxCurrent >= EVMaximumCurrentLimit:
|
||||||
return EVMaximumCurrentLimit
|
return EVMaximumCurrentLimit
|
||||||
return self.accuMaxCurrent
|
return self.accuMaxCurrent
|
||||||
elif getConfigValue("charge_parameter_backend")=="chademo":
|
elif getConfigValue("charge_parameter_backend") in ["chademo", "mqtt"]:
|
||||||
return self.accuMaxCurrent #set by CAN
|
return self.accuMaxCurrent #set by CAN or MQTT
|
||||||
#todo: get max charging current from the BMS
|
#todo: get max charging current from the BMS
|
||||||
self.accuMaxCurrent = 10
|
self.accuMaxCurrent = 10
|
||||||
return self.accuMaxCurrent
|
return self.accuMaxCurrent
|
||||||
|
|
||||||
def getAccuMaxVoltage(self):
|
def getAccuMaxVoltage(self):
|
||||||
if getConfigValue("charge_parameter_backend")=="chademo":
|
if getConfigValue("charge_parameter_backend") in ["chademo", "mqtt"]:
|
||||||
return self.accuMaxVoltage #set by CAN
|
return self.accuMaxVoltage #set by CAN or MQTT
|
||||||
elif getConfigValue("charge_target_voltage"):
|
elif getConfigValue("charge_target_voltage"):
|
||||||
self.accuMaxVoltage = getConfigValue("charge_target_voltage")
|
self.accuMaxVoltage = getConfigValue("charge_target_voltage")
|
||||||
else:
|
else:
|
||||||
|
@ -209,12 +245,15 @@ class hardwareInterface():
|
||||||
def getSoc(self):
|
def getSoc(self):
|
||||||
if self.callbackShowStatus:
|
if self.callbackShowStatus:
|
||||||
self.callbackShowStatus(format(self.soc_percent,".1f"), "soc")
|
self.callbackShowStatus(format(self.soc_percent,".1f"), "soc")
|
||||||
if (getConfigValue("digital_output_device")=="celeron55device"):
|
if (getConfigValue("digital_output_device") in ["celeron55device", "mqtt"]):
|
||||||
return self.soc_percent
|
return self.soc_percent
|
||||||
#todo: get SOC from the BMS
|
#todo: get SOC from the BMS
|
||||||
self.callbackShowStatus(format(self.simulatedSoc,".1f"), "soc")
|
self.callbackShowStatus(format(self.simulatedSoc,".1f"), "soc")
|
||||||
return self.simulatedSoc
|
return self.simulatedSoc
|
||||||
|
|
||||||
|
def stopRequest(self):
|
||||||
|
return not self.enabled
|
||||||
|
|
||||||
def isUserAuthenticated(self):
|
def isUserAuthenticated(self):
|
||||||
# If the user needs to authorize, fill this function in a way that it returns False as long as
|
# If the user needs to authorize, fill this function in a way that it returns False as long as
|
||||||
# we shall wait for the users authorization, and returns True if the authentication was successfull.
|
# we shall wait for the users authorization, and returns True if the authentication was successfull.
|
||||||
|
@ -239,16 +278,24 @@ class hardwareInterface():
|
||||||
# Port configuration according to https://github.com/jsphuebner/pyPLC/commit/475f7fe9f3a67da3d4bd9e6e16dfb668d0ddb1d6
|
# Port configuration according to https://github.com/jsphuebner/pyPLC/commit/475f7fe9f3a67da3d4bd9e6e16dfb668d0ddb1d6
|
||||||
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
|
||||||
|
|
||||||
|
if (getConfigValue("digital_output_device") == "mqtt"):
|
||||||
|
self.mqttclient = mqtt.Client(client_id="pyplc")
|
||||||
|
self.mqttclient.on_connect = self.mqtt_on_connect
|
||||||
|
self.mqttclient.on_message = self.mqtt_on_message
|
||||||
|
self.mqttclient.connect(getConfigValue("mqtt_broker"), 1883, 60)
|
||||||
|
|
||||||
def __init__(self, callbackAddToTrace=None, callbackShowStatus=None, homeplughandler=None):
|
def __init__(self, callbackAddToTrace=None, callbackShowStatus=None, homeplughandler=None, mode=C_EVSE_MODE):
|
||||||
self.callbackAddToTrace = callbackAddToTrace
|
self.callbackAddToTrace = callbackAddToTrace
|
||||||
self.callbackShowStatus = callbackShowStatus
|
self.callbackShowStatus = callbackShowStatus
|
||||||
self.homeplughandler = homeplughandler
|
self.homeplughandler = homeplughandler
|
||||||
|
self.mode = mode
|
||||||
|
|
||||||
self.loopcounter = 0
|
self.loopcounter = 0
|
||||||
self.outvalue = 0
|
self.outvalue = 0
|
||||||
self.simulatedSoc = 20.0 # percent
|
self.simulatedSoc = 20.0 # percent
|
||||||
self.demoAuthenticationCounter = 0
|
self.demoAuthenticationCounter = 0
|
||||||
|
self.enabled = True #Charging enabled
|
||||||
|
|
||||||
self.inletVoltage = 0.0 # volts
|
self.inletVoltage = 0.0 # volts
|
||||||
self.accuVoltage = 0.0
|
self.accuVoltage = 0.0
|
||||||
|
@ -400,6 +447,9 @@ class hardwareInterface():
|
||||||
if (getConfigValue("digital_output_device")=="celeron55device"):
|
if (getConfigValue("digital_output_device")=="celeron55device"):
|
||||||
self.mainfunction_celeron55device()
|
self.mainfunction_celeron55device()
|
||||||
|
|
||||||
|
if (getConfigValue("digital_output_device")=="mqtt"):
|
||||||
|
self.mainfunction_mqtt()
|
||||||
|
|
||||||
if getConfigValueBool("exit_on_session_end"):
|
if getConfigValueBool("exit_on_session_end"):
|
||||||
# TODO: This is a hack. Do this in fsmPev instead and publish some
|
# TODO: This is a hack. Do this in fsmPev instead and publish some
|
||||||
# of these values into there if needed.
|
# of these values into there if needed.
|
||||||
|
@ -477,7 +527,52 @@ class hardwareInterface():
|
||||||
if self.accuMaxCurrent != 0:
|
if self.accuMaxCurrent != 0:
|
||||||
self.addToTrace("CHAdeMO: No current limit update for over 1s, setting current to 0")
|
self.addToTrace("CHAdeMO: No current limit update for over 1s, setting current to 0")
|
||||||
self.accuMaxCurrent = 0
|
self.accuMaxCurrent = 0
|
||||||
|
|
||||||
|
def mainfunction_mqtt(self):
|
||||||
|
self.mqttclient.loop(timeout=0.1)
|
||||||
|
|
||||||
|
# The callback for when the client receives a CONNACK response from the server.
|
||||||
|
def mqtt_on_connect(self, client, userdata, flags, rc):
|
||||||
|
self.addToTrace(f"MQTT connected with result code {rc}")
|
||||||
|
|
||||||
|
# Subscribing in on_connect() means that if we lose the connection and
|
||||||
|
# reconnect then subscriptions will be renewed.
|
||||||
|
if self.mode == C_EVSE_MODE:
|
||||||
|
client.subscribe(getConfigValue("mqtt_topic") + "/charger_max_voltage") #todo
|
||||||
|
client.subscribe(getConfigValue("mqtt_topic") + "/charger_max_current") #todo
|
||||||
|
client.subscribe(getConfigValue("mqtt_topic") + "/charger_voltage")
|
||||||
|
client.subscribe(getConfigValue("mqtt_topic") + "/charger_current")
|
||||||
|
client.subscribe(getConfigValue("mqtt_topic") + "/enabled")
|
||||||
|
elif self.mode == C_EVSE_MODE:
|
||||||
|
client.subscribe(getConfigValue("mqtt_topic") + "/battery_voltage")
|
||||||
|
client.subscribe(getConfigValue("mqtt_topic") + "/target_voltage")
|
||||||
|
client.subscribe(getConfigValue("mqtt_topic") + "/target_current")
|
||||||
|
client.subscribe(getConfigValue("mqtt_topic") + "/soc")
|
||||||
|
client.subscribe(getConfigValue("mqtt_topic") + "/inlet_voltage")
|
||||||
|
|
||||||
|
def mqtt_on_message(self, client, userdata, msg):
|
||||||
|
baseTopic = getConfigValue("mqtt_topic")
|
||||||
|
|
||||||
|
if msg.topic == (f"{baseTopic}/battery_voltage"):
|
||||||
|
self.accuVoltage = float(msg.payload)
|
||||||
|
self.addToTrace("MQTT: Set battery voltage to %f V" % self.accuVoltage)
|
||||||
|
elif msg.topic == (f"{baseTopic}/target_voltage"):
|
||||||
|
self.accuMaxVoltage = float(msg.payload)
|
||||||
|
self.addToTrace("MQTT: Set target voltage to %f V" % self.accuMaxVoltage)
|
||||||
|
elif msg.topic == (f"{baseTopic}/target_current") or msg.topic == (f"{baseTopic}/charger_current"):
|
||||||
|
self.accuMaxCurrent = float(msg.payload)
|
||||||
|
self.addToTrace("MQTT: Set current request to %s A" % self.accuMaxCurrent)
|
||||||
|
elif msg.topic == (f"{baseTopic}/soc"):
|
||||||
|
self.simulatedSoc = float(msg.payload)
|
||||||
|
self.soc_percent = self.simulatedSoc
|
||||||
|
self.addToTrace("MQTT: Set SoC to %f %%" % self.simulatedSoc)
|
||||||
|
elif msg.topic == (f"{baseTopic}/inlet_voltage") or msg.topic == (f"{baseTopic}/charger_voltage"):
|
||||||
|
self.inletVoltage = float(msg.payload) #in EVSE mode this is present charger voltage
|
||||||
|
self.addToTrace("MQTT: Set present voltage to %f V" % self.inletVoltage)
|
||||||
|
elif msg.topic == (f"{baseTopic}/enabled"):
|
||||||
|
self.enabled = bool(int(msg.payload))
|
||||||
|
self.addToTrace("MQTT: Setting enable flag to %d" % self.enabled)
|
||||||
|
|
||||||
def myPrintfunction(s):
|
def myPrintfunction(s):
|
||||||
print("myprint " + s)
|
print("myprint " + s)
|
||||||
|
|
||||||
|
@ -488,6 +583,8 @@ if __name__ == "__main__":
|
||||||
hw.mainfunction()
|
hw.mainfunction()
|
||||||
if (i==20):
|
if (i==20):
|
||||||
hw.showOnDisplay("Hello", "A DEMO", "321.0V")
|
hw.showOnDisplay("Hello", "A DEMO", "321.0V")
|
||||||
|
hw.setChargerParameters(500, 125)
|
||||||
|
hw.setChargerVoltageAndCurrent(360, 100)
|
||||||
if (i==50):
|
if (i==50):
|
||||||
hw.setStateC()
|
hw.setStateC()
|
||||||
if (i==100):
|
if (i==100):
|
||||||
|
@ -505,6 +602,6 @@ if __name__ == "__main__":
|
||||||
hw.setRelay2Off()
|
hw.setRelay2Off()
|
||||||
if (i==320):
|
if (i==320):
|
||||||
hw.showOnDisplay("This", "...is...", "DONE :-)")
|
hw.showOnDisplay("This", "...is...", "DONE :-)")
|
||||||
sleep(0.03)
|
sleep(0.01)
|
||||||
hw.close()
|
hw.close()
|
||||||
print("finished.")
|
print("finished.")
|
||||||
|
|
26
simulateMqttBackend.py
Normal file
26
simulateMqttBackend.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
from configmodule import getConfigValue, getConfigValueBool
|
||||||
|
|
||||||
|
def mqtt_on_connect(client, userdata, flags, rc):
|
||||||
|
client.subscribe(getConfigValue("mqtt_topic") + "/#")
|
||||||
|
|
||||||
|
def mqtt_on_message(client, userdata, msg):
|
||||||
|
global simulatedInletVoltage
|
||||||
|
if msg.topic == getConfigValue("mqtt_topic") + "/fsm_state":
|
||||||
|
if "CableCheck" in msg.payload.decode("utf-8"):
|
||||||
|
simulatedInletVoltage = 0
|
||||||
|
if "PreCharging" in msg.payload.decode("utf-8"):
|
||||||
|
client.publish(getConfigValue("mqtt_topic") + "/inlet_voltage", simulatedInletVoltage)
|
||||||
|
simulatedInletVoltage = simulatedInletVoltage + 15
|
||||||
|
elif msg.topic == getConfigValue("mqtt_topic") + "/pev_voltage":
|
||||||
|
client.publish(getConfigValue("mqtt_topic") + "/inlet_voltage", msg.payload)
|
||||||
|
elif msg.topic == getConfigValue("mqtt_topic") + "/pev_current":
|
||||||
|
client.publish(getConfigValue("mqtt_topic") + "/target_current", msg.payload)
|
||||||
|
|
||||||
|
simulatedInletVoltage = 0
|
||||||
|
mqttclient = mqtt.Client()
|
||||||
|
mqttclient.on_connect = mqtt_on_connect
|
||||||
|
mqttclient.on_message = mqtt_on_message
|
||||||
|
mqttclient.connect(getConfigValue("mqtt_broker"), 1883, 60)
|
||||||
|
|
||||||
|
mqttclient.loop_forever()
|
22
starter.sh
22
starter.sh
|
@ -39,9 +39,11 @@ sysctl net.ipv6.conf.eth0.keep_addr_on_down=1
|
||||||
# Todo: Why this needed? On raspberry, where the NetworkManager is not runnning, this disturbs, because
|
# Todo: Why this needed? On raspberry, where the NetworkManager is not runnning, this disturbs, because
|
||||||
# afterwards the pyPlc does not see the interfaces IPv6 address.
|
# afterwards the pyPlc does not see the interfaces IPv6 address.
|
||||||
# Todo: make this configurable, for the cases we need this.
|
# Todo: make this configurable, for the cases we need this.
|
||||||
# ip link set eth0 down
|
ip link set eth0 down
|
||||||
|
ip link set can0 down
|
||||||
sleep 1
|
sleep 1
|
||||||
# ip link set eth0 up
|
ip link set eth0 up
|
||||||
|
ip link set can0 up type can restart-ms 100 bitrate 500000
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
# show the addresses
|
# show the addresses
|
||||||
|
@ -53,17 +55,25 @@ pwd
|
||||||
# create directory for the log files.
|
# create directory for the log files.
|
||||||
mkdir -p log
|
mkdir -p log
|
||||||
# prepare the file names for the log files
|
# prepare the file names for the log files
|
||||||
|
pushd log
|
||||||
|
gzip *.log || true
|
||||||
|
popd
|
||||||
date=$(date "+%Y-%m-%d_%H%M%S")
|
date=$(date "+%Y-%m-%d_%H%M%S")
|
||||||
logfile=./log/"$date"_pevNoGui.log
|
#logfile=./log/"$date"_pevNoGui.log
|
||||||
tcpdump_logfile=./log/"$date"_tcpdump.pcap
|
#logfile=./log/pevNoGui.log
|
||||||
|
index=`cat log/logindex`
|
||||||
|
logfile=`printf "log/%04d_pevNoGui.log" $index`
|
||||||
|
tcpdump_logfile=`printf "log/%04d_tcpdump.pcap" $index`
|
||||||
|
index=$(($index + 1))
|
||||||
|
echo $index > log/logindex
|
||||||
|
|
||||||
echo "logfile: $logfile"
|
echo "logfile: $logfile"
|
||||||
echo "tcpdump_logfile: $tcpdump_logfile"
|
echo "tcpdump_logfile: $tcpdump_logfile"
|
||||||
|
|
||||||
# start the tcpdump
|
# start the tcpdump
|
||||||
start_tcpdump "$tcpdump_logfile"
|
#start_tcpdump "$tcpdump_logfile"
|
||||||
|
|
||||||
echo "$date" >> "$logfile"
|
echo "$date" > "$logfile"
|
||||||
git log --oneline -1 >> "$logfile" || echo "Not a git repo" >> "$logfile"
|
git log --oneline -1 >> "$logfile" || echo "Not a git repo" >> "$logfile"
|
||||||
ip addr >> "$logfile"
|
ip addr >> "$logfile"
|
||||||
pwd >> "$logfile"
|
pwd >> "$logfile"
|
||||||
|
|
Loading…
Reference in a new issue