1 #include "stdafx.h"
2 #include "1WireByOWFS.h"
3 #include "../../main/mainworker.h"
4 #include "../../main/Logger.h"
5
6 #include <fstream>
7 #include <algorithm>
8
9 #ifdef WIN32
10 #include "../../main/dirent_windows.h"
11 #else
12 #include <dirent.h>
13 #endif
14
15 #include "../../main/Helper.h"
16
C1WireByOWFS(const std::string & path)17 C1WireByOWFS::C1WireByOWFS(const std::string& path):
18 m_path(path)
19 {
20 if (m_path.empty())
21 m_path = "/mnt/1wire";
22
23 m_simultaneousTemperaturePath = m_path;
24 m_simultaneousTemperaturePath.append("/simultaneous/temperature");
25
26 _log.Log(LOG_STATUS,"Using 1-Wire support (OWFS)...");
27 }
28
FindDevice(const std::string & sID,_t1WireDevice & device) const29 bool C1WireByOWFS::FindDevice(const std::string &sID, /*out*/_t1WireDevice& device) const
30 {
31 return FindDevice(m_path, sID, device);
32 }
33
FindDevice(const std::string & inDir,const std::string & sID,_t1WireDevice & device) const34 bool C1WireByOWFS::FindDevice(const std::string &inDir, const std::string &sID, /*out*/_t1WireDevice& device) const
35 {
36 bool found = false;
37 DIR *d=opendir(inDir.c_str());
38 if (d != NULL)
39 {
40 struct dirent *de=NULL;
41 // Loop while not NULL or not found
42 while((de=readdir(d)) && !found)
43 {
44 // Check dir
45 if (!IsValidDir(de))
46 continue;
47
48 // Get the device from it's dirname
49 GetDevice(inDir, de->d_name, device);
50
51 // Check if it's the device we are looking for
52 if (device.devid.compare(0,sID.length(),sID)==0)
53 {
54 found=true;
55 continue;
56 }
57
58 // Else, try to scan hubs (recursively)
59 if (device.family==microlan_coupler)
60 {
61 // Search in "main" and "aux" dir
62 found=FindDevice(inDir + "/" + de->d_name + HUB_MAIN_SUB_PATH, sID, device);
63 if(!found)
64 found=FindDevice(inDir + "/" + de->d_name + HUB_AUX_SUB_PATH, sID, device);
65 }
66 }
67 }
68
69 closedir(d);
70 return found;
71 }
72
GetDevices(std::vector<_t1WireDevice> & devices) const73 void C1WireByOWFS::GetDevices(/*out*/std::vector<_t1WireDevice>& devices) const
74 {
75 return GetDevices(m_path, devices);
76 }
77
GetDevices(const std::string & inDir,std::vector<_t1WireDevice> & devices) const78 void C1WireByOWFS::GetDevices(const std::string &inDir, /*out*/std::vector<_t1WireDevice>& devices) const
79 {
80 DIR *d=opendir(inDir.c_str());
81 if (d != NULL)
82 {
83 struct dirent *de=NULL;
84 // Loop while not NULL
85 while((de=readdir(d)))
86 {
87 // Check dir
88 if (!IsValidDir(de))
89 continue;
90
91 // Get the device from it's dirname
92 _t1WireDevice device;
93 GetDevice(inDir, de->d_name, device);
94
95 // Add device to list
96 _log.Debug(DEBUG_HARDWARE, "1Wire (OWFS): Add device %s", device.filename.c_str());
97 devices.push_back(device);
98
99 // If device is a hub, scan for all hub connected devices (recursively)
100 if (device.family==microlan_coupler)
101 {
102 // Scan in "main" and "aux" dir
103 GetDevices(inDir + "/" + de->d_name + HUB_MAIN_SUB_PATH, devices);
104 GetDevices(inDir + "/" + de->d_name + HUB_AUX_SUB_PATH, devices);
105 }
106 }
107 }
108
109 closedir(d);
110 }
111
readRawData(const std::string & filename) const112 std::string C1WireByOWFS::readRawData(const std::string& filename) const
113 {
114 std::ifstream file;
115 file.open(filename.c_str());
116 if (file.is_open())
117 {
118 std::string sLine;
119 getline(file, sLine);
120 file.close();
121 if (is_number(sLine))
122 return sLine;
123 }
124 return "";
125 }
126
writeData(const _t1WireDevice & device,const std::string & propertyName,const std::string & value) const127 void C1WireByOWFS::writeData(const _t1WireDevice& device, const std::string &propertyName, const std::string &value) const
128 {
129 std::ofstream file;
130 file.open(std::string(device.filename+"/"+propertyName).c_str());
131 if (!file.is_open())
132 return;
133
134 file << value;
135 file.close();
136 }
137
SetLightState(const std::string & sId,int unit,bool value,const unsigned int level)138 void C1WireByOWFS::SetLightState(const std::string& sId,int unit,bool value, const unsigned int level)
139 {
140 _t1WireDevice device;
141 if (!FindDevice(sId, device))
142 return;
143
144 switch(device.family)
145 {
146 case Addresable_Switch:
147 {
148 writeData(device,"PIO",value?"yes":"no");
149 break;
150 }
151 case dual_addressable_switch_plus_1k_memory:
152 case Temperature_IO:
153 case dual_channel_addressable_switch:
154 {
155 if (unit<0 || unit>1)
156 return;
157 writeData(device,std::string("PIO.").append(1,'A'+(char)unit),value?"yes":"no");
158 break;
159 }
160 case _8_channel_addressable_switch:
161 {
162 if (unit<0 || unit>7)
163 return;
164 writeData(device,std::string("PIO.").append(1,'0'+(char)unit),value?"yes":"no");
165 break;
166 }
167 case _4k_EEPROM_with_PIO:
168 {
169 if (unit<0 || unit>1)
170 return;
171 writeData(device,std::string("PIO.").append(1,'0'+(char)unit),value?"yes":"no");
172 break;
173 }
174 case microlan_coupler:
175 {
176 // 1 = Unconditionally off (non-conducting), 2 = Unconditionally on (conducting)
177 writeData(device,"control",value?"2":"1");
178 break;
179 }
180 case digital_potentiometer:
181 {
182 writeData(device, "chargepump", "1");
183 unsigned int wiper = static_cast<unsigned int>(level * (255.0 / 15.0));
184 writeData(device, "wiper", std::to_string(wiper));
185 break;
186 }
187
188 default:
189 return;
190 }
191 }
192
GetTemperature(const _t1WireDevice & device) const193 float C1WireByOWFS::GetTemperature(const _t1WireDevice& device) const
194 {
195 std::string readValue=readRawData(std::string(device.filename+"/temperature"));
196
197 _log.Debug(DEBUG_HARDWARE, "1Wire (OWFS): Get Temperature from %s = %s", device.filename.c_str(), readValue.c_str());
198
199 if (readValue.empty())
200 return -1000.0;
201 return static_cast<float>(atof(readValue.c_str()));
202 }
203
GetHumidity(const _t1WireDevice & device) const204 float C1WireByOWFS::GetHumidity(const _t1WireDevice& device) const
205 {
206 std::string readValue=readRawData(std::string(device.filename+"/humidity"));
207
208 _log.Debug(DEBUG_HARDWARE, "1Wire (OWFS): Get Humidity from %s = %s", device.filename.c_str(), readValue.c_str());
209
210 if (readValue.empty())
211 return -1000.0;
212 return static_cast<float>(atof(readValue.c_str()));
213 }
214
GetPressure(const _t1WireDevice & device) const215 float C1WireByOWFS::GetPressure(const _t1WireDevice& device) const
216 {
217 std::string realFilename = device.filename + nameHelper(device.filename, device.family); // for family 26 (DS2438) + pressure + HobbyBoards
218
219 std::string readValue=readRawData(std::string(realFilename+"/pressure"));
220 if (readValue.empty())
221 return -1000.0;
222 return static_cast<float>(atof(readValue.c_str()));
223 }
224
GetLightState(const _t1WireDevice & device,int unit) const225 bool C1WireByOWFS::GetLightState(const _t1WireDevice& device,int unit) const
226 {
227 std::string fileName(device.filename);
228
229 switch(device.family)
230 {
231 case Addresable_Switch:
232 {
233 fileName.append("/sensed");
234 break;
235 }
236 case dual_addressable_switch_plus_1k_memory:
237 case Temperature_IO:
238 case dual_channel_addressable_switch:
239 {
240 if (unit<0 || unit>1)
241 return false;
242 fileName.append("/sensed.").append(1,'A'+(char)unit);
243 break;
244 }
245 case _8_channel_addressable_switch:
246 {
247 if (unit<0 || unit>7)
248 return false;
249 fileName.append("/sensed.").append(1,'0'+(char)unit);
250 break;
251 }
252 case _4k_EEPROM_with_PIO:
253 {
254 if (unit<0 || unit>1)
255 return false;
256 fileName.append("/sensed.").append(1,'0'+(char)unit);
257 break;
258 }
259 case microlan_coupler:
260 {
261 fileName.append("/control");
262 break;
263 }
264 }
265
266 std::string readValue=readRawData(fileName);
267 if (readValue.empty())
268 return false;
269
270 switch(device.family)
271 {
272 case Addresable_Switch:
273 case dual_addressable_switch_plus_1k_memory:
274 case Temperature_IO:
275 case dual_channel_addressable_switch:
276 case _8_channel_addressable_switch:
277 case _4k_EEPROM_with_PIO:
278 // Caution : read value correspond to voltage level, inverted from the transistor state
279 // We have to invert the read value
280 return (readValue!="1");
281 case microlan_coupler:
282 {
283 // 1 = Unconditionally off (non-conducting)
284 // 2 = Unconditionally on (conducting)
285 int iValue=atoi(readValue.c_str())==2;
286 if (iValue!=1 && iValue!=2)
287 return false;
288 else
289 return (iValue==2);
290 }
291 }
292 return false;
293 }
294
GetNbChannels(const _t1WireDevice & device) const295 unsigned int C1WireByOWFS::GetNbChannels(const _t1WireDevice& device) const
296 {
297 std::string readValue=readRawData(std::string(device.filename+"/channels"));
298 if (readValue.empty())
299 return 0;
300 return atoi(readValue.c_str());
301 }
302
GetCounter(const _t1WireDevice & device,int unit) const303 unsigned long C1WireByOWFS::GetCounter(const _t1WireDevice& device,int unit) const
304 {
305 // Depending on OWFS version, file can be "counter" or "counters". So try both.
306 std::string readValue=readRawData(std::string(device.filename+"/counter.").append(1,'A'+(char)unit));
307 if (readValue.empty())
308 readValue=readRawData(std::string(device.filename+"/counters.").append(1,'A'+(char)unit));
309 if (readValue.empty())
310 return 0;
311 return (unsigned long)atol(readValue.c_str());
312 }
313
GetVoltage(const _t1WireDevice & device,int unit) const314 int C1WireByOWFS::GetVoltage(const _t1WireDevice& device,int unit) const
315 {
316 std::string fileName(device.filename);
317
318 switch(device.family)
319 {
320 case smart_battery_monitor:
321 {
322 static const std::string unitNames[] = { "VAD", "VDD", "vis" };
323 if (unit >= int((sizeof(unitNames)/sizeof(unitNames[0]))))
324 return 0;
325 fileName.append("/").append(unitNames[unit]);
326 break;
327 }
328 default:
329 {
330 fileName.append("/volt.").append(1, static_cast<char>('A'+unit));
331 break;
332 }
333 }
334
335 std::string readValue=readRawData(fileName);
336 if (readValue.empty())
337 return -1000;
338 return static_cast<int>((atof(readValue.c_str())*1000.0));
339 }
340
GetIlluminance(const _t1WireDevice & device) const341 float C1WireByOWFS::GetIlluminance(const _t1WireDevice& device) const
342 {
343 // Both terms "illumination" and "illuminance" are in the OWFS documentation, so try both
344 std::string readValue=readRawData(std::string(device.filename+"/S3-R1-A/illuminance"));
345 if (readValue.empty())
346 readValue=readRawData(std::string(device.filename+"/S3-R1-A/illumination"));
347 if (readValue.empty())
348 return -1000.0;
349 return (float)(atof(readValue.c_str())*1000.0);
350 }
351
GetWiper(const _t1WireDevice & device) const352 int C1WireByOWFS::GetWiper(const _t1WireDevice& device) const
353 {
354 std::string readValue = readRawData(std::string(device.filename + "/wiper"));
355 if (readValue.empty())
356 return -1;
357 return atoi(readValue.c_str());
358 }
359
StartSimultaneousTemperatureRead()360 void C1WireByOWFS::StartSimultaneousTemperatureRead()
361 {
362 _log.Debug(DEBUG_HARDWARE, "1Wire (OWFS): Initiating simultaneous temperature read");
363 std::ofstream file;
364 file.open(m_simultaneousTemperaturePath.c_str());
365 if (file.is_open())
366 {
367 file << "1";
368 file.close();
369 _log.Debug(DEBUG_HARDWARE, "1Wire (OWFS): Simultaneous temperature read successful");
370 sleep_milliseconds(800);
371 }
372 }
373
PrepareDevices()374 void C1WireByOWFS::PrepareDevices()
375 {
376 }
377
IsValidDir(const struct dirent * const de)378 bool C1WireByOWFS::IsValidDir(const struct dirent*const de)
379 {
380 // Check dirent type
381 if (de->d_type!=DT_DIR)
382 return false;
383
384 // Filter system dirs "." and ".."
385 std::string dirname = de->d_name;
386 if ((dirname==".")||(dirname=="..")||(dirname.size()<3))
387 return false;
388
389 // Check dir name format (must be something like xx.xxxxxxxxxxxx where x is hexa)
390 if (dirname.size()!=15||
391 dirname[2]!='.'||
392 !isxdigit(dirname[0])||!isxdigit(dirname[1])||
393 !isxdigit(dirname[3])||!isxdigit(dirname[4])||
394 !isxdigit(dirname[5])||!isxdigit(dirname[6])||
395 !isxdigit(dirname[7])||!isxdigit(dirname[8])||
396 !isxdigit(dirname[9])||!isxdigit(dirname[10])||
397 !isxdigit(dirname[11])||!isxdigit(dirname[12])||
398 !isxdigit(dirname[13])||!isxdigit(dirname[14]))
399 return false;
400
401 // The dir is a valid device name
402 return true;
403 }
404
GetDevice(const std::string & inDir,const std::string & dirname,_t1WireDevice & device) const405 void C1WireByOWFS::GetDevice(const std::string &inDir, const std::string &dirname, /*out*/_t1WireDevice& device) const
406 {
407 // OWFS device name format, see http://owfs.org/uploads/owfs.1.html#sect30
408 // OWFS must be configured to use the default format ff.iiiiiiiiiiii, with :
409 // - ff : family (1 byte)
410 // - iiiiiiiiiiii : id (6 bytes)
411
412 // Retrieve family from the first 2 chars
413 device.family=ToFamily(dirname.substr(0,2));
414
415 // Device Id (6 chars after '.')
416 std::string id=dirname.substr(3,3+6*2);
417 // OWFS give us the device ID inverted, so reinvert it
418 for (int i=0;i<6/2;i++)
419 {
420 char c;
421 int left_position = i * 2; // Point on first byte
422 int right_position=(6-i-1)*2; // Point on last byte
423 c=id[left_position]; id[left_position]=id[right_position]; id[right_position]=c;
424 c=id[left_position+1]; id[left_position+1]=id[right_position+1]; id[right_position+1]=c;
425 }
426 device.devid=id;
427
428 device.filename=inDir;
429 if (device.family == Environmental_Monitors) {
430 device.filename+="/" + dirname + nameHelper(dirname, device.family);
431 } else {
432 device.filename+="/" + dirname;
433 }
434 }
435
nameHelper(const std::string & dirname,const _e1WireFamilyType family) const436 std::string C1WireByOWFS::nameHelper(const std::string& dirname, const _e1WireFamilyType family) const {
437 std::string name;
438 DIR *d=NULL;
439
440 d=opendir(std::string(std::string(m_path) + "/" + dirname.c_str()).c_str());
441 if (d != NULL)
442 {
443 struct dirent *de = NULL;
444 while ((de = readdir(d)))
445 {
446 name = de->d_name;
447 if (de->d_type == DT_DIR)
448 {
449 if ( ((family == Environmental_Monitors) && (name.compare(0, 3, "EDS") == 0))
450 || ((family == smart_battery_monitor) && (name.compare(0, 7, "B1-R1-A") == 0)) ) {
451 closedir(d);
452 return "/" + name;
453 }
454 }
455 }
456 closedir(d);
457 }
458 return "";
459 }
460
461