mirror of https://github.com/arendst/Tasmota.git
Support for HDMI CEC protocol (#19434)
This commit is contained in:
parent
48cf04db3a
commit
f7c4c16a9c
|
@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
## [13.1.0.2]
|
||||
### Added
|
||||
- Support for HDMI CEC protocol
|
||||
|
||||
### Breaking Changed
|
||||
|
||||
|
|
|
@ -771,6 +771,13 @@
|
|||
// Commands xdrv_60_shift595.ino - 74x595 family shift register driver
|
||||
#define D_CMND_SHIFT595_DEVICE_COUNT "Shift595DeviceCount"
|
||||
|
||||
// Commands xdrv_70_hdmi_cec.ino
|
||||
#define D_PRFX_HDMI "Hdmi"
|
||||
#define D_CMND_HDMI_SEND "Send"
|
||||
#define D_CMND_HDMI_SEND_RAW "SendRaw"
|
||||
#define D_CMND_HDMI_TYPE "Type"
|
||||
#define D_CMND_HDMI_ADDR "Addr"
|
||||
|
||||
// Commands xdrv_89_dali.ino
|
||||
#define D_CMND_DALI_POWER "power"
|
||||
#define D_CMND_DALI_DIMMER "dim"
|
||||
|
|
|
@ -211,6 +211,7 @@ enum UserSelectablePins {
|
|||
GPIO_LOX_O2_RX, // LOX-O2 RX
|
||||
GPIO_GM861_TX, GPIO_GM861_RX, // GM861 Serial interface
|
||||
GPIO_DINGTIAN_OE, // New version of Dingtian relay board where PL is not shared with OE
|
||||
GPIO_HDMI_CEC, // Support for HDMI CEC
|
||||
GPIO_SENSOR_END };
|
||||
|
||||
// Error as warning to rethink GPIO usage with max 2045
|
||||
|
@ -469,6 +470,7 @@ const char kSensorNames[] PROGMEM =
|
|||
D_SENSOR_LOX_O2_RX "|"
|
||||
D_SENSOR_GM861_TX "|" D_SENSOR_GM861_RX "|"
|
||||
D_GPIO_DINGTIAN_OE "|"
|
||||
D_SENSOR_HDMI_CEC "|"
|
||||
;
|
||||
|
||||
const char kSensorNamesFixed[] PROGMEM =
|
||||
|
@ -697,6 +699,10 @@ const uint16_t kGpioNiceList[] PROGMEM = {
|
|||
AGPIO(GPIO_MCP23XXX_INT) + MAX_MCP23XXX,
|
||||
#endif
|
||||
|
||||
#ifdef USE_HDMI_CEC
|
||||
AGPIO(GPIO_HDMI_CEC), // HDMI CEC bus
|
||||
#endif
|
||||
|
||||
AGPIO(GPIO_TXD), // Serial interface
|
||||
AGPIO(GPIO_RXD), // Serial interface
|
||||
|
||||
|
|
|
@ -742,7 +742,7 @@ typedef struct {
|
|||
uint16_t modbus_sbaudrate; // 736
|
||||
uint16_t shutter_motorstop; // 738
|
||||
uint8_t battery_level_percent; // 73A
|
||||
uint8_t free_73B[2]; // 73B
|
||||
uint8_t hdmi_addr[2]; // 73B HDMI CEC physical address - warning this is a non-aligned uint16
|
||||
|
||||
uint8_t novasds_startingoffset; // 73D
|
||||
uint8_t web_color[18][3]; // 73E
|
||||
|
@ -845,7 +845,7 @@ typedef struct {
|
|||
uint8_t tcp_config; // F5F
|
||||
uint8_t light_step_pixels; // F60
|
||||
|
||||
uint8_t ex_modbus_sbaudrate; // F61 - v12.2.0.5
|
||||
uint8_t hdmi_cec_device_type; // F61 - v13.1.0.1 (was ex_modbus_sbaudrate v12.2.0.5)
|
||||
|
||||
uint8_t modbus_sconfig; // F62
|
||||
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Speler"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Bezet"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "Reproductor MP3"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Ocupat"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS In"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIn"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOut"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Speler"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Bezet"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "נגן מוזיקה"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 lejátszó"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 elfoglalt"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S - WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S - DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S - DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "Riproduttore MP3"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 occupato"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Speler"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Bezet"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "Odtwarzacz MP3"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 zajęty"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "Leitor de MP3"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Ocupado"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -656,6 +656,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 spelare"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 upptaget"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -655,6 +655,7 @@
|
|||
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
|
||||
#define D_SENSOR_I2S_DIN "I2S DIN"
|
||||
#define D_SENSOR_I2S_DOUT "I2S DOUT"
|
||||
#define D_SENSOR_HDMI_CEC "HDMI CEC"
|
||||
#define D_SENSOR_WS2812 "WS2812"
|
||||
#define D_SENSOR_DFR562 "MP3 Player"
|
||||
#define D_SENSOR_DFR562_BUSY "MP3 Busy"
|
||||
|
|
|
@ -783,6 +783,9 @@
|
|||
|
||||
#endif // USE_SPI
|
||||
|
||||
// -- One wire sensors ----------------------------
|
||||
// #define USE_HDMI_CEC // Add support for HDMI CEC bus (+7k code)
|
||||
|
||||
// -- Serial sensors ------------------------------
|
||||
//#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code)
|
||||
//#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code)
|
||||
|
|
|
@ -1731,7 +1731,7 @@ void SettingsDelta(void) {
|
|||
Settings->energy_current_calibration2 = Settings->energy_current_calibration;
|
||||
}
|
||||
if (Settings->version < 0x0C020005) { // 12.2.0.5
|
||||
Settings->modbus_sbaudrate = Settings->ex_modbus_sbaudrate;
|
||||
Settings->modbus_sbaudrate = Settings->hdmi_cec_device_type; // was ex_modbus_sbaudrate
|
||||
Settings->param[P_SERIAL_SKIP] = 0;
|
||||
}
|
||||
if (Settings->version < 0x0C030102) { // 12.3.1.2
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
xdrv_70_hdmi_cec.ino - support for HDMI CEC bus (control TV via HDMI)
|
||||
|
||||
Copyright (C) 2021 Theo Arends, Stephan Hadinger
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef USE_HDMI_CEC
|
||||
/*********************************************************************************************\
|
||||
* HDMI CEC send and receive using lib https://github.com/lucadentella/ArduinoLib_CEClient
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XDRV_70 70
|
||||
|
||||
const char kHDMICommands[] PROGMEM = D_PRFX_HDMI "|"
|
||||
D_CMND_HDMI_SEND_RAW "|" D_CMND_HDMI_SEND "|"
|
||||
D_CMND_HDMI_TYPE "|" D_CMND_HDMI_ADDR;
|
||||
|
||||
void (* const HDMICommand[])(void) PROGMEM = {
|
||||
&CmndHDMISendRaw, CmndHDMISend,
|
||||
&CmndHDMIType, &CmndHDMIAddr,
|
||||
};
|
||||
|
||||
|
||||
// This is called after the logical address has been allocated
|
||||
void HDMI_OnReady(class CEC_Device* self, int logical_address) {
|
||||
int physical_address = self->getPhysicalAddress();
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("CEC: HDMI CEC initialized on GPIO %i, Logical address %d, Physical address 0x%04X"), self->getGPIO(), logical_address, physical_address);
|
||||
}
|
||||
|
||||
void HDMI_OnReceive(class CEC_Device *self, int32_t from, int32_t to, uint8_t* buf, size_t len, bool ack)
|
||||
{
|
||||
AddLog(LOG_LEVEL_DEBUG, "CEC: Packet received: (%1X->%1X) %1X%1X%*_H %s", from, to, from, to, len, buf, ack ? PSTR("ACK") : PSTR("NAK"));
|
||||
|
||||
Response_P(PSTR("{\"HdmiReceived\":{\"From\":%i,\"To\":%i,\"Data\":\"%*_H\"}}"), from, to, len, buf);
|
||||
if (to == self->getLogicalAddress() || to == 0x0F) {
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings->flag.mqtt_sensor_retain);
|
||||
}
|
||||
XdrvRulesProcess(0); // apply rules
|
||||
}
|
||||
|
||||
void HDMI_OnTransmit(class CEC_Device *self, uint8_t* buf, size_t len, bool ack)
|
||||
{
|
||||
// This is called after a frame is transmitted.
|
||||
AddLog(LOG_LEVEL_DEBUG, "CEC: Packet sent: %*_H %s", len, buf, ack ? PSTR("ACK") : PSTR("NAK"));
|
||||
}
|
||||
|
||||
// singleton for HDMI CEC object, could be expanded if we manage multiple HDMI in parallel
|
||||
CEC_Device *HDMI_CEC_device = nullptr;
|
||||
|
||||
void HdmiCecInit(void)
|
||||
{
|
||||
// CEC device type
|
||||
CEC_Device::CEC_DEVICE_TYPE device_type = (CEC_Device::CEC_DEVICE_TYPE) Settings->hdmi_cec_device_type;
|
||||
if (device_type == CEC_Device::CDT_TV || device_type >= CEC_Device::CDT_LAST) {
|
||||
// if type in Settings is invalid, default to PLAYBACK_DEVICE
|
||||
device_type = CEC_Device::CDT_PLAYBACK_DEVICE;
|
||||
Settings->hdmi_cec_device_type = (uint8_t) device_type;
|
||||
SettingsSaveAll();
|
||||
}
|
||||
// GPIO configuration
|
||||
int32_t cec_gpio = Pin(GPIO_HDMI_CEC);
|
||||
if (cec_gpio >= 0) {
|
||||
HDMI_CEC_device = new CEC_Device(cec_gpio, device_type, true); // Promiscuous mode
|
||||
if (HDMI_CEC_device == nullptr) {
|
||||
AddLog(LOG_LEVEL_ERROR, PSTR("CEC: HDMI_CEC_device init failed"));
|
||||
return;
|
||||
}
|
||||
HDMI_CEC_device->setOnReceiveCB(&HDMI_OnReceive);
|
||||
HDMI_CEC_device->setOnTransmitCB(&HDMI_OnTransmit);
|
||||
HDMI_CEC_device->setOnReadyCB(&HDMI_OnReady);
|
||||
HDMI_CEC_device->start(); // start the protocol
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interrupt management
|
||||
\*********************************************************************************************/
|
||||
|
||||
void IRAM_ATTR CEC_Run(void *self) {
|
||||
CEC_Device *cec_device = (CEC_Device*)self;
|
||||
cec_device->serviceGpioISR();
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
//
|
||||
// Command HdmiSendRaw
|
||||
//
|
||||
// HdmiSendRaw <hex>
|
||||
// Send the HEX sequence as-is with no control
|
||||
//
|
||||
void CmndHDMISendRaw(void) {
|
||||
if (HDMI_CEC_device) {
|
||||
RemoveSpace(XdrvMailbox.data);
|
||||
SBuffer buf = SBuffer::SBufferFromHex(XdrvMailbox.data, strlen(XdrvMailbox.data));
|
||||
if (buf.len() > 0 && buf.len() < 16) {
|
||||
HDMI_CEC_device->transmitRaw(buf.buf(), buf.len());
|
||||
ResponseCmndDone();
|
||||
} else {
|
||||
ResponseCmndChar_P(PSTR("Buffer too large"));
|
||||
}
|
||||
} else {
|
||||
ResponseCmndError();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Command HdmiSend
|
||||
//
|
||||
// HdmiSend <hex>
|
||||
// HdmiSend { ["To":<to>,] "Data":"<hex>"}
|
||||
// Send the HEX payload to the target (unicast of broadcast)
|
||||
// "To": 0-15 (optional) target logical address, defaults to 0 (TV)
|
||||
// "Hex": payload without the first byte (source/dst) which is inferred
|
||||
//
|
||||
// Examples:
|
||||
// HdmiSend 8F -- ask TV its power state
|
||||
// or HdmiSend {"Data":"8F"}
|
||||
// or HdmiSend {"To":0, "Data":"8F"}
|
||||
//
|
||||
//
|
||||
// HdmiSend 8C -- ask TV its vendor id
|
||||
// or HdmiSend {"Data":"8C"}
|
||||
// or HdmiSend {"To":0, "Data":"8C"}
|
||||
//
|
||||
void CmndHDMISend(void) {
|
||||
if (HDMI_CEC_device) {
|
||||
RemoveSpace(XdrvMailbox.data);
|
||||
if (XdrvMailbox.data[0] == '{') {
|
||||
|
||||
// JSON
|
||||
JsonParser parser(XdrvMailbox.data);
|
||||
JsonParserObject root = parser.getRoot();
|
||||
|
||||
if (!parser || !(root.isObject())) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
|
||||
|
||||
JsonParserToken val;
|
||||
uint32_t to = root.getUInt(PSTR("To"), 0);
|
||||
const char * payload = root.getStr(PSTR("Data"));
|
||||
SBuffer buf = SBuffer::SBufferFromHex(payload, strlen(payload));
|
||||
if (buf.len() > 0 && buf.len() < 15) {
|
||||
HDMI_CEC_device->transmitFrame(to, buf.buf(), buf.len());
|
||||
ResponseCmndDone();
|
||||
} else {
|
||||
if (buf.len() == 0) {
|
||||
ResponseCmndChar_P(PSTR("Buffer empty"));
|
||||
} else {
|
||||
ResponseCmndChar_P(PSTR("Buffer too large"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Hex
|
||||
SBuffer buf = SBuffer::SBufferFromHex(XdrvMailbox.data, strlen(XdrvMailbox.data));
|
||||
if (buf.len() > 0 && buf.len() < 15) {
|
||||
HDMI_CEC_device->transmitFrame(0, buf.buf(), buf.len());
|
||||
ResponseCmndDone();
|
||||
} else {
|
||||
if (buf.len() == 0) {
|
||||
ResponseCmndChar_P(PSTR("Buffer empty"));
|
||||
} else {
|
||||
ResponseCmndChar_P(PSTR("Buffer too large"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ResponseCmndError();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Command CmndHDMIType
|
||||
//
|
||||
//
|
||||
void CmndHDMIType(void) {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
if ((XdrvMailbox.payload < 1) && (XdrvMailbox.payload >= CEC_Device::CDT_LAST)) {
|
||||
uint8_t type = XdrvMailbox.payload;
|
||||
if (type != Settings->hdmi_cec_device_type) {
|
||||
Settings->hdmi_cec_device_type = XdrvMailbox.payload;
|
||||
SettingsSaveAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
ResponseCmndNumber(Settings->hdmi_cec_device_type);
|
||||
}
|
||||
|
||||
#define HDMI_EDID_ADDRESS 0x50 // HDMI EDID address is 0x50
|
||||
|
||||
// Read FULL EDID 256 bytes from address 0x50
|
||||
// Return true if failed
|
||||
// The buffer must be allocated to uint8_t[256] by caller
|
||||
// Only checksum is checked
|
||||
bool ReadEdid256(uint8_t *buf) {
|
||||
if (!TasmotaGlobal.i2c_enabled) { return true; } // abort if I2C is not started
|
||||
|
||||
if (I2cReadBuffer(HDMI_EDID_ADDRESS, 0, buf , 128)) { return true; }
|
||||
if (I2cReadBuffer(HDMI_EDID_ADDRESS, 128, buf + 128, 128)) { return true; }
|
||||
|
||||
// verify checksum for block 0
|
||||
uint8_t chk0 = 0;
|
||||
for (uint32_t i = 0; i < 128; i++) {
|
||||
chk0 += buf[i];
|
||||
}
|
||||
if (chk0 != 0) { return true; }
|
||||
|
||||
// verify checksum for block 1
|
||||
uint8_t chk1 = 0;
|
||||
for (uint32_t i = 128; i < 256; i++) {
|
||||
chk1 += buf[i];
|
||||
}
|
||||
if (chk1 != 0) { return true; }
|
||||
|
||||
// check prefix
|
||||
uint32_t * buf32 = (uint32_t*) buf;
|
||||
if (buf32[0] != 0xFFFFFF00 || buf32[1] != 0x00FFFFFF) { return true; }
|
||||
|
||||
return false; // OK
|
||||
}
|
||||
|
||||
// HDMI get physical address
|
||||
// This is done by reading EDID via I2C, and looking for a vendor specific extension
|
||||
//
|
||||
// Return 0x0000 if not found
|
||||
uint16_t HDMIGetPhysicalAddress(void) {
|
||||
uint8_t buf[256] = {0};
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("CEC: trying to read physical address"));
|
||||
if (ReadEdid256(buf)) { return 0x0000; } // unable to get an address
|
||||
|
||||
uint8_t edid_extensions = buf[126];
|
||||
if (HighestLogLevel() >= LOG_LEVEL_DEBUG) {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("CEC: successfully read EDID 256 bytes, extensions count %i"), edid_extensions);
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("CEC: EDID: %*_H"), sizeof(buf),buf);
|
||||
}
|
||||
if (edid_extensions == 0) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("CEC: Error: EDID has no extension"));
|
||||
}
|
||||
|
||||
// Read first extension which is mandatory for HDMI
|
||||
if (buf[128] != 0x02 || buf[129] < 0x03) { return 0x0000; } // invalid extension
|
||||
|
||||
uint32_t extensions_first_byte = 128 + 4;
|
||||
uint32_t extensions_last_byte = 128 + buf[130];
|
||||
uint32_t idx = extensions_first_byte;
|
||||
while (idx < extensions_last_byte) {
|
||||
uint8_t data_block_header = buf[idx];
|
||||
uint32_t type = (data_block_header >> 5);
|
||||
uint32_t number_of_bytes = (data_block_header & 0x1F);
|
||||
|
||||
// AddLog(LOG_LEVEL_DEBUG, "CEC: idx %i extension type %i, number of bytes %i", idx, type, number_of_bytes);
|
||||
|
||||
if (type == 3) {
|
||||
// Vendor specific extension
|
||||
// 030C00 for "HDMI Licensing, LLC"
|
||||
if (buf[idx+1] == 0x03 && buf[idx+2] == 0x0C && buf[idx+3] == 0x00) {
|
||||
uint16_t addr = (buf[idx+4] << 8) | buf[idx+5];
|
||||
AddLog(LOG_LEVEL_DEBUG, "CEC: physical address found: 0x%04X", addr);
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
idx += 1 + number_of_bytes;
|
||||
}
|
||||
|
||||
AddLog(LOG_LEVEL_DEBUG, "CEC: physical address not found");
|
||||
return 0x0000; // TODO
|
||||
}
|
||||
|
||||
|
||||
void CmndHDMIAddr(void) {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
if ((XdrvMailbox.payload < 1)) {
|
||||
uint16_t hdmi_addr = XdrvMailbox.payload;
|
||||
Settings->hdmi_addr[0] = (hdmi_addr) & 0xFF;
|
||||
Settings->hdmi_addr[1] = (hdmi_addr >> 8) & 0xFF;
|
||||
}
|
||||
}
|
||||
uint16_t hdmi_addr = HDMIGetPhysicalAddress();
|
||||
Response_P(PSTR("{\"%s\":\"0x%04X\"}"), XdrvMailbox.command, hdmi_addr);
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xdrv70(uint32_t function)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
switch (function) {
|
||||
case FUNC_INIT:
|
||||
HdmiCecInit();
|
||||
break;
|
||||
case FUNC_LOOP:
|
||||
case FUNC_SLEEP_LOOP:
|
||||
if (HDMI_CEC_device) {
|
||||
HDMI_CEC_device->run();
|
||||
}
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
if (HDMI_CEC_device) {
|
||||
result = DecodeCommand(kHDMICommands, HDMICommand);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_HDMI_CEC
|
Loading…
Reference in New Issue