Skip to content

Instantly share code, notes, and snippets.

@keklikhasan
Forked from sdabet/UsbCecConnection.java
Created August 28, 2018 12:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save keklikhasan/b5df308b36b39bbb4fafe8e40c773a7c to your computer and use it in GitHub Desktop.
Save keklikhasan/b5df308b36b39bbb4fafe8e40c773a7c to your computer and use it in GitHub Desktop.
Send simple HDMI-CEC commands from an Android app via Pulse-Eight's USB-CEC adapter
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.util.Log;
import com.felhr.usbserial.UsbSerialDevice;
import com.felhr.usbserial.UsbSerialInterface;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* This class allows to send HDMI CEC commands through Pulse-Eight's USB-CEC adapter.
* Command syntax is inspired by libCEC (https://github.com/Pulse-Eight/libcec).
*
* It requires UsbSerial library (https://github.com/felHR85/UsbSerial)
*
* IMPORTANT: the provided {@link UsbDevice} must have been granted permission.
*
*/
public class UsbCecConnection implements UsbSerialInterface.UsbReadCallback {
private static final String LOG_TAG = UsbCecConnection.class.getSimpleName();
private static final byte MSG_START = (byte)255, MSG_END = (byte)254;
private enum MsgCode {
MSGCODE_NOTHING,
MSGCODE_PING,
MSGCODE_TIMEOUT_ERROR,
MSGCODE_HIGH_ERROR,
MSGCODE_LOW_ERROR,
MSGCODE_FRAME_START,
MSGCODE_FRAME_DATA,
MSGCODE_RECEIVE_FAILED,
MSGCODE_COMMAND_ACCEPTED,
MSGCODE_COMMAND_REJECTED,
MSGCODE_SET_ACK_MASK,
MSGCODE_TRANSMIT,
MSGCODE_TRANSMIT_EOM,
MSGCODE_TRANSMIT_IDLETIME,
MSGCODE_TRANSMIT_ACK_POLARITY,
MSGCODE_TRANSMIT_LINE_TIMEOUT,
MSGCODE_TRANSMIT_SUCCEEDED,
MSGCODE_TRANSMIT_FAILED_LINE,
MSGCODE_TRANSMIT_FAILED_ACK,
MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA,
MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE,
MSGCODE_FIRMWARE_VERSION,
MSGCODE_START_BOOTLOADER,
MSGCODE_GET_BUILDDATE,
MSGCODE_SET_CONTROLLED,
MSGCODE_GET_AUTO_ENABLED,
MSGCODE_SET_AUTO_ENABLED,
MSGCODE_GET_DEFAULT_LOGICAL_ADDRESS,
MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS,
MSGCODE_GET_LOGICAL_ADDRESS_MASK,
MSGCODE_SET_LOGICAL_ADDRESS_MASK,
MSGCODE_GET_PHYSICAL_ADDRESS,
MSGCODE_SET_PHYSICAL_ADDRESS,
MSGCODE_GET_DEVICE_TYPE,
MSGCODE_SET_DEVICE_TYPE,
MSGCODE_GET_HDMI_VERSION,
MSGCODE_SET_HDMI_VERSION,
MSGCODE_GET_OSD_NAME,
MSGCODE_SET_OSD_NAME,
MSGCODE_WRITE_EEPROM,
MSGCODE_GET_ADAPTER_TYPE,
MSGCODE_SET_ACTIVE_SOURCE,
MSGCODE_UNKNOWN
}
private final UsbSerialDevice usbSerialDevice;
private final List<Byte> currentPacket = new ArrayList<>();
public UsbCecConnection(UsbDevice device, UsbDeviceConnection usbConnection) {
usbSerialDevice = UsbSerialDevice.createUsbSerialDevice(device, usbConnection);
usbSerialDevice.open();
usbSerialDevice.read(this);
// Optional, just attempt to get some information
sendCommand(MsgCode.MSGCODE_FIRMWARE_VERSION);
sendCommand(MsgCode.MSGCODE_SET_CONTROLLED, 1);
sendCommand(MsgCode.MSGCODE_GET_OSD_NAME);
}
public void switchTvOn() {
sendCommand(MsgCode.MSGCODE_SET_CONTROLLED, 1);
sendCommand(MsgCode.MSGCODE_TRANSMIT_ACK_POLARITY, 0);
sendCommand(MsgCode.MSGCODE_TRANSMIT, 16);
sendCommand(MsgCode.MSGCODE_TRANSMIT_EOM, 4);
}
public void switchTvOff() {
sendCommand(MsgCode.MSGCODE_SET_CONTROLLED, 1);
sendCommand(MsgCode.MSGCODE_TRANSMIT_ACK_POLARITY, 0);
sendCommand(MsgCode.MSGCODE_TRANSMIT, 16);
sendCommand(MsgCode.MSGCODE_TRANSMIT_EOM, 54);
}
public void activeSource() {
sendCommand(MsgCode.MSGCODE_SET_CONTROLLED, 1);
// marking the adapter as active source
sendCommand(MsgCode.MSGCODE_SET_ACTIVE_SOURCE, 1);
// powering on 'TV'
sendCommand(MsgCode.MSGCODE_TRANSMIT_ACK_POLARITY, 0);
sendCommand(MsgCode.MSGCODE_TRANSMIT, 16);
sendCommand(MsgCode.MSGCODE_TRANSMIT_EOM, 4);
// active source
sendCommand(MsgCode.MSGCODE_TRANSMIT_ACK_POLARITY, 1);
sendCommand(MsgCode.MSGCODE_TRANSMIT, 31);
sendCommand(MsgCode.MSGCODE_TRANSMIT, 130);
sendCommand(MsgCode.MSGCODE_TRANSMIT, 16);
sendCommand(MsgCode.MSGCODE_TRANSMIT_EOM, 0);
}
@Override
public void onReceivedData(byte[] bytes) {
Log.v(LOG_TAG, "onReceivedData: " + Arrays.toString(bytes));
for(byte b : bytes) {
currentPacket.add(b);
if(b == MSG_END) {
byte[] packetBytes = new byte[currentPacket.size()];
for(int i=0; i<packetBytes.length; i++) { packetBytes[i] = currentPacket.get(i); }
onReceivedPacket(packetBytes);
currentPacket.clear();
}
}
}
private void onReceivedPacket(byte[] bytes) {
Log.d(LOG_TAG, "onReceivedPacket: " + Arrays.toString(bytes));
if(bytes.length <= 2) {
Log.w(LOG_TAG, "Invalid response: " + Arrays.toString(bytes));
}
else {
MsgCode msgCode = getMsgCode(bytes[1]);
byte[] params = new byte[bytes.length-3];
System.arraycopy(bytes, 2, params, 0, params.length);
onReceivedPacket(msgCode, params);
}
}
private void onReceivedPacket(MsgCode msgCode, byte[] params) {
Log.d(LOG_TAG, "onReceivedPacket: " + msgCode + ", " + Arrays.toString(params));
switch(msgCode) {
case MSGCODE_COMMAND_ACCEPTED:
Log.d(LOG_TAG, "ACCEPTED " + getMsgCode(params[0]));
break;
case MSGCODE_COMMAND_REJECTED:
Log.w(LOG_TAG, "REJECTED: " + getMsgCode(params[0]));
break;
case MSGCODE_FIRMWARE_VERSION:
int firmwareVersion = 255 * (params[0] & 0xFF) + (params[1] & 0xFF);
Log.i(LOG_TAG, "Firmware version is " + firmwareVersion);
break;
case MSGCODE_GET_OSD_NAME:
String osdName = new String(params);
Log.i(LOG_TAG, "OSD name is '" + osdName + "'");
break;
}
}
private void sendCommand(MsgCode code) {
sendCommand(code, new byte[0]);
}
private void sendCommand(MsgCode code, int param) {
sendCommand(code, new byte[] { (byte) param });
}
private void sendCommand(MsgCode code, byte[] params) {
Log.i(LOG_TAG, "sendCommand: " + code + ", " + Arrays.toString(params));
sendCommand((byte) code.ordinal(), params);
}
private void sendCommand(byte command, byte[] params) {
Log.d(LOG_TAG, "sendCommand: " + command + ", " + Arrays.toString(params));
byte[] data = new byte[3 + params.length];
data[0] = MSG_START;
data[1] = command;
System.arraycopy(params, 0, data, 2, params.length);
data[data.length-1] = MSG_END;
sendData(data);
}
private void sendData(byte[] data) {
Log.v(LOG_TAG, "sendData: " + Arrays.toString(data));
usbSerialDevice.write(data);
}
private static MsgCode getMsgCode(byte b) {
int i = b & 0xFF; // unsigned int
if(i < MsgCode.values().length) {
return MsgCode.values()[i];
}
else {
return MsgCode.MSGCODE_UNKNOWN;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment