• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wirenboard / wb-mqtt-w1 / 2

03 Jun 2026 01:27PM UTC coverage: 61.229% (+2.1%) from 59.111%
2

Pull #35

github

b82308
sikmir
update Jenkinsfile
Pull Request #35: Port for debian 13

165 of 241 branches covered (68.46%)

289 of 472 relevant lines covered (61.23%)

4.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.18
/sysfs_w1.cpp
1
#include "sysfs_w1.h"
2

3
#include "file_utils.h"
4
#include <algorithm>
5
#include <fcntl.h>
6
#include <fstream>
7
#include <unistd.h>
8
#include <wblib/utils.h>
9

10
using namespace std::chrono;
11

12
#define LOG(logger) logger.Log() << "[w1 driver] "
13

14
namespace
15
{
16
    const char BULK_CONVERSION_TRIGGER[] = "trigger";
17
    const auto MAX_CONVERSION_TIME = milliseconds(2000);
18
    const auto MAX_VALUE_CHANGE = 10 * 1000;    // 1 degree per second for DEFAULT_POLL_INTERVALL_MS
19
    const auto MEASUREMENT_ERROR_VALUE = 85000; // sensor power on temperature value (read without conversion)
20
    const auto MEASUREMENT_MAX_VALUE = 127937;  // max possible temperature value (for some chineese clones)
21

22
    std::unordered_map<std::string, int> lastValueMap;
23

24
    template<class T, class Pred> void erase_if(T& c, Pred pred)
25
    {
26
        auto last = c.end();
27
        for (auto i = c.begin(); i != last;) {
28
            if (pred(*i)) {
29
                i = c.erase(i);
30
            } else {
31
                ++i;
32
            }
33
        }
34
    }
35

36
    std::string ReadLine(const std::string& fileName)
7✔
37
    {
38
        std::ifstream file;
14✔
39
        OpenWithException(file, fileName);
7✔
40
        std::string str;
7✔
41
        std::getline(file, str);
7✔
42
        return str;
14✔
43
    }
44

45
    bool RunBulkRead(const std::string& fileName, WBMQTT::TLogger& logger)
2✔
46
    {
47
        auto fd = open(fileName.c_str(), O_WRONLY | O_APPEND);
2✔
48
        if (fd < 0) {
2✔
49
            LOG(logger) << "Can't open file:" << fileName;
×
50
            return false;
×
51
        }
52
        auto s = write(fd, BULK_CONVERSION_TRIGGER, sizeof(BULK_CONVERSION_TRIGGER)); // Write trailing zero
2✔
53
        close(fd);
2✔
54
        return (s == sizeof(BULK_CONVERSION_TRIGGER));
2✔
55
    }
56

57
    double GetTemperatureFromString(const std::string& str, const std::string& deviceFileName)
10✔
58
    {
59
        if (str.empty()) {
10✔
60
            throw TOneWireReadErrorException("Can't read temperature", deviceFileName);
×
61
        }
62

63
        int dataInt = std::stoi(str.c_str());
10✔
64

65
        // Thermometer can't measure temperature?
66
        if (dataInt == MEASUREMENT_ERROR_VALUE) {
10✔
67
            auto it = lastValueMap.find(deviceFileName);
2✔
68
            if (it == lastValueMap.end() || abs(dataInt - it->second) > MAX_VALUE_CHANGE) {
2✔
69
                throw TOneWireReadErrorException("Measurement error", deviceFileName);
1✔
70
            }
71
        }
72

73
        // returned max possible temp, probably an error (it happens for chineese clones)
74
        if (dataInt == MEASUREMENT_MAX_VALUE) {
9✔
75
            throw TOneWireReadErrorException("Thermometer error", deviceFileName);
×
76
        }
77

78
        lastValueMap[deviceFileName] = dataInt;
9✔
79
        return dataInt / 1000.0; // Temperature given by kernel is in thousandths of degrees
9✔
80
    }
81

82
    double GetBulkReadTemperature(const std::string& deviceFileName)
5✔
83
    {
84
        return GetTemperatureFromString(ReadLine(deviceFileName), deviceFileName);
5✔
85
    }
86

87
    double GetDirectConvertedTemperature(const std::string& deviceFileName)
7✔
88
    {
89
        std::string data;
14✔
90
        bool crcOk = false;
7✔
91

92
        const std::string tag("t=");
14✔
93

94
        std::ifstream file;
14✔
95
        OpenWithException(file, deviceFileName);
7✔
96

97
        /*  reading file till eof could lead to a stuck
98
            when device is removed */
99
        while (file.good()) {
30✔
100
            std::string sLine;
48✔
101
            std::getline(file, sLine);
24✔
102
            if (sLine.find("crc=") != std::string::npos) {
24✔
103
                if (sLine.find("YES") != std::string::npos) {
6✔
104
                    crcOk = true;
5✔
105
                }
106
            } else {
107
                size_t tpos = sLine.find(tag);
18✔
108
                if (tpos != std::string::npos) {
18✔
109
                    data = sLine.substr(tpos + tag.length());
6✔
110
                }
111
            }
112
        }
113

114
        if (!crcOk) {
6✔
115
            throw TOneWireReadErrorException("Bad CRC", deviceFileName);
1✔
116
        }
117

118
        return GetTemperatureFromString(data, deviceFileName);
10✔
119
    }
120

121
    struct TBusMaster
122
    {
123
        std::string Dir;
124
        bool SupportsBulkRead;
125
    };
126
}
127

128
TSysfsOneWireThermometer::TSysfsOneWireThermometer(const std::string& id, const std::string& dir, bool bulkRead)
16✔
129
    : Id(id),
130
      Status(TSysfsOneWireThermometer::New),
131
      BulkRead(bulkRead)
16✔
132
{
133
    SetDeviceFileName(dir);
16✔
134
}
16✔
135

136
void TSysfsOneWireThermometer::SetDeviceFileName(const std::string& dir)
16✔
137
{
138
    DeviceFileName = dir + "/" + Id + (BulkRead ? "/temperature" : "/w1_slave");
16✔
139
}
16✔
140

141
double TSysfsOneWireThermometer::GetTemperature() const
12✔
142
{
143
    if (BulkRead) {
12✔
144
        return GetBulkReadTemperature(DeviceFileName);
5✔
145
    }
146
    return GetDirectConvertedTemperature(DeviceFileName);
7✔
147
}
148

149
const std::string& TSysfsOneWireThermometer::GetId() const
20✔
150
{
151
    return Id;
20✔
152
}
153

154
TSysfsOneWireThermometer::PresenceStatus TSysfsOneWireThermometer::GetStatus() const
8✔
155
{
156
    return Status;
8✔
157
}
158

159
void TSysfsOneWireThermometer::MarkAsDisconnected()
1✔
160
{
161
    Status = Disconnected;
1✔
162
}
1✔
163

164
bool TSysfsOneWireThermometer::FoundAgain(const std::string& dir)
×
165
{
166
    Status = Connected;
×
167
    if (WBMQTT::StringStartsWith(DeviceFileName, dir)) {
×
168
        return true;
×
169
    }
170
    SetDeviceFileName(dir);
×
171
    return false;
×
172
}
173

174
TSysfsOneWireManager::TSysfsOneWireManager(const std::string& devicesDir,
7✔
175
                                           WBMQTT::TLogger& debugLogger,
176
                                           WBMQTT::TLogger& errorLogger)
7✔
177
    : DevicesDir(devicesDir),
178
      DebugLogger(debugLogger),
179
      ErrorLogger(errorLogger)
7✔
180
{}
7✔
181

182
std::vector<std::shared_ptr<TSysfsOneWireThermometer>> TSysfsOneWireManager::RescanBusAndRead()
9✔
183
{
184
    const auto prefixes = {"28-", "10-", "22-"};
9✔
185
    erase_if(Devices, [](const auto& it) { return it.second->GetStatus() == TSysfsOneWireThermometer::Disconnected; });
11✔
186
    for (auto& d: Devices) {
10✔
187
        d.second->MarkAsDisconnected();
1✔
188
    }
189

190
    std::vector<TBusMaster> busMasters;
18✔
191

192
    IterateDir(DevicesDir, [&](const auto& name) {
9✔
193
        if (!WBMQTT::StringStartsWith(name, "w1_bus_master")) {
11✔
194
            return false;
×
195
        }
196
        TBusMaster bm;
11✔
197
        bm.Dir = DevicesDir + name;
11✔
198
        bm.SupportsBulkRead = (access((bm.Dir + "/therm_bulk_read").c_str(), F_OK) == 0);
11✔
199
        busMasters.push_back(bm);
11✔
200

201
        if (bm.SupportsBulkRead) {
11✔
202
            RunBulkRead(bm.Dir + "/therm_bulk_read", ErrorLogger);
2✔
203
        }
204

205
        IterateDir(bm.Dir, [&](const auto& name) {
11✔
206
            for (const auto& prefix: prefixes) {
67✔
207
                if (WBMQTT::StringStartsWith(name, prefix)) {
42✔
208
                    auto it = Devices.find(name);
10✔
209
                    if (it == Devices.end()) {
10✔
210
                        it =
10✔
211
                            Devices
212
                                .insert({name,
213
                                         std::make_shared<TSysfsOneWireThermometer>(name, bm.Dir, bm.SupportsBulkRead)})
20✔
214
                                .first;
215
                    } else {
216
                        if (!it->second->FoundAgain(bm.Dir)) {
×
217
                            LOG(DebugLogger) << name << " is switched to " << bm.Dir;
×
218
                        }
219
                    }
220
                }
221
            }
222
            return false;
14✔
223
        });
224
        return false;
11✔
225
    });
18✔
226

227
    auto time = steady_clock::now();
9✔
228
    bool inConversion = true;
9✔
229
    while (inConversion && (duration_cast<milliseconds>(steady_clock::now() - time) < MAX_CONVERSION_TIME)) {
18✔
230
        inConversion = false;
9✔
231
        for (auto& bm: busMasters) {
20✔
232
            if (bm.SupportsBulkRead) {
11✔
233
                try {
234
                    auto status = ReadLine(bm.Dir + "/therm_bulk_read");
2✔
235
                    inConversion |= (status.empty() || status[0] != '1');
2✔
236
                } catch (const std::exception& e) {
×
237
                    LOG(ErrorLogger) << e.what();
×
238
                }
239
            }
240
        }
241
        if (inConversion) {
9✔
242
            std::this_thread::sleep_for(milliseconds(100));
×
243
        }
244
    }
245
    if (inConversion) {
9✔
246
        LOG(DebugLogger) << "Conversion takes too much time";
×
247
    }
248

249
    std::vector<std::shared_ptr<TSysfsOneWireThermometer>> res;
9✔
250
    for (auto& d: Devices) {
20✔
251
        res.push_back(d.second);
11✔
252
    }
253
    std::sort(res.begin(), res.end(), [](const auto& v1, const auto& v2) { return v1->GetId() < v2->GetId(); });
15✔
254
    return res;
18✔
255
}
256

257
TOneWireReadErrorException::TOneWireReadErrorException(const std::string& message, const std::string& deviceFileName)
2✔
258
    : std::runtime_error(message + " (" + deviceFileName + ")")
2✔
259
{}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc