Skip to content

Commit 2083b13

Browse files
committed
Initial commit
0 parents  commit 2083b13

17 files changed

+2223
-0
lines changed

LICENSE.md

Lines changed: 675 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# CEClient
2+
Arduino library for HDMI CEC communication
3+
4+
Original code at: https://code.google.com/archive/p/cec-arduino/ (c) Phil Burr and Andrew N. Carr
5+
Based on the work of Florian Echtler: https://github.com/floe/CEC
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
Example1_Listening.ino
3+
4+
Basic example to demonstrate the use of the CEClient library
5+
The client is configured in promiscuous and monitor mode
6+
to receive all the messages on the CEC bus
7+
8+
No specific callback function is defined, therefore the client
9+
calls the default one, which prints the packets on the Serial port
10+
11+
Use http://www.cec-o-matic.com/ to decode captured messages
12+
*/
13+
14+
#include "CEClient.h"
15+
16+
#define CEC_PHYSICAL_ADDRESS 0x1000
17+
#define CEC_INPUT_PIN 2
18+
#define CEC_OUTPUT_PIN 3
19+
20+
// create a CEC client
21+
CEClient ceclient(CEC_PHYSICAL_ADDRESS, CEC_INPUT_PIN, CEC_OUTPUT_PIN);
22+
23+
24+
void setup() {
25+
26+
Serial.begin(115200);
27+
28+
// initialize the client with the default device type (PLAYBACK)
29+
ceclient.begin();
30+
31+
// enable promiscuous mode (print all the incoming messages)
32+
ceclient.setPromiscuous(true);
33+
34+
// enable monitor mode (do not transmit)
35+
ceclient.setMonitorMode(true);
36+
}
37+
38+
void loop() {
39+
40+
// run the client
41+
ceclient.run();
42+
}

keywords.txt

Whitespace-only changes.

library.properties

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name=CEClient
2+
version=0.0.1
3+
author=Luca Dentella <luca@dentella.it>, Florian Echtler, Phil Burr, Andrew N. Carr
4+
maintainer=Luca Dentella <luca@dentella.it>
5+
sentence=A library which allows communication with HDMI CEC capable devices.
6+
paragraph=Supports HDMI v1.3a CEC wire protocol.
7+
category=Communication
8+
url=https://github.com/lucadentella/ArduinoLib_CEClient
9+
architectures=avr
10+
includes=CEClient.h

