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