Package: py-kelctl
KELctlThis is a Python 3 library to provide easier means of controlling a Korad KEL103 electronic load over a serial connection. It will probably also work with a KEL102 load but this is untested.
I would recommend, in addition to this documentation, to also check the test.py file for examples on hwo to use the library. Additionally more details about certain aspects on how the load works are provided in the KEL-103 protocol documentation.It uses theaenum library.
The minimum required python version required is 3.10.This library has been initially copied from thepy-korad-serial projectand been modified to work with the electronic loads instead of power supplies.Python packageThe library is available as a package at PyPI aspy-kelctl.HardwareTheKorad KEL103/KEL102is sold under different names and brands i.e. RND 320-KEL103.UsageExamples for basic usage of all functions are included in the test.py file.The object supports the pythonwithstatement to release the serial
port automatically:from KELSerial import KELSerial

with KELSerial('/dev/ttyACM0') as load:
    print("Model: ", load.model)
    print("Status: ", load.status)Class  DocumentationKELSerialClassConstructor__init__(port, rate, debug=False)The constructor takes a string containing the serial device to attach to.
Rate determines the Baudrate to run at. Optional, defaults to 115200 and takes anBaudRateEnum value.
Ifdebugis set toTrue, then data sent and received is printed to output. Optional and defaults toFalse.inputAttributeTurns on and off the input of the load.load.input.on()andload.input.off()Gets state of input and returns anOnOffStateload.input.get()returns OnOffState.off or OnOffState.onmemoriesAttributeThis is an array of memory settings for all memories from 1-100 on unit mapped to the array from 0 to 99. Can be used to save and recall memories on unit.
The saved values can not be retrieved directly from the unit. When recalling a memory the corresponding mode on unit will be set together with the saved value.
Memories will not return anything.m1 = load.memories[0]
m1.recall()
m2 = load.memories[99]
m2.save()modelproperty (read-only)Returns the load model information.RND 320-KEL103 V2.60 SN:01234567device_infoproperty (read-only)Returns some device information as a multiline string.load.device_inforeturns:DHCP:0
IP:192.168.1.198
NETMASK:255.255.255.0
GateWay:192.168.1.1
MAC:70-2f-eb-48-4d-56
PORT:18190
BAUDRATE:115200statuspropertyReturns aStatus objectrepresenting current status of certain settings.load.statusreturnsStatusvoltagepropertySets the voltage value for the constant voltage mode and switches to it. Takes float as value in Volts.RaisesValueOutOfLimitErrorwhen trying to set value above set limits.load.voltage = 30.05Gets the voltage value for constant voltage mode. Returns float value in Volts.load.voltagereturns30.05as float or returns nonecurrentpropertySets the current value for the constant current mode and switches to it. Takes float as value in Amps.RaisesValueOutOfLimitErrorwhen trying to set value above set limits.load.current = 10.05Gets the current value for constant current mode. Returns float value in Amps.load.currentreturns10.05as float or returns nonepowerpropertySets the power value for the constant power mode and switches to it. Takes float as value in Watts.RaisesValueOutOfLimitErrorwhen trying to set value above set limits.load.power = 20.05Gets the power value for constant power mode. Returns float value in Watts.load.powerreturns20.05as float or returns noneresistancepropertySets the resistance value for the constant resistance mode and switches to it. Takes float as value in Ohms.RaisesValueOutOfLimitErrorwhen trying to set value above set limits.load.resistance = 2000.05Gets the resistance value for constant resistance mode. Returns float value in Ohms.load.resistancereturns2000.05as float or returns nonemeasured_voltageproperty (read-only)Reads the voltage currently present at device. Returns float value in Volts.load.measured_voltagereturns30.05as float or returns nonemeasured_powerproperty (read-only)Reads the power currently present at device. Returns float value in Watts.load.measured_powerreturns20.05as float or returns nonemeasured_currentproperty (read-only)Reads the current currently present at device. Returns float value in Amps.load.measured_currentreturns10.05as float or returns nonefunctionpropertySets the mode/function of the load. Setting the function in this way is limited to certain modes(constant-voltage, constant-current, constant-resistance, constant-power, short). Trying to set other mode will raise aNoModeSetError.
Takes aMode(Enum) as input.load.function = Mode.constant_voltageGets currently active function and returns it asMode(Enum).load.functionreturnsMode.constant_voltageset_listfunctionSets and saves a list and optionally recalls the list on device. Recall option defaults to true if not used. Takes aLoadList objectas input and will validate the list before setting it to prevent errors on device. Will raise a ValueError orValueOutOfLimitErroron failed validation.steps = [ListStep(3, 0.002, 5), ListStep(2, 0.003, 3), ListStep(2, 0.005, 2)]
testList = LoadList(3, 10, steps, 6)
load.set_list(testList, True)get_listfunctionRecalls and then retrieves selected list by save-slot and returnsLoadList object. Will raise ValueError if trying to get invalid list(valid is 1-7).load.get_list(3)returnsLoadListrecall_listfunctionRecalls existing list on device. Recalling non-existing list will result in error on device. Will raise ValueError if trying to get invalid list(valid is 1-7).load.recall_list(3)set_ocpfunctionSets and saves an OCP-list and if set recalls the list on device. Recall option defaults to true if not used. Takes aOCPList objectas input and will validate the list before setting it to prevent errors on device. Will raise a ValueError orValueOutOfLimitErroron failed validation.ocpList = OCPList(1, 10, 4, 20, 15, 1, 1, 5, 30, 10.52, 8.34)
load.set_ocp(ocpList)get_ocpfunctionRecalls and then retrieves selected OCP-list by save-slot and returnsOCPList object. Will raise ValueError if trying to get invalid list(valid is 1-10).load.get_ocp(1)returnsOCPListrecall_ocpfunctionRecalls existing OCP-list on device. Recalling non-existing list will result in error on device. Will raise ValueError if trying to get invalid list(valid is 1-10).load.recall_ocp(1)set_oppfunctionSets and saves an OPP-list and if set recalls the list on device. Recall option defaults to true if not used. Takes anOPPList objectas input and will validate the list before setting it to prevent errors on device. Will raise a ValueError orValueOutOfLimitErroron failed validation.oppList = OPPList(1, 11, 4, 20, 15, 1, 1, 5, 30, 10.52, 8.34)
load.set_opp(oppList)get_oppfunctionRecalls and then retrieves selected OPP-list by save-slot and returnsOPPList object. Will raise ValueError if trying to get invalid list(valid is 1-10).load.get_opp(1)returnsOPPListrecall_oppfunctionRecalls existing OPP-list on device. Recalling non-existing list will result in error on device. Will raise ValueError if trying to get invalid list(valid is 1-10).load.recall_opp(1)set_battfunctionSets and saves a Battery-test-list and if set recalls the list on device. Recall option defaults to true if not used. Takes anBattList objectas input and will validate the list before setting it to prevent errors on device. Will raise a ValueError orValueOutOfLimitErroron failed validation.battList = BattList(1, 20.23, 10, 10, 30, 5)
load.set_batt(battList)get_battfunctionRecalls and then retrieves selected Battery-test-list by save-slot and returnsBattList object. Will raise ValueError if trying to get invalid list(valid is 1-10).load.get_batt(1)returnsBattListrecall_battfunctionRecalls existing Battery-test-list on device. Recalling non-existing list will result in error on device. Will raise ValueError if trying to get invalid list(valid is 1-10).load.recall_batt(1)get_batt_capfunctionReturns current capacity measured from battery-test. Returns AH as float.load.get_batt_cap()returns5.1234as float or returns noneget_batt_timefunctionReturns current time battery-test has been running. Returns minutes as float.load.get_batt_time()returns5.1234as float or returns noneset_dynamic_modefunctionSets and saves a dynamic-mode-list and if set recalls the list on device. Recall option defaults to true if not used. Takes one of the sixdynamic-list objectsas input and will validate the list before setting it to prevent errors on device. Will raise aValueOutOfLimitErroron failed validation.dyn1List = CVList(30.02, 20.05, 1.5, 30)
load.set_dynamic_mode(dyn1List)get_dynamic_modefunctionRecalls and gets currently set dynamic-mode and returns appropriatedynamic-list objectdepending on which of the dynamic modes is set. Will raiseInvalidModeErrorwhen function is called with device not in a dynamic mode.load.get_dynamic_mode()returnsdynamic-list objectrecall_dynamic_modefunctionRecalls dynamic mode and returns string returned from unit. This function is required to be called after setting a dynamic mode before being able to use it(Per default is done in set method already).load.recall_dynamic_mode()settingsattributeThe settings attribute is an instance of the Settings class giving access to the various settings of the device.beepattributeTurns on or off the "beep" setting or returns state asOnOffState.load.settings.beep.on()orload.settings.beep.off()Gets state of setting and returnsOnOffState.load.settings.beep.get()returns OnOffState.on or OnOffState.offlockattributeTurns on or off the "lock" setting causing the keys on front of unit to be locked or unlocked.load.settings.lock.on()orload.settings.lock.off()Gets state of setting and returnsOnOffState.load.settings.lock.get()returns OnOffState.on or OnOffState.offdhcpattributeTurns on or off "DHCP" setting.load.settings.dhcp.on()orload.settings.dhcp.off()Gets state of setting and returnsOnOffState.load.settings.dhcp.get()returns OnOffState.on or OnOffState.offtriggerattributeTurns on or off the "Trigger" setting which will enable or disable the external trigger input on device. Does not affect the trigger command over serial or on device.load.settings.trigger.on()orload.settings.trigger.off()Gets state of setting and returnsOnOffState.load.settings.trigger.get()returns OnOffState.on or OnOffState.offcompensationattributeTurns on or off the "Compensation" setting which will enable or disable the external compensation function. With compensation on according to official documentation Memories are unavailable.load.settings.compensation.on()orload.settings.compensation.off()Gets state of setting and returnsOnOffState.load.settings.compensation.get()returns OnOffState.on or OnOffState.offipaddresspropertySets the IP-Address of device. IP-Address will be verified to ensure proper formatting. Will raise ValueError if improperly formattedload.settings.ipaddress = "10.0.0.123"Gets the IP-Adress of device.load.settings.ipaddressreturns10.0.0.123subnetmaskpropertySets the Subnetmask of device. Subnetmask will be verified to ensure proper formatting. Will raise ValueError if improperly formattedload.settings.subnetmask = "255.255.255.0"Gets the Subnetmask of device.load.settings.subnetmaskreturns255.255.255.0gatewaypropertySets the Gateway of device. Gateway will be verified to ensure proper formatting. Will raise ValueError if improperly formattedload.settings.gateway = "10.0.0.1"Gets the Gateway of device.load.settings.gatewayreturns10.0.0.1macaddresspropertySets the Mac-Address of device. Mac-Address will be verified to ensure proper formatting. Will raise ValueError if improperly formatted. Will take addresses using either:or-as separator.load.settings.macaddress = "00-80-41-ae-fd-7e"Gets the Mac-Address of device.load.settings.macaddressreturns01:80:41:ae:fd:7ebaudratepropertySets the Baudrate of device. TakesBaudRateEnum.load.settings.baudrate = BaudRate(3)"orload.settings.baudrate = BaudRate(57600)"orload.settings.baudrate = BaudRate.R57600"Gets the Baudrate of device.load.settings.baudratereturnsBaudRateEnum.portpropertySets the network Port of device.load.settings.port = 12345"Gets the network Port of device.load.settings.portreturns12345as Integervoltage_limitpropertySets the voltage limit of device. Takes float as value in Volts.load.settings.voltage_limit = 100.05Gets the voltage limit of device. Returns float value in Volts.load.settings.voltage_limitreturns100.05as float or returns nonecurrent_limitpropertySets the current limit of device. Takes float as value in Amps.load.settings.current_limit = 20.05Gets the current limit of device. Returns float value in Amps.load.settings.current_limitreturns20.05as float or returns nonepower_limitpropertySets the power limit of device. Takes float as value in Watts.load.settings.power_limit = 200.05Gets the power limit of device. Returns float value in Watts.load.settings.power_limitreturns200.05as float or returns noneresistance_limitpropertySets the resistance limit of device. Takes float as value in Ohms.load.settings.resistance_limit = 2000.05Gets the resistance limit of device. Returns float value in Ohms.load.settings.resistancer_limitreturns2000.05as float or returns nonefactoryresetfunctionResets the device to it's factory-settings. This may disrupt the serial connection for it to be reenabled/baud rate set again on device directly.load.settings.factoryreset()StatusclassObject representing the values returned by the:STAT ?command.
Consists ofbeep,lock,trigger,commasOnOffStateandbaudrateasBaudRate.__str__functionWill return the mentioned variables as readable String.Beep: on, Lock: on, Baudrate: 115200, Trigger: on, Comm: onListStepclassThe ListStep class represents a single step from a LoadList class.
It consists of the following values:current(in Amps, float),current_slope(rate at which current will change to defined value in A/uS, float),duration(duration of this step in seconds, float)step = ListStep(3, 0.002, 5)LoadListclassThe LoadList class represents all values required for using the list function of the device.It consists of the following values:save-slot(in which save-slot 1-7 the list will be saved in, int),current_range(the limit up to which value the current can be set in Amps, Lists are not restricted by limits set in settings, float),steps(an array of ListStep objects),loop_number(how often the steps will be repeated, int)steps = [ListStep(3, 0.002, 5), ListStep(2, 0.003, 3), ListStep(2, 0.005, 2)]
testlist = LoadList(3, 10, steps, 6)validatefunctionThis function will validate a LoadList to make sure that the save-slot is between 1 and 7, that there are a maximum of 84 steps, a minimum of 2 steps, that current values are within set range and current slope values are below device limit.
Will raise ValueError orValueOutOfLimitErrorif validation failed.testlist.validate()__str__functionFunction will return the string required to be sent to device to set the list.OCPListclassThe OCPList class represents all values required for using the OCP function of the device.It consists of the following values:save-slot(in which save-slot 1-10 the list will be saved in, int),on_voltage(voltage above which test will start in Volts, float),on_delay(delay in seconds after reaching on_voltage after which test starts, float),current_range(the limit up to which value the current can be set in Amps, Lists are not restricted by limits set in settings, float),initial_current(current at which test starts out in Amps, float),step_current(current value in Amps which each step will go down by, float),step_delay(delay in seconds after each step before next step down, float),off_current(current in Amps below which test will stop, float),ocp_voltage(value in Volts above voltage has to rise for successful test, float),max_overcurrent(max current value in Amps at which OCP has to disengage at for successful test, float),min_overcurrent(min current value in Amps at which OCP has to disengage at for successful test, float)ocpList = OCPList(1, 10, 4, 20, 15, 1, 1, 5, 30, 10.52, 8.34)validatefunctionThis function will validate an OCPList to make sure that the save-slot is between 1 and 10, that current values are within set range and several checks for values(refer to protocol documentation for detailed requirements on values).
Will raise ValueError orValueOutOfLimitErrorif validation failed.ocplist.validate()__str__functionFunction will return the string required to be sent to device to set the list.OPPListclassThe OPPList class represents all values required for using the OPP function of the device.It consists of the following values:save-slot(in which save-slot 1-10 the list will be saved in, int),on_voltage(voltage above which test will start in Volts, float),on_delay(delay in seconds after reaching on_voltage after which test starts, float),current_range(the limit up to which value the current can be set in Amps, Lists are not restricted by limits set in settings, float),initial_power(power at which test starts out in Watts, float),step_power(power value in Watts which each step will go down by, float),step_delay(delay in seconds after each step before next step down, float),off_power(power in Watts below which test will stop, float),opp_voltage(value in Volts above voltage has to rise for successful test, float),max_overpower(max power value in Watts at which OPP has to disengage at for successful test, float),min_overpower(min power value in Watts at which OPP has to disengage at for successful test, float)oppList = OPPList(1, 10, 4, 20, 15, 1, 1, 5, 30, 10.52, 8.34)validatefunctionThis function will validate an OPPList to make sure that the save-slot is between 1 and 10 and several checks for values(refer to protocol documentation for detailed requirements on values).
Will raise ValueError orValueOutOfLimitErrorif validation failed.opplist.validate()__str__functionFunction will return the string required to be sent to device to set the list.BattListclassThe BattList class represents all values required for using the Battery test function of the device.It consists of the following values:save-slot(in which save-slot 1-10 the list will be saved in, int),current_range(the limit up to which value the current can be set in Amps, Lists are not restricted by limits set in settings, float),discharge_current(current in Amps at which the battery will be discharged at, float),cutoff_voltage(voltage in Volts at which test will stop, float),cutoff_capacity(capacity in AH at which test will stop, float),cutoff_time(time in minutes after which the test will stop, float)battList = BattList(1, 20.23, 10, 10, 30, 5)validatefunctionThis function will validate an BattList to make sure that the save-slot is between 1 and 10 and that the discharge current is within set current range.
Will raise ValueError if validation failed.battList.validate()__str__functionFunction will return the string required to be sent to device to set the list.Dynamic ListsThese are the 6 different dynamic list classes.CVListclassThe CVList class represents all values required for using the constant voltage dynamic mode function of the device.It consists of the following values:voltage1(first voltage value in Volts, float),voltage2(second voltage in Volts, float),frequency(frequency in Hz at which voltage switching will perform full cycle, float),duty_cycle(portion in % the second voltage will be in use, float)dyn1List = CVList(30.02, 20.05, 1.5, 30)validatefunctionThis function will validate a CVList to make sure that voltage values are below provided limit value and that the duty cycle is below 100%.
Will raiseValueOutOfLimitErrorif validation failed.
Needs alimit(value in volts, float) as argument to compare against. Typically, limit should just be the set device limit.dyn1List.validate(20.23)__str__functionFunction will return the string required to be sent to device to set the list.CCListclassThe CCList class represents all values required for using the constant current dynamic mode function of the device.It consists of the following values:slope1(rate at which current will rise to first value in A/uS, float),slope2(rate at which current will rise to second value in A/uS, float),current1(first current value in Amps, float),current2(second current in Amps, float),frequency(frequency in Hz at which current switching will perform full cycle, float),duty_cycle(portion in % the second current will be in use, float)dyn2List = CCList(0.4, 0.5, 5, 6, 3, 40)validatefunctionThis function will validate a CCList to make sure that current values are below provided limit value, that the duty cycle is below 100% and current slope values are below device limit.
Will raiseValueOutOfLimitErrorif validation failed.
Needs alimit(value in amps, float) as argument to compare against. Typically, limit should just be the set device limit.dyn2List.validate(20.12)__str__functionFunction will return the string required to be sent to device to set the list.CRListclassThe CRList class represents all values required for using the constant resistance dynamic mode function of the device.It consists of the following values:resistance1(first resistance value in Ohms, float),resistance2(second resistance in Ohms, float),frequency(frequency in Hz at which resistance switching will perform full cycle, float),duty_cycle(portion in % the second resistance will be in use, float)dyn3List = CRList(30.02, 20.05, 1.5, 30)validatefunctionThis function will validate a CRList to make sure that resistance values are below provided limit value and that the duty cycle is below 100%.
Will raiseValueOutOfLimitErrorif validation failed.
Needs alimit(value in Ohms, float) as argument to compare against. Typically, limit should just be the set device limit.dyn3List.validate(20.56)__str__functionFunction will return the string required to be sent to device to set the list.CWListclassThe CWList class represents all values required for using the constant power dynamic mode function of the device.It consists of the following values:power1(first power value in Watts, float),power2(second power in Watts, float),frequency(frequency in Hz at which power switching will perform full cycle, float),duty_cycle(portion in % the second power will be in use, float)dyn4List = CWList(6, 9, 3, 40)validatefunctionThis function will validate a CWList to make sure that power values are below provided limit value and that the duty cycle is below 100%.
Will raiseValueOutOfLimitErrorif validation failed.
Needs alimit(value in watts, float) as argument to compare against. Typically, limit should just be the set device limit.dyn4List.validate(20.87)__str__functionFunction will return the string required to be sent to device to set the list.PulseListclassThe PulseList class represents all values required for using the pulse dynamic mode function of the device.It consists of the following values:slope1(rate at which current will rise to first value in A/uS, float),slope2(rate at which current will rise to second value in A/uS, float),current1(first current in Amps, float),current2(second current in Amps, float),duration(duration in seconds the second current value will stay on after receiving trigger, float)dyn5List = PulseList(0.3, 0.2, 4, 6, 5)validatefunctionThis function will validate a PulseList to make sure that current values are below provided limit value and current slope values are below device limit.
Will raiseValueOutOfLimitErrorif validation failed.
Needs alimit(value in amps, float) as argument to compare against. Typically, limit should just be the set device limit.dyn5List.validate(20.34)__str__functionFunction will return the string required to be sent to device to set the list.ToggleListclassThe ToggleList class represents all values required for using the toggle dynamic mode function of the device.It consists of the following values:slope1(rate at which current will rise to first value in A/uS, float),slope2(rate at which current will rise to second value in A/uS, float),current1(first current in Amps, float),current2(second current in Amps, float)dyn6List = ToggleList(0.3, 0.2, 4, 6)validatefunctionThis function will validate a ToggleList to make sure that current values are below provided limit value and current slope values are below device limit.
Will raiseValueOutOfLimitErrorif validation failed.
Needs alimit(value in amps, float) as argument to compare against. Typically, limit should just be the set device limit.dyn6List.validate(20.47)__str__functionFunction will return the string required to be sent to device to set the list.EnumsDescribes the Enums used, making use of aenums MultiValueEnum.ModeclassRepresents the various modes/functions the device operates in to provide more useful names bound to the values used by the load.
The following modes are included:constant_voltage = "CV"
constant_current = "CC"
constant_resistance = "CR"
constant_power = "CW"
battery = "BATTERY"  # read only
short = "SHORt"
OCP = "OCP"  # read only
LIST = "LIST"  # read only
OPP = "OPP"  # read only
dynamic_cv = "CONTINUOUS CV"  # read only like all dynamic modes
dynamic_cc = "CONTINUOUS CC"
dynamic_cr = "CONTINUOUS CR"
dynamic_cw = "CONTINUOUS CW"
dynamic_pulse = "PULSE"
dynamic_toggle = "TOGGLE"BaudRateclassRepresents the available baud rates the device operates in to provide a single type of value for the different ways the load outputs it in.
Due to using a MultiValueEnum you can get both values from the Enum like this:rate = BaudRate.R57600
print("First Value: ", rate.a)
print("Second Value: ", rate.b)The following rates are included:R9600 = (0, 9600)
R19200 = (1, 19200)
R38400 = (2, 38400)
R57600 = (3, 57600)
R115200 = (4, 115200)OnOffStateclassRepresents the on/off state to provide a single type of value for the different ways the load outputs it in.
Due to using a MultiValueEnum you can get both values from the Enum like this:state = OnOffState.on
print("First Value: ", state.a)
print("Second Value: ", state.b)The following states are included:off = (0, "OFF", "0")
on = (1, "ON", "1")Custom ErrorsInvalidModeErrorclassThis exception is raised when trying to get a mode that is not currently set. For example if the current mode is not a dynamic mode, trying to get the information from a dynamic mode will result in this error since the unit can only return a dynamic mode that is set and recalled.The error will return both themodethe load currently operates in and amessage.ValueOutOfLimitErrorclassThis exception is raised for errors in the input value. Since there apparently exists a firmware bug allowing setting values above limits for resistance and power(see protocol docs) the library will check and limit values itself and raise this error if trying to set values outside of limits.The error will return thevaluethat was tried to be set, thelimitthat is currently set and amessageNoModeSetErrorclassThis exception is raised when trying to set a mode that does not support being set directly(read-only inMode-Enum).The error will return themodethat was tried to be set and amessage.