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

wirenboard / wb-mqtt-serial / 2

29 Dec 2025 12:28PM UTC coverage: 76.817% (+4.0%) from 72.836%
2

Pull #1045

github

54aa0c
pgasheev
up changelog
Pull Request #1045: Fix firmware version in WB-M1W2 template

6873 of 9161 branches covered (75.02%)

12966 of 16879 relevant lines covered (76.82%)

1651.61 hits per line

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

78.95
/src/devices/pulsar_device.cpp
1
/* vim: set ts=4 sw=4: */
2

3
#include <cstdlib>
4
#include <cstring>
5

6
#include "pulsar_device.h"
7

8
namespace
9
{
10
    const TRegisterTypes RegisterTypes{{TPulsarDevice::REG_DEFAULT, "default", "value", Double, true},
11
                                       {TPulsarDevice::REG_SYSTIME, "systime", "value", U64, true}};
12
}
13

14
void TPulsarDevice::Register(TSerialDeviceFactory& factory)
288✔
15
{
16
    factory.RegisterProtocol(
288✔
17
        new TUint32SlaveIdProtocol("pulsar", RegisterTypes),
576✔
18
        new TBasicDeviceFactory<TPulsarDevice>("#/definitions/simple_device", "#/definitions/common_channel"));
576✔
19
}
288✔
20

21
TPulsarDevice::TPulsarDevice(PDeviceConfig config, PProtocol protocol)
2✔
22
    : TSerialDevice(config, protocol),
23
      TUInt32SlaveId(config->SlaveId),
4✔
24
      RequestID(0)
2✔
25
{}
2✔
26

27
uint16_t TPulsarDevice::CalculateCRC16(const uint8_t* buffer, size_t size)
4✔
28
{
29
    uint16_t w;
30
    uint16_t shift_cnt, f;
31
    const uint8_t* ptrByte;
32

33
    uint16_t byte_cnt = size;
4✔
34
    ptrByte = buffer;
4✔
35
    w = (uint16_t)0xFFFF;
4✔
36

37
    for (; byte_cnt > 0; byte_cnt--) {
52✔
38
        w = (uint16_t)(w ^ (uint16_t)(*ptrByte++));
48✔
39
        for (shift_cnt = 0; shift_cnt < 8; shift_cnt++) {
432✔
40
            f = (uint8_t)((w)&0x01);
384✔
41
            w >>= 1;
384✔
42
            if (f)
384✔
43
                w = (uint16_t)(w ^ 0xa001);
174✔
44
        }
45
    }
46

47
    return w;
4✔
48
}
49

50
void TPulsarDevice::WriteBCD(uint64_t value, uint8_t* buffer, size_t size, bool big_endian)
2✔
51
{
52
    for (size_t i = 0; i < size; i++) {
10✔
53
        // form byte from the end of value
54
        uint8_t byte = value % 10;
8✔
55
        value /= 10;
8✔
56
        byte |= (value % 10) << 4;
8✔
57
        value /= 10;
8✔
58

59
        buffer[big_endian ? size - i - 1 : i] = byte;
8✔
60
    }
61
}
2✔
62

63
void TPulsarDevice::WriteHex(uint64_t value, uint8_t* buffer, size_t size, bool big_endian)
6✔
64
{
65
    for (size_t i = 0; i < size; i++) {
22✔
66
        buffer[big_endian ? size - i - 1 : i] = value & 0xFF;
16✔
67
        value >>= 8;
16✔
68
    }
69
}
6✔
70

71
uint64_t TPulsarDevice::ReadBCD(const uint8_t* buffer, size_t size, bool big_endian)
2✔
72
{
73
    uint64_t result = 0;
2✔
74

75
    for (size_t i = 0; i < size; i++) {
10✔
76
        result *= 100;
8✔
77

78
        uint8_t bcd_byte = buffer[big_endian ? i : size - i - 1];
8✔
79
        uint8_t dec_byte = (bcd_byte & 0x0F) + 10 * ((bcd_byte >> 4) & 0x0F);
8✔
80

81
        result += dec_byte;
8✔
82
    }
83

84
    return result;
2✔
85
}
86

87
uint64_t TPulsarDevice::ReadHex(const uint8_t* buffer, size_t size, bool big_endian)
6✔
88
{
89
    uint64_t result = 0;
6✔
90

91
    for (size_t i = 0; i < size; i++) {
22✔
92
        result <<= 8;
16✔
93
        result |= buffer[big_endian ? i : size - i - 1];
16✔
94
    }
95

96
    return result;
6✔
97
}
98

99
void TPulsarDevice::WriteDataRequest(TPort& port, uint32_t addr, uint32_t mask, uint16_t id)
2✔
100
{
101
    port.CheckPortOpen();
2✔
102

103
    uint8_t buf[14];
104

105
    /* header = device address in big-endian BCD */
106
    WriteBCD(addr, buf, sizeof(uint32_t), true);
2✔
107

108
    /* data request => F == 1, L == 14 */
109
    buf[4] = 1;
2✔
110
    buf[5] = 14;
2✔
111

112
    /* data mask in little-endian */
113
    WriteHex(mask, &buf[6], sizeof(uint32_t), false);
2✔
114

115
    /* request ID */
116
    WriteHex(id, &buf[10], sizeof(uint16_t), false);
2✔
117

118
    /* CRC16 */
119
    uint16_t crc = CalculateCRC16(buf, 12);
2✔
120
    WriteHex(crc, &buf[12], sizeof(uint16_t), false);
2✔
121

122
    port.WriteBytes(buf, 14);
2✔
123
}
2✔
124

