1 /************************************************************************
2 *
3 PiFace Interface board driver for Domoticz
4 Date: 01-10-2013
5 Written by: Robin Stevenhagen
6 License: Public domain
7
8 Date: 18-06-2015
9 GizMoCuz: Added messages to a send queue
10
11 Date: 08-04-2016
12 BobKersten: Added multiple counter types
13
14 SPI code is based on code from:
15 The WiringPi project
16 * Copyright (c) 2012 Gordon Henderson
17 ***********************************************************************
18 * See https://projects.drogon.net/raspberry-pi/wiringpi/
19 ***********************************************************************
20
21 Code uses SPI dev 0.0, as this is the interface were the PiFace resides.
22 PiFace has 8 Input (4 buttons) and 8 Output pins, of which output 0 and 1 are relay outputs.
23 Driver autodetects all available PiFace boards on startup, and scans them.
24
25 Default config (without config):
26 Input 0..3 enabled with rising edge detection
27 Input 4..7 enabled for pulse counting
28 Output 0..7 enbled with level feedback reporting.
29
30 ************************************************************************/
31
32 #include "stdafx.h"
33 #include "PiFace.h"
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <stdlib.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <string>
40 #ifdef HAVE_LINUX_SPI
41 #include <linux/ioctl.h>
42 #include <linux/types.h>
43 #include <linux/spi/spidev.h>
44 #include <unistd.h>
45 #include <sys/ioctl.h>
46 #endif
47 #include "../main/Helper.h"
48 #include "../main/Logger.h"
49 #include "hardwaretypes.h"
50 #include "../main/RFXtrx.h"
51 #include "../main/SQLHelper.h"
52 #include "MCP23x17.h"
53 #include <fstream>
54 #include <algorithm>
55 #include <functional>
56 #include <cctype>
57 #include <locale>
58 #include "../main/localtime_r.h"
59 #include "../main/WebServer.h"
60 #include "../main/mainworker.h"
61
62 #define PIFACE_INPUT_PROCESS_INTERVAL 5 // 100 msec
63 #define PIFACE_COUNTER_COUNTER_INTERVAL 1
64 #define PIFACE_WORKER_THREAD_SLEEP_INTERVAL_MS 20
65
66 #define DISABLE_NEW_FUNCTIONS
67
68 extern std::string szUserDataFolder;
69
70 const std::string CPiFace::ParameterNames[CONFIG_NR_OF_PARAMETER_TYPES] = {"enable","enabled","pin_type","count_enable","count_enabled","count_update_interval_sec","count_update_interval_s","count_update_interval","count_update_interval_diff_perc","count_initial_value","count_minimum_pulse_period_msec","count_type","count_divider"};
71 const std::string CPiFace::ParameterBooleanValueNames[CONFIG_NR_OF_PARAMETER_BOOL_TYPES] = {"false","0","true","1"};
72 const std::string CPiFace::ParameterPinTypeValueNames[CONFIG_NR_OF_PARAMETER_PIN_TYPES] = {"level","inv_level","rising","falling"};
73 const std::string CPiFace::ParameterCountTypeValueNames[CONFIG_NR_OF_PARAMETER_COUNT_TYPES] = {"generic","rfxmeter","energy"};
74
CPiFace(const int ID)75 CPiFace::CPiFace(const int ID)
76 {
77 m_HwdID=ID;
78 m_bIsStarted=false;
79
80 for (int i = 0; i < 4; i++)
81 {
82 m_Inputs[i].Callback_pntr = (void*)this; // Set callback pointer, to enable events from IO class
83 m_Inputs[i].SetID(i);
84 m_Outputs[i].Callback_pntr = (void*)this; // Set callback pointer, to enable events from IO class
85 m_Outputs[i].SetID(i);
86 }
87 m_fd = 0;
88 }
89
~CPiFace()90 CPiFace::~CPiFace()
91 {
92
93 }
94
95
96 /***** config file stuff *****/
97
98 // trim from start
ltrim(std::string & s)99 std::string & CPiFace::ltrim(std::string &s)
100 {
101 s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
102 return s;
103 }
104
105 // trim from end
rtrim(std::string & s)106 std::string & CPiFace::rtrim(std::string &s)
107 {
108 s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
109 return s;
110 }
111
112 // trim from both ends
trim(std::string & s)113 std::string & CPiFace::trim(std::string &s)
114 {
115 return ltrim(rtrim(s));
116 }
117
118 //strip all the unwanted data from the string before its passed back for further processing
preprocess(std::string & s)119 std::string & CPiFace::preprocess(std::string &s)
120 {
121 std::string temp;
122 std::string tempstripped;
123
124 temp.resize(s.size());
125 std::transform(s.begin(),s.end(),temp.begin(),::tolower);
126
127 tempstripped = trim(temp);
128
129 s=tempstripped;
130 return s;
131 }
132
LocateValueInParameterArray(std::string Parametername,const std::string * ParameterArray,int Items)133 int CPiFace::LocateValueInParameterArray(std::string Parametername,const std::string *ParameterArray,int Items)
134 {
135 int NameFound=-1; //assume not found
136 int Parameter_Index;
137
138 for (Parameter_Index=0;(Parameter_Index < Items) && (NameFound==-1);Parameter_Index++)
139 {
140 if (Parametername.compare(ParameterArray[Parameter_Index])==0)
141 {
142 NameFound=Parameter_Index;
143 }
144 }
145 return (NameFound);
146 }
147
GetParameterString(std::string TargetString,const char * SearchStr,int StartPos,std::string & Parameter)148 int CPiFace::GetParameterString(std::string TargetString,const char * SearchStr, int StartPos, std::string &Parameter)
149 {
150 int EndPos=-1;
151 std::string Substring;
152 std::string SearchString(SearchStr);
153
154 EndPos=TargetString.find(SearchString,StartPos);
155 if (EndPos >= 0)
156 {
157 Substring=TargetString.substr(StartPos,EndPos-StartPos);
158 Parameter=preprocess(Substring);
159 EndPos=EndPos+SearchString.length(); //set endposition to new search start position
160 }
161 return EndPos;
162 }
163
164
LoadConfig(void)165 int CPiFace::LoadConfig(void)
166 {
167 int result=-1;
168
169 int StartPos=-1;
170 int EndPos=-1;
171
172 std::string input;
173
174 std::string Line;
175 std::string Parameter;
176 std::string Porttype;
177 std::string Pinnumber;
178 std::string Parametername;
179 std::string Parametervalue;
180
181 unsigned char Address=0;
182 unsigned char PinNumber=0;
183 unsigned char PortType=0;
184 int NameFound=0;
185 int ValueFound=0;
186 CIOPort *IOport=NULL;
187 bool Regenerate_Config=false;
188
189 std::string configfile=szUserDataFolder + "piface.conf";
190
191 std::fstream ConfigFile(configfile.c_str(), std::ios::in);
192
193 if (!ConfigFile.is_open())
194 {
195 //try to create one for the default board 0
196 ConfigFile.close();
197 _log.Log(LOG_ERROR,"PiFace: Error config file: %s not found", configfile.c_str() );
198 _log.Log(LOG_ERROR,"PiFace: Auto creating for board 0");
199 LoadDefaultConfig();
200 AutoCreate_piface_config();
201 std::fstream ConfigFile(configfile.c_str(), std::ios::in);
202 }
203
204
205 if (ConfigFile.is_open())
206 {
207 while (ConfigFile.good()==true)
208 {
209 do
210 {
211 StartPos=-1;
212 EndPos=-1;
213 getline(ConfigFile,input,'\n'); //get line from config file
214 Line=preprocess(input);
215
216 //find any comments
217 StartPos=Line.find("//",0);
218 if (StartPos>=0)
219 {
220 //comment found, remove it
221 Line=Line.substr(0,StartPos);
222 StartPos=-1; // reset marker
223 }
224
225 //find linesync
226 EndPos=GetParameterString(Line,".",0,Parameter);
227 if (EndPos>=0)
228 {
229 if (Parameter.compare("piface")==0)
230 {
231 StartPos=EndPos;
232 }
233 }
234 }
235 while ((StartPos<0) && (ConfigFile.good()==true));
236
237 if (StartPos > 0)
238 {
239 //first lets get PiFace Address
240 EndPos=GetParameterString(Line,".",StartPos,Parameter);
241 if (EndPos>=0)
242 {
243 Address=(unsigned char)(strtol(Parameter.c_str(),NULL,0) & 0xFF); //data can be restricted further but as we check later on keep it wide for future use.
244 StartPos=EndPos;
245 }
246
247 //Now lets get Port Type
248 EndPos=GetParameterString(Line,".",StartPos,Parameter);
249 if (EndPos>=0)
250 {
251 if ( Parameter.compare("input") == 0)
252 {
253 // we have an input
254 PortType='I';
255 }
256 else if ( Parameter.compare("output") == 0)
257 {
258 // we have an output
259 PortType='O';
260 }
261
262 StartPos=EndPos;
263 }
264
265 //Now lets get Pinnumber
266 EndPos=GetParameterString(Line,".",StartPos,Parameter);
267 if (EndPos>=0)
268 {
269 PinNumber=(unsigned char)(strtol(Parameter.c_str(),NULL,0) & 0xFF); //data can be restricted further but as we check later on keep it wide for future use.
270 StartPos=EndPos;
271 }
272
273 //Now lets get parameter name
274 EndPos=GetParameterString(Line,"=",StartPos,Parameter);
275 if (EndPos>=0)
276 {
277 Parametername=Parameter;
278 StartPos=EndPos;
279 }
280
281 //finaly lets get parameter value
282 Parametervalue=Line.substr(StartPos,Line.length()-StartPos);
283 Parametervalue=preprocess(Parametervalue);
284
285 if ((Address <= 3) && (PinNumber <= 7) && ((PortType=='I') || (PortType=='O')) && (Parametername.length() >0 ) && (Parametervalue.length() >0 ))
286 {
287 _log.Log(LOG_STATUS,"PiFace: config file: Valid address: %d , Pin: %d and Port %c Parameter: %s , Value %s",Address,PinNumber,PortType,Parametername.c_str(),Parametervalue.c_str());
288 NameFound=LocateValueInParameterArray(Parametername,ParameterNames,CONFIG_NR_OF_PARAMETER_TYPES);
289
290 if (PortType=='I')
291 {
292 IOport=&m_Inputs[Address];
293 m_Inputs[Address].Pin[PinNumber].Direction = 'I';
294 }
295 else {
296 IOport=&m_Outputs[Address];
297 m_Outputs[Address].Pin[PinNumber].Direction = 'O';
298 }
299
300 result=0;
301
302 switch (NameFound)
303 {
304 default:
305 _log.Log(LOG_ERROR,"PiFace: Error config file: unknown parameter %s found", Parametername.c_str() );
306 break;
307 case 0:
308 case 1:
309 //found enable(d)
310 ValueFound=LocateValueInParameterArray(Parametervalue,ParameterBooleanValueNames,CONFIG_NR_OF_PARAMETER_BOOL_TYPES);
311 if (ValueFound >=0)
312 {
313 if ((ValueFound == 0) || (ValueFound == 1))
314 {
315 IOport->Pin[PinNumber].Enabled=false;
316 }
317 else {
318 IOport->Pin[PinNumber].Enabled=true;
319 }
320 result++;
321 }
322 else _log.Log(LOG_ERROR,"PiFace: Error config file: unknown value %s found", Parametervalue.c_str() );
323 break;
324
325 case 2:
326 //found pintype
327 ValueFound=LocateValueInParameterArray(Parametervalue,ParameterPinTypeValueNames,CONFIG_NR_OF_PARAMETER_PIN_TYPES);
328 switch ( ValueFound )
329 {
330 default:
331 _log.Log(LOG_ERROR,"PiFace: Error config file: unknown value %s found =>setting default level %d", Parametervalue.c_str(),ValueFound );
332 IOport->Pin[PinNumber].Type=LEVEL;
333 break;
334
335 case 0:
336 IOport->Pin[PinNumber].Type=LEVEL;
337 break;
338 case 1:
339 IOport->Pin[PinNumber].Type=INV_LEVEL;
340 break;
341 case 2:
342 IOport->Pin[PinNumber].Type=TOGGLE_RISING;
343 break;
344 case 3:
345 IOport->Pin[PinNumber].Type=TOGGLE_FALLING;
346 break;
347 }
348 result++;
349 break;
350
351 case 3:
352 case 4:
353 //found count_enable(d)
354 ValueFound=LocateValueInParameterArray(Parametervalue,ParameterBooleanValueNames,CONFIG_NR_OF_PARAMETER_BOOL_TYPES);
355
356 if (ValueFound >=0)
357 {
358 if ((ValueFound == 0) || (ValueFound == 1))
359 {
360 IOport->ConfigureCounter(PinNumber,false);
361 }
362 else {
363 IOport->ConfigureCounter(PinNumber,true);
364 }
365 result++;
366 }
367 else _log.Log(LOG_ERROR,"PiFace: Error config file: unknown value %s found", Parametervalue.c_str() );
368 break;
369
370 case 5:
371 case 6:
372 case 7:
373 //count_update_interval(_s)(ec)
374 unsigned long UpdateInterval;
375
376 UpdateInterval=strtol(Parametervalue.c_str(),NULL,0);
377 IOport->Pin[PinNumber].Count.SetUpdateInterval(UpdateInterval*1000);
378 result++;
379 break;
380
381 case 8:
382 //count_update_interval_diff_perc
383 unsigned long UpdateIntervalPerc;
384
385 UpdateIntervalPerc=strtol(Parametervalue.c_str(),NULL,0);
386 if ( UpdateIntervalPerc < 1 || UpdateIntervalPerc > 1000 )
387 {
388 _log.Log(LOG_ERROR,"PiFace: Error config file: invalid value %s found", Parametervalue.c_str());
389 break;
390 }
391 IOport->Pin[PinNumber].Count.SetUpdateIntervalPerc(UpdateIntervalPerc);
392 result++;
393
394 #ifndef DISABLE_NEW_FUNCTIONS
395 /* disabled until code part is completed and tested */
396 case 9:
397 //count_initial_value
398 unsigned long StartValue;
399
400 StartValue=strtol(Parametervalue.c_str(),NULL,0);
401 IOport->Pin[PinNumber].Count.SetTotal(StartValue);
402 result++;
403 Regenerate_Config=true;
404 break;
405 #endif
406 case 10:
407 //count_minimum_pulse_period_msec
408 unsigned long Min_Pulse_Period;
409
410 Min_Pulse_Period=strtol(Parametervalue.c_str(),NULL,0); // results in 0 if str is invalid
411 IOport->Pin[PinNumber].Count.SetRateLimit(Min_Pulse_Period);
412 result++;
413 break;
414
415 case 11:
416 //count_type
417 ValueFound=LocateValueInParameterArray(Parametervalue,ParameterCountTypeValueNames,CONFIG_NR_OF_PARAMETER_COUNT_TYPES);
418 switch (ValueFound )
419 {
420 default:
421 _log.Log(LOG_ERROR,"PiFace: Error config file: unknown value %s found", Parametervalue.c_str());
422 break;
423
424 case 0: // generic
425 IOport->Pin[PinNumber].Count.Type = COUNT_TYPE_GENERIC;
426 break;
427 case 1: // rfxmeter
428 IOport->Pin[PinNumber].Count.Type = COUNT_TYPE_RFXMETER;
429 break;
430 case 2: // energy
431 IOport->Pin[PinNumber].Count.Type = COUNT_TYPE_ENERGY;
432 break;
433 }
434 result++;
435 break;
436
437 case 12:
438 //count_divider
439 IOport->Pin[PinNumber].Count.SetDivider(strtol(Parametervalue.c_str(), NULL, 0));
440 result++;
441 break;
442 }
443 }
444 else _log.Log(LOG_ERROR,"PiFace: Error config file: misformed config line %s found", Line.c_str() );
445 }
446 }
447 ConfigFile.close();
448 #ifndef DISABLE_NEW_FUNCTIONS
449 if (Regenerate_Config)
450 {
451 Regenerate_Config=false;
452 _log.Log(LOG_ERROR,"PiFace: We got an initial value setting, so we are now recreating the logfile to avoid looping", configfile.c_str() );
453 AutoCreate_piface_config();
454 }
455 #endif
456 }
457 else {
458 _log.Log(LOG_ERROR,"PiFace: Error PiFace config file: %s not found", configfile.c_str() );
459 _log.Log(LOG_ERROR,"PiFace: loading defaults for PiFace(s)");
460 LoadDefaultConfig();
461 AutoCreate_piface_config();
462 }
463 return result;
464 }
465
LoadDefaultConfig(void)466 void CPiFace::LoadDefaultConfig(void)
467 {
468 for (int i=0; i<=3 ;i++)
469 {
470 for (int PinNr=0; PinNr<=7 ;PinNr++)
471 {
472 if (PinNr < 4)
473 {
474 m_Inputs[i].Pin[PinNr].Enabled=true;
475 }
476 m_Inputs[i].Pin[PinNr].Type=TOGGLE_RISING;
477 m_Inputs[i].Pin[PinNr].Count.SetUpdateInterval(10000);
478 m_Inputs[i].Pin[PinNr].Direction = 'I';
479
480 if (PinNr >= 4)
481 {
482 m_Inputs[i].ConfigureCounter(PinNr,true);
483 }
484 m_Outputs[i].ConfigureCounter(PinNr,true);
485 m_Outputs[i].Pin[PinNr].Type=LEVEL;
486 m_Outputs[i].Pin[PinNr].Count.SetUpdateInterval(10000);
487 m_Outputs[i].Pin[PinNr].Direction = 'O';
488 }
489 }
490 }
491
AutoCreate_piface_config(void)492 void CPiFace::AutoCreate_piface_config(void)
493 {
494 char const * const explanation[] = {
495 "//piface.conf [Autogenerated]\r\n",
496 "//This file is part of the PiFace driver (written by Robin Stevenhagen) for Domoticz, and allows for more advanced pin configuration options\r\n",
497 "//This file can optionally hold all the configuration for all attached PiFace boards.\r\n",
498 "//Please note that if this file is found the default config is discarded, and only the configuration in this file is used, all IO that is not specified will\r\n/",
499 "//be configured as not used.\r\n",
500 "//\r\n",
501 "// Configuration line syntax:\r\n",
502 "// piface.<board address>.<I/O>.<pin number>.<parameter>=<value>\r\n",
503 "//\r\n",
504 "// <board address>: Check piface (JP1 & JP2) jumper settings, value between 0..3\r\n",
505 "//\r\n",
506 "// <I/O>: input\r\n",
507 "// output\r\n",
508 "//\r\n",
509 "// <pinnumber>: value between 0..7\r\n",
510 "//\r\n",
511 "// <parameter>: enabled\r\n",
512 "// pin_type\r\n",
513 "// count_enabled\r\n",
514 "// count_type\r\n",
515 "// count_divider\r\n",
516 "// count_update_interval_sec\r\n",
517 "// count_update_interval_diff_perc\r\n",
518 #ifndef DISABLE_NEW_FUNCTIONS
519 "// count_initial_value\r\n",
520 #endif
521 "// count_minimum_pulse_period\r\n",
522 "//\r\n",
523 "// Available <parameter> / <values>:\r\n",
524 "// enabled: true or 1\r\n",
525 "// false or 0\r\n",
526 "//\r\n",
527 "// pin_type: level // best to keep this parameter to level for outputs, as Domoticz does not like other values\r\n",
528 "// inv_level\r\n",
529 "// rising\r\n",
530 "// falling\r\n",
531 "//\r\n",
532 "// count_enabled: true or 1\r\n",
533 "// false or 0\r\n",
534 "//\r\n",
535 "// count_type: generic or rfxmeter // default\r\n",
536 "// energy\r\n",
537 "//\r\n",
538 "// count_divider: 1000 // default\r\n",
539 "// Pulses per unit.\r\n",
540 "//\r\n",
541 "// count_update_interval_sec: 0 to 3600\r\n",
542 "// count_update_interval_diff_perc: 1 to 1000\r\n",
543 "// This parameter can be used to force trigger an update when the difference in pulse intervals is above a certain percentage.\r\n",
544 "// The value is a percentage of the previous interval. This can be useful for energy counters to quickly report large fluctuations\r\n",
545 "// in active power.\r\n",
546 "//\r\n",
547 #ifndef DISABLE_NEW_FUNCTIONS
548 "// count_initial_value: 0 to 4294967295 counts\r\n",
549 "// Note1: The initial_value will only be used once. After it has been set, a new piface.conf will automatically be generated without the initial_value parameter(s)\r\n",
550 "// to ensure that it will only be used once.\r\n",
551 "// Using the initial_value to set a counter to a desired (start) value, will result in spikes in the Domoticz graphs.\r\n",
552 "// Note2: The initial_value is in counter counts, Domoticz scales (divides) this by the factor that is set in the GUI settings \r\n",
553 "//\r\n",
554 #endif
555 "// count_minimum_pulse_period_msec: 0 to 4294967295 milliseconds\r\n",
556 "// This parameter sets a pulse rate limiter.\r\n",
557 "// If multiple pulses are detected within the specified time period, they are ignored until the specified idle time has been met.\r\n",
558 "// This can be useful for opto reflective sensors that count slow moving dials. Like water meters or gas meters,\r\n",
559 "// where the dial can stop on an opto barrier and cause oscillations (and unwanted counts).\r\n",
560 "// 0 = off \r\n",
561 "// < 100 msec times will not be very effective.\r\n",
562 "//\r\n",
563 NULL
564 };
565
566 char configline[100];
567 std::string ValueText;
568 std::string PortType;
569 int Value;
570 CIOPort *IOport;
571
572 std::string configfile=szUserDataFolder + "piface.conf";
573 std::fstream ConfigFile(configfile.c_str(), std::ios::out);
574
575 int total_explanations = sizeof(explanation);
576
577 if (ConfigFile.is_open())
578 {
579 int i = 0;
580 while (explanation[i]!=NULL)
581 {
582 ConfigFile.write(explanation[i],strlen(explanation[i]));
583 i++;
584 }
585
586 for (int BoardNr=0; BoardNr< 1 ;BoardNr++) // Note there could be 4 boards, but this will create a lot of entires in the configfile
587 {
588 for (int PinNr=0; PinNr<=7 ;PinNr++)
589 {
590 for (int IOType=0; IOType <= 1 ; IOType++)
591 {
592 if (IOType==0)
593 {
594 PortType="output";
595 IOport=&m_Outputs[BoardNr];
596 }
597 else {
598 PortType="input";
599 IOport=&m_Inputs[BoardNr];
600 }
601
602 if (IOport->Pin[PinNr].Enabled)
603 ValueText="true";
604 else ValueText="false";
605 sprintf(configline,"piface.%d.%s.%d.enabled=%s\r\n",BoardNr,PortType.c_str(),PinNr,ValueText.c_str());
606 ConfigFile.write(configline,strlen(configline));
607
608 ValueText=ParameterPinTypeValueNames[IOport->Pin[PinNr].Type];
609 sprintf(configline,"piface.%d.%s.%d.pin_type=%s\r\n",BoardNr,PortType.c_str(),PinNr,ValueText.c_str());
610 ConfigFile.write(configline,strlen(configline));
611
612 if (IOport->Pin[PinNr].Count.Enabled)
613 ValueText="true";
614 else ValueText="false";
615 sprintf(configline,"piface.%d.%s.%d.count_enabled=%s\r\n",BoardNr,PortType.c_str(),PinNr,ValueText.c_str());
616 ConfigFile.write(configline,strlen(configline));
617
618 ValueText=ParameterCountTypeValueNames[IOport->Pin[PinNr].Count.Type];
619 sprintf(configline,"piface.%d.%s.%d.count_type=%s\r\n",BoardNr,PortType.c_str(),PinNr,ValueText.c_str());
620 ConfigFile.write(configline,strlen(configline));
621
622 if (
623 IOport->Pin[PinNr].Count.Type != COUNT_TYPE_GENERIC
624 && IOport->Pin[PinNr].Count.Type != COUNT_TYPE_RFXMETER
625 ) {
626 Value=IOport->Pin[PinNr].Count.GetDivider();
627 sprintf(configline,"piface.%d.%s.%d.count_divider=%d\r\n",BoardNr,PortType.c_str(),PinNr,Value);
628 ConfigFile.write(configline,strlen(configline));
629 }
630
631 Value=IOport->Pin[PinNr].Count.GetUpdateInterval()/1000;
632 sprintf(configline,"piface.%d.%s.%d.count_update_interval_sec=%d\r\n",BoardNr,PortType.c_str(),PinNr,Value);
633 ConfigFile.write(configline,strlen(configline));
634
635 Value=IOport->Pin[PinNr].Count.GetUpdateIntervalPerc();
636 if ( Value > 0 ) {
637 sprintf(configline,"piface.%d.%s.%d.count_update_interval_diff_perc=%d\r\n",BoardNr,PortType.c_str(),PinNr,Value);
638 ConfigFile.write(configline,strlen(configline));
639 }
640
641 Value=IOport->Pin[PinNr].Count.GetRateLimit();
642 if ( Value > 0 ) {
643 sprintf(configline,"piface.%d.%s.%d.count_minimum_pulse_period_msec=%d\r\n",BoardNr,PortType.c_str(),PinNr,Value);
644 ConfigFile.write(configline,strlen(configline));
645 }
646
647 sprintf(configline,"\r\n");
648 ConfigFile.write(configline,strlen(configline));
649 }
650 }
651 }
652
653 }
654 ConfigFile.close();
655 }
656
657 /***** end of config file stuff *****/
658
CallBackSendEvent(const unsigned char * pEventPacket,const unsigned int PacketLength)659 void CPiFace::CallBackSendEvent(const unsigned char *pEventPacket, const unsigned int PacketLength)
660 {
661 std::string sendData;
662 sendData.insert(sendData.begin(), pEventPacket, pEventPacket + PacketLength);
663 std::lock_guard<std::mutex> l(m_queue_mutex);
664 if (m_send_queue.size() < 100)
665 m_send_queue.push_back(sendData);
666 else
667 _log.Log(LOG_ERROR, "PiFace: to much messages on queue!");
668 }
669
CallBackSetPinInterruptMode(unsigned char devId,unsigned char pinID,bool Interrupt_Enable)670 void CPiFace::CallBackSetPinInterruptMode(unsigned char devId,unsigned char pinID, bool Interrupt_Enable)
671 {
672 unsigned char Cur_Int_Enable_State;
673 unsigned char pinmask;
674 //make sure that we clear the interrupts states before we update the port
675 Read_MCP23S17_Register (devId,MCP23x17_INTFB);
676 Read_MCP23S17_Register (devId,MCP23x17_INTCAPB);
677
678 pinmask=1;
679 pinmask<<=pinID;
680 Cur_Int_Enable_State=Read_MCP23S17_Register (devId,MCP23x17_GPINTENB);
681
682 if (Interrupt_Enable)
683 Cur_Int_Enable_State|=pinmask; //enable pin interrupt
684 else Cur_Int_Enable_State&=~pinmask; //disable pin interrupt
685
686 Write_MCP23S17_Register (devId, MCP23x17_GPINTENB, Cur_Int_Enable_State);
687
688 //_log.Log(LOG_NORM,"PiFace: SetPin Interrupt mode: devid: %d, Pinnr: %d, Enable %d-- Prev 0x%02X Cur 0x%02X",devId,pinID,Interrupt_Enable,Prev_Int_Enable_State,Cur_Int_Enable_State);
689 }
690
691
StartHardware()692 bool CPiFace::StartHardware()
693 {
694 StopHardware();
695
696 RequestStart();
697 m_TaskQueue.RequestStart();
698
699 m_InputSample_waitcntr=(PIFACE_INPUT_PROCESS_INTERVAL)*20;
700 m_CounterEdgeSample_waitcntr=(PIFACE_COUNTER_COUNTER_INTERVAL)*20;
701
702 memset(m_DetectedHardware,0,sizeof(m_DetectedHardware));
703
704 #ifndef DISABLE_NEW_FUNCTIONS
705 LoadConfig();
706 #endif
707 //Start worker thread
708 if (Init_SPI_Device(1) > 0)
709 {
710 if (Detect_PiFace_Hardware() > 0)
711 {
712 //we have hardware, so lets use it
713 for (int devId = 0; devId < 4; devId++)
714 {
715 if (m_DetectedHardware[devId] == true)
716 Init_Hardware(devId);
717 }
718 #ifdef DISABLE_NEW_FUNCTIONS
719 LoadConfig();
720 #endif
721
722 for (int devId = 0; devId < 4; devId++)
723 {
724 if (m_DetectedHardware[devId] == true)
725 GetAndSetInitialDeviceState(devId);
726 }
727
728 m_thread = std::make_shared<std::thread>(&CPiFace::Do_Work, this);
729 SetThreadNameInt(m_thread->native_handle());
730 m_queue_thread = std::make_shared<std::thread>(&CPiFace::Do_Work_Queue, this);
731 SetThreadName(m_queue_thread->native_handle(), "PiFaceQueue");
732 }
733 else
734 {
735 return false;
736 }
737 }
738 else
739 {
740 return false;
741 }
742 m_bIsStarted=true;
743 sOnConnected(this);
744 return (m_thread != nullptr);
745 }
746
StopHardware()747 bool CPiFace::StopHardware()
748 {
749 RequestStop();
750 m_TaskQueue.RequestStop();
751
752 if (m_thread)
753 {
754 m_thread->join();
755 m_thread.reset();
756 }
757
758 if (m_queue_thread)
759 {
760 m_queue_thread->join();
761 m_queue_thread.reset();
762 }
763 #ifdef HAVE_LINUX_SPI
764 if (m_fd > 0) {
765 close(m_fd);
766 m_fd = 0;
767 }
768 #endif
769 m_bIsStarted=false;
770 return true;
771 }
772
WriteToHardware(const char * pdata,const unsigned char length)773 bool CPiFace::WriteToHardware(const char *pdata, const unsigned char length)
774 {
775 const tRBUF *SendData = reinterpret_cast<const tRBUF*>(pdata);
776 int mask=0x01;
777 unsigned char CurrentLatchState;
778 unsigned char OutputData;
779 unsigned char PortType=' ';
780
781 int devId =0;
782 int pinnr =0;
783
784 if ((SendData->LIGHTING1.packettype == pTypeLighting1) && (SendData->LIGHTING1.subtype == sTypeIMPULS))
785 {
786 PortType=(SendData->LIGHTING1.housecode);
787 devId=(SendData->LIGHTING1.unitcode /10)&0x03;
788 pinnr =(SendData->LIGHTING1.unitcode %10)&0x07;
789 if (PortType == 'O')
790 {
791 //_log.Log(LOG_NORM,"Piface: WriteToHardware housecode %c, packetlength %d", SendData->LIGHTING1.housecode,SendData->LIGHTING1.packetlength );
792 CurrentLatchState = Read_MCP23S17_Register(devId, MCP23x17_OLATA);
793 //_log.Log(LOG_NORM,"PiFace: Read input state 0x%02X", m_OutputState[devId].Current);
794
795 OutputData = CurrentLatchState;
796 mask <<= pinnr;
797
798 if (SendData->LIGHTING1.cmnd == light1_sOff)
799 {
800 OutputData &= ~mask;
801 }
802 else OutputData |= mask;
803
804 Write_MCP23S17_Register(devId, MCP23x17_GPIOA, OutputData);
805 // _log.Log(LOG_NORM,"Piface: WriteToHardware housecode %c, devid %d, output %d, PrevOut 0x%02X, Set 0x%02X",PortType, devId, pinnr, CurrentLatchState,OutputData );
806 }
807 else
808 {
809 _log.Log(LOG_ERROR, "Piface: wrong housecode %c", PortType);
810 return false;
811 }
812 }
813 else
814 {
815 _log.Log(LOG_ERROR, "PiFace: WriteToHardware packet type %d or subtype %d unknown", SendData->LIGHTING1.packettype, SendData->LIGHTING1.subtype);
816 return false;
817 }
818 return true;
819 }
820
Do_Work()821 void CPiFace::Do_Work()
822 {
823 int devId;
824 _log.Log(LOG_STATUS,"PiFace: Worker started...");
825 int msec_counter = 0;
826 int sec_counter = 0;
827 while (!IsStopRequested(PIFACE_WORKER_THREAD_SLEEP_INTERVAL_MS))
828 {
829 msec_counter++;
830 if (msec_counter == (1000 / PIFACE_WORKER_THREAD_SLEEP_INTERVAL_MS))
831 {
832 msec_counter = 0;
833 sec_counter++;
834 if (sec_counter % 12 == 0) {
835 m_LastHeartbeat = mytime(NULL);
836 }
837 }
838
839 m_InputSample_waitcntr++;
840 m_CounterEdgeSample_waitcntr++;
841
842 //sample interrupt states faster for edge detection on count
843 if (m_CounterEdgeSample_waitcntr>=PIFACE_COUNTER_COUNTER_INTERVAL)
844 {
845 m_CounterEdgeSample_waitcntr=0;
846 for (devId=0; devId<4; devId++)
847 {
848 Sample_and_Process_Input_Interrupts(devId);
849 }
850 }
851
852 //sample pin states more slowly to avoid spamming Domoticz
853 if (m_InputSample_waitcntr>=PIFACE_INPUT_PROCESS_INTERVAL)
854 {
855 m_InputSample_waitcntr=0;
856 for (devId=0; devId<4; devId++)
857 {
858 Sample_and_Process_Inputs(devId);
859 Sample_and_Process_Outputs(devId);
860 }
861 }
862 }
863 _log.Log(LOG_STATUS,"PiFace: Worker stopped...");
864 }
865
Do_Work_Queue()866 void CPiFace::Do_Work_Queue()
867 {
868 std::vector<std::string>::iterator itt;
869 while (!m_TaskQueue.IsStopRequested(100))
870 {
871 if (m_send_queue.empty())
872 continue;
873
874 std::string sendData;
875 m_queue_mutex.lock();
876 itt = m_send_queue.begin();
877 sendData = *itt;
878 m_send_queue.erase(itt);
879 m_queue_mutex.unlock();
880 sDecodeRXMessage(this, (const unsigned char*)sendData.c_str(), NULL, 255);
881 }
882 }
883
884 // Open a connection to the piface
885 // Returns a file id, or -1 if not open/error
Init_SPI_Device(int Init)886 int CPiFace::Init_SPI_Device(int Init)
887 {
888 m_fd=0;
889 int result=-1;
890 unsigned char spiMode = 0 ;
891 unsigned char spiBPW = 8 ;
892 int speed = 4000000 ;
893
894 _log.Log(LOG_STATUS,"PiFace: Starting PiFace_SPI_Start()");
895 #ifdef HAVE_LINUX_SPI
896 // Open port for reading and writing
897 if ((m_fd = open("/dev/spidev0.0", O_RDWR)) >= 0)
898 {
899 // Set the port options and set the address of the device
900 if (ioctl (m_fd, SPI_IOC_WR_MODE, &spiMode) >= 0)
901 {
902 if (ioctl (m_fd, SPI_IOC_WR_BITS_PER_WORD, &spiBPW) >= 0)
903 {
904 if (ioctl (m_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) >= 0)
905 {
906 result=1;
907 //we are successfull
908 _log.Log(LOG_NORM,"PiFace: SPI device opened successfully");
909
910 }
911 else
912 _log.Log(LOG_NORM,"PiFace: SPI Speed Change failure: %s", strerror (errno)) ;
913 }
914 else
915 _log.Log(LOG_NORM,"PiFace: SPI BPW Change failure: %s", strerror (errno)) ;
916
917 }
918 else
919 _log.Log(LOG_NORM,"PiFace: SPI Mode Change failure: %s", strerror (errno)) ;
920 }
921 else
922 _log.Log(LOG_NORM,"PiFace: Unable to open SPI device: %s", strerror (errno));
923
924 #endif
925
926 if (result == -1)
927 {
928 #ifdef HAVE_LINUX_SPI
929 close(m_fd);
930 #endif
931 return -1;
932 }
933 else return m_fd;
934 }
935
Detect_PiFace_Hardware(void)936 int CPiFace::Detect_PiFace_Hardware(void)
937 {
938 int NrOfFoundBoards=0;
939 unsigned char read_iocon;
940 unsigned char read_ioconb;
941 int devId;
942
943 //first write to all possible PiFace addresses.
944 for (devId=0; devId<4; devId++)
945 {
946 Write_MCP23S17_Register (devId, MCP23x17_IOCON, IOCON_INIT | IOCON_HAEN) ;
947 Write_MCP23S17_Register (devId, MCP23x17_IOCONB, IOCON_INIT | IOCON_HAEN) ;
948 }
949
950 //now read them back, to determine if they are present
951
952 for (devId=0; devId<4; devId++)
953 {
954 read_iocon=Read_MCP23S17_Register (devId, MCP23x17_IOCON);
955 read_ioconb=Read_MCP23S17_Register (devId, MCP23x17_IOCONB);
956
957 if ((read_iocon == ( IOCON_INIT | IOCON_HAEN)) && (read_ioconb == ( IOCON_INIT | IOCON_HAEN)))
958 {
959 //hé someone appears to be home...
960 m_DetectedHardware[devId]=true;
961 NrOfFoundBoards++;
962 }
963 }
964
965 if (NrOfFoundBoards)
966 {
967 _log.Log(LOG_STATUS,"PiFace: Found the following PiFaces:");
968 for (devId=0; devId<4; devId++)
969 {
970 if (m_DetectedHardware[devId]==true)
971 _log.Log(LOG_STATUS,"PiFace: %d",devId);
972 }
973 }
974 else _log.Log(LOG_STATUS,"PiFace: Sorry, no PiFaces were found");
975 return NrOfFoundBoards;
976 }
977
Init_Hardware(unsigned char devId)978 void CPiFace::Init_Hardware(unsigned char devId)
979 {
980 Write_MCP23S17_Register (devId, MCP23x17_IOCON, IOCON_INIT | IOCON_HAEN) ;
981 Write_MCP23S17_Register (devId, MCP23x17_IOCONB, IOCON_INIT | IOCON_HAEN) ;
982
983 //setup PortA as output
984 Write_MCP23S17_Register (devId, MCP23x17_IODIRA, 0x00) ; //set all pins on Port A as output
985
986 //lets init the other registers so we always have a clear startup situation
987 Write_MCP23S17_Register (devId, MCP23x17_GPINTENA, 0x00); //The PiFace does not support the interrupt capabilities, so set it to 0, and useless for output.
988 Write_MCP23S17_Register (devId, MCP23x17_DEFVALA, 0x00); //Default compare value register, useless for output
989 Write_MCP23S17_Register (devId, MCP23x17_INTCONA, 0x00); //Interrupt based on current and prev pin state, not Default value
990 //Write_MCP23S17_Register (devId, MCP23x17_INTFA, 0); // Read only interrupt flag register,not used on output
991 //Write_MCP23S17_Register (devId, MCP23x17_INTCAPA, ); // Read only interrupt status register, captures port status on interrupt, not ued on output
992 Write_MCP23S17_Register (devId, MCP23x17_OLATA, 0xFF); // Set the output latches to 1, otherwise the relays are activated
993
994 //setup PortB as input
995 Write_MCP23S17_Register (devId, MCP23x17_IODIRB, 0xFF) ; //set all pins on Port B as input
996 Write_MCP23S17_Register (devId, MCP23x17_GPPUB, 0xFF) ; //Enable pullup resistors, so the buttons work immediately
997 Write_MCP23S17_Register (devId, MCP23x17_IPOLB, 0xFF) ; //Invert input pin state, so we will see an active state as as 1.
998
999 //lets init the other registers so we always have a clear startup situation
1000 Write_MCP23S17_Register (devId, MCP23x17_GPINTENB, 0x00); //The PiFace does not support the interrupt capabilities, so set it to 0.
1001 //Todo: use the int detection for small pulses
1002 Write_MCP23S17_Register (devId, MCP23x17_DEFVALB, 0x00); //Default compare value register, we do not use it (yet), but lets set it to 0 (assuming not active state).
1003 Write_MCP23S17_Register (devId, MCP23x17_INTCONB, 0x00); //Interrupt based on current and prev pin state, not Default value
1004 //Write_MCP23S17_Register (devId, MCP23x17_INTFB, 0); // Read only interrupt flag register, listed as a reminder
1005 //Write_MCP23S17_Register (devId, MCP23x17_INTCAPB, ); // Read only interrupt status register, captures port status on interrupt, listed as a reminder
1006 Write_MCP23S17_Register (devId, MCP23x17_OLATB, 0x00); // Set the output latches to 0, note: not used.
1007
1008 Write_MCP23S17_Register (devId, MCP23x17_GPIOA, 0x00) ; //set all pins on Port A as output, and deactivate
1009 }
1010
GetAndSetInitialDeviceState(int devId)1011 void CPiFace::GetAndSetInitialDeviceState(int devId)
1012 {
1013 unsigned char PortState;
1014 PortState= Read_MCP23S17_Register (devId, MCP23x17_OLATA) ;
1015 m_Outputs[devId].Init(true,m_HwdID,devId,'O',PortState);
1016
1017 PortState= Read_MCP23S17_Register (devId, MCP23x17_GPIOB) ;
1018 m_Inputs[devId].Init(true,m_HwdID,devId,'I',PortState);
1019 }
1020
Read_Write_SPI_Byte(unsigned char * data,int len)1021 int CPiFace::Read_Write_SPI_Byte(unsigned char *data, int len)
1022 {
1023 #ifdef HAVE_LINUX_SPI
1024 struct spi_ioc_transfer spi ;
1025 memset (&spi, 0, sizeof(spi));
1026
1027 spi.tx_buf = (unsigned long)data ;
1028 spi.rx_buf = (unsigned long)data ;
1029 spi.len = len ;
1030 spi.delay_usecs = 0 ;
1031 spi.speed_hz = 4000000;
1032 spi.bits_per_word = 8 ;
1033
1034 return ioctl (m_fd, SPI_IOC_MESSAGE(1), &spi) ;
1035 #else
1036 return 0;
1037 #endif
1038 }
1039
Read_MCP23S17_Register(unsigned char devId,unsigned char reg)1040 int CPiFace::Read_MCP23S17_Register(unsigned char devId, unsigned char reg)
1041 {
1042 unsigned char spiData [4] ;
1043
1044 spiData [0] = CMD_READ | ((devId & 7) << 1);
1045 spiData [1] = reg ;
1046
1047 Read_Write_SPI_Byte(spiData, 3);
1048
1049 return spiData [2];
1050 }
1051
Write_MCP23S17_Register(unsigned char devId,unsigned char reg,unsigned char data)1052 int CPiFace::Write_MCP23S17_Register(unsigned char devId, unsigned char reg, unsigned char data)
1053 {
1054 unsigned char spiData [4] ;
1055
1056 spiData [0] = CMD_WRITE | ((devId & 7) << 1) ;
1057 spiData [1] = reg ;
1058 spiData [2] = data ;
1059
1060 return (Read_Write_SPI_Byte( spiData, 3)) ;
1061 }
1062
Sample_and_Process_Inputs(unsigned char devId)1063 void CPiFace::Sample_and_Process_Inputs(unsigned char devId)
1064 {
1065 unsigned char PortState;
1066
1067 if ( m_Inputs[devId].IsDevicePresent())
1068 {
1069 PortState=Read_MCP23S17_Register (devId, MCP23x17_GPIOB);
1070 m_Inputs[devId].Update(PortState);
1071 }
1072 }
1073
Sample_and_Process_Outputs(unsigned char devId)1074 void CPiFace::Sample_and_Process_Outputs(unsigned char devId)
1075 {
1076 unsigned char PortState;
1077
1078 if ( m_Outputs[devId].IsDevicePresent())
1079 {
1080 PortState=Read_MCP23S17_Register (devId, MCP23x17_GPIOA);
1081 m_Outputs[devId].Update(PortState);
1082 }
1083 }
1084
Sample_and_Process_Input_Interrupts(unsigned char devId)1085 void CPiFace::Sample_and_Process_Input_Interrupts(unsigned char devId)
1086 {
1087 unsigned char PortInterruptState;
1088 unsigned char PortState;
1089
1090 if ( m_Inputs[devId].IsDevicePresent())
1091 {
1092 PortInterruptState=Read_MCP23S17_Register(devId, MCP23x17_INTFB);
1093 PortState=Read_MCP23S17_Register(devId, MCP23x17_INTCAPB);
1094 m_Inputs[devId].UpdateInterrupt(PortInterruptState,PortState);
1095 }
1096 }
1097
SetUpdateInterval(unsigned long NewValue_ms)1098 void CIOCount::SetUpdateInterval(unsigned long NewValue_ms)
1099 {
1100 UpdateInterval_ms=NewValue_ms;
1101 UpdateDownCount_ms=UpdateInterval_ms;
1102 }
1103
ProcessUpdateInterval(unsigned long PassedTime_ms)1104 bool CIOCount::ProcessUpdateInterval(unsigned long PassedTime_ms)
1105 {
1106 bool Update=false;
1107 if (Enabled)
1108 {
1109 if (UpdateDownCount_ms > PassedTime_ms)
1110 UpdateDownCount_ms-=PassedTime_ms;
1111 else {
1112 UpdateDownCount_ms=UpdateInterval_ms;
1113 Update=true;
1114 }
1115 if (!InitialStateSent)
1116 {
1117 InitialStateSent=true;
1118 Update=true;
1119 }
1120
1121 boost::posix_time::ptime Now = boost::posix_time::microsec_clock::universal_time();
1122
1123 if (
1124 UpdateIntervalPerc > 0 &&
1125 Cur_Interval.total_milliseconds() > 0 &&
1126 Last_Interval.total_milliseconds() > 0 &&
1127 //rate limit the callbacks to max 1 per sec to prevent spamming the upper layer when power suddenly drops
1128 ( Now - Last_Callback ).total_milliseconds() >= 1000
1129 )
1130 {
1131 if ( Now - Cur_Pulse > Cur_Interval )
1132 {
1133 Cur_Interval = Now - Cur_Pulse;
1134 }
1135
1136 double perc = (100. / Last_Interval.total_milliseconds()) * labs((long)(Cur_Interval.total_milliseconds() - Last_Interval.total_milliseconds()));
1137 if ( perc > UpdateIntervalPerc )
1138 {
1139 UpdateDownCount_ms=UpdateInterval_ms;
1140 Update=true;
1141 }
1142 }
1143 }
1144 return Update;
1145 }
1146
Update(unsigned long Counts)1147 int CIOCount::Update(unsigned long Counts)
1148 {
1149 int result = -1;
1150
1151 Last_Pulse = Cur_Pulse;
1152 Cur_Pulse = boost::posix_time::microsec_clock::universal_time();
1153 Cur_Interval = Cur_Pulse - Last_Pulse;
1154
1155 if (
1156 Enabled && (
1157 Minimum_Pulse_Period_ms == 0 ||
1158 Minimum_Pulse_Period_ms <= Cur_Interval.total_milliseconds()
1159 )
1160 )
1161 {
1162 Total += Counts;
1163 result = 1;
1164 }
1165
1166 return result;
1167 }
1168
1169
CIOCount()1170 CIOCount::CIOCount()
1171 {
1172 ResetTotal();
1173 UpdateInterval_ms = 10000;
1174 UpdateIntervalPerc = 0;
1175 Enabled = false;
1176 Type = COUNT_TYPE_GENERIC;
1177 InitialStateSent = false;
1178 UpdateDownCount_ms = 0;
1179 Minimum_Pulse_Period_ms = 0;
1180 Divider = 1000;
1181 Last_Pulse = boost::posix_time::microsec_clock::universal_time();
1182 Cur_Pulse = boost::posix_time::microsec_clock::universal_time();
1183 Last_Callback = boost::posix_time::microsec_clock::universal_time();
1184 Last_Interval = boost::posix_time::milliseconds(0);
1185 Cur_Interval = boost::posix_time::milliseconds(0);
1186 };
1187
~CIOCount()1188 CIOCount::~CIOCount()
1189 {
1190
1191 };
1192
Update(bool New)1193 int CIOPinState::Update(bool New)
1194 {
1195 int StateChange=-1; //nothing changed
1196
1197 Last=Current;
1198 Current=New;
1199
1200 if (Enabled)
1201 {
1202 switch (Type)
1203 {
1204 default:
1205 case LEVEL:
1206 if ((Last ^ Current) == true)
1207 {
1208 if (Current)
1209 StateChange=1;
1210 else
1211 StateChange=0;
1212 }
1213 break;
1214 case INV_LEVEL: //inverted input
1215 if ((Last ^ Current) == true)
1216 {
1217 if (Current)
1218 StateChange=0;
1219 else
1220 StateChange=1;
1221 }
1222 break;
1223 case TOGGLE_RISING:
1224 if (((Last ^ Current) == true) && (Current == true))
1225 {
1226 if (Toggle)
1227 StateChange=1;
1228 else
1229 StateChange=0;
1230 Toggle=!Toggle;
1231 }
1232 break;
1233 case TOGGLE_FALLING:
1234 if (((Last ^ Current) == true) && (Current == false))
1235 {
1236 if (Toggle)
1237 StateChange=1;
1238 else
1239 StateChange=0;
1240 Toggle=!Toggle;
1241 }
1242 break;
1243 }
1244 }
1245
1246 if (Direction == 'O')
1247 {
1248 if (((Last ^ Current) == true) && (Current == true))
1249 {
1250 Count.Update(1);
1251 }
1252 }
1253
1254 return StateChange;
1255 }
1256
UpdateInterrupt(bool IntFlag,bool PinState)1257 int CIOPinState::UpdateInterrupt(bool IntFlag,bool PinState)
1258 {
1259 int Changed=-1;
1260
1261 if (IntFlag && PinState) //we have a rising edge so count
1262 {
1263 Changed=Count.Update(1);
1264 }
1265 return (Changed);
1266 }
1267
1268
GetInitialState(void)1269 int CIOPinState::GetInitialState(void)
1270 {
1271 int CurState=-1; //Report not enabled
1272
1273 if (Enabled)
1274 {
1275 switch (Type)
1276 {
1277 default:
1278 case LEVEL:
1279 if (Current)
1280 CurState=1;
1281 else
1282 CurState=0;
1283 break;
1284 case INV_LEVEL: //inverted input
1285 if (Current)
1286 CurState=0;
1287 else
1288 CurState=1;
1289 break;
1290 case TOGGLE_RISING:
1291 if (Toggle)
1292 CurState=1;
1293 else
1294 CurState=0;
1295 break;
1296
1297 case TOGGLE_FALLING:
1298 if (Toggle)
1299 CurState=1;
1300 else
1301 CurState=0;
1302 break;
1303 }
1304 }
1305 return CurState;
1306 }
1307
1308
CIOPinState()1309 CIOPinState::CIOPinState()
1310 {
1311 Last=false;
1312 Current=true;
1313 Id=0;
1314 Type=0;
1315 Enabled=false;
1316 Toggle=false;
1317 InitialStateSent=false;
1318 Direction=' ';
1319 };
1320
~CIOPinState()1321 CIOPinState::~CIOPinState()
1322 {
1323
1324 };
1325
Init(bool Available,int hwdId,int devId,unsigned char housecode,unsigned char initial_state)1326 void CIOPort::Init(bool Available, int hwdId, int devId /* 0 - 4 */, unsigned char housecode, unsigned char initial_state)
1327 {
1328 int PinNr;
1329 unsigned long value;
1330 bool found;
1331
1332 std::vector<std::vector<std::string> > result;
1333 std::vector<std::string> resultparts;
1334
1335 Present = Available;
1336
1337 //set generic packet info for LIGHTING1 packet, so we do not have every packet
1338 IOPinStatusPacket.LIGHTING1.packetlength = sizeof(IOPinStatusPacket.LIGHTING1) -1;
1339 IOPinStatusPacket.LIGHTING1.housecode = housecode;//report to user as input
1340 IOPinStatusPacket.LIGHTING1.packettype = pTypeLighting1;
1341 IOPinStatusPacket.LIGHTING1.subtype = sTypeIMPULS;
1342 IOPinStatusPacket.LIGHTING1.rssi = 12;
1343 IOPinStatusPacket.LIGHTING1.seqnbr = 0;
1344
1345 for (PinNr=0;PinNr <= 7;PinNr++)
1346 {
1347 found = false;
1348 switch( Pin[PinNr].Count.Type )
1349 {
1350 case COUNT_TYPE_GENERIC:
1351 case COUNT_TYPE_RFXMETER:
1352 //set generic packet info for RFXMETER packet, so we do not have every packet
1353 Pin[PinNr].IOPinCounterPacket.RFXMETER.packetlength = sizeof(Pin[PinNr].IOPinCounterPacket.RFXMETER) -1;
1354 Pin[PinNr].IOPinCounterPacket.RFXMETER.packettype = pTypeRFXMeter;
1355 Pin[PinNr].IOPinCounterPacket.RFXMETER.subtype = sTypeRFXMeterCount;
1356 Pin[PinNr].IOPinCounterPacket.RFXMETER.rssi = 12;
1357 Pin[PinNr].IOPinCounterPacket.RFXMETER.seqnbr = 0;
1358
1359 int meterid;
1360 if (housecode == 'I')
1361 meterid=100+PinNr + (devId*10);
1362 else
1363 meterid=0+PinNr + (devId*10);
1364
1365 result = m_sql.safe_query("SELECT sValue FROM DeviceStatus WHERE (HardwareID=%d AND Type=%d AND DeviceID='%d')", hwdId, int(pTypeRFXMeter), meterid);
1366 if (result.size() == 1)
1367 {
1368 value = atol(result[0][0].c_str());
1369 found = true;
1370 }
1371 break;
1372
1373 case COUNT_TYPE_ENERGY:
1374 int nodeId;
1375 if (housecode == 'I')
1376 nodeId=300+devId;
1377 else
1378 nodeId=200+devId;
1379
1380 int dID = (nodeId << 8) | PinNr;
1381
1382 result = m_sql.safe_query("SELECT sValue FROM DeviceStatus WHERE (HardwareID=%d AND Type=%d AND DeviceID='%08X')", hwdId, int(pTypeGeneral), dID);
1383 if (result.size() == 1)
1384 {
1385 StringSplit(result[0][0], ";", resultparts);
1386 double dValue = 0;
1387 if (resultparts.size() == 1)
1388 {
1389 dValue = atol(resultparts[0].c_str()) / ( 1000. / Pin[PinNr].Count.GetDivider() );
1390 found = true;
1391 }
1392 else if (resultparts.size() == 2)
1393 {
1394 dValue = atol(resultparts[1].c_str()) / ( 1000. / Pin[PinNr].Count.GetDivider() );
1395 found = true;
1396 }
1397 value = (unsigned long)dValue;
1398 }
1399 break;
1400 }
1401
1402 if ( found ) {
1403 Pin[PinNr].Count.SetTotal(value);
1404 }
1405 }
1406
1407 Last=initial_state;
1408 Current=initial_state;
1409 Update(initial_state);
1410 }
1411
Update(unsigned char New)1412 int CIOPort::Update(unsigned char New)
1413 {
1414 int mask=0x01;
1415 int ChangeState=-1;
1416 bool UpdateCounter=false;
1417
1418 CPiFace *myCallback = reinterpret_cast<CPiFace*>(Callback_pntr);
1419
1420 Last=Current;
1421 Current=New;
1422
1423 for (int PinNr=0; PinNr<=7 ;PinNr++)
1424 {
1425 ChangeState=Pin[PinNr].Update((New&mask)==mask);
1426 if ((ChangeState >=0) || ((!Pin[PinNr].InitialStateSent) && Pin[PinNr].Enabled))
1427 {
1428 Pin[PinNr].InitialStateSent=true;
1429 if (ChangeState <0)
1430 {
1431 //we have to sent current state
1432 ChangeState=Pin[PinNr].GetInitialState();
1433 }
1434 //We have something to report to the upper layer
1435 IOPinStatusPacket.LIGHTING1.cmnd = (ChangeState != 0) ? light1_sOn : light1_sOff;
1436 IOPinStatusPacket.LIGHTING1.seqnbr++;
1437 IOPinStatusPacket.LIGHTING1.unitcode = PinNr + (devId*10) ; //report inputs from PiFace X (X0..X9)
1438 myCallback->CallBackSendEvent((const unsigned char *)&IOPinStatusPacket, sizeof(IOPinStatusPacket.LIGHTING1));
1439 }
1440
1441 UpdateCounter=Pin[PinNr].Count.ProcessUpdateInterval(PIFACE_INPUT_PROCESS_INTERVAL*PIFACE_WORKER_THREAD_SLEEP_INTERVAL_MS);
1442 if (UpdateCounter)
1443 {
1444 unsigned long Count = Pin[PinNr].Count.GetTotal();
1445 unsigned long LastCount = Pin[PinNr].Count.GetLastTotal();
1446
1447 Pin[PinNr].Count.Last_Callback = boost::posix_time::microsec_clock::universal_time();
1448
1449 switch( Pin[PinNr].Count.Type )
1450 {
1451 case COUNT_TYPE_GENERIC:
1452 case COUNT_TYPE_RFXMETER:
1453 int meterid;
1454 if (IOPinStatusPacket.LIGHTING1.housecode == 'I')
1455 meterid=100+PinNr + (devId*10);
1456 else
1457 meterid=0+PinNr + (devId*10);
1458
1459 Pin[PinNr].IOPinCounterPacket.RFXMETER.id1=((meterid >>8) & 0xFF);
1460 Pin[PinNr].IOPinCounterPacket.RFXMETER.id2= (meterid & 0xFF);
1461
1462 if (Count != LastCount)
1463 {
1464 Pin[PinNr].Count.SetLastTotal(Count);
1465 // _log.Log(LOG_NORM,"Counter %lu\n",Count);
1466 Pin[PinNr].IOPinCounterPacket.RFXMETER.count1 = (unsigned char)((Count >> 24) & 0xFF);
1467 Pin[PinNr].IOPinCounterPacket.RFXMETER.count2 = (unsigned char)((Count >> 16) & 0xFF);
1468 Pin[PinNr].IOPinCounterPacket.RFXMETER.count3 = (unsigned char)((Count >> 8) & 0xFF);
1469 Pin[PinNr].IOPinCounterPacket.RFXMETER.count4 = (unsigned char)((Count)& 0xFF);
1470 Pin[PinNr].IOPinCounterPacket.RFXMETER.seqnbr++;
1471 // _log.Log(LOG_NORM,"RFXMeter Packet C1 %d, C2 %d C3 %d C4 %d\n",Pin[PinNr].IOPinCounterPacket.RFXMETER.count1,Pin[PinNr].IOPinCounterPacket.RFXMETER.count2,Pin[PinNr].IOPinCounterPacket.RFXMETER.count3,Pin[PinNr].IOPinCounterPacket.RFXMETER.count4);
1472 myCallback->CallBackSendEvent((const unsigned char *)&Pin[PinNr].IOPinCounterPacket, sizeof(Pin[PinNr].IOPinCounterPacket.RFXMETER));
1473 }
1474 break;
1475
1476 case COUNT_TYPE_ENERGY:
1477 int nodeId;
1478 if (IOPinStatusPacket.LIGHTING1.housecode == 'I')
1479 nodeId=300+devId;
1480 else
1481 nodeId=200+devId;
1482
1483 // Energy devices are updated *every* time because their current power usage (Watt) drops if no pulses
1484 // are registered between the interval.
1485 Pin[PinNr].Count.SetLastTotal(Count);
1486
1487 if (
1488 Pin[PinNr].Count.Cur_Interval.total_milliseconds() > 0 &&
1489 Pin[PinNr].Count.GetDivider() > 0
1490 )
1491 {
1492 Pin[PinNr].Count.Last_Interval = Pin[PinNr].Count.Cur_Interval;
1493 double dEnergy = ( ( 1. / Pin[PinNr].Count.GetDivider() ) * Count ); // kWh
1494 double dPower = ( ( 1000. / Pin[PinNr].Count.GetDivider() ) * ( 1000. / Pin[PinNr].Count.Cur_Interval.total_milliseconds() ) * 3600 ); // Watt
1495 myCallback->SendKwhMeter(nodeId, PinNr, 255, dPower, dEnergy, "kWh Meter" );
1496 }
1497 break;
1498 }
1499 }
1500 mask<<=1;
1501 }
1502
1503 return (ChangeState);
1504 }
1505
UpdateInterrupt(unsigned char IntFlag,unsigned char PinState)1506 int CIOPort::UpdateInterrupt(unsigned char IntFlag,unsigned char PinState)
1507 {
1508 int mask=0x01;
1509 int ChangeState=-1; //nothing changed
1510 int Changed;
1511
1512 for (int PinNr=0; PinNr<=7 ;PinNr++)
1513 {
1514 Changed=Pin[PinNr].UpdateInterrupt(((IntFlag&mask)==mask),((PinState&mask)==mask));
1515 if (Changed != -1)
1516 ChangeState=Changed;
1517 mask<<=1;
1518 }
1519 return (ChangeState);
1520 }
1521
ConfigureCounter(unsigned char Pinnr,bool Enable)1522 void CIOPort::ConfigureCounter(unsigned char Pinnr,bool Enable)
1523 {
1524 CPiFace *myCallback = reinterpret_cast<CPiFace*>(Callback_pntr);
1525
1526 Pin[Pinnr].Count.Enabled=Enable;
1527 if (Pin[Pinnr].Direction == 'I')
1528 {
1529 myCallback->CallBackSetPinInterruptMode(devId, Pinnr, Enable);
1530 }
1531 }
1532
CIOPort()1533 CIOPort::CIOPort()
1534 {
1535 Last=0;
1536 Current=0;
1537 Present=false;
1538 Callback_pntr = NULL;
1539 PortType = 0;
1540 devId = 0;
1541 for (int PinNr=0; PinNr<=7 ;PinNr++)
1542 {
1543 Pin[PinNr].Id=PinNr;
1544 }
1545
1546 };
1547
~CIOPort()1548 CIOPort::~CIOPort()
1549 {
1550
1551 };
1552
1553 //Webserver helpers
1554 namespace http {
1555 namespace server {
ReloadPiFace(WebEmSession & session,const request & req,std::string & redirect_uri)1556 void CWebServer::ReloadPiFace(WebEmSession & session, const request& req, std::string & redirect_uri)
1557 {
1558 redirect_uri = "/index.html";
1559 if (session.rights != 2)
1560 {
1561 session.reply_status = reply::forbidden;
1562 return; //Only admin user allowed
1563 }
1564
1565 std::string idx = request::findValue(&req, "idx");
1566 if (idx == "") {
1567 return;
1568 }
1569
1570 m_mainworker.RestartHardware(idx);
1571 }
1572 }
1573 }
1574
1575
1576