Hi,Over the weekend I set myself a task to write a basic 'Automated Test System' base requirement and beginners implementation.
I have a *few* USB car chargers that say they perform in a certain way. I want to give them a good test to see if they behave the way they say.
The DUT is quite cheap - USB car chargers - they are available on Aliexpress.
The test equipment I selected to do this is the RD6006 and ZKE-EBD-USB as they are both supported by sigrok, cheap, and its an easy system to wire up.
I first wrote a bit of a want list for what an 'Automated Test System' should include. See the Word Document. I understand that not all these features are in smuview or maybe sigrok, but more of a discussion point on why an ATS would want to do this.
I then set about writing a first pass demo. Note *I am not a software coder*. There will be many things in there that people will say are the wrong way to do it. Please dont flame me - I am sharing what I was able to do in 2 days work. My objective was to run the test and test hardware I have - see it smuview has the basic capabilities - learn a bit more of Python. I think this script shows smuview has promise. Some observations:
- my coding knowledge is horrible and I dont know how to make Python classes - I need to learn how to do that
- the ZKE often hangs. You can see in the code where it does a 'set current to 0.01A, set current to 0A'. That is because the ZKE has a LED on it that comes on when you set a current. Setting the current to 0.01A is the lowest step on the ZKE, and it makes the LED turn on then off. When the current is supposed to be set to 0A, you can see the ZKE flashing when working and when broken - does not flash - it is either hard on or off. Comms to the ZKE falls over - and you have to shut down smuview and pull the ZKE to recover.
- there is a 'hardware bypass' define that you can set and see the script run and get a report even with no hardware attached.
I provide my code to here and is free to re-use in anyway and include into the sigrok/smuview project.
My long term hope is that there is some sort of ATS code base. It would become an 'import ats' and developing an ATS is as simple as defining the worker functions, initialisation function, customer strings, and go - test your hardware.
Once again - thanks to knarfS for assistance on smuview - I have not got the latest build and will do that this week
Regards Stuart
# # smuview DC input USB power module test code # # Product: DC input USB power supply # Written by: Stuart Tyler - Relyt Designs # # Test rig # Devices - # PSU : RDtech-rd (DC output) # DUT : DC input to USB output (car USB charger) # LOAD : ZKETECH-EBD-USB # PC : Host PC, with smuview and running this script # # Test Wiring - # DC power : PSU to DUT : connect PSU DC output to DUT DC input with as wire length <100mm AWG suitable for 10A # USB connection : DUT to LOAD : Plug DUT USBtypeA host connector into LOAD USBtypeA device connector # # Communications Wiring - # USB connection: PC to PSU : USBtypeA host to USBmicroB device # USB connection: PC to LOAD : USBtypeA host to USBmicroB device # # Objective - be able to check that USB DC input device functional using RDTECH-RD PSU and ZKETECH-EBD-USB # # IMPORTS import smuview import time from datetime import datetime import sys import inspect import os from decimal import * # GLOBALS # get the start time of the test timestarttest=datetime.now() # The test hardware required for this is a PSU and LOAD. # These are the conn strings to point to the hardware # Edit the connection point below to the location needed on your setup PSU_DEV_CONN_STRING="rdtech-rd:conn=COM19" PSU_DEV_ID_STRING="RDTech:RD6006:18576" LOAD_DEV_CONN_STRING="zketech-ebd-usb:conn=COM18" LOAD_DEV_ID_STRING="ZKETECH:EBD-USB:COM18" # DUT/Customer information DUT_COMPANY="Customer Name" DUT_NAME= "Product Name" DUT_PART= "Product Part Number" DUT_VERSION="Product Hardware Version" DUT_SERIAL= "<UNKNOWN>" #operator is prompted to enter this during test # Test rig information TESTRIG_COMPANY="Relyt Designs" TESTRIG_NAME= "BenchAssembly" TESTRIG_PART= "<NONE>" TESTRIG_VERSION="<NONE>" TESTRIG_SERIAL= "<NONE>" dotests=tuple() skiptests=tuple() #setting skiphardware to true just does a logic flow test skiphardware=True #skiphardware=False #dotests=('all') skiptests=('all') skiptests=('T01','T02','T04') # declaring data variables being used as global floats psuV = 0.0; psuI = 0.0; psuP = 0.0; loadV = 0.0; loadI = 0.0 testvalue=0.0 timestamp="<Unknown>" TestsPassed=0 TestsFailed=0 TestsSkipped=0 TestsErrored=0 #END OF GLOBALS #FUNCTIONS # ApplyInstumentDefaultSettings() - sets all instruments to default values def ApplyInstumentDefaultSettings(): if skiphardware: return # Set load to 0A # The ZKE has steps of 10mA current. It also contains a LED. # So doing a 10mA then 0A setting makes the LED on it flash quickly and you can observe comms is functional or stuck loadconf.set_config(smuview.ConfigKey.CurrentLimit, 0.01) loadconf.set_config(smuview.ConfigKey.CurrentLimit, 0.0) # Set PSU to 14.0V 6A OVP 39V OCP 6.1A - Turned OFF psuconf.set_config(smuview.ConfigKey.Enabled, False) psuconf.set_config(smuview.ConfigKey.CurrentLimit, 6.0) psuconf.set_config(smuview.ConfigKey.VoltageTarget, 14.0) psuconf.set_config(smuview.ConfigKey.OverVoltageProtectionThreshold, 39.0) psuconf.set_config(smuview.ConfigKey.OverCurrentProtectionThreshold, 6.1) #make the ZKE load LED flash time.sleep(0.3) loadconf.set_config(smuview.ConfigKey.CurrentLimit, 0.01) loadconf.set_config(smuview.ConfigKey.CurrentLimit, 0.0) print("<D>") time.sleep(0.7) def DeviceConnection(): global psudev global loaddev global psuconf global loadconf print_ttt("Information","Device,Connection") # Get all connected devices from the session devices = Session.devices() print (devices) for deviceId in devices: device = devices[deviceId] print (deviceId) if deviceId == LOAD_DEV_ID_STRING: print_ttt("Information","Device,ZKE Load device found") loaddev = device if (deviceId == PSU_DEV_ID_STRING): print_ttt("Information","Device,RD6006 PSU device found") psudev = device if 'loaddev' in globals(): print_ttt("Information","Load device connected") else: print_ttt("Information","Load device not connected... attempting connect") try: loaddev = Session.connect_device(LOAD_DEV_CONN_STRING)[0] except: print_ttt("Information","Load device not connected. Please connect and attempt script again. Aborting...") sys.exit("Load device missing") print_ttt("Information","Load device connected.",loaddev) if 'psudev' in globals(): print_ttt("Information","PSU device connected") else: print_ttt("Information","PSU device not connected... attempting connect") try: psudev = Session.connect_device(PSU_DEV_CONN_STRING)[0] except: print_ttt("Information","PSU device not connected. Please connect and attempt script again. Aborting...") sys.exit("PSU device missing") print_ttt("Information","PSU device connected.",psudev) # get the configurables loadconf = loaddev.configurables()[""] psuconf = psudev.configurables()[""] # reset instrument settings to safe situation ApplyInstumentDefaultSettings() #end of DeviceConnection #customised print function #print_ttt # ttt stands for time, test, title #this should be the *only* print function used in here for recorded teest data #to ensure the output data can be easily analysed def print_ttt( * args): #add timestamp mystring="TimeFromStart,"+str('%.3f'%((datetime.now()-timestarttest).total_seconds())) count = 0 for arg in args: if count == 0: mystring+=",Test,"+str(arg) elif count == 1: mystring+=",Title,"+str(arg) else: mystring+=","+str(arg) count+=1 print (mystring) #end of print_ttt # getdata() - aquires the instrument datapoints for this test rig # the test rig consists of: # PSU: rdtech-rd rd6006 # USB LOAD: zketech-ebd-usb # # PSU datapoints: Voltage, Current, Power (V,I,P) # USB LOAD datapoints: Voltage, Current (V,I) # def getdata(): global psuV global psuI global psuP global loadV global loadI global psudev global loaddev global timestamp timestamp="TimeFromStart,"+str('%.3f'%((datetime.now()-timestarttest).total_seconds()))+"," psuV = float("{:.5f}".format(psudev.channels()["V"].actual_signal().get_last_sample(True)[1])) psuI = float("{:.5f}".format(psudev.channels()["I"].actual_signal().get_last_sample(True)[1])) psuP = float("{:.5f}".format(psudev.channels()["P"].actual_signal().get_last_sample(True)[1])) loadV = float("{:.5f}".format(loaddev.channels()["V"].actual_signal().get_last_sample(True)[1])) loadI = float("{:.5f}".format(loaddev.channels()["I"].actual_signal().get_last_sample(True)[1])) #end of getdata #inform the operator to do something... then press enter to continue # mo mechanical rig, so must use human mustle to do the work def TestRig_Operator_Message(test,title,information): print_ttt(test,"Operator Message",title) UiProxy.show_message_box("Test "+test,information) #end of TestRig_Operator_Message #checkskip is called at the start of the test # returns true = test should be skipped / dont run # false = ok to run def checkskip(test ="<UNKNOWN>" ,title="<UNKNOWN>"): global TestsSkipped global TestsErrored if test == "<UNKNOWN>": print_ttt(test,title,"Checkskip error: test is <UNKNOWN>") TestsErrored+=1 return True checkforskip=True if test in dotests or 'all' in dotests: checkforskip=False if checkforskip and (test in skiptests or 'all' in skiptests): print_ttt(test,title,"Skip") TestsSkipped+=1 return True if skiphardware : print_ttt(test,title,"HardwareSkip") TestsErrored+=1 return True return False #end of checkskip #function to perform an examination on test results and determine pass/fail result def atsExamination(test,title,testvalue,resultLCL,resultUCL): global TestsPassed global TestsFailed if (testvalue >= resultLCL and testvalue <= resultUCL): result = "PASS" TestsPassed+=1 else: result = "FAIL" TestsFailed+=1 print_ttt(test,title,"LCL",resultLCL,"UCL",resultUCL,"value",testvalue,"result",result) #end of atsExamination # TestRig functions - these perform the low level work # function: TestRig_USBPOWER_Test_VoltsLoadPower # objective: measure the input power and output voltage comes up for a given input voltage and load current # test procedure: # 1. Apply input volts # 2. Apply load current # 3. Do load voltage and input power examinations # Note: This test can be used on a target that has an on/off button, so the output may not always come on. def TestRig_USBPOWER_Test_VoltsLoadPower(test="<UNKNOWN>" ,title="<UNKNOWN>", OutputVolts=14.0, LoadCurrent=0.0, VLCL=0.0, VUCL=0.0, PLCL=0.0, PUCL=0.0): global TestsErrored if checkskip(test,title): return print_ttt(test,title,"Start") if OutputVolts != 5: TestsErrored +=1 print_ttt(test,title,"Error,Cannot yet change output volts") return # reset instrument settings to safe situation ApplyInstumentDefaultSettings() psuconf.set_config(smuview.ConfigKey.Enabled, True) print_ttt(test,title,"Progress,Turn on PSU") for x in range(10): loadconf.set_config(smuview.ConfigKey.CurrentLimit, 0.01) loadconf.set_config(smuview.ConfigKey.CurrentLimit, 0.0) time.sleep(0.5) getdata() print_ttt(test,"Measurements","psuV",psuV,"psuI",psuI,"psuP",psuP,"loadV",loadV,"loadI",loadI) print_ttt(test,title,"Progress,Set load current") for x in range(10): loadconf.set_config(smuview.ConfigKey.CurrentLimit, LoadCurrent+0.01) loadconf.set_config(smuview.ConfigKey.CurrentLimit, LoadCurrent) time.sleep(0.5) getdata() print_ttt(test,"Measurements","psuV",psuV,"psuI",psuI,"psuP",psuP,"loadV",loadV,"loadI",loadI) loadconf.set_config(smuview.ConfigKey.CurrentLimit, 0.0) atsExamination(test,"psuP", psuP, PLCL,PUCL) atsExamination(test,"loadV",loadV,VLCL,VUCL) # reset instrument settings to safe situation ApplyInstumentDefaultSettings() print_ttt(test,title,"End") #end of function TestRig_USBPOWER_Test_OFFPower # function: TestRig_USBPOWER_Test_ChangeInputVolts # test procedure: # 1. Apply default input volts - DUT output voltage should come up # 2. Apply load current - DUT output should still be up # 3. Apply test input volts - this may result in DUT being up or down - test tells us the correct state - check DUT is in correct state # 4. Sweep test input volts as instructed - stop when DUT turns on/off - or voltage sweep has got to the end # 5. Do input voltage examination # Note: this test is for on state testing only. It does a number of checks to ensure correct DUT state progression def TestRig_USBPOWER_Test_ChangeInputVolts(test="<UNKNOWN>" ,title="<UNKNOWN>", StartVolts=Decimal("14.0"), EndVolts=Decimal("15.0"), StepVolts=Decimal("0.1"), LoadCurrent=0.0, VLCL=0.0, VUCL=0.0, TestChecksTurningOff=True): global TestsErrored TargetV=StartVolts if StartVolts > EndVolts: TestChecksFallingVoltage = True StepV=-StepVolts else: TestChecksFallingVoltage = False StepV=StepVolts global TestsErrored if checkskip(test,title): return print_ttt(test,title,"Start") # reset instrument settings to safe situation ApplyInstumentDefaultSettings() print_ttt(test,title,"Progress,Turn on PSU") psuconf.set_config(smuview.ConfigKey.Enabled, True) for x in range(10): loadconf.set_config(smuview.ConfigKey.CurrentLimit, 0.01) loadconf.set_config(smuview.ConfigKey.CurrentLimit, 0.0) time.sleep(0.5) getdata() print_ttt(test,"Measurements","psuV",psuV,"psuI",psuI,"psuP",psuP,"loadV",loadV,"loadI",loadI) #error if loadV is down if loadV < 2.0 : print_ttt(test,"Error,LoadV not coming up") TestsErrored+=1 ApplyInstumentDefaultSettings() return print_ttt(test,"Progress,Set load current") for x in range(10): loadconf.set_config(smuview.ConfigKey.CurrentLimit, LoadCurrent+0.01) loadconf.set_config(smuview.ConfigKey.CurrentLimit, LoadCurrent) time.sleep(0.5) getdata() print_ttt(test,"Measurements","psuV",psuV,"psuI",psuI,"psuP",psuP,"loadV",loadV,"loadI",loadI) #error if loadV is down if loadV < 2.0 : print_ttt(test,"Error,LoadV went down") TestsErrored+=1 ApplyInstumentDefaultSettings() return print_ttt(test,"Progress,Set Output Voltage") psuconf.set_config(smuview.ConfigKey.VoltageTarget, float(TargetV)) for x in range(10): loadconf.set_config(smuview.ConfigKey.CurrentLimit, LoadCurrent+0.01) loadconf.set_config(smuview.ConfigKey.CurrentLimit, LoadCurrent) time.sleep(0.5) getdata() print_ttt(test,"Measurements","psuV",psuV,"psuI",psuI,"psuP",psuP,"loadV",loadV,"loadI",loadI,"TargetV",TargetV) print_ttt(test,"Progress,Sweep Output Voltage") if TestChecksTurningOff ^ (loadV > 2.0): print_ttt(test,"Error,LoadV incorrect state") TestsErrored+=1 ApplyInstumentDefaultSettings() return print_ttt(test,"Progress,Correct loadV > now changing psuV") while (TestChecksTurningOff ^ (loadV < 2.0 )) and (TestChecksFallingVoltage ^ (TargetV < EndVolts)): TargetV += StepV psuconf.set_config(smuview.ConfigKey.VoltageTarget, float(TargetV)) loadconf.set_config(smuview.ConfigKey.CurrentLimit, LoadCurrent+0.01) loadconf.set_config(smuview.ConfigKey.CurrentLimit, LoadCurrent) time.sleep(0.5) getdata() print_ttt(test,"Measurements","psuV",psuV,"psuI",psuI,"psuP",psuP,"loadV",loadV,"loadI",loadI,"TargetV",TargetV) print_ttt(test,"Progress,Test end point found") loadconf.set_config(smuview.ConfigKey.CurrentLimit, 0.0) atsExamination(test,"psuV",psuV,VLCL,VUCL) # reset instrument settings to safe situation ApplyInstumentDefaultSettings() print_ttt(test,title,"End") #end of function TestRig_USBPOWER_Test_ChangeInputVolts # Start of main() print_ttt("Information","StartOfFile") if skiphardware: print_ttt("Information","Skipping hardware") else: print_ttt("Information","Loading hardware") DeviceConnection() print_ttt("Information","TestScriptFilename,",os.path.split(inspect.getfile(inspect.currentframe()))[1]) # current date and time timestamp="Datetime,"+timestarttest.strftime("%Y/%m/%d-%H:%M:%S") print_ttt("Information","StartTime",timestamp) operator="" operator=UiProxy.show_string_input_dialog("Operator","Enter your operator ID") DUT_SERIAL=UiProxy.show_string_input_dialog("Unit Serial Number","Enter the units serial number") print_ttt("Information","DUT Company",DUT_COMPANY) print_ttt("Information","DUT Name",DUT_NAME) print_ttt("Information","DUT Part",DUT_PART) print_ttt("Information","DUT Version",DUT_VERSION) print_ttt("Information","DUT Serial",DUT_SERIAL) print_ttt("Information","Testrig Company",TESTRIG_COMPANY) print_ttt("Information","Testrig Name",TESTRIG_NAME) print_ttt("Information","Testrig Part",TESTRIG_PART) print_ttt("Information","Testrig Version",TESTRIG_VERSION) print_ttt("Information","Testrig Serial",TESTRIG_SERIAL) print_ttt("Information","Operator",operator) # Sleep 1s to give the devices the chance to create signals. # As per sample code from other demos time.sleep(1) print_ttt("Information","Device,Initialisation") ApplyInstumentDefaultSettings() print_ttt("Information","StartOfTests") TestRig_Operator_Message("M01","Set switch position 1","Set switch to position 1") TestRig_USBPOWER_Test_VoltsLoadPower("T01","Input Power OFF Position Test", 5, 0.0, -0.1, 0.1, -0.01, 0.01) TestRig_Operator_Message("M02","Set switch position 2","Set switch to position 2") TestRig_USBPOWER_Test_VoltsLoadPower("T02","Output Voltage 5V - No Load", 5, 0.0, 4.75, 5.5, -0.01, 0.01) TestRig_USBPOWER_Test_VoltsLoadPower("T03","Output Voltage 5V - 15W Load", 5, 3.0, 4.5, 5.5, 16, 18) TestRig_USBPOWER_Test_VoltsLoadPower("T04","Output Voltage 9V - No Load", 9, 0.0, 8.5, 9.5, -0.01, 0.01) TestRig_USBPOWER_Test_VoltsLoadPower("T05","Output Voltage 9V - 18W Load", 9, 2.0, 8.5, 9.5, 19, 21) TestRig_USBPOWER_Test_VoltsLoadPower("T06","Output Voltage 12V - No Load", 12, 1.5, 11.5, 12.5, -0.01, 0.01) TestRig_USBPOWER_Test_VoltsLoadPower("T07","Output Voltage 12V - 18W Load",12, 1.5, 11.5, 12.5, 19, 21) TestRig_USBPOWER_Test_ChangeInputVolts("T08","ON Test A",Decimal("13.0"),Decimal("9.5"),Decimal("0.05"), 0.1, 10.0 , 12.0, True) TestRig_USBPOWER_Test_ChangeInputVolts("T09","ON Test B",Decimal("9.0"),Decimal("13.0"),Decimal("0.05"), 0.0, 10.0 , 12.0, False) TestRig_USBPOWER_Test_ChangeInputVolts("T10","ON Test C",Decimal("20.0"),Decimal("35.0"),Decimal("0.2"), 0.1, 24.0 , 34.0, True) TestRig_USBPOWER_Test_ChangeInputVolts("T11","ON Test D",Decimal("35.0"),Decimal("20.0"),Decimal("0.2"), 0.0, 24.0 , 34.0, False) TestRig_Operator_Message("M12","Set switch position 3","Set switch to position 3") TestRig_USBPOWER_Test_ChangeInputVolts("T12","XXX Test A",Decimal("13.2"),Decimal("12.4"),Decimal("0.02"), 0.1, 12.6 , 12.9, True) TestRig_USBPOWER_Test_ChangeInputVolts("T13","XXX Test B",Decimal("12.0"),Decimal("14.0"),Decimal("0.02"), 0.0, 13.4 , 13.7, False) TestRig_USBPOWER_Test_ChangeInputVolts("T14","XXX Test C",Decimal("20.0"),Decimal("35.0"),Decimal("0.2"), 0.1, 24.0 , 34.0, True) TestRig_USBPOWER_Test_ChangeInputVolts("T15","XXX Test D",Decimal("35.0"),Decimal("20.0"),Decimal("0.2"), 0.0, 24.0 , 34.0, False) # reset instrument settings to safe situation # just in case someone wrote a badly formed test and skipped all the well written tests after it ApplyInstumentDefaultSettings() print_ttt("Information","Tesing Summary") print_ttt("Result","Passed",str(TestsPassed)) print_ttt("Result","Failed",str(TestsFailed)) print_ttt("Result","Skipped",str(TestsSkipped)) print_ttt("Result","Errored",str(TestsErrored)) print_ttt("Result","Total",str(TestsPassed+TestsFailed+TestsSkipped+TestsErrored)) print_ttt("Information","EndOfFile") UiProxy.show_message_box("Done","Job Done") # End of script
AutomatedTestSystem Objectives.docx
Description: MS-Word 2007 document
_______________________________________________ sigrok-devel mailing list sigrok-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/sigrok-devel