2022-12-02 20:25:20 +00:00
|
|
|
|
|
|
|
# For serial (including USB-to-serial) interfaces:
|
|
|
|
# https://pyserial.readthedocs.io/en/latest/pyserial.html
|
|
|
|
# Install pyserial library:
|
|
|
|
# python -m pip install pyserial
|
|
|
|
# List ports:
|
|
|
|
# python -m serial.tools.list_ports
|
|
|
|
|
|
|
|
import serial # the pyserial
|
|
|
|
from serial.tools.list_ports import comports
|
|
|
|
from time import sleep
|
2023-04-15 18:35:51 +00:00
|
|
|
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
|
2022-12-02 20:25:20 +00:00
|
|
|
|
2022-12-06 17:35:17 +00:00
|
|
|
class hardwareInterface():
|
2023-04-15 18:35:51 +00:00
|
|
|
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.
|
|
|
|
|
2022-12-06 17:35:17 +00:00
|
|
|
def findSerialPort(self):
|
|
|
|
ports = []
|
|
|
|
self.addToTrace('Available serial ports:')
|
|
|
|
for n, (port, desc, hwid) in enumerate(sorted(comports()), 1):
|
2023-01-24 16:52:23 +00:00
|
|
|
if (port=="/dev/ttyAMA0"):
|
|
|
|
self.addToTrace("ignoring /dev/ttyAMA0, because this is not an USB serial port")
|
|
|
|
else:
|
|
|
|
self.addToTrace('{:2}: {:20} {!r}'.format(n, port, desc))
|
|
|
|
ports.append(port)
|
2022-12-06 17:35:17 +00:00
|
|
|
if (len(ports)<1):
|
2023-04-15 18:35:51 +00:00
|
|
|
if (self.needsSerial()):
|
|
|
|
self.addToTrace("ERROR: No serial ports found. No hardware interaction possible.")
|
|
|
|
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
|
2022-12-06 17:35:17 +00:00
|
|
|
else:
|
|
|
|
self.addToTrace("ok, we take the first port, " + ports[0])
|
|
|
|
try:
|
|
|
|
self.ser = serial.Serial(ports[0], 19200, timeout=0)
|
2023-04-15 18:35:51 +00:00
|
|
|
self.isSerialInterfaceOk = True
|
2022-12-06 17:35:17 +00:00
|
|
|
except:
|
|
|
|
self.addToTrace("ERROR: Could not open serial port.")
|
|
|
|
self.ser = None
|
2023-04-15 18:35:51 +00:00
|
|
|
self.isSerialInterfaceOk = False
|
2022-12-06 17:35:17 +00:00
|
|
|
|
|
|
|
def addToTrace(self, s):
|
|
|
|
self.callbackAddToTrace("[HARDWAREINTERFACE] " + s)
|
|
|
|
|
|
|
|
def setStateB(self):
|
|
|
|
self.addToTrace("Setting CP line into state B.")
|
2023-04-15 18:35:51 +00:00
|
|
|
if (getConfigValue("digital_output_device")=="beaglebone"):
|
|
|
|
GPIO.output("P8_18", GPIO.LOW)
|
2022-12-08 23:22:18 +00:00
|
|
|
self.outvalue &= ~1
|
2022-12-06 17:35:17 +00:00
|
|
|
|
|
|
|
def setStateC(self):
|
|
|
|
self.addToTrace("Setting CP line into state C.")
|
2023-04-15 18:35:51 +00:00
|
|
|
if (getConfigValue("digital_output_device")=="beaglebone"):
|
|
|
|
GPIO.output("P8_18", GPIO.HIGH)
|
2022-12-08 23:22:18 +00:00
|
|
|
self.outvalue |= 1
|
|
|
|
|
|
|
|
def setPowerRelayOn(self):
|
|
|
|
self.addToTrace("Switching PowerRelay ON.")
|
2023-04-15 18:35:51 +00:00
|
|
|
if (getConfigValue("digital_output_device")=="beaglebone"):
|
|
|
|
GPIO.output("P8_16", GPIO.HIGH)
|
2022-12-08 23:22:18 +00:00
|
|
|
self.outvalue |= 2
|
|
|
|
|
|
|
|
def setPowerRelayOff(self):
|
|
|
|
self.addToTrace("Switching PowerRelay OFF.")
|
2023-04-15 18:35:51 +00:00
|
|
|
if (getConfigValue("digital_output_device")=="beaglebone"):
|
|
|
|
GPIO.output("P8_16", GPIO.LOW)
|
2022-12-08 23:22:18 +00:00
|
|
|
self.outvalue &= ~2
|
2022-12-16 12:03:36 +00:00
|
|
|
|
|
|
|
def setRelay2On(self):
|
2022-12-19 17:09:39 +00:00
|
|
|
self.addToTrace("Switching Relay2 ON.")
|
2022-12-16 12:03:36 +00:00
|
|
|
self.outvalue |= 4
|
|
|
|
|
|
|
|
def setRelay2Off(self):
|
2022-12-19 17:09:39 +00:00
|
|
|
self.addToTrace("Switching Relay2 OFF.")
|
2022-12-16 12:03:36 +00:00
|
|
|
self.outvalue &= ~4
|
2022-12-08 23:22:18 +00:00
|
|
|
|
2023-05-03 19:25:01 +00:00
|
|
|
def getPowerRelayConfirmation(self):
|
|
|
|
return 1 # todo: self.contactor_confirmed
|
|
|
|
|
2022-12-08 23:22:18 +00:00
|
|
|
def getInletVoltage(self):
|
2023-02-27 09:53:42 +00:00
|
|
|
# uncomment this line, to take the simulated inlet voltage instead of the really measured
|
|
|
|
# self.inletVoltage = self.simulatedInletVoltage
|
2022-12-08 23:22:18 +00:00
|
|
|
return self.inletVoltage
|
|
|
|
|
|
|
|
def getAccuVoltage(self):
|
|
|
|
#todo: get real measured voltage from the accu
|
|
|
|
self.accuVoltage = 230
|
|
|
|
return self.accuVoltage
|
|
|
|
|
|
|
|
def getAccuMaxCurrent(self):
|
|
|
|
#todo: get max charging current from the BMS
|
|
|
|
self.accuMaxCurrent = 10
|
|
|
|
return self.accuMaxCurrent
|
|
|
|
|
2022-12-09 13:23:32 +00:00
|
|
|
def getAccuMaxVoltage(self):
|
|
|
|
#todo: get max charging voltage from the BMS
|
|
|
|
self.accuMaxVoltage = 230
|
|
|
|
return self.accuMaxVoltage
|
|
|
|
|
2022-12-08 23:22:18 +00:00
|
|
|
def getIsAccuFull(self):
|
|
|
|
#todo: get "full" indication from the BMS
|
|
|
|
self.IsAccuFull = (self.simulatedSoc >= 98)
|
|
|
|
return self.IsAccuFull
|
2022-12-09 13:23:32 +00:00
|
|
|
|
|
|
|
def getSoc(self):
|
|
|
|
#todo: get SOC from the BMS
|
2022-12-19 17:09:39 +00:00
|
|
|
self.callbackShowStatus(format(self.simulatedSoc,".1f"), "soc")
|
2022-12-09 13:23:32 +00:00
|
|
|
return self.simulatedSoc
|
2023-04-15 18:35:51 +00:00
|
|
|
|
|
|
|
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
|
2022-12-06 17:35:17 +00:00
|
|
|
|
2022-12-19 13:48:23 +00:00
|
|
|
def __init__(self, callbackAddToTrace=None, callbackShowStatus=None):
|
2022-12-06 17:35:17 +00:00
|
|
|
self.callbackAddToTrace = callbackAddToTrace
|
2022-12-19 13:48:23 +00:00
|
|
|
self.callbackShowStatus = callbackShowStatus
|
2022-12-06 17:35:17 +00:00
|
|
|
self.loopcounter = 0
|
|
|
|
self.outvalue = 0
|
2022-12-08 23:22:18 +00:00
|
|
|
self.simulatedSoc = 20.0 # percent
|
|
|
|
self.inletVoltage = 0.0 # volts
|
2022-12-19 13:48:23 +00:00
|
|
|
self.rxbuffer = ""
|
2022-12-06 17:35:17 +00:00
|
|
|
self.findSerialPort()
|
2023-04-15 18:35:51 +00:00
|
|
|
self.initPorts()
|
|
|
|
|
2022-12-10 16:08:51 +00:00
|
|
|
|
|
|
|
def resetSimulation(self):
|
|
|
|
self.simulatedInletVoltage = 0.0 # volts
|
|
|
|
self.simulatedSoc = 20.0 # percent
|
|
|
|
|
|
|
|
def simulatePreCharge(self):
|
|
|
|
if (self.simulatedInletVoltage<230):
|
|
|
|
self.simulatedInletVoltage = self.simulatedInletVoltage + 1.0 # simulate increasing voltage during PreCharge
|
2022-12-06 17:35:17 +00:00
|
|
|
|
|
|
|
def close(self):
|
2023-04-15 18:35:51 +00:00
|
|
|
if (self.isSerialInterfaceOk):
|
2022-12-06 17:35:17 +00:00
|
|
|
self.ser.close()
|
2022-12-19 13:48:23 +00:00
|
|
|
|
|
|
|
def evaluateReceivedData(self, s):
|
|
|
|
self.rxbuffer += s
|
|
|
|
x=self.rxbuffer.find("A0=")
|
|
|
|
if (x>=0):
|
|
|
|
s = self.rxbuffer[x+3:x+7]
|
|
|
|
if (len(s)==4):
|
|
|
|
try:
|
2023-02-27 09:53:42 +00:00
|
|
|
self.inletVoltage = int(s) / 1024.0 * 1.08 * (6250) / (4.7+4.7)
|
2023-04-15 18:35:51 +00:00
|
|
|
if (getConfigValue("analog_input_device")=="dieter"):
|
|
|
|
self.callbackShowStatus(format(self.inletVoltage,".1f"), "uInlet")
|
2022-12-19 13:48:23 +00:00
|
|
|
except:
|
|
|
|
# keep last known value, if nothing new valid was received.
|
|
|
|
pass
|
|
|
|
#self.addToTrace("RX data ok " + s)
|
|
|
|
self.rxbuffer = self.rxbuffer[x+3:] # consume the receive buffer entry
|
2023-02-01 18:20:35 +00:00
|
|
|
|
|
|
|
def showOnDisplay(self, s1, s2, s3):
|
|
|
|
# show the given string s on the display which is connected to the serial port
|
2023-04-15 18:35:51 +00:00
|
|
|
if (getConfigValueBool("display_via_serial") and self.isSerialInterfaceOk):
|
|
|
|
s = "lc" + s1 + "\n" + "lc" + s2 + "\n" + "lc" + s3 + "\n"
|
|
|
|
self.ser.write(bytes(s, "utf-8"))
|
2023-02-01 18:20:35 +00:00
|
|
|
|
2022-12-06 17:35:17 +00:00
|
|
|
def mainfunction(self):
|
2023-04-15 18:35:51 +00:00
|
|
|
if (getConfigValueBool("soc_simulation")):
|
|
|
|
if (self.simulatedSoc<100):
|
|
|
|
if ((self.outvalue & 2)!=0):
|
|
|
|
# while the relay is closed, simulate increasing SOC
|
|
|
|
self.simulatedSoc = self.simulatedSoc + 0.01
|
2022-12-19 17:09:39 +00:00
|
|
|
|
|
|
|
|
2022-12-06 17:35:17 +00:00
|
|
|
self.loopcounter+=1
|
2023-04-15 18:35:51 +00:00
|
|
|
if (self.isSerialInterfaceOk):
|
2022-12-06 17:35:17 +00:00
|
|
|
if (self.loopcounter>15):
|
|
|
|
self.loopcounter=0
|
|
|
|
# self.ser.write(b'hello world\n')
|
2022-12-12 07:26:18 +00:00
|
|
|
s = "000" + str(self.outvalue)
|
|
|
|
self.ser.write(bytes("do"+s+"\n", "utf-8")) # set outputs of dieter, see https://github.com/uhi22/dieter
|
2022-12-06 17:35:17 +00:00
|
|
|
s = self.ser.read(100)
|
|
|
|
if (len(s)>0):
|
2022-12-19 13:48:23 +00:00
|
|
|
try:
|
|
|
|
s = str(s, 'utf-8').strip()
|
|
|
|
except:
|
|
|
|
s = "" # for the case we received corrupted data (not convertable as utf-8)
|
|
|
|
self.addToTrace(str(len(s)) + " bytes received: " + s)
|
|
|
|
self.evaluateReceivedData(s)
|
2022-12-06 17:35:17 +00:00
|
|
|
|
|
|
|
def myPrintfunction(s):
|
|
|
|
print("myprint " + s)
|
|
|
|
|
2022-12-02 20:25:20 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
print("Testing hardwareInterface...")
|
2022-12-06 17:35:17 +00:00
|
|
|
hw = hardwareInterface(myPrintfunction)
|
2022-12-16 12:03:36 +00:00
|
|
|
for i in range(0, 350):
|
2022-12-06 17:35:17 +00:00
|
|
|
hw.mainfunction()
|
2023-02-01 18:20:35 +00:00
|
|
|
if (i==20):
|
|
|
|
hw.showOnDisplay("Hello", "A DEMO", "321.0V")
|
2022-12-16 12:03:36 +00:00
|
|
|
if (i==50):
|
|
|
|
hw.setStateC()
|
2022-12-06 17:35:17 +00:00
|
|
|
if (i==100):
|
2022-12-16 12:03:36 +00:00
|
|
|
hw.setStateB()
|
|
|
|
if (i==150):
|
2022-12-06 17:35:17 +00:00
|
|
|
hw.setStateC()
|
2022-12-16 12:03:36 +00:00
|
|
|
hw.setPowerRelayOn()
|
2023-02-01 18:20:35 +00:00
|
|
|
hw.showOnDisplay("", "..middle..", "")
|
2022-12-06 17:35:17 +00:00
|
|
|
if (i==200):
|
|
|
|
hw.setStateB()
|
2022-12-16 12:03:36 +00:00
|
|
|
hw.setPowerRelayOff()
|
2022-12-06 17:35:17 +00:00
|
|
|
if (i==250):
|
2022-12-16 12:03:36 +00:00
|
|
|
hw.setRelay2On()
|
|
|
|
if (i==300):
|
|
|
|
hw.setRelay2Off()
|
2023-02-01 18:20:35 +00:00
|
|
|
if (i==320):
|
|
|
|
hw.showOnDisplay("This", "...is...", "DONE :-)")
|
2022-12-06 17:35:17 +00:00
|
|
|
sleep(0.03)
|
|
|
|
hw.close()
|
2023-01-24 16:52:23 +00:00
|
|
|
print("finished.")
|