src/CEC/CEC.cpp

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#include "CEC.h"
2+
3+
int CEC_LogicalDevice::_validLogicalAddresses[6][5] =
4+
{ {CLA_TV, CLA_FREE_USE, CLA_UNREGISTERED, CLA_UNREGISTERED, CLA_UNREGISTERED, },
5+
{CLA_RECORDING_DEVICE_1, CLA_RECORDING_DEVICE_2, CLA_RECORDING_DEVICE_3, CLA_UNREGISTERED, CLA_UNREGISTERED, },
6+
{CLA_PLAYBACK_DEVICE_1, CLA_PLAYBACK_DEVICE_2, CLA_PLAYBACK_DEVICE_3, CLA_UNREGISTERED, CLA_UNREGISTERED, },
7+
{CLA_TUNER_1, CLA_TUNER_2, CLA_TUNER_3, CLA_TUNER_4, CLA_UNREGISTERED, },
8+
{CLA_AUDIO_SYSTEM, CLA_UNREGISTERED, CLA_UNREGISTERED, CLA_UNREGISTERED, CLA_UNREGISTERED, },
9+
{CLA_UNREGISTERED, CLA_UNREGISTERED, CLA_UNREGISTERED, CLA_UNREGISTERED, CLA_UNREGISTERED, },
10+
};
11+
12+
#define MAKE_ADDRESS(s,d) ((((s) & 0xf) << 4) | ((d) & 0xf))
13+
14+
15+
CEC_LogicalDevice::CEC_LogicalDevice(int physicalAddress)
16+
: CEC_Electrical(CLA_UNREGISTERED)
17+
, _physicalAddress(physicalAddress)
18+
, _logicalAddress(CLA_UNREGISTERED)
19+
, _done(false)
20+
, _waitTime(0)
21+
, _primaryState(CEC_ALLOCATE_LOGICAL_ADDRESS)
22+
, _deviceType(CDT_OTHER)
23+
{
24+
_secondaryState = CEC_XMIT_POLLING_MESSAGE;
25+
_tertiaryState = 0;
26+
}
27+
28+
void CEC_LogicalDevice::Initialize(CEC_DEVICE_TYPE type)
29+
{
30+
CEC_Electrical::Initialize();
31+
_deviceType = type;
32+
33+
if (MonitorMode)
34+
{
35+
_primaryState = CEC_READY;
36+
}
37+
}
38+
39+
bool CEC_LogicalDevice::ProcessStateMachine(bool* success)
40+
{
41+
unsigned char buffer[1];
42+
bool wait = false;
43+
44+
switch (_primaryState)
45+
{
46+
case CEC_ALLOCATE_LOGICAL_ADDRESS:
47+
switch (_secondaryState)
48+
{
49+
case CEC_XMIT_POLLING_MESSAGE:
50+
// Section 6.1.3 specifies that <Polling Message> while allocating a Logical Address
51+
// will have the same initiator and destination address
52+
buffer[0] = MAKE_ADDRESS(_validLogicalAddresses[_deviceType][_tertiaryState], _validLogicalAddresses[_deviceType][_tertiaryState]);
53+
ClearTransmitBuffer();
54+
Transmit(buffer, 1);
55+
56+
_secondaryState = CEC_RCV_POLLING_MESSAGE;
57+
wait = true;
58+
break;
59+
60+
case CEC_RCV_POLLING_MESSAGE:
61+
if (success)
62+
{
63+
if (*success)
64+
{
65+
// Someone is there, try the next
66+
_tertiaryState++;
67+
if (_validLogicalAddresses[_deviceType][_tertiaryState] != CLA_UNREGISTERED)
68+
_secondaryState = CEC_XMIT_POLLING_MESSAGE;
69+
else
70+
{
71+
_logicalAddress = CLA_UNREGISTERED;
72+
//DbgPrint("Logical address assigned: %d\n", _logicalAddress);
73+
_primaryState = CEC_READY;
74+
}
75+
}
76+
else
77+
{
78+
// We hereby claim this as our logical address!
79+
_logicalAddress = _validLogicalAddresses[_deviceType][_tertiaryState];
80+
SetAddress(_logicalAddress);
81+
//DbgPrint("Logical address assigned: %d\n", _logicalAddress);
82+
_primaryState = CEC_READY;
83+
}
84+
}
85+
else
86+
wait = true;
87+
break;
88+
}
89+
break;
90+
91+
case CEC_READY:
92+
_primaryState = CEC_IDLE;
93+
OnReady();
94+
wait = true;
95+
break;
96+
97+
case CEC_IDLE:
98+
wait = true;
99+
break;
100+
}
101+
102+
return wait;
103+
}
104+
105+
void CEC_LogicalDevice::OnReceiveComplete(unsigned char* buffer, int count)
106+
{
107+
ASSERT(count >= 1);
108+
int sourceAddress = (buffer[0] >> 4) & 0x0f;
109+
int targetAddress = buffer[0] & 0x0f;
110+
OnReceive(sourceAddress, targetAddress, buffer + 1, count - 1);
111+
}
112+
113+
bool CEC_LogicalDevice::TransmitFrame(int targetAddress, unsigned char* buffer, int count)
114+
{
115+
if (_primaryState != CEC_IDLE)
116+
return false;
117+
118+
unsigned char addr[1];
119+
120+
addr[0] = MAKE_ADDRESS(_logicalAddress, targetAddress);
121+
ClearTransmitBuffer();
122+
if (!TransmitPartial(addr, 1))
123+
return false;
124+
return Transmit(buffer, count);
125+
}
126+
127+
void CEC_LogicalDevice::OnTransmitComplete(bool success)
128+
{
129+
if (_primaryState == CEC_ALLOCATE_LOGICAL_ADDRESS &&
130+
_secondaryState == CEC_RCV_POLLING_MESSAGE &&
131+
_logicalAddress == CLA_UNREGISTERED)
132+
{
133+
while (!ProcessStateMachine(&success))
134+
;
135+
}
136+
else
137+
//DbgPrint("Transmit: %d\n", success);
138+
}
139+
140+
void CEC_LogicalDevice::Run()
141+
{
142+
// Initial pump for the state machine (this will cause a transmit to occur)
143+
while (!ProcessStateMachine(NULL))
144+
;
145+
146+
if (((_waitTime == (unsigned long)-1 && !TransmitPending()) || (_waitTime != (unsigned long)-1 && _waitTime > micros())) && !IsISRTriggered())
147+
return;
148+
149+
unsigned long wait = Process();
150+
if (wait != (unsigned long)-2)
151+
_waitTime = wait;
152+
return;
153+
}
154+
155+

