mirror of
https://github.com/uhi22/pyPLC.git
synced 2024-11-10 01:05:42 +00:00
feature: added ini file. Prepared GPIO for beaglebone.
This commit is contained in:
parent
68a4a6ef85
commit
658beda580
6 changed files with 142 additions and 26 deletions
|
@ -85,7 +85,8 @@ class addressManager():
|
||||||
# 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
# 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||||
# 3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
# 3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||||
# print("This is a heading")
|
# print("This is a heading")
|
||||||
if (line.find(": eth")>0):
|
sFind = ": " + getConfigValue("eth_interface") # e.g. "eth0"
|
||||||
|
if (line.find(sFind)>0):
|
||||||
# print("This is the heading for the ethernet.")
|
# print("This is the heading for the ethernet.")
|
||||||
blInTheEthernetChapter = 1 # we are in the ethernet chapter
|
blInTheEthernetChapter = 1 # we are in the ethernet chapter
|
||||||
else:
|
else:
|
||||||
|
|
22
configmodule.py
Normal file
22
configmodule.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
|
||||||
|
# See https://docs.python.org/3/library/configparser.html
|
||||||
|
|
||||||
|
import configparser
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read('pyPlc.ini')
|
||||||
|
|
||||||
|
def getConfigValue(s):
|
||||||
|
return config['general'][s]
|
||||||
|
|
||||||
|
def getConfigValueBool(s):
|
||||||
|
return config.getboolean('general', s)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("Testing configmodule...")
|
||||||
|
print(str(config.sections()))
|
||||||
|
print(config['general']['mode'])
|
||||||
|
for key in config['general']:
|
||||||
|
print(key + " has value " + config['general'][key])
|
||||||
|
print(config.getboolean('general', 'display_via_serial'))
|
14
fsmPev.py
14
fsmPev.py
|
@ -10,8 +10,8 @@ from datetime import datetime
|
||||||
from helpers import prettyHexMessage, compactHexMessage, combineValueAndMultiplier
|
from helpers import prettyHexMessage, compactHexMessage, combineValueAndMultiplier
|
||||||
from exiConnector import * # for EXI data handling/converting
|
from exiConnector import * # for EXI data handling/converting
|
||||||
import json
|
import json
|
||||||
|
from configmodule import getConfigValue, getConfigValueBool
|
||||||
|
|
||||||
PARAM_U_DELTA_MAX_FOR_END_OF_PRECHARGE = 10 # volts between inlet and accu, to change from PreCharge to PowerDelivery
|
|
||||||
|
|
||||||
stateNotYetInitialized = 0
|
stateNotYetInitialized = 0
|
||||||
stateConnecting = 1
|
stateConnecting = 1
|
||||||
|
@ -393,7 +393,7 @@ class fsmPev():
|
||||||
except:
|
except:
|
||||||
self.addToTrace("ERROR: Could not decode the PreChargeResponse")
|
self.addToTrace("ERROR: Could not decode the PreChargeResponse")
|
||||||
self.addToTrace("PreChargeResponse received.")
|
self.addToTrace("PreChargeResponse received.")
|
||||||
if (self.USE_EVSEPRESENTVOLTAGE_FOR_PRECHARGE_END):
|
if (getConfigValueBool("use_evsepresentvoltage_for_precharge_end")):
|
||||||
# We want to use the EVSEPresentVoltage, which was reported by the charger, as end-criteria for the precharging.
|
# We want to use the EVSEPresentVoltage, which was reported by the charger, as end-criteria for the precharging.
|
||||||
s = "EVSEPresentVoltage " + str(u) + "V, "
|
s = "EVSEPresentVoltage " + str(u) + "V, "
|
||||||
else:
|
else:
|
||||||
|
@ -402,7 +402,7 @@ class fsmPev():
|
||||||
s = "U_Inlet " + str(u) + "V, "
|
s = "U_Inlet " + str(u) + "V, "
|
||||||
s= s + "U_Accu " + str(self.hardwareInterface.getAccuVoltage()) + "V"
|
s= s + "U_Accu " + str(self.hardwareInterface.getAccuVoltage()) + "V"
|
||||||
self.addToTrace(s)
|
self.addToTrace(s)
|
||||||
if (abs(u-self.hardwareInterface.getAccuVoltage()) < PARAM_U_DELTA_MAX_FOR_END_OF_PRECHARGE):
|
if (abs(u-self.hardwareInterface.getAccuVoltage()) < float(getConfigValue("u_delta_max_for_end_of_precharge"))):
|
||||||
self.addToTrace("Difference between accu voltage and inlet voltage is small. Sending PowerDeliveryReq.")
|
self.addToTrace("Difference between accu voltage and inlet voltage is small. Sending PowerDeliveryReq.")
|
||||||
self.publishStatus("PreCharge done")
|
self.publishStatus("PreCharge done")
|
||||||
if (self.isLightBulbDemo):
|
if (self.isLightBulbDemo):
|
||||||
|
@ -471,7 +471,7 @@ class fsmPev():
|
||||||
self.callbackShowStatus(format(u,".1f"), "EVSEPresentVoltage")
|
self.callbackShowStatus(format(u,".1f"), "EVSEPresentVoltage")
|
||||||
except:
|
except:
|
||||||
self.addToTrace("ERROR: Could not decode the PreChargeResponse")
|
self.addToTrace("ERROR: Could not decode the PreChargeResponse")
|
||||||
if (self.USE_PHYSICAL_INLET_VOLTAGE_DURING_CHARGELOOP):
|
if (getConfigValueBool("use_physical_inlet_voltage_during_chargeloop")):
|
||||||
# Instead of using the voltage which is reported by the charger, use the physically measured.
|
# Instead of using the voltage which is reported by the charger, use the physically measured.
|
||||||
u = self.hardwareInterface.getInletVoltage()
|
u = self.hardwareInterface.getInletVoltage()
|
||||||
# as long as the accu is not full and no stop-demand from the user, we continue charging
|
# as long as the accu is not full and no stop-demand from the user, we continue charging
|
||||||
|
@ -606,14 +606,10 @@ class fsmPev():
|
||||||
self.cyclesInState = 0
|
self.cyclesInState = 0
|
||||||
self.DelayCycles = 0
|
self.DelayCycles = 0
|
||||||
self.rxData = []
|
self.rxData = []
|
||||||
self.isLightBulbDemo = True
|
self.isLightBulbDemo = getConfigValueBool("light_bulb_demo")
|
||||||
self.isBulbOn = False
|
self.isBulbOn = False
|
||||||
self.cyclesLightBulbDelay = 0
|
self.cyclesLightBulbDelay = 0
|
||||||
self.isUserStopRequest = False
|
self.isUserStopRequest = False
|
||||||
self.USE_EVSEPRESENTVOLTAGE_FOR_PRECHARGE_END = 1 # to configure, which criteria is used for end of PreCharge
|
|
||||||
self.USE_PHYSICAL_INLET_VOLTAGE_DURING_CHARGELOOP = 0 # to configure, whether to display the measured or charger-reported
|
|
||||||
# voltage during the charging loop
|
|
||||||
|
|
||||||
# we do NOT call the reInit, because we want to wait with the connection until external trigger comes
|
# we do NOT call the reInit, because we want to wait with the connection until external trigger comes
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
|
|
|
@ -9,8 +9,23 @@
|
||||||
import serial # the pyserial
|
import serial # the pyserial
|
||||||
from serial.tools.list_ports import comports
|
from serial.tools.list_ports import comports
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
from configmodule import getConfigValue, getConfigValueBool
|
||||||
|
|
||||||
|
if (getConfigValue("digital_output_device")=="beaglebone"):
|
||||||
|
# In case we run on beaglebone, we want to use GPIO ports.
|
||||||
|
import Adafruit_BBIO.GPIO as GPIO
|
||||||
|
|
||||||
class hardwareInterface():
|
class hardwareInterface():
|
||||||
|
def needsSerial(self):
|
||||||
|
# Find out, whether we need a serial port. This depends on several configuration items.
|
||||||
|
if (getConfigValueBool("display_via_serial")):
|
||||||
|
return True # a display is expected to be connected to serial port.
|
||||||
|
if (getConfigValue("digital_output_device")=="dieter"):
|
||||||
|
return True # a "dieter" output device is expected to be connected on serial port.
|
||||||
|
if (getConfigValue("analog_input_device")=="dieter"):
|
||||||
|
return True # a "dieter" input device is expected to be connected on serial port.
|
||||||
|
return False # non of the functionalities need a serial port.
|
||||||
|
|
||||||
def findSerialPort(self):
|
def findSerialPort(self):
|
||||||
ports = []
|
ports = []
|
||||||
self.addToTrace('Available serial ports:')
|
self.addToTrace('Available serial ports:')
|
||||||
|
@ -21,36 +36,49 @@ class hardwareInterface():
|
||||||
self.addToTrace('{:2}: {:20} {!r}'.format(n, port, desc))
|
self.addToTrace('{:2}: {:20} {!r}'.format(n, port, desc))
|
||||||
ports.append(port)
|
ports.append(port)
|
||||||
if (len(ports)<1):
|
if (len(ports)<1):
|
||||||
self.addToTrace("ERROR: No serial ports found. No hardware interaction possible.")
|
if (self.needsSerial()):
|
||||||
self.ser = None
|
self.addToTrace("ERROR: No serial ports found. No hardware interaction possible.")
|
||||||
self.isInterfaceOk = False
|
self.ser = None
|
||||||
|
self.isSerialInterfaceOk = False
|
||||||
|
else:
|
||||||
|
self.addToTrace("We found no serial port, but also do not need it. No problem.")
|
||||||
|
self.ser = None
|
||||||
|
self.isSerialInterfaceOk = False
|
||||||
else:
|
else:
|
||||||
self.addToTrace("ok, we take the first port, " + ports[0])
|
self.addToTrace("ok, we take the first port, " + ports[0])
|
||||||
try:
|
try:
|
||||||
self.ser = serial.Serial(ports[0], 19200, timeout=0)
|
self.ser = serial.Serial(ports[0], 19200, timeout=0)
|
||||||
self.isInterfaceOk = True
|
self.isSerialInterfaceOk = True
|
||||||
except:
|
except:
|
||||||
self.addToTrace("ERROR: Could not open serial port.")
|
self.addToTrace("ERROR: Could not open serial port.")
|
||||||
self.ser = None
|
self.ser = None
|
||||||
self.isInterfaceOk = False
|
self.isSerialInterfaceOk = False
|
||||||
|
|
||||||
def addToTrace(self, s):
|
def addToTrace(self, s):
|
||||||
self.callbackAddToTrace("[HARDWAREINTERFACE] " + s)
|
self.callbackAddToTrace("[HARDWAREINTERFACE] " + s)
|
||||||
|
|
||||||
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"):
|
||||||
|
GPIO.output("P8_18", GPIO.LOW)
|
||||||
self.outvalue &= ~1
|
self.outvalue &= ~1
|
||||||
|
|
||||||
def setStateC(self):
|
def setStateC(self):
|
||||||
self.addToTrace("Setting CP line into state C.")
|
self.addToTrace("Setting CP line into state C.")
|
||||||
|
if (getConfigValue("digital_output_device")=="beaglebone"):
|
||||||
|
GPIO.output("P8_18", GPIO.HIGH)
|
||||||
self.outvalue |= 1
|
self.outvalue |= 1
|
||||||
|
|
||||||
def setPowerRelayOn(self):
|
def setPowerRelayOn(self):
|
||||||
self.addToTrace("Switching PowerRelay ON.")
|
self.addToTrace("Switching PowerRelay ON.")
|
||||||
|
if (getConfigValue("digital_output_device")=="beaglebone"):
|
||||||
|
GPIO.output("P8_16", GPIO.HIGH)
|
||||||
self.outvalue |= 2
|
self.outvalue |= 2
|
||||||
|
|
||||||
def setPowerRelayOff(self):
|
def setPowerRelayOff(self):
|
||||||
self.addToTrace("Switching PowerRelay OFF.")
|
self.addToTrace("Switching PowerRelay OFF.")
|
||||||
|
if (getConfigValue("digital_output_device")=="beaglebone"):
|
||||||
|
GPIO.output("P8_16", GPIO.LOW)
|
||||||
self.outvalue &= ~2
|
self.outvalue &= ~2
|
||||||
|
|
||||||
def setRelay2On(self):
|
def setRelay2On(self):
|
||||||
|
@ -90,7 +118,12 @@ class hardwareInterface():
|
||||||
#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 initPorts(self):
|
||||||
|
if (getConfigValue("digital_output_device") == "beaglebone"):
|
||||||
|
# Port configuration according to https://github.com/jsphuebner/pyPLC/commit/475f7fe9f3a67da3d4bd9e6e16dfb668d0ddb1d6
|
||||||
|
GPIO.setup("P8_16", GPIO.OUT) #output for port relays
|
||||||
|
GPIO.setup("P8_18", GPIO.OUT) #output for CP
|
||||||
|
|
||||||
def __init__(self, callbackAddToTrace=None, callbackShowStatus=None):
|
def __init__(self, callbackAddToTrace=None, callbackShowStatus=None):
|
||||||
self.callbackAddToTrace = callbackAddToTrace
|
self.callbackAddToTrace = callbackAddToTrace
|
||||||
|
@ -101,6 +134,8 @@ class hardwareInterface():
|
||||||
self.inletVoltage = 0.0 # volts
|
self.inletVoltage = 0.0 # volts
|
||||||
self.rxbuffer = ""
|
self.rxbuffer = ""
|
||||||
self.findSerialPort()
|
self.findSerialPort()
|
||||||
|
self.initPorts()
|
||||||
|
|
||||||
|
|
||||||
def resetSimulation(self):
|
def resetSimulation(self):
|
||||||
self.simulatedInletVoltage = 0.0 # volts
|
self.simulatedInletVoltage = 0.0 # volts
|
||||||
|
@ -111,7 +146,7 @@ class hardwareInterface():
|
||||||
self.simulatedInletVoltage = self.simulatedInletVoltage + 1.0 # simulate increasing voltage during PreCharge
|
self.simulatedInletVoltage = self.simulatedInletVoltage + 1.0 # simulate increasing voltage during PreCharge
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if (self.isInterfaceOk):
|
if (self.isSerialInterfaceOk):
|
||||||
self.ser.close()
|
self.ser.close()
|
||||||
|
|
||||||
def evaluateReceivedData(self, s):
|
def evaluateReceivedData(self, s):
|
||||||
|
@ -122,8 +157,8 @@ class hardwareInterface():
|
||||||
if (len(s)==4):
|
if (len(s)==4):
|
||||||
try:
|
try:
|
||||||
self.inletVoltage = int(s) / 1024.0 * 1.08 * (6250) / (4.7+4.7)
|
self.inletVoltage = int(s) / 1024.0 * 1.08 * (6250) / (4.7+4.7)
|
||||||
|
if (getConfigValue("analog_input_device")=="dieter"):
|
||||||
self.callbackShowStatus(format(self.inletVoltage,".1f"), "uInlet")
|
self.callbackShowStatus(format(self.inletVoltage,".1f"), "uInlet")
|
||||||
except:
|
except:
|
||||||
# keep last known value, if nothing new valid was received.
|
# keep last known value, if nothing new valid was received.
|
||||||
pass
|
pass
|
||||||
|
@ -132,18 +167,20 @@ class hardwareInterface():
|
||||||
|
|
||||||
def showOnDisplay(self, s1, s2, s3):
|
def showOnDisplay(self, s1, s2, s3):
|
||||||
# show the given string s on the display which is connected to the serial port
|
# show the given string s on the display which is connected to the serial port
|
||||||
s = "lc" + s1 + "\n" + "lc" + s2 + "\n" + "lc" + s3 + "\n"
|
if (getConfigValueBool("display_via_serial") and self.isSerialInterfaceOk):
|
||||||
self.ser.write(bytes(s, "utf-8"))
|
s = "lc" + s1 + "\n" + "lc" + s2 + "\n" + "lc" + s3 + "\n"
|
||||||
|
self.ser.write(bytes(s, "utf-8"))
|
||||||
|
|
||||||
def mainfunction(self):
|
def mainfunction(self):
|
||||||
if (self.simulatedSoc<100):
|
if (getConfigValueBool("soc_simulation")):
|
||||||
if ((self.outvalue & 2)!=0):
|
if (self.simulatedSoc<100):
|
||||||
# while the relay is closed, simulate increasing SOC
|
if ((self.outvalue & 2)!=0):
|
||||||
self.simulatedSoc = self.simulatedSoc + 0.01
|
# while the relay is closed, simulate increasing SOC
|
||||||
|
self.simulatedSoc = self.simulatedSoc + 0.01
|
||||||
|
|
||||||
|
|
||||||
self.loopcounter+=1
|
self.loopcounter+=1
|
||||||
if (self.isInterfaceOk):
|
if (self.isSerialInterfaceOk):
|
||||||
if (self.loopcounter>15):
|
if (self.loopcounter>15):
|
||||||
self.loopcounter=0
|
self.loopcounter=0
|
||||||
# self.ser.write(b'hello world\n')
|
# self.ser.write(b'hello world\n')
|
||||||
|
|
55
pyPlc.ini
Normal file
55
pyPlc.ini
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
|
||||||
|
[general]
|
||||||
|
# mode can be either PevMode to use as car, or EvseMode to use as charger
|
||||||
|
mode = PevMode
|
||||||
|
|
||||||
|
# Simulation without modem
|
||||||
|
# For development purposes, make it possible to run two instances (pev and evse) on one machine, without
|
||||||
|
# a modem connected. This feature is not tested since a long time, most likely does not work as intended.
|
||||||
|
# todo: replace isSimulationMode by this setting
|
||||||
|
is_simulation_without_modems = false
|
||||||
|
|
||||||
|
# The Ethernet interface. Usually eth0 on Raspberry.
|
||||||
|
# Todo: This setting is considered only on linux. Find a clean solution for windows is to be done.
|
||||||
|
eth_interface = eth0
|
||||||
|
|
||||||
|
# If a display is connected via serial line, e.g. an WIFI-KIT-32 running the software from https://github.com/uhi22/SerialToOLED
|
||||||
|
display_via_serial = yes
|
||||||
|
|
||||||
|
# LightBulbDemo turns on the relay with a short delay in the charging loop, to stabilize the voltage
|
||||||
|
# before the resistive load is connected.
|
||||||
|
light_bulb_demo = yes
|
||||||
|
|
||||||
|
# SOC simulation.
|
||||||
|
# In PevMode, simulate a rising SOC while charging.
|
||||||
|
soc_simulation = yes
|
||||||
|
|
||||||
|
# Device selection for the digital outputs, for CP state and power relays
|
||||||
|
# Possible options:
|
||||||
|
# dieter: Serial controlled device, which controls the digital outputs. E.g. arduino from https://github.com/uhi22/dieter
|
||||||
|
# beaglebone: GPIO pins of the beagleBone, as used in https://github.com/jsphuebner/pyPLC/tree/beaglebone
|
||||||
|
digital_output_device = dieter
|
||||||
|
#digital_output_device = beaglebone
|
||||||
|
|
||||||
|
# Device to read the physically measured inlet voltage in PevMode
|
||||||
|
# Either the high-voltage dieter from https://github.com/uhi22/dieter, which is connected on serial port.
|
||||||
|
# Or "none", if no measurement is intended.
|
||||||
|
#analog_input_device = dieter
|
||||||
|
analog_input_device = none
|
||||||
|
|
||||||
|
# Criteria for ending the PreCharge phase in PevMode
|
||||||
|
# Possible options:
|
||||||
|
# yes: use the EVSEPresentVoltage which is reported by the charger, to decide the end of the precharge
|
||||||
|
# no: use the physically measured inlet voltage to decide the end of the precharge
|
||||||
|
use_evsepresentvoltage_for_precharge_end = yes
|
||||||
|
|
||||||
|
# Use the physically measured inlet voltage to show on display during the charge loop.
|
||||||
|
# If false, we are showing the EVSEPresentVoltage which is reported by the charger.
|
||||||
|
use_physical_inlet_voltage_during_chargeloop = no
|
||||||
|
|
||||||
|
# Voltage threshold for the end-of-precharge decision
|
||||||
|
# This is the maximum difference voltage between the charger voltage and the accu voltage. If the actual voltage
|
||||||
|
# difference is lower than this threshold, we will close the power relay, to connect the accu to the charger.
|
||||||
|
# Unit: volt
|
||||||
|
u_delta_max_for_end_of_precharge = 10
|
||||||
|
|
5
pyPlc.py
5
pyPlc.py
|
@ -11,6 +11,7 @@ import time
|
||||||
import pyPlcWorker
|
import pyPlcWorker
|
||||||
from pyPlcModes import *
|
from pyPlcModes import *
|
||||||
import sys # for argv
|
import sys # for argv
|
||||||
|
from configmodule import getConfigValue, getConfigValueBool
|
||||||
|
|
||||||
startTime_ms = round(time.time()*1000)
|
startTime_ms = round(time.time()*1000)
|
||||||
|
|
||||||
|
@ -60,6 +61,10 @@ def cbShowStatus(s, selection=""):
|
||||||
root.update()
|
root.update()
|
||||||
|
|
||||||
myMode = C_LISTEN_MODE
|
myMode = C_LISTEN_MODE
|
||||||
|
if (getConfigValue("mode")=="PevMode"):
|
||||||
|
myMode = C_PEV_MODE
|
||||||
|
if (getConfigValue("mode")=="EvseMode"):
|
||||||
|
myMode = C_EVSE_MODE
|
||||||
if (len(sys.argv) > 1):
|
if (len(sys.argv) > 1):
|
||||||
if (sys.argv[1] == "P"):
|
if (sys.argv[1] == "P"):
|
||||||
myMode = C_PEV_MODE
|
myMode = C_PEV_MODE
|
||||||
|
|
Loading…
Reference in a new issue