1 #include "stdafx.h"
2 #include "Comm5Serial.h"
3 #include "../main/Helper.h"
4 #include "../main/localtime_r.h"
5 #include "../main/Logger.h"
6 #include "../main/RFXtrx.h"
7 
8 /*
9 	This driver allows Domoticz to control any I/O module from the MA-4xxx Family
10 
11 	These modules provide relays and digital sensors in the range of 5-30V DC.
12 */
13 
Comm5Serial(const int ID,const std::string & devname,unsigned int baudRate)14 Comm5Serial::Comm5Serial(const int ID, const std::string& devname, unsigned int baudRate /*= 115200*/) :
15 	m_szSerialPort(devname),
16 	m_baudRate(baudRate)
17 {
18 	m_HwdID=ID;
19 	lastKnownSensorState = 0;
20 	initSensorData = true;
21 	reqState = Idle;
22 	notificationEnabled = false;
23 	m_bReceiverStarted = false;
24 	currentState = STSTART_OCTET1;
25 	frameSize = 0;
26 	frameCRC = 0;
27 }
28 
WriteToHardware(const char * pdata,const unsigned char)29 bool Comm5Serial::WriteToHardware(const char * pdata, const unsigned char /*length*/)
30 {
31 	const tRBUF *pSen = reinterpret_cast<const tRBUF*>(pdata);
32 
33 	unsigned char packettype = pSen->ICMND.packettype;
34 	//unsigned char subtype = pSen->ICMND.subtype;
35 
36 	if (packettype == pTypeLighting2 && pSen->LIGHTING2.id3 == 0)
37 	{
38 		//light command
39 
40 		uint8_t Relay = pSen->LIGHTING2.id4 - 1;
41 		if (Relay > 8)
42 			return false;
43 		std::string data("\x02\x02", 2);
44 		uint8_t relayMask = relayStatus;
45 		if (pSen->LIGHTING2.cmnd == light2_sOff) {
46 			relayMask &= ~(1 << Relay);
47 		} else {
48 			relayMask |= 1 << Relay;
49 		}
50 
51 		data.push_back(relayMask);
52 		writeFrame(data);
53 
54 		relayStatus = relayMask;
55 		return true;
56 	}
57 	return false;
58 
59 }
60 
StartHardware()61 bool Comm5Serial::StartHardware()
62 {
63 	RequestStart();
64 
65 	m_thread = std::make_shared<std::thread>(&Comm5Serial::Do_Work, this);
66 	SetThreadNameInt(m_thread->native_handle());
67 
68 	//Try to open the Serial Port
69 	try
70 	{
71 		open(
72 			m_szSerialPort,
73 			m_baudRate,
74 			boost::asio::serial_port_base::parity(
75 			boost::asio::serial_port_base::parity::none),
76 			boost::asio::serial_port_base::character_size(8)
77 			);
78 	}
79 	catch (boost::exception & e)
80 	{
81 		Log(LOG_ERROR,"Error opening serial port!");
82 #ifdef _DEBUG
83 		Log(LOG_ERROR,"-----------------\n%s\n-----------------",boost::diagnostic_information(e).c_str());
84 #else
85 		(void)e;
86 #endif
87 		return false;
88 	}
89 	catch ( ... )
90 	{
91 		_log.Log(LOG_ERROR,"Error opening serial port!!!");
92 		return false;
93 	}
94 	m_bIsStarted=true;
95 	setReadCallback(boost::bind(&Comm5Serial::readCallBack, this, _1, _2));
96 
97 	sOnConnected(this);
98 	return true;
99 }
100 
StopHardware()101 bool Comm5Serial::StopHardware()
102 {
103 	if (m_thread)
104 	{
105 		RequestStop();
106 		m_thread->join();
107 		m_thread.reset();
108 	}
109 	m_bIsStarted = false;
110 	return true;
111 }
112 
Do_Work()113 void Comm5Serial::Do_Work()
114 {
115 	//int sec_counter = 0;
116 	int msec_counter = 0;
117 
118 	queryRelayState();
119 	querySensorState();
120 	enableNotifications();
121 
122 	Log(LOG_STATUS, "Worker started...");
123 
124 
125 	while (!IsStopRequested(100))
126 	{
127 		m_LastHeartbeat = mytime(NULL);
128 		if (msec_counter++ >= 40)
129 		{
130 			//every 4 seconds ?
131 			msec_counter = 0;
132 			querySensorState();
133 		}
134 	}
135 	terminate();
136 
137 	Log(LOG_STATUS, "Worker stopped...");
138 }
139 
requestDigitalInputResponseHandler(const std::string & mframe)140 void Comm5Serial::requestDigitalInputResponseHandler(const std::string & mframe)
141 {
142 	//uint8_t mask = mframe[5];
143 	uint8_t sensorStatus = mframe[6];
144 
145 	for (int i = 0; i < 8; ++i) {
146 		bool on = (sensorStatus & (1 << i)) != 0 ? true : false;
147 		if (((lastKnownSensorState & (1 << i)) ^ (sensorStatus & (1 << i))) || initSensorData) {
148 			SendSwitchUnchecked((i + 1) << 8, 1, 255, on, 0, "Sensor " + std::to_string(i + 1));
149 		}
150 	}
151 	lastKnownSensorState = sensorStatus;
152 	initSensorData = false;
153 	reqState = Idle;
154 }
155 
requestDigitalOutputResponseHandler(const std::string & mframe)156 void Comm5Serial::requestDigitalOutputResponseHandler(const std::string & mframe)
157 {
158 	//  0     1     2       3       4        5        6
159 	// 0x05 0x64 <size> <control> <id> <operation> <value>
160 
161 	relayStatus = mframe[6];
162 	for (int i = 0; i < 8; ++i) {
163 		bool on = (relayStatus & (1 << i)) != 0 ? true : false;
164 		SendSwitch(i + 1, 1, 255, on, 0, "Relay " + std::to_string(i + 1));
165 	}
166 }
167 
enableNotificationResponseHandler(const std::string &)168 void Comm5Serial::enableNotificationResponseHandler(const std::string & /*mframe*/)
169 {
170 	//uint8_t operation = mframe[5];
171 	//uint8_t operationType = mframe[6];
172 	//uint8_t mask = mframe[7];
173 }
174 
readCallBack(const char * data,size_t len)175 void Comm5Serial::readCallBack(const char * data, size_t len)
176 {
177 	if (!m_bEnableReceive)
178 		return; //receiving not enabled
179 
180 	ParseData((const unsigned char*)data, static_cast<int>(len));
181 }
182 
crc16_update(uint16_t crc,const uint8_t data)183 uint16_t Comm5Serial::crc16_update(uint16_t crc, const uint8_t data)
184 {
185 #define POLYNOME 0x13D65
186 	int i;
187 
188 	crc = crc ^ ((uint16_t)data << 8);
189 	for (i = 0; i<8; i++)
190 	{
191 		if (crc & 0x8000)
192 			crc = (uint16_t)((crc << 1) ^ POLYNOME);
193 		else
194 			crc <<= 1;
195 	}
196 
197 	return crc;
198 #undef POLYNOME
199 }
200 
ParseData(const unsigned char * data,const size_t len)201 void Comm5Serial::ParseData(const unsigned char* data, const size_t len)
202 {
203 
204 	uint16_t readCRC;
205 	for (size_t i = 0; i < len; ++i) {
206 		switch ( currentState ) {
207 		case STSTART_OCTET1:
208 			frame.clear();
209 			if (data[i] == 0x05) {
210 				frame.push_back(data[i]);
211 				frameCRC = crc16_update(0, data[i]);
212 				currentState = STSTART_OCTET2;
213 
214 			}
215 			else {
216 				Log(LOG_ERROR, "Framing error");
217 				currentState = STSTART_OCTET1;
218 			}
219 			break;
220 
221 		case STSTART_OCTET2:
222 			if (data[i] == 0x64) {
223 				frame.push_back(data[i]);
224 				frameCRC = crc16_update(frameCRC, data[i]);
225 				currentState = STFRAME_SIZE;
226 			}
227 			else {
228 				Log(LOG_ERROR, "Framing error");
229 				currentState = STSTART_OCTET1;
230 			}
231 			break;
232 
233 		case STFRAME_SIZE:
234 			frameSize = data[i] + 2 + 1 + 1; // FrameSize + Start Tokens + Control Byte + Frame Size field
235 			frame.push_back(data[i]);
236 			frameCRC = crc16_update(frameCRC, data[i]);
237 			currentState = STFRAME_CONTROL;
238 			break;
239 
240 		case STFRAME_CONTROL:
241 			frame.push_back(data[i]);
242 			frameCRC = crc16_update(frameCRC, data[i]);
243 			currentState = STFRAME_DATA;
244 			break;
245 
246 		case STFRAME_DATA:
247 			frame.push_back(data[i]);
248 			frameCRC = crc16_update(frameCRC, data[i]);
249 
250 			if ( frame.size() >= frameSize )
251 				currentState = STFRAME_CRC1;
252 			break;
253 
254 		case STFRAME_CRC1:
255 			frame.push_back(data[i]);
256 			frameCRC = crc16_update(frameCRC, 0);
257 			currentState = STFRAME_CRC2;
258 			break;
259 
260 		case STFRAME_CRC2:
261 			frame.push_back(data[i]);
262 			frameCRC = crc16_update(frameCRC, 0);
263 			readCRC =  (uint16_t)(frame.at(frame.size() - 2) << 8) | (frame.at(frame.size() - 1) & 0xFF);
264 			if (frameCRC == readCRC)
265 				parseFrame(frame);
266 			currentState = STSTART_OCTET1;
267 			frame.clear();
268 			break;
269 		}
270 
271 	}
272 }
273 
parseFrame(const std::string & mframe)274 void Comm5Serial::parseFrame(const std::string & mframe)
275 {
276 	switch (mframe.at(4)) {
277 	case 0x01:
278 		requestDigitalInputResponseHandler(mframe);
279 		break;
280 	case 0x02:
281 		requestDigitalOutputResponseHandler(mframe);
282 		break;
283 	case 0x04:
284 		enableNotificationResponseHandler(mframe);
285 		break;
286 	}
287 }
288 
writeFrame(const std::string & data)289 bool Comm5Serial::writeFrame(const std::string & data)
290 {
291 	char length = (uint8_t)data.size();
292 	std::string mframe("\x05\x64", 2);
293 	mframe.push_back(length);
294 	mframe.push_back(0x00);
295 	mframe.append(data);
296 	uint16_t crc = 0;
297 	for (size_t i = 0; i < mframe.size(); ++i)
298 		crc = crc16_update(crc, mframe.at(i));
299 	crc = crc16_update(crc, 0);
300 	crc = crc16_update(crc, 0);
301 
302 	mframe.append(1, crc >> 8);
303 	mframe.append(1, crc & 0xFF);
304 	write(mframe);
305 	return true;
306 }
307 
queryRelayState()308 void Comm5Serial::queryRelayState()
309 {
310 	//                  id    op     mask
311 	std::string data("\x02\x01\xFF", 3);
312 	writeFrame(data);
313 }
314 
querySensorState()315 void Comm5Serial::querySensorState()
316 {
317 	//                  id    mask
318 	std::string data("\x01\xFF", 2);
319 	writeFrame(data);
320 }
321 
enableNotifications()322 void Comm5Serial::enableNotifications()
323 {
324 	//                  id    op   on/off  mask
325 	std::string data("\x04\x02\x01\xFF");
326 	writeFrame(data);
327 }
328 
OnError(const std::exception e)329 void Comm5Serial::OnError(const std::exception e)
330 {
331 	Log(LOG_ERROR, "Error: %s", e.what());
332 }
333 
334 
335