src/CEC/CEC.h

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#ifndef CEC_H__
2+
#define CEC_H__
3+
4+
#include "CEC_Electrical.h"
5+
6+
class CEC_LogicalDevice : public CEC_Electrical
7+
{
8+
public:
9+
typedef enum {
10+
CDT_TV,
11+
CDT_RECORDING_DEVICE,
12+
CDT_PLAYBACK_DEVICE,
13+
CDT_TUNER,
14+
CDT_AUDIO_SYSTEM,
15+
CDT_OTHER, // Not a real CEC type..
16+
} CEC_DEVICE_TYPE;
17+
18+
public:
19+
CEC_LogicalDevice(int physicalAddress);
20+
void Initialize(CEC_DEVICE_TYPE type);
21+
22+
virtual void Run();
23+
virtual bool TransmitFrame(int targetAddress, unsigned char* buffer, int count);
24+
25+
protected:
26+
virtual bool IsISRTriggered() = 0;
27+
28+
bool ProcessStateMachine(bool* success);
29+
30+
virtual void OnReceiveComplete(unsigned char* buffer, int count);
31+
virtual void OnTransmitComplete(bool);
32+
33+
virtual void OnReady() {;}
34+
virtual void OnReceive(int sourceAddress, int targetAddress, unsigned char* buffer, int count) = 0;
35+
36+
private:
37+
typedef enum {
38+
CLA_TV = 0,
39+
CLA_RECORDING_DEVICE_1,
40+
CLA_RECORDING_DEVICE_2,
41+
CLA_TUNER_1,
42+
CLA_PLAYBACK_DEVICE_1,
43+
CLA_AUDIO_SYSTEM,
44+
CLA_TUNER_2,
45+
CLA_TUNER_3,
46+
CLA_PLAYBACK_DEVICE_2,
47+
CLA_RECORDING_DEVICE_3,
48+
CLA_TUNER_4,
49+
CLA_PLAYBACK_DEVICE_3,
50+
CLA_RESERVED_1,
51+
CLA_RESERVED_2,
52+
CLA_FREE_USE,
53+
CLA_UNREGISTERED,
54+
} CEC_LOGICAL_ADDRESS;
55+
56+
typedef enum {
57+
CEC_IDLE,
58+
CEC_READY,
59+
CEC_ALLOCATE_LOGICAL_ADDRESS,
60+
} CEC_PRIMARY_STATE;
61+
62+
typedef enum {
63+
CEC_XMIT_POLLING_MESSAGE,
64+
CEC_RCV_POLLING_MESSAGE,
65+
} CEC_SECONDARY_STATE;
66+
67+
typedef enum {
68+
} CEC_TERTIARY_STATE;
69+
70+
protected:
71+
static int _validLogicalAddresses[6][5];
72+
int _logicalAddress;
73+
int _physicalAddress;
74+
unsigned long _waitTime;
75+
bool _done;
76+
77+
CEC_DEVICE_TYPE _deviceType;
78+
CEC_PRIMARY_STATE _primaryState;
79+
CEC_SECONDARY_STATE _secondaryState;
80+
int _tertiaryState;
81+
82+
};
83+
84+
#endif // CEC_H__

src/CEC/CEC_Device.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#include "CEC_Device.h"
2+
#include <Arduino.h>
3+
4+
CEC_Device::CEC_Device(int physicalAddress, int in_line, int out_line)
5+
: CEC_LogicalDevice(physicalAddress)
6+
, _isrTriggered(false)
7+
, _lastLineState2(true)
8+
, _in_line(in_line)
9+
, _out_line(out_line)
10+
{
11+
}
12+
13+
void CEC_Device::Initialize(CEC_DEVICE_TYPE type)
14+
{
15+
pinMode(_out_line, OUTPUT);
16+
pinMode( _in_line, INPUT);
17+
18+
digitalWrite(_out_line, LOW);
19+
delay(200);
20+
21+
CEC_LogicalDevice::Initialize(type);
22+
}
23+
24+
void CEC_Device::OnReady()
25+
{
26+
// This is called after the logical address has been
27+
// allocated
28+
DbgPrint("Device ready\n");
29+
}
30+
31+
void CEC_Device::OnReceive(int source, int dest, unsigned char* buffer, int count)
32+
{
33+
// This is called when a frame is received. To transmit
34+
// a frame call TransmitFrame. To receive all frames, even
35+
// those not addressed to this device, set Promiscuous to true.
36+
DbgPrint("Packet received at %ld: %02d -> %02d: %02X", millis(), source, dest, ((source&0x0f)<<4)|(dest&0x0f));
37+
for (int i = 0; i < count; i++)
38+
DbgPrint(":%02X", buffer[i]);
39+
DbgPrint("\n");
40+
}
41+
42+
bool CEC_Device::LineState()
43+
{
44+
int state = digitalRead(_in_line);
45+
return state == LOW;
46+
}
47+
48+
void CEC_Device::SetLineState(bool state)
49+
{
50+
digitalWrite(_out_line, state?LOW:HIGH);
51+
// give enough time for the line to settle before sampling
52+
// it
53+
delayMicroseconds(50);
54+
_lastLineState2 = LineState();
55+
}
56+
57+
void CEC_Device::SignalIRQ()
58+
{
59+
// This is called when the line has changed state
60+
_isrTriggered = true;
61+
}
62+
63+
bool CEC_Device::IsISRTriggered()
64+
{
65+
if (_isrTriggered)
66+
{
67+
_isrTriggered = false;
68+
return true;
69+
}
70+
return false;
71+
}
72+
73+
void CEC_Device::Run()
74+
{
75+
bool state = LineState();
76+
if (_lastLineState2 != state)
77+
{
78+
_lastLineState2 = state;
79+
SignalIRQ();
80+
}
81+
CEC_LogicalDevice::Run();
82+
}

0 commit comments

Comments
 (0)