125
void TPulsarDevice::WriteSysTimeRequest(TPort& port, uint32_t addr, uint16_t id)
×
126
{
127
    port.CheckPortOpen();
×
128

129
    uint8_t buf[10];
130

131
    /* header = device address in big-endian BCD */
132
    WriteBCD(addr, buf, sizeof(uint32_t), true);
×
133

134
    /* sys time request => F == 4, L == 10 */
135
    buf[4] = 1;
×
136
    buf[5] = 10;
×
137

138
    /* request ID */
139
    WriteHex(id, &buf[6], sizeof(uint16_t), false);
×
140

141
    /* CRC16 */
142
    uint16_t crc = CalculateCRC16(buf, 8);
×
143
    WriteHex(crc, &buf[8], sizeof(uint16_t), false);
×
144

145
    port.WriteBytes(buf, 10);
×
146
}
147

148
void TPulsarDevice::ReadResponse(TPort& port, uint32_t addr, uint8_t* payload, size_t size, uint16_t id)
2✔
149
{
150
    const int exp_size = size + 10; /* payload size + service bytes */
2✔
151
    std::vector<uint8_t> response(exp_size);
2✔
152

153
    int nread = port.ReadFrame(response.data(),
2✔
154
                               response.size(),
155
                               GetResponseTimeout(port),
2✔
156
                               GetFrameTimeout(port),
2✔
157
                               [](uint8_t* buf, int size) { return size >= 6 && size == buf[5]; })
34✔
158
                    .Count;
2✔
159

160
    /* check size */
161
    if (nread < 6)
2✔
162
        throw TSerialDeviceTransientErrorException("frame is too short");
×
163

164
    if (nread != exp_size)
2✔
165
        throw TSerialDeviceTransientErrorException("unexpected end of frame");
×
166

167
    if (exp_size != response[5])
2✔
168
        throw TSerialDeviceTransientErrorException("unexpected frame length");
×
169

170
    /* check CRC16 */
171
    uint16_t crc_recv = ReadHex(&response[nread - 2], sizeof(uint16_t), false);
2✔
172
    if (crc_recv != CalculateCRC16(response.data(), nread - 2))
2✔
173
        throw TSerialDeviceTransientErrorException("CRC mismatch");
×
174

175
    /* check address */
176
    uint32_t addr_recv = ReadBCD(response.data(), sizeof(uint32_t), true);
2✔
177
    if (addr_recv != addr)
2✔
178
        throw TSerialDeviceTransientErrorException("slave address mismatch");
×
179

180
    /* check request ID */
181
    uint16_t id_recv = ReadHex(&response[nread - 4], sizeof(uint16_t), false);
2✔
182
    if (id_recv != id)
2✔
183
        throw TSerialDeviceTransientErrorException("request ID mismatch");
×
184

185
    /* copy payload data to external buffer */
186
    memcpy(payload, response.data() + 6, size);
2✔
187
}
2✔
188

189
TRegisterValue TPulsarDevice::ReadDataRegister(TPort& port, const TRegisterConfig& reg)
2✔
190
{
191
    auto addr = GetUint32RegisterAddress(reg.GetAddress());
2✔
192
    // raw payload data
193
    uint8_t payload[sizeof(uint64_t)];
194

195
    // form register mask from address
196
    uint32_t mask = 1 << addr;
2✔
197

198
    // send data request and receive response
199
    WriteDataRequest(port, SlaveId, mask, RequestID);
2✔
200
    ReadResponse(port, SlaveId, payload, reg.GetByteWidth(), RequestID);
2✔
201

202
    ++RequestID;
2✔
203

204
    // decode little-endian double64_t value
205
    return TRegisterValue{ReadHex(payload, reg.GetByteWidth(), false)};
4✔
206
}
207

208
TRegisterValue TPulsarDevice::ReadSysTimeRegister(TPort& port, const TRegisterConfig& reg)
×
209
{
210
    // raw payload data
211
    uint8_t payload[6];
212

213
    // send system time request and receive response
214
    WriteSysTimeRequest(port, SlaveId, RequestID);
×
215
    ReadResponse(port, SlaveId, payload, sizeof(payload), RequestID);
×
216

217
    ++RequestID;
×
218

219
    // decode little-endian double64_t value
220
    return TRegisterValue{ReadHex(payload, sizeof(payload), false)};
×
221
}
222

223
TRegisterValue TPulsarDevice::ReadRegisterImpl(TPort& port, const TRegisterConfig& reg)
2✔
224
{
225
    port.SkipNoise();
2✔
226

227
    switch (reg.Type) {
2✔
228
        case REG_DEFAULT:
2✔
229
            return ReadDataRegister(port, reg);
2✔
230
        case REG_SYSTIME:
×
231
            return ReadSysTimeRegister(port, reg);
×
232
        default:
×
233
            throw TSerialDeviceException("Pulsar protocol: wrong register type");
×
234
    }
235
}
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