Датчик дождя Hydreon RG-11
Jun. 29th, 2022 11:44 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
![[profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Документация у этой железяки весьма скудная: куча описания того, как его подключить к дворникам (странно было бы видеть на крыше легковушки такую "дуру") или просто к релюхе, которая кричит "ахтунг" при начале дождя или ливня (можно переключателями настроить пороговые уровни). Но вот выдача данных по последовательному порту совсем уж малодокументирована. В основном мануале сказано, что, мол, читай вспомогательный. Во вспомогательном немного написано, а что до протокола, мол, читай файлик RG-11_12.ser (в котором в непонятной форме тупо описана структура данных и битовые поля, вообще без пояснений, что какое поле знает, мол, сам по названию поймешь).
Первые попытки подключиться к датчику окончились неудачей: валилась всякая откровенная дрянь. В мануале все было четко: судя по картинке, на выходе МК стоит обычный биполярный транзистор-повторитель, подтягивающий шину к +5В, когда МК выдает высокий уровень. И что-то сразу я не подумал посмотреть на осциллографе, как выглядит разделитель между пакетами (оказалось, что там был низкий уровень, но это было позже). Нагуглил, что, оказывается, сигнал-то там инвертированный (а в документации про это — ни слова)! ОК, смонтировал на макетке, используя мосфет, которым я включал питание для ИК-датчика.

И таки пошли нормальные данные!!!
ОК, набросал код:
#include <stdio.h>
#include <usefull_macros.h>
typedef struct{
uint8_t PeakRS;
uint8_t SPeakRS;
uint8_t RainAD8;
uint8_t LRA;
uint8_t TransRat;
uint8_t AmbLNoise;
uint8_t RGBits;
uint8_t SlowRegIngex;
uint8_t SlowRegValue;
} rg11;
typedef struct{
uint8_t RevLevel; // 12
uint8_t EmLevel; // 30..80
uint8_t RecEmStr; // 60..66
uint8_t ABLevel; // 10
uint8_t TmprtrF; // 70..100
uint8_t PUGain; // 34..39
uint8_t ClearTR; // 60..170
uint8_t AmbLight;
uint8_t Bucket;
uint8_t Barrel;
uint8_t RGConfig;
uint8_t DwellT;
uint8_t SinceRn;
uint8_t MonoStb;
uint8_t LightAD; // 120..136
uint8_t RainThr;
} slowregs;
// RGBits values:
#define PkOverThr (1<<0)
#define Raining (1<<1)
#define Out1On (1<<2)
#define HtrOn (1<<3)
#define IsDark (1<<4)
#define Cndnstn (1<<5)
#define Freeze (1<<6)
#define Storm (1<<7)
static const char* rgbitnames[8] = {
"PkOverThr",
"Raining",
"Out1On",
"HtrOn",
"IsDark",
"Cndnstn",
"Freeze",
"Storm"
};
static const char *slowregnames[16] = {
"RevLevel",
"EmLevel",
"RecEmStr",
"ABLevel",
"TmprtrF",
"PUGain",
"ClearTR",
"AmbLight",
"Bucket",
"Barrel",
"RGConfig",
"DwellT",
"SinceRn",
"MonoStb",
"LightAD",
"RainThr",
};
// minimal packet length (without slow registers)
#define REGMINLEN (14)
// standard packet length
#define REGLEN (18)
#define BUFLEN (32)
static int getv(char s, uint8_t *v){
if(s >= '0' && s <= '9'){
*v = s - '0';
return 1;
}else if(s >= 'a' && s <= 'f'){
*v = 10 + s - 'a';
return 1;
}
return 0;
}
static int encodepacket(const char *buf, int len){
uint8_t databuf[REGLEN/2] = {0};
static slowregs slow = {0};
if(len != REGMINLEN && len != REGLEN) return 0;
for(int i = 0; i < len; ++i){
int l = i&1; // low part
int idx = i/2; // data index
uint8_t v;
if(!getv(buf[i], &v)) return 0;
if(l) databuf[idx] |= v;
else databuf[idx] |= v << 4;
}
rg11 r = *((rg11*)databuf);
printf("PeakRS=%d\n", r.PeakRS); // 0..5
printf("SPeakRS=%d\n", r.SPeakRS); // 0..5
printf("RainAD8=%d\n", r.RainAD8); // 64..192
printf("LRA=%d\n", r.LRA); // ?
printf("TransRat=%d\n", r.TransRat); // 60..170
printf("AmbLNoise=%d\n", r.AmbLNoise); // ?
if(r.RGBits){
for(int i = 0; i < 8; ++i){
if(r.RGBits & 1<<i) printf("%s=1\n", rgbitnames[i]);
}
}
uint8_t *s = (uint8_t*) &slow;
if(len == REGLEN){
if(r.SlowRegIngex < 16){
s[r.SlowRegIngex] = r.SlowRegValue;
}
}
for(int i = 0; i < 16; ++i){
printf("%s=%u\n", slowregnames[i], s[i]);
}
printf("\n\n");
return 1;
}
static int getpacket(TTY_descr *d){
static int startpacket = 0, buflen = 0;
static char strbuf[BUFLEN];
int l = read_tty(d);
if(l < 1) return 0;
char s = d->buf[0];
if(s == 's'){
startpacket = 1;
if(buflen){
l = encodepacket(strbuf, buflen);
buflen = 0;
return l;
}
}else{
startpacket = 0;
strbuf[buflen++] = s;
if(buflen >= BUFLEN){
WARNX("Buffer overfull");
buflen = 0;
}
}
return 0;
}
int main(){
TTY_descr *x = new_tty("/dev/ttyUSB0", 1200, 1);
if(!x) return 1;
x = tty_open(x, 1);
if(!x) return 2;
double t0 = dtime();
while(dtime() - t0 < 10.){
if(getpacket(x)) t0 = dtime();
}
close_tty(&x);
return 0;
}
И на выходе стал получать нечто вроде
PeakRS=0 SPeakRS=0 RainAD8=112 LRA=122 TransRat=148 AmbLNoise=0 Raining=1 IsDark=1 Storm=1 RevLevel=14 EmLevel=38 RecEmStr=65 ABLevel=10 TmprtrF=85 PUGain=36 ClearTR=143 AmbLight=0 Bucket=12 Barrel=7 RGConfig=0 DwellT=5 SinceRn=0 MonoStb=15 LightAD=127 RainThr=12
Остается лишь разобраться, что там придумали авторы, т.к. их волшебный файлик 'RG-11_12.ser' вообще ни хрена никакой информации не содержит, кроме грубого описания битовых полей некоторых регистров, да имен регистров с их порядковыми номерами!
Разве что стало понятно, что PeakRS и SPeakRS отражают информацию о "каплях", LRA - счетчик от последнего детектирования "дождя", а остальное — да хрен бы и знал!.. Но, по крайней мере, уже с этой информацией можно как-то жить.
Гугол насчет значения полей датчика ничего не подсказал. Народ обычно использует лишь его релюшку, которая срабатывает, в зависимости от настроек переключателями на плате. Но мы же идем другим путем! И путь этот оказался вполне себе правильным. В общем, следующей стадией испытаний будет подключить провода, загерметизировать датчик обратно, да провести испытания в тазике. Жена предложила множество вариантов: от покапать пипеткой и побрызгать из пульверизатора до "обоссать" ☺