Monday 22 February 2021

USB power delivery (PD) and quick charge (QC) compatibility

PD 3.0

The PD standard on USB-C ports supports different voltages depending on the supplies power rating as shown on the chart below. So for example a supply which is rated at 40W can supply 5V, 9V and 15V but not 20V. 

The original PD 2.0 standard initially supported 5V, 12V and 20V but 12V was a bit too high for devices to fast charge batteries and a bit too low for devices which can be used in cars as when running a car battery is typically about 14.4V. So 9V and 15V was added and 12V was deprecated. Some supplies may still support 12V though.

Programmable Power Supply (PPS)

This is an optional extension to the PD 3.0 standard and allows to device being charged to request a different voltage and also a higher current. The voltage requested can be between 3.3V and 21V in 20mV increments. This is commonly used in battery fast chargers and for example the Samsung Note 10+ requests 10V at 4.5A when doing a super fast charge. Without PPS support the next PD profile would be chosen which would be 9V at 3A so 27W instead of 45W.

E-marker

Regular USB-C cables are only rated at a 3A charge current and 480Mbps bandwidth which is less bandwidth than USB3 cables. Cables can be rated for higher bandwidths and charge currents but these need a chip in the cable to tell the devices the cable supports faster data transfer and can safely handle higher charge currents.

Data speeds supported are 480Mbps, 5 Gbps (usb3.1 gen 1) and 10Gbps (usb3.1 gen 2).

A standard cable is rated at 3A and is commonly referred to as a 60W cable as 20V at 3A is the maximum it can support. Higher rated cables support up to 5A and are commonly marked as 100W cables and must have the E-marker chip fitted.

This is however quite confusing as a device which supports PPS can provide more than 3A at power levels less than 45W. A Samsung phone which wants to super fast charge at 10V 4.5A would require a 100W cable in order to get more than the 3A of current even though it is only drawing 45W. Another common confusion is a laptop which wants a 65W charger. Often people use a third party USB-C supply which is rated at 65W and leave a bad review when their laptop reports it is not charging at the full 65W. Most likely the charger is fine but they are using a standard cable so the charge controller in the laptop is limiting the charge current to 3A so it is only getting 60W.

Quick Charge (QC)

QC2 supports 5V, 9V, 12V, and 20V with a 2A current limit and 18W maximum power. The 20V output is therefore of little use.

QC3 allows the voltage to be chosen between 3.6V and 20V in 200mV steps. If it is provided over a USB-C interface then the current can also be increased to 3A.

QC4 is basically backwards compatible with PD and keeps the adjustable voltages of QC3 so in that regard is very similar to the PPS extension however when falling back to PD it only supports up to 27W. When operating in QC4 mode it supports up to 100W and has slight differences to PD such as the ability to detect the quality of the USB cable and adjust the charging rate accordingly.

Testers

There are many types of USB tester which generally fall into 3 categories.

1) Very basic ones with simple voltage, current and accumulated charge display. The Uni-T UT658 is a good example of one of these.

2) A more advanced one often with a colour TFT display which is also capable of identifying the PD and QC protocol being used and can graph the current. They can often have a bluetooth interface and an app available on a mobile device. A company called RuiDeng make a lot of these with common models being the TC64 and UM25.

