# pdSink module | Status | Last Update | API Version | | ------ | ------ | ------ | | Active | 2019-11-20 | V1.00.00 | ## Description This module provides access to Power Delivery protocol as a role of sink. ## pdSink and fastChgTrig As you might think, the `pdSink` module should be fit into `fastChgTrig` module, PD is also a fast-charge protocol after all. But there're a few reasons that one prefers not to do so: - PD protocol is much more powerful than the others, its operation principle is so different, making it hard to fit into `fastChgTrig` software model. - Unlike protocols in `fastChgTrig`, PD doesn't use D+ D- as communication lines. In fact, the hardware for PD and that for other protocols are totally independent. ## Precautions - Using this module requires some basic knowledge of PD protocol, whose specification could be found on USB-IF website. - Though the hardware for PD and other protocols are independent (In theory, they could work simultaneously), the charger would not be happy about two protocols running at the same time, this could make conflicting requests and generate undefined behaviors. Before using pdSink, one should call `fastChgTrig.open()` just like using `fastChgTrig` module, this prevents the situation stated above. - The module is designed to make a balance between complexity and flexibility. One who fully understands the APIs, but with lack of the knowledge of PD and the very hardware, bottom layer software implementation on this meter, could easily find his script not working, and difficult to find out the reason. It's highly recommended to have a look at the example code at the bottom of this page. ## Typical call sequence The following lists the sequence of calls to establish PD with the charger and get voltage & current you want from the charger: 1.Call `pdSink.init()` to initialize hardware and bottom software layer, check its result, if `pdSink.OK`, continue operation. 2.Check `pdSink.isSrcCapReceived()` with a timeout of 2 seconds, if timeouts, operation fail, or you call `pdSink.sendHardReset()` and restart from step 1. Notice that after CC is attached, the charger typically sends `src_cap` message for only a few seconds (Detailed information could be found on the specification), this means except your code runs on start-up, your attempt to receive `src_cap` is doomed to end up with a timeout. To handle this case, you issues a hard reset with `pdSink.sendHardReset()` to reset the charger and you can receive `src_cap` again. 3.Call `pdSink.getSrcCapNum()` and `pdSink.getSrcCap()` to pass source capabilities to lua region, this step could be omitted if you already knows your charger's capabilities or you don't need such information. 4.Call `pdSink.request()` to get voltage and current limit you wanted. ## pdSink.init() ### Description This initializes the pdSink module, it does the following things: - Get source attachment status, if no source attached, it returns `pdSink.FAIL`. - If attachment detected, initialize PD communications on the corresponding CC pin and return `pdSink.OK`. Reset bottom layer software status. One should check the result to determine if further operation could be performed. ### Parameters nil ### Return value number `pdSink.FAIL` if failed. `pdSink.OK` if done. ### Example call ```lua rtval = pdSink.init() ``` ## pdSink.getCCStatus() ### Description This function doen't do any hardware work, it just returns the CC attachment status after `pdSink.init()` call. ### Parameters nil ### Return value number `pdSink.CC1_SRC_ATTACHED` if source is attached on CC1. `pdSink.CC2_SRC_ATTACHED` if source is attached on CC2. `pdSink.NO_SRC_ATTACHED` if source is not attached. ### Example call ```lua status = pdSink.getCCStatus() ``` ## pdSink.isSrcCapReceived() ### Description Check if src_cap message is received. This status is cleared when `pdSink.init()` is called, and raised when a src_cap message is received. ### Parameters nil ### Return value boolean `true` if received. `false` if haven't received yet.. ### Example call ```lua received = pdSink.isSrcCapReceived() ``` ## pdSink.getNumofSrcCap() ### Description Get number of objects in `Source_Capability` message. ### Parameters nil ### Return value number Number of objects in `Source_Capability` message. ### Example call ```lua pdo_num = pdSink.getNumofSrcCap() ``` ## pdSink.getSrcCap() Get specified PDO with an index. ### Parameters | Name | Type | Range | Usage | | ------ | ------ | ------ | ------ | | <index> | number| 0~6 |Specify the index of the object to get| ### Return value table Returns a table with the following key-value pairs: | Key | Type | Range | Usage | | ------ | ------ | ------ | ------ | | type | number| pdSink.FIXED pdSink.BATTERY pdSink.VARIABLE pdSink.AUGMENTED|Type of the PDO| | voltage | number| |If the PDO is type FIXED, this is the voltage of the PDO. If the PDO is type AUGMENTED, this is the minimum voltage of the PDO.| | voltageMax | number| |If the PDO is type FIXED, this field has no meaning. If the PDO is type AUGMENTED, this is the maximum voltage of the PDO.| | currentMax | number| |Maximum current of the PDO.| ### Example call ```lua pdo0 = pdSink.getSrcCap(0) ``` ## pdSink.sendHardReset() ### Description Send a hard reset message. ### Parameters nil ### Return value nil ### Example call ```lua pdSink.sendHardReset() ``` ## pdSink.request() ### Description Send a request message to request specified voltage and current limit. ### Parameters | Name | Type | Range | Usage | | ------ | ------ | ------ | ------ | | <pdo> | number| 0~6 |Specify the index of the PDO to request| | [voltage] | number| |If the PDO is FIXED, the FIXED voltage should be this parameter. If the PDO is AUGMENTED, this specifies the voltage to request, should be in the range of the corresponding PDO.If not specified, and the PDO is FIXED, the voltage will be the FIXED voltage.| | [current] | number| |Specify the operation current to request, this should be in the range of the corresponding PDO. If not specified, the current is the maxium current of the PDO.| ### Return value number `pdSink.OK` if the request success with `PS_Ready` received. `pdSink.FAIL` if the request timed out or without `PS_Ready` received. ### Example call ```lua --Request PDO with index 1, voltage 9V, current 3A result = pdSink.request(1,9.00,3.00) ``` ## pdSink.deinit() ### Description Disable PD communication, and reset bottom layer software to default status. Notice that this won't reset the charger, you need to call `pdSink.sendHardReset()` additionally if you want to do so. ### Parameters nil ### Return value nil ### Example call ```lua pdSink.deinit() ``` ## pdSink.onSrcCapReceived() ### Description Register or unregister a callback when a source capability message is received. ### Parameters | Name | Type | Range | Usage | | ------ | ------ | ------ | ------ | | <callback> | nil or function| |Specify the callback function, when such event happened, the callback is called, the callback *won't* be unregistered automatically. To unregister the callback, pass `nil` to this function.| ### Return value nil ### Example call ```lua function onSrcCapReceived() print("Source Capability received.") end pdSink.onSrcCapReceived(onSrcCapReceived) ``` ## Example code (Procedural) ```lua --[[This is a demo for PD APIs. You connect the meter to a PD charger with a C-C cable, and DO NOT plug-in any other device. The output of the demo is on the debug terminal. The demo detect if any source is attached first, if no source is detected, it exits. Then it waits for src_cap, if not received, it sends a HARD_RST message to reset the charger, if still no src_cap is received, it exits. After the src_cap received, it prints out the src_cap on the terminal. Then it started to request FIXED pdos of the charger one by one. If there is any AUGMENTED(PPS) pdo, it requests them one by one. The voltage reading and request status is on the terminal. Detailed Documentation: Version: 191120 Author: yanke928 ]] function WaitForSourceCap() i = 2000 while(i > 0) do i = i - 1; if(pdSink.isSrcCapReceived()) then return true end delay.ms(1) end return false end function DemoEnd() print("---End of PD demo---") os.exit(0) end print("---Start of PD demo---") --Open fastChgTrig if(fastChgTrig.open() ~= fastChgTrig.OK) then print("Failed to open fastChgTrig") DemoEnd() end --Init PD pdSink.init() print("PD inited") --Check if any source is attached if(pdSink.getCCStatus() == pdSink.NO_SRC_ATTACHED) then print("No CC attached") DemoEnd() end print("Waiting for src_cap") --Wait for src_cap. received = WaitForSourceCap() --In case of time out, try a hard reset if(received == false) then print("src_cap timeout, sending hard reset") pdSink.sendHardReset() pdSink.init() print("Waiting for src_cap again.") received = WaitForSourceCap() --If we still cannot receive src_cap if(received == false) then print("No src_cap received, exiting...") DemoEnd() end end print("src_cap received") delay.ms(1000) --Get src_cap into table src_cap_num = pdSink.getNumofSrcCap() print(string.format("We have %d src_caps:",src_cap_num)) src_caps = {} for i = 0,src_cap_num -1 do src_caps[i] = pdSink.getSrcCap(i) --Fixed type, print voltage and max current if(src_caps[i].type == pdSink.FIXED) then print(string.format("No.[%d] Type[FIXED] Voltage[%.2fV] Current[%.2fA]",i,src_caps[i].voltage,src_caps[i].currentMax)) elseif (src_caps[i].type == pdSink.AUGMENTED) then print(string.format("No.[%d] Type[AUGMENTED] Voltage[%.2f~%.2fV] Current[%.2fA] ",i,src_caps[i].voltage,src_caps[i].voltageMax,src_caps[i].currentMax)) else print(string.format("No.[%d] Unknown Type",i)) end end meter.setDataSource(meter.INSTANT) print("Started to request FIXED PDOs") for i = 0,src_cap_num -1 do if(src_caps[i].type == pdSink.FIXED) then print(string.format("Requesting FIXED No.%d",i)) if(pdSink.request(i,src_caps[i].voltage,src_caps[i].currentMax) ~= pdSink.OK) then print("Request failed") DemoEnd() end print(string.format("Request Success, voltage now: %.3fV",meter.readVoltage())) delay.ms(1000) end end print("Started to request AUGMENTED PDOs") for i = 0,src_cap_num -1 do if(src_caps[i].type == pdSink.AUGMENTED) then --Here, we use the middle point of the PPS voltage to give an example. req_volt = (src_caps[i].voltage + src_caps[i].voltageMax) / 2 print(string.format("Requesting AUGMENTED No.%d Voltage %.3fV",i,req_volt)) if(pdSink.request(i,req_volt,src_caps[i].currentMax) ~= pdSink.OK) then print("Request failed") DemoEnd() end print(string.format("Request Success, voltage now: %.3fV",meter.readVoltage())) delay.ms(1000) end end DemoEnd() ``` ## Example code (Event-Driven) ``` lua --[[This is a event-driven demo for PD APIs. You connect the meter to a PD charger with a C-C cable, and DO NOT plug-in any other device. The output of the demo is on the debug terminal. The demo detect if any source is attached first, if no source is detected, it exits. Then it waits for src_cap, if not received, it sends a HARD_RST message to reset the charger, if still no src_cap is received, it exits. After the src_cap received, it prints out the src_cap on the terminal. Then it started to request FIXED pdos of the charger one by one. If there is any AUGMENTED(PPS) pdo, it requests them one by one. The voltage reading and request status is on the terminal. Detailed Documentation: Version: 191120 Author: yanke928 ]] timeoutTimerCount = 0 srcCapOK = false src_caps = {} src_cap_num = 0 request_index = 0 function demoEnd() print("----End of demo----") os.exit() end function requestTimer_callback(tim) if(src_caps[request_index].type == pdSink.FIXED) then print(string.format("Requesting FIXED No.%d",request_index)) if(pdSink.request(request_index,src_caps[request_index].voltage,src_caps[request_index].currentMax) ~= pdSink.OK) then print("Request failed") DemoEnd() end print(string.format("Request Success, voltage now: %.3fV",meter.readVoltage())) else if(src_caps[request_index].type == pdSink.AUGMENTED) then --Here, we use the middle point of the PPS voltage to give an example. req_volt = (src_caps[request_index].voltage + src_caps[request_index].voltageMax) / 2 print(string.format("Requesting AUGMENTED No.%d Voltage %.3fV",request_index,req_volt)) if(pdSink.request(request_index,req_volt,src_caps[request_index].currentMax) ~= pdSink.OK) then print("Request failed") DemoEnd() end print(string.format("Request Success, voltage now: %.3fV",meter.readVoltage())) end end request_index = request_index + 1 if(request_index == src_cap_num) then print("All PDO requested, exiting...") demoEnd() end end function timeoutTimer_callback(tim) timeoutTimerCount = timeoutTimerCount + 1 if(srcCapOK ~= true) then if(timeoutTimerCount == 2) then print("No src_cap received, exiting...") demoEnd() end print("src_cap timeout, sending hard reset...") pdSink.sendHardReset() pdSink.init() else requestTimer= tmr.new() requestTimer:setup(0,1000,tmr.AUTO_RELOAD,requestTimer_callback) requestTimer:start() pdSink.onSrcCapReceived(nil) tim:remove() end end function onSrcCapReceived() print("Source Capability Received") srcCapOK = true --Get src_cap into table src_cap_num = pdSink.getNumofSrcCap() print(string.format("We have %d src_caps:",src_cap_num)) for i = 0,src_cap_num -1 do src_caps[i] = pdSink.getSrcCap(i) --Fixed type, print voltage and max current if(src_caps[i].type == pdSink.FIXED) then print(string.format("No.[%d] Type[FIXED] Voltage[%.2fV] Current[%.2fA]",i,src_caps[i].voltage,src_caps[i].currentMax)) elseif (src_caps[i].type == pdSink.AUGMENTED) then print(string.format("No.[%d] Type[AUGMENTED] Voltage[%.2f~%.2fV] Current[%.2fA] ",i,src_caps[i].voltage,src_caps[i].voltageMax,src_caps[i].currentMax)) else print(string.format("No.[%d] Unknown Type",i)) end end end print("----Start of demo----") if(fastChgTrig.open() ~= fastChgTrig.OK) then print("Failed to open fastChgTrig") demoEnd() end --Init PD pdSink.init() print("PD inited") --Check if any source is attached if(pdSink.getCCStatus() == pdSink.NO_SRC_ATTACHED) then print("No CC attached") DemoEnd() end timeoutTimer = tmr.new() timeoutTimer :setup(2000,2000,tmr.AUTO_RELOAD,timeoutTimer_callback) timeoutTimer :start() pdSink.onSrcCapReceived(onSrcCapReceived) print("Waiting for src_cap...") ```