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

Attachment: 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

Reply via email to