Finally models like the FINRSI FNB48 and the Qway Web-U2 (and it's clone the T18-X6). The FNB48 has a bluetooth option but they all have useful extra features that enable them to probe the supply they are connected to and display all the fast charge standards they are capable of, measure a cables resistance, and automatically read the E-marker chip to find the cables capability and detect the presence of an Apple MFI chip.

I have a T18 on order. As an example this is a picture of it's supply detection mode.


Saturday 14 June 2014

How to decode a 433Mhz RF signal (Arduino)

So you have a device which has a 433Mhz RF remote control and you would like to have an Arduino with a RF transmitter emulate the remote and control the device directly.
The first thing is to order a couple of parts that you will need. You will want a 433Mhz transmitter and receiver and you can get the ones advertised for the Arduino very cheaply off ebay. You will also need a prototype board and some jumper wires to temporarily connecting the RF receiver.
Next you will need a logic analyser so you can view the signal and decode it. I bought a 'USB Logic Analyser' from the ebay seller EllieShang. This logic analyser is programmable and there is a good guide that you should read about programming it to work with different software on jwandrews.co.uk. My logic analyser happened to already be set to work with the Salae Logic analyser software (can be freely downloaded) so I didn't need to reprogram it.

Now that you have all that you need connect the RF receiver to the prototype board. You can use the 5V and GND from the Arduino to power the RF receiver. You dont need to solder an additional antenna to the RF receiver as it will work fine if you just hold the transmitter remote control within a few cm. Connect the logic analyser to the GND and its first input to the RF receiver data pin and you are ready to go.


Now you should be able to hold the remote near the RF receiver, hold down a button and capture the waveform in the Saleae software. It should look something like this
Head over to Tinkerman's blog for instructions on how to decode the waveform. The only thing not mentioned there is how to calculate the pulse length. A 'short' is one pulse width wide and a 'long' is 3 pulse widths. So you can see that each of the four possible values are 8 pulse widths in length. There are twelve codes so that give 12*8=96. In addition there is also a synchronisation pulse at the end followed by 31 low pulse widths. So overall the transmission is 128 pulse widths. So you can calculate the pulse width as 39.24ms/128 = 307us. If you wish to make sure your Arduino is spot on with its timing you can just capture its signal and compare and tweak the timing as required.

To see how to program an Arduino with this data have a look at my blog entry Controlling Bye Bye Standby devices using an Arduino


Controlling Bye Bye Standby devices using an Arduino

Note: Since the receivers are learning and the old Bye Bye units had a switch to select the channel and a 'house code' I don't know if all the transmitters are the same. It would make sense if they were not otherwise two neighbours could interfere with each others devices. Therefore the codes below probably wont work straight away but you could teach your devices to use these codes. If you wish to still use your existing remote then take a look at my next blog which gives instructions on how to decode the RF signal yourself.

First you will need a 433Mhz RF transmitter which can be bought cheaply off ebay. You will also need to download the RCSwitch library.
The Bye Bye Standby units use a quad state code which the RCSwitch library does not support. However on Tinkerman's blog he details the few changes that need to be added to get this supported. Its a very good blog so well worth reading any way.
The RF transmitter can work with a supply voltage up to 12V and with a good antenna can have a range of many hundreds of meters. A good option when interfacing with an Arduino is to run it off the built in 5V supply and solder a 17cm long wire to the antenna pad. I found that I was able to reliably control a device behind the TV in the front downstairs room from the back bedroom.
Next all you need are the codes for the Bye Bye Standby which I have included in the following sketch.
#include <RCSwitch.h>
#define MAX_STRING_LEN 25

RCSwitch mySwitch = RCSwitch();
String lineinput = "";

void setup() {
  Serial.begin(9600);
  mySwitch.enableTransmit(10);
  mySwitch.setProtocol(1);
  mySwitch.setRepeatTransmit(10);
  mySwitch.setPulseLength(300); // Bye Bye Standby
}

void loop() {
  while (Serial.available() > 0)
  {
    char c = Serial.read();
    if (c != '\n')
    {
      lineinput += c;
    }
    else {
      // handle entered data or request
      Serial.println("Entry='" + lineinput + "'");
      lineinput.trim();
      if (lineinput == "1on")
      {
          mySwitch.sendTriState("X0X1F00XXX11");
      }
      if (lineinput == "1off")
      {
          mySwitch.sendTriState("X0X1F00XXXF1");
      }
      if (lineinput == "2on")
      {
          mySwitch.sendTriState("X0X1F00XXX1F");
      }
      if (lineinput == "2off")
      {
          mySwitch.sendTriState("X0X1F00XXXFF");
      }
      if (lineinput == "3on")
      {
          mySwitch.sendTriState("X0X1F00XXXX1");
      }
      if (lineinput == "3off")
      {
          mySwitch.sendTriState("X0X1F00XXX01");
      }
      if (lineinput == "4on")
      {
          mySwitch.sendTriState("X0X1F00XXX1X");
      }
      if (lineinput == "4off")
      {
          mySwitch.sendTriState("X0X1F00XXXFX");
      }
      if (lineinput == "5on")
      {
          mySwitch.sendTriState("X0X1F00XXX10");
      }
      if (lineinput == "5off")
      {
          mySwitch.sendTriState("X0X1F00XXXF0");
      }
      if (lineinput == "6on")
      {
          mySwitch.sendTriState("X0X1F00XXXXX");
      }
      if (lineinput == "6off")
      {
          mySwitch.sendTriState("X0X1F00XXX0X");
      }
      if (lineinput == "allon")
      {
          mySwitch.sendTriState("X0X1F00XXXXF");
      }
      if (lineinput == "alloff")
      {
          mySwitch.sendTriState("X0X1F00XXX0F");
      }
      lineinput = "";
    }
  }
}



Wednesday 24 July 2013

How to pause and resume Asterisk call recordings

If you record your calls you may wish to enable pausing and unpausing of the recordings. This is relativly simple to do once you understand the features.conf file and the ActivateOn and ActivatedBy fields.
Firstly lets take the example where you receive an incoming call and wish to pause and unpause it. features.conf would contain the following two lines in the [applicationmap] section :-
InPauseMonitor   => #1,peer/callee,Macro,recpause,recording-disabled
InUnpauseMonitor => #3,peer/callee,Macro,recunpause,recording-enabled
It is the callee which is allowed to pause and unpause and since the Monitor() application is being run on the inbound channel the macro needs to be run on the peer.
For doing the same with outbound calls its the caller who is allowed to pause and unpause and since this is the same channel which ran the Monitor() application the macro needs to be run on the same channel :-
OutPauseMonitor   => #1,self/caller,Macro,recpause,recording-disabled
OutUnpauseMonitor => #3,self/caller,Macro,recunpause,recording-enabled
Here are the macros which are called and should be placed in extensions.conf. Rather than pausing and unapusing the recording we are actually stopping and starting the recording. That is purely because we have all the individual recordings sent to a remote server which combines and converts them to a mp3 file therefore reducing the cpu load on the asterisk server.
[macro-recpause]
exten => s,1,Playback(/var/lib/asterisk/sounds/recording/beep/beep)
exten => s,n,NoOp(Call Paused - Channel=${CHANNEL} BrigdePeer=${BRIDGEPEER})
exten => s,n,StopMonitor
[macro-recunpause]
exten => s,1,Monitor(wav,${FNAME}_${EPOCH})
exten => s,n,NoOp(Call Unpaused - Channel=${CHANNEL} BrigdePeer=${BRIDGEPEER})
exten => s,n,Playback(/var/lib/asterisk/sounds/recording/beepbeep/beepbeep)
In the features.conf file in addition to specifying the macro to be run we also specified a music on hold class to be used for playing music on hold to the other party. Shown below are the contents of musiconhold.conf and exactly the same files are played as in the ones in the macros. This ensures both parties hear the exact same audio. You can use different audio files if you wish although make sure the music on hold files are the exact same duration otherwise they will be cut short if they are longer or partially repeated if they are shorter.
[recording-disabled]
mode=files
directory=/var/lib/asterisk/sounds/recording/beep
[recording-enabled]
mode=files
directory=/var/lib/asterisk/sounds/recording/beepbeep

How to get the SIP response code in Asterisk 10+

In early Asterisk 1.8 you used to be able to get the SIP response code by using a dialplan entry like :-
exten => _X.,n,Set(SIPcause=${MASTER_CHANNEL(HASH(SIP_CAUSE,${CDR(dstchannel)}))}
The Asterisk developers discovered the way the information was being populated caused a significant performance hit and so decided to turn the feature off by default. It can be enabled by editing sip.conf and adding the following line

storesipcause=yes

From asterisk version 10 there is now a new way to get the SIP cause however the way in which it is read is a bit convoluted. Contrary to the example given on the official Asterisk WIKI you cannot query it in the channel which performed the dial. It has to be queried by the channel which actually generated the SIP response.
You therefore have to do a few things :-
  1. Add a hangup handler to the destination channel so that a dialplan routine is run when that channel hangs up.
  2. Use the new 'b' option to the Dial command which causes a dialplan routine to be called just before the dial happens and this is used to setup the hangup handler on the destination channel.
  3. In the hangup handler get a list of hangupcause strings which in most cases will be a single string which we can just use. If you simultaneously dialled two destinations you will have two strings however and will need to decide which to use.
  4. If the hangupcause string is blank then it probably just means the call was answered and was hungup normally so we can just return.
  5. Once we have the hangupcause to check read the SIP cause string.
  6. Now the problem of how to pass the variable back. Normal variable inheritance does not work properly because of the channel being hung up at this point so we use yet another new feature by setting a shared variable in the master channel.
  7. After the original dial we read the shared variable out of its own channel shared area and set a normal variable and then use the cut function to read the numerical value.
Finally here is a complete example :-


 [dial_sip]
exten => _X.,1,Dial(SIP/${ddi}@${carrier},,b(dial_sip^set_handler^1))
exten => _X.,n,Set(SIPcause=${SHARED(SIPcause)}, Responsetime=$[${EPOCH}-${dialtime}])
exten => _X.,n,Set(SIPcode=${CUT(SIPcause," ",2)})

exten => set_handler,1,Set(CHANNEL(hangup_handler_push)=dial_sip,outbound_handler,1)
exten => set_handler,n,Return()

exten => outbound_handler,1,NoOp(Destination channel has hungup)
same => n,Set(HANGUPCAUSE_STRING=${HANGUPCAUSE_KEYS()})
; If no hangup causes are available then its probably because it is a regular call and the call ended normally so we just return.
same => n,ExecIf($["${HANGUPCAUSE_STRING}" = ""]?Return())
same => n,NoOp(Got Channel ID ${HANGUPCAUSE_STRING} master ${MASTERCHANNEL} with Technology Cause Code ${HANGUPCAUSE(${HANGUPCAUSE_STRING},tech)}, Asterisk Cause Code ${HANGUPCAUSE(${HANGUPCAUSE_STRING},ast)})
same => n,Set(SHARED(SIPcause,${MASTERCHANNEL})=${HANGUPCAUSE(${HANGUPCAUSE_STRING},tech)})
same => n,Return()