-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve Holman ws5029, Add support for AOK-5056 and correction for Emax #2419
Merged
Merged
Changes from 9 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
c71d0d1
Improve Holman ws5029 and Add support for AOK-5056
ProfBoc75 e75765c
Improve Holman ws5029 and Add support for AOK-5056
ProfBoc75 fdd879b
Improve Holman ws5029 and Add support for AOK-5056
ProfBoc75 5b64354
Improve Holman ws5029 and Add support for AOK-5056
ProfBoc75 9122409
correct ref Optex 990018
ProfBoc75 145b805
change wind speed from int to float
ProfBoc75 9df18d9
change wind speed from int to float
ProfBoc75 a5473f8
add full checksum control with reverse Galois algo
ProfBoc75 2898754
add full checksum check with xor_shift_bytes for WS5029 PWM device
ProfBoc75 eb1a43d
add full checksum check with xor_shift_bytes for WS5029 PWM device
ProfBoc75 183f2fe
minor corrections
ProfBoc75 4062dbb
bytes convertion correction for 0x00 = ff
ProfBoc75 c7aedb9
Merge branch 'merbanan:master' into master
ProfBoc75 dae10e1
comment fn and log corrections
ProfBoc75 64b0640
Merge branch 'master' of https://github.com/ProfBoc75/rtl_433
ProfBoc75 f64bfbc
remove new line from log msg
ProfBoc75 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
/** @file | ||
Decoder for Holman Industries WS5029 weather station. | ||
AOK Electronic Limited weather station. | ||
|
||
Copyright (C) 2023 Bruno OCTAU (ProfBoc75) (improve integrity check for all devices here and add support for AOK-5056 weather station PR #2419) | ||
Copyright (C) 2023 Christian W. Zuckschwerdt <zany@triq.net> ( reverse galois and xor_shift_bytes check algorithms PR #2419) | ||
Copyright (C) 2019 Ryan Mounce <ryan@mounce.com.au> (PCM version) | ||
Copyright (C) 2018 Brad Campbell (PWM version) | ||
|
||
|
@@ -10,30 +12,37 @@ | |
(at your option) any later version. | ||
*/ | ||
/** | ||
Decoder for Holman Industries WS5029 weather station, | ||
a.k.a. Holman iWeather Station. | ||
https://www.holmanindustries.com.au/products/iweather-station/ | ||
AOK Electronic Limited weather station. | ||
|
||
Appears to be related to the Fine Offset WH1080 and Digitech XC0348. | ||
Known Rebrand compatible with: | ||
- Holman iWeather Station ws5029. https://www.holmanindustries.com.au/products/iweather-station/ | ||
- Conrad Renkforce AOK-5056 | ||
- Optex Electronique 990018 SM-018 5056 | ||
|
||
Appears to be related to the Fine pos WH1080 and Digitech XC0348. | ||
|
||
- Modulation: FSK PCM | ||
- Frequency: 917.0 MHz +- 40 kHz | ||
- 10 kb/s bitrate, 100 us symbol/bit time | ||
|
||
A transmission burst is sent every 57 seconds. Each burst consists of 3 | ||
repititions of the same 192 bit "package" separated by a 1 ms gap. | ||
repetitions of the same "package" separated by a 1 ms gap. | ||
The length of 196 or 218 bits depends on the device type. | ||
|
||
Package format: | ||
- Preamble {48}0xAAAAAAAAAAAA | ||
- Header {24}0x98F3A5 | ||
- Payload {96} see below | ||
- Checksum {8} unidentified | ||
- Trailer/Postamble {16} ??? | ||
- Payload {96 or 146} see below | ||
- zeros {36} 0 with battery ? | ||
- Checksum/CRC {8} xor 12 bytes then reverse Galois algorithm (gen = 0x00, key = 0x31) PR #2419 | ||
- Trailer/postamble {20} direction (previous ?) and 3 zeros | ||
|
||
Payload format: | ||
Payload format: Without UV Lux sensor | ||
|
||
Fixed Values 0x : AA AA AA AA AA AA 98 F3 A5 | ||
|
||
Byte (dec) 09 10 11 12 13 14 15 16 17 18 19 20 | ||
Nibble key II II CC CH HR RR WW Dx xx xx xx xx | ||
Byte position : 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | ||
Payload : II II CC CH HR RR WW Dx xx xx ?x xx ss 0d 00 0 | ||
|
||
- IIII station ID (randomised on each battery insertion) | ||
- CCC degrees C, signed, in multiples of 0.1 C | ||
|
@@ -42,80 +51,157 @@ Payload format: | |
- WW wind speed in km/h | ||
- D wind direction (0 = N, 4 = E, 8 = S, 12 = W) | ||
- xxxxxxxxx ???, usually zero | ||
- ss xor 12 bytes then reverse Galois algorithm (gen = 0x00 , key = 0x31) PR #2419 | ||
|
||
Payload format: With UV Lux sensor | ||
|
||
Fixed Values 0x : AA AA AA AA AA AA 98 F3 A5 | ||
|
||
Byte position : 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | ||
Payload : II II CC CH HR RR WW | | NN SS 0D 00 00 00 00 0 | ||
+-----------+ +-------------+ | ||
| | | ||
| 07 08 09 10 | | ||
bits details : DDDDUUUU ULLLLLLL LLLLLLLL LLBBNNNN | ||
|
||
- I station ID (randomised on each battery insertion) | ||
- C degrees C, signed, in multiples of 0.1 C | ||
- H humidity % | ||
- R cumulative rain in mm | ||
- W wind speed in km/h | ||
- D wind direction (0 = N, 4 = E, 8 = S, 12 = W) | ||
- U Index UV | ||
- L Lux | ||
- B Battery | ||
- N Payload number, increase at each message 000->FFF but not always, strange behavior. no clue | ||
- S xor 12 bytes then reverse Galois algorithm (gen = 0x00 , key = 0x31) PR #2419 | ||
- D Previous Wind direction | ||
- Fixed values to 9 zeros | ||
|
||
To get raw data | ||
$ rtl_433 -f 917M -X 'name=WS5029,modulation=FSK_PCM,short=100,long=100,preamble={48}0xAAAAAAAAAAAA,reset=19200' | ||
$ rtl_433 -f 917M -X 'name=AOK,modulation=FSK_PCM,short=100,long=100,preamble={48}0xAAAAAA98F3A5,reset=22000' | ||
|
||
@sa holman_ws5029pwm_decode() | ||
|
||
*/ | ||
|
||
#include "decoder.h" | ||
|
||
// see #2419 for more details about the xor_shift_bytes , used by PWM device | ||
static uint8_t xor_shift_bytes(uint8_t const message[], unsigned num_bytes, uint8_t shift_up) | ||
{ | ||
uint8_t result0 = 0; | ||
for (unsigned i = 0; i < num_bytes; i += 2) { | ||
result0 ^= message[i]; | ||
} | ||
uint8_t result1 = 0; | ||
for (unsigned i = 1; i < num_bytes; i += 2) { | ||
result1 ^= message[i]; | ||
} | ||
uint8_t resultx = 0; | ||
for (unsigned j = 0; j < 7; ++j) { | ||
if (shift_up & (1 << j)) | ||
resultx ^= result0 << (j + 1); | ||
} | ||
return result0 ^ result1 ^ resultx; | ||
} | ||
|
||
static int holman_ws5029pcm_decode(r_device *decoder, bitbuffer_t *bitbuffer) | ||
{ | ||
int const wind_dir_degr[] = {0, 23, 45, 68, 90, 113, 135, 158, 180, 203, 225, 248, 270, 293, 315, 338}; | ||
uint8_t const preamble[] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x98, 0xF3, 0xA5}; | ||
uint8_t const preamble[] = {0xAA, 0xAA, 0xAA, 0x98, 0xF3, 0xA5}; | ||
|
||
data_t *data; | ||
uint8_t b[24]; | ||
uint8_t b[18]; | ||
|
||
if (bitbuffer->num_rows != 1) { | ||
if (decoder->verbose) { | ||
fprintf(stderr, "%s: wrong number of rows (%d)\n", __func__, bitbuffer->num_rows); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Always use log functions |
||
} | ||
return DECODE_ABORT_EARLY; | ||
} | ||
|
||
unsigned bits = bitbuffer->bits_per_row[0]; | ||
|
||
// FSK sometimes decodes an extra bit at the start | ||
// and likely extra 2-4 bits at the end | ||
// let's allow for the leading bit and the whole gap period | ||
if (bits < 192 || bits > 203) { | ||
if (bits < 192 ) { // too small | ||
return DECODE_ABORT_LENGTH; | ||
} | ||
|
||
unsigned offset = bitbuffer_search(bitbuffer, 0, 0, preamble, sizeof (preamble) * 8); | ||
if (offset + 192 > bitbuffer->bits_per_row[0]) { | ||
unsigned pos = bitbuffer_search(bitbuffer, 0, 0, preamble, sizeof (preamble) * 8); | ||
|
||
if (pos >= bits) { | ||
return DECODE_ABORT_EARLY; | ||
} | ||
bitbuffer_extract_bytes(bitbuffer, 0, offset, b, 192); | ||
|
||
// byte 21 looks like a checksum - no success with brute force | ||
/* | ||
for (uint8_t firstbyte = 0; firstbyte < 21; firstbyte++) { | ||
for (uint8_t poly=0; poly<255; poly++) { | ||
if (crc8(&b[firstbyte], 21-firstbyte, poly, 0x00) == b[21]) { | ||
decoder_logf(decoder, 3, __func__, "CORRECT CRC8 with offset %u poly 0x%x", firstbyte, poly); | ||
} | ||
if (crc8le(&b[firstbyte], 21-firstbyte, poly, 0x00) == b[21]) { | ||
decoder_logf(decoder, 3, __func__, "CORRECT CRC8LE with offset %u poly 0x%x", firstbyte, poly); | ||
} | ||
} | ||
} | ||
*/ | ||
|
||
int device_id = (b[9] << 8) | b[10]; | ||
int temp_raw = (int16_t)((b[11] << 8) | (b[12] & 0xf0)); // uses sign-extend | ||
float temp_c = (temp_raw >> 4) * 0.1f; | ||
int humidity = ((b[12] & 0x0f) << 4) | ((b[13] & 0xf0) >> 4); | ||
int rain_raw = ((b[13] & 0x0f) << 8) | b[14]; | ||
float rain_mm = rain_raw * 0.79f; | ||
int speed_kmh = b[15]; | ||
int direction_deg = wind_dir_degr[(b[16] & 0xf0) >> 4]; | ||
decoder_logf(decoder, 2, __func__, "Found AOK preamble pos: %d", pos); | ||
|
||
/* clang-format off */ | ||
data = data_make( | ||
"model", "", DATA_STRING, "Holman-WS5029", | ||
"id", "StationID", DATA_FORMAT, "%04X", DATA_INT, device_id, | ||
"temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, | ||
"humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, | ||
"rain_mm", "Total rainfall", DATA_FORMAT, "%.01f mm", DATA_DOUBLE, rain_mm, | ||
"wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%u km/h", DATA_INT, speed_kmh, | ||
"wind_dir_deg", "Wind Direction", DATA_INT, direction_deg, | ||
NULL); | ||
/* clang-format on */ | ||
pos += sizeof(preamble) * 8; | ||
|
||
decoder_output_data(decoder, data); | ||
return 1; | ||
bitbuffer_extract_bytes(bitbuffer, 0, pos, b, sizeof(b) * 8); | ||
|
||
uint8_t chk_digest = b[12]; | ||
uint8_t chk_calc = xor_bytes(b, 12); | ||
// reverse Galois algorithm then (gen = 0x00, key = 0x31) PR #2419 | ||
int chk_expected = lfsr_digest8_reflect(&chk_calc, 1, 0x00, 0x31); | ||
|
||
if (chk_expected != chk_digest) { | ||
return DECODE_FAIL_MIC; | ||
} | ||
|
||
int device_id = (b[0] << 8) | b[1]; | ||
int temp_raw = (int16_t)((b[2] << 8) | (b[3] & 0xf0)); // uses sign-extend | ||
float temp_c = (temp_raw >> 4) * 0.1f; | ||
int humidity = ((b[3] & 0x0f) << 4) | ((b[4] & 0xf0) >> 4); | ||
int rain_raw = ((b[4] & 0x0f) << 8) | b[5]; | ||
float speed_kmh = (float)b[6]; | ||
int direction_deg = wind_dir_degr[(b[7] & 0xf0) >> 4]; | ||
|
||
if (bits < 200) { // model without UV LUX | ||
float rain_mm = rain_raw * 0.79f; | ||
|
||
/* clang-format off */ | ||
data = data_make( | ||
"model", "", DATA_STRING, "Holman-WS5029", | ||
"id", "StationID", DATA_FORMAT, "%04X", DATA_INT, device_id, | ||
"temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, | ||
"humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, | ||
"rain_mm", "Total rainfall", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_mm, | ||
"wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%.1f km/h", DATA_DOUBLE, speed_kmh, | ||
"wind_dir_deg", "Wind Direction", DATA_INT, direction_deg, | ||
"mic", "Integrity", DATA_STRING, "CHECKSUM", | ||
NULL); | ||
/* clang-format on */ | ||
|
||
decoder_output_data(decoder, data); | ||
return 1; | ||
} | ||
else if (bits < 221) { // model with UV LUX | ||
float rain_mm = rain_raw * 1.0f; | ||
int uv_index = ((b[7] & 0x07) << 1) | ((b[8] & 0x80) >> 7); | ||
int light_lux = ((b[8] & 0x7F) << 10) | (b[9] << 2) | ((b[10] & 0xC0) >> 6); | ||
int battery_low = ((b[10] & 0x30) >> 4); | ||
int counter = ((b[10] & 0x0f) << 8 | b[11]); | ||
/* clang-format off */ | ||
data = data_make( | ||
"model", "", DATA_STRING, "AOK-5056", | ||
"id", "StationID", DATA_FORMAT, "%04X", DATA_INT, device_id, | ||
"temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, | ||
"humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, | ||
"rain_mm", "Total rainfall", DATA_FORMAT, "%.1f mm", DATA_DOUBLE, rain_mm, | ||
"wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%.1f km/h", DATA_DOUBLE, speed_kmh, | ||
"wind_dir_deg", "Wind Direction", DATA_INT, direction_deg, | ||
"uv", "UV Index", DATA_FORMAT, "%u", DATA_INT, uv_index, | ||
"light_lux", "Lux", DATA_FORMAT, "%u", DATA_INT, light_lux, | ||
"counter", "Counter", DATA_FORMAT, "%u", DATA_INT, counter, | ||
"battery_ok", "battery", DATA_FORMAT, "%u", DATA_INT, !battery_low, | ||
"mic", "Integrity", DATA_STRING, "CHECKSUM", | ||
NULL); | ||
/* clang-format on */ | ||
|
||
decoder_output_data(decoder, data); | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
|
||
static char const *output_fields[] = { | ||
|
@@ -127,12 +213,15 @@ static char const *output_fields[] = { | |
"rain_mm", | ||
"wind_avg_km_h", | ||
"wind_dir_deg", | ||
"uv", | ||
"light_lux", | ||
"counter", | ||
"mic", | ||
NULL, | ||
}; | ||
|
||
r_device const holman_ws5029pcm = { | ||
.name = "Holman Industries iWeather WS5029 weather station (newer PCM)", | ||
.name = "AOK Weather Station rebrand Holman Industries iWeather WS5029, Conrad AOK-5056, Optex 990018", | ||
.modulation = FSK_PULSE_PCM, | ||
.short_width = 100, | ||
.long_width = 100, | ||
|
@@ -144,26 +233,40 @@ r_device const holman_ws5029pcm = { | |
/** | ||
Holman Industries WS5029 weather station using PWM. | ||
|
||
- The checksum used is an xor of all 11 bytes. | ||
- The bottom nybble results in 0. The top does not | ||
- and I've been unable to figure out why. We only | ||
- check the bottom nybble therefore. | ||
- Have tried all permutations of init/poly for lfsr8 & crc8 | ||
- Rain is 0.79mm / count | ||
618 counts / 488.2mm - 190113 - Multiplier is exactly 0.79 | ||
- Wind is discrete kph | ||
- Preamble is 0xaa 0xa5. Device is 0x98 | ||
Package format: (invert) | ||
- Preamble {24} 0xAAA598 | ||
- Payload {56} [ see below ] | ||
- Checksum/CRC {8} xor_shift_bytes (key = 0x18) PR #2419 | ||
- Trailer/postamble {8} 0x00 or 0x80 | ||
|
||
Payload format: | ||
|
||
Byte position : 00 01 02[03 04 05 06 07 08 09]10 11 | ||
Payload : AA A5 98 II BC CC HH RR RW WD SS 00 | ||
|
||
- I station ID | ||
- B battery low indicator | ||
- C degrees C, signed, in multiples of 0.1 C | ||
- H Humidity 0-100 % | ||
- R Rain is 0.79mm / count , 618 counts / 488.2mm - 190113 - Multiplier is exactly 0.79 | ||
- W Wind speed in km/h | ||
- D Wind direction, clockwise from North, in multiples of 22.5 deg | ||
- S xor_shift_bytes , see PR #2419 | ||
|
||
To get the raw data : | ||
$ rtl_433 -f 433.92M -X "n=Holman-WS5029-PWM,m=FSK_PWM,s=488,l=976,g=2000,r=6000,invert" | ||
|
||
*/ | ||
|
||
static int holman_ws5029pwm_decode(r_device *decoder, bitbuffer_t *bitbuffer) | ||
{ | ||
uint8_t const preamble[] = {0x55, 0x5a, 0x67}; // Preamble/Device inverted | ||
|
||
data_t *data; | ||
uint8_t *b; | ||
uint16_t temp_raw; | ||
int id, humidity, speed_kmh, wind_dir, battery_low; | ||
float temp_c, rain_mm; | ||
int id, humidity, wind_dir, battery_low; | ||
float temp_c, rain_mm, speed_kmh; | ||
|
||
// Data is inverted, but all these checks can be performed | ||
// and validated prior to inverting the buffer. Invert | ||
|
@@ -178,31 +281,36 @@ static int holman_ws5029pwm_decode(r_device *decoder, bitbuffer_t *bitbuffer) | |
if (memcmp(b, preamble, 3)) | ||
return DECODE_FAIL_SANITY; | ||
|
||
// Test Checksum. | ||
if ((xor_bytes(b, 11) & 0xF) ^ 0xF) | ||
return DECODE_FAIL_MIC; | ||
|
||
// Invert data for processing | ||
bitbuffer_invert(bitbuffer); | ||
|
||
uint8_t chk_digest = b[10]; | ||
// xor_shift_bytes , see PR #2419 | ||
int chk_calc = xor_shift_bytes(b, 10, 0x18); | ||
//fprintf(stderr, "%s: 11th byte %02x chk_calc %02x \n", __func__, chk_digest, chk_calc ); | ||
|
||
if (chk_calc != chk_digest) { | ||
return DECODE_FAIL_MIC; | ||
} | ||
|
||
id = b[3]; // changes on each power cycle | ||
battery_low = (b[4] & 0x80); // High bit is low battery indicator | ||
temp_raw = (int16_t)(((b[4] & 0x0f) << 12) | (b[5] << 4)); // uses sign-extend | ||
temp_c = (temp_raw >> 4) * 0.1f; // Convert sign extended int to float | ||
humidity = b[6]; // Simple 0-100 RH | ||
rain_mm = ((b[7] << 4) + (b[8] >> 4)) * 0.79f; // Multiplier tested empirically over 618 pulses | ||
speed_kmh = ((b[8] & 0xF) << 4) + (b[9] >> 4); // In discrete kph | ||
rain_mm = ((b[7] << 4) + (b[8] >> 4)) * 0.79f; // Multiplier tested empirically over 618 pulses | ||
speed_kmh = (float)(((b[8] & 0xF) << 4) + (b[9] >> 4)); // In discrete kph | ||
wind_dir = b[9] & 0xF; // 4 bit wind direction, clockwise from North | ||
|
||
/* clang-format off */ | ||
data = data_make( | ||
"model", "", DATA_STRING, "Holman-WS5029", | ||
"id", "", DATA_INT, id, | ||
"battery_ok", "Battery", DATA_INT, !battery_low, | ||
"temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, | ||
"humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, | ||
"rain_mm", "Total rainfall", DATA_FORMAT, "%.01f mm", DATA_DOUBLE, rain_mm, | ||
"wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%u km/h", DATA_INT, speed_kmh, | ||
"temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temp_c, | ||
"humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, | ||
"rain_mm", "Total rainfall", DATA_FORMAT, "%.01f mm", DATA_DOUBLE, rain_mm, | ||
"wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%.01f km/h", DATA_DOUBLE, speed_kmh, | ||
"wind_dir_deg", "Wind Direction", DATA_INT, (int)(wind_dir * 22.5), | ||
"mic", "Integrity", DATA_STRING, "CHECKSUM", | ||
NULL); | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 14 now needs to be
/** int holman_ws5029pcm_decode(r_device *decoder, bitbuffer_t *bitbuffer)
otherwise the doc-comment above attaches to this helper function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, but I moved the xor_shift_bytes function to the bottom and enrich both doc-comments for PCM and PWM.
Let me know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good, thanks. One thing:
%.01f
is not a sensible format ;) It is often in our code but it should be%.1f
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it was a copy paste of %.01f from the initial author, I just commit these "minor" corrections.