The following warnings occurred:
Warning [2] count(): Parameter must be an array or an object that implements Countable - Line: 895 - File: showthread.php PHP 7.3.14-1~deb10u1 (Linux)
File Line Function
/showthread.php 895 errorHandler->error



  • 10 dec 2017: forum version update. In case of issues use this topic.
  • 30 nov 2017: pilight moved servers. In case of issues use this topic.
Hello There, Guest! Login Register


Pearl weatherstation sensor for FWS-686 & FWS-665
#1
Hi everybody,

I've bought a weatherstation sensor from Pearl branded as infactory wireless sensor for FWS-686 & FWS-665. It didn't work out of the box with pilight, but I've reverse engineered the protocol to some extend.

I've used the tfa source code to make some new draft protocol code. That's why I just renamed every occurrence of tfa to tfa2. Here is the tfa2.c:

Code:
/*
    Copyright (C) 2014 CurlyMo

    This file is part of pilight.

    pilight is free software: you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
    Foundation, either version 3 of the License, or (at your option) any later
    version.

    pilight is distributed in the hope that it will be useful, but WITHOUT ANY
    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
    A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with pilight. If not, see    <http://www.gnu.org/licenses/>
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "../../core/pilight.h"
#include "../../core/common.h"
#include "../../core/dso.h"
#include "../../core/log.h"
#include "../protocol.h"
#include "../../core/binary.h"
#include "../../core/gc.h"
#include "tfa2.h"

#define PULSE_MULTIPLIER    6
#define MIN_PULSE_LENGTH    460
#define MAX_PULSE_LENGTH    530
#define AVG_PULSE_LENGTH    471
#define RAW_LENGTH                82

typedef struct settings_t {
    double id;
    double channel;
    double temp;
    double humi;
    struct settings_t *next;
} settings_t;

static struct settings_t *settings = NULL;

static int validate(void) {
    if(tfa2->rawlen == RAW_LENGTH) {
        if(tfa2->raw[tfa2->rawlen-1] >= (MIN_PULSE_LENGTH*PULSE_DIV) &&
           tfa2->raw[tfa2->rawlen-1] <= (MAX_PULSE_LENGTH*PULSE_DIV)) {
            return 0;
        }
    }

    return -1;
}

static void parseCode(void) {
    int binary[RAW_LENGTH/2];
    int temp1 = 0;//, temp2 = 0, temp3 = 0;
    int humi1 = 0, humi2 = 0;
    int id = 0, battery = 0;
    int channel = 0;
    int i = 0, x = 0;
    double humi_offset = 0.0, temp_offset = 0.0;
    double temperature = 0.0, humidity = 0.0;
    char binstr[RAW_LENGTH/2+1];

    for(x=1;x<tfa2->rawlen-2;x+=2) {
        if(tfa2->raw[x] > AVG_PULSE_LENGTH*PULSE_MULTIPLIER) {
            binstr[i]='1';
            binary[i++] = 1;
        } else {
            binstr[i]='0';
            binary[i++] = 0;
        }
    }
    binstr[i]='\0';

    id = binToDecRev(binary, 0, 7);
    channel = binToDecRev(binary, 38, 39);

    temp1 = binToDecRev(binary, 16, 27);
                                                         /* Convert F to C */
    temperature = (int)((float)(((temp1*10) - 9000) - 3200) * ((float)5/(float)9));

    humi1 = binToDecRev(binary, 28, 31);
    humi2 = binToDecRev(binary, 32, 35);
    humidity = ((humi1*10)+(humi2));

    if(binToDecRev(binary, 36, 37) > 1) {
        battery = 0;
    } else {
        battery = 1;
    }

    struct settings_t *tmp = settings;
    while(tmp) {
        if(fabs(tmp->id-id) < EPSILON && fabs(tmp->channel-channel) < EPSILON) {
            humi_offset = tmp->humi;
            temp_offset = tmp->temp;
            break;
        }
        tmp = tmp->next;
    }

    temperature += temp_offset;
    humidity += humi_offset;

    tfa2->message = json_mkobject();
    json_append_member(tfa2->message, "id", json_mknumber(id, 0));
    json_append_member(tfa2->message, "temperature", json_mknumber(temperature/100, 2));
    json_append_member(tfa2->message, "humidity", json_mknumber(humidity, 2));
    json_append_member(tfa2->message, "battery", json_mknumber(battery, 0));
    json_append_member(tfa2->message, "channel", json_mknumber(channel, 0));
    json_append_member(tfa2->message, "binary", json_mkstring(binstr));
}

static int checkValues(struct JsonNode *jvalues) {
    struct JsonNode *jid = NULL;

    if((jid = json_find_member(jvalues, "id"))) {
        struct settings_t *snode = NULL;
        struct JsonNode *jchild = NULL;
        struct JsonNode *jchild1 = NULL;
        double channel = -1, id = -1;
        int match = 0;

        jchild = json_first_child(jid);
        while(jchild) {
            jchild1 = json_first_child(jchild);
            while(jchild1) {
                if(strcmp(jchild1->key, "channel") == 0) {
                    channel = jchild1->number_;
                }
                if(strcmp(jchild1->key, "id") == 0) {
                    id = jchild1->number_;
                }
                jchild1 = jchild1->next;
            }
            jchild = jchild->next;
        }

        struct settings_t *tmp = settings;
        while(tmp) {
            if(fabs(tmp->id-id) < EPSILON && fabs(tmp->channel-channel) < EPSILON) {
                match = 1;
                break;
            }
            tmp = tmp->next;
        }

        if(match == 0) {
            if((snode = MALLOC(sizeof(struct settings_t))) == NULL) {
                fprintf(stderr, "out of memory\n");
                exit(EXIT_FAILURE);
            }
            snode->id = id;
            snode->channel = channel;
            snode->temp = 0;
            snode->humi = 0;

            json_find_number(jvalues, "temperature-offset", &snode->temp);
            json_find_number(jvalues, "humidity-offset", &snode->humi);

            snode->next = settings;
            settings = snode;
        }
    }
    return 0;
}

static void gc(void) {
    struct settings_t *tmp = NULL;
    while(settings) {
        tmp = settings;
        settings = settings->next;
        FREE(tmp);
    }
    if(settings != NULL) {
        FREE(settings);
    }
}

#if !defined(MODULE) && !defined(_WIN32)
__attribute__((weak))
#endif
void tfa2Init(void) {
    protocol_register(&tfa2);
    protocol_set_id(tfa2, "tfa2");
    protocol_device_add(tfa2, "tfa2", "inFactory weather sensor");
    tfa2->devtype = WEATHER;
    tfa2->hwtype = RF433;
    tfa2->maxgaplen = MAX_PULSE_LENGTH*PULSE_DIV;
    tfa2->mingaplen = MIN_PULSE_LENGTH*PULSE_DIV;
    tfa2->minrawlen = 82;
    tfa2->maxrawlen = 82;
    
    options_add(&tfa2->options, 't', "temperature", OPTION_HAS_VALUE, DEVICES_VALUE, JSON_NUMBER, NULL, "^[0-9]{1,3}$");
    options_add(&tfa2->options, 'i', "id", OPTION_HAS_VALUE, DEVICES_ID, JSON_NUMBER, NULL, "[0-9]");
    options_add(&tfa2->options, 'c', "channel", OPTION_HAS_VALUE, DEVICES_ID, JSON_NUMBER, NULL, "[0-9]");
    options_add(&tfa2->options, 'h', "humidity", OPTION_HAS_VALUE, DEVICES_VALUE, JSON_NUMBER, NULL, "[0-9]");
    options_add(&tfa2->options, 'b', "battery", OPTION_HAS_VALUE, DEVICES_VALUE, JSON_NUMBER, NULL, "^[01]$");

    // options_add(&tfa2->options, 0, "decimals", OPTION_HAS_VALUE, DEVICES_SETTING, JSON_NUMBER, (void *)2, "[0-9]");
    options_add(&tfa2->options, 0, "temperature-offset", OPTION_HAS_VALUE, DEVICES_SETTING, JSON_NUMBER, (void *)0, "[0-9]");
    options_add(&tfa2->options, 0, "humidity-offset", OPTION_HAS_VALUE, DEVICES_SETTING, JSON_NUMBER, (void *)0, "[0-9]");
    options_add(&tfa2->options, 0, "temperature-decimals", OPTION_HAS_VALUE, GUI_SETTING, JSON_NUMBER, (void *)2, "[0-9]");
    options_add(&tfa2->options, 0, "humidity-decimals", OPTION_HAS_VALUE, GUI_SETTING, JSON_NUMBER, (void *)2, "[0-9]");
    options_add(&tfa2->options, 0, "show-humidity", OPTION_HAS_VALUE, GUI_SETTING, JSON_NUMBER, (void *)1, "^[10]{1}$");
    options_add(&tfa2->options, 0, "show-temperature", OPTION_HAS_VALUE, GUI_SETTING, JSON_NUMBER, (void *)1, "^[10]{1}$");
    options_add(&tfa2->options, 0, "show-battery", OPTION_HAS_VALUE, GUI_SETTING, JSON_NUMBER, (void *)1, "^[10]{1}$");
    
    tfa2->parseCode=&parseCode;
    tfa2->checkValues=&checkValues;
    tfa2->validate=&validate;
    tfa2->gc=&gc;
}

#if defined(MODULE) && !defined(_WIN32)
void compatibility(struct module_t *module) {
    module->name = "tfa2";
    module->version = "1.0";
    module->reqversion = "6.0";
    module->reqcommit = "84";
}

void init(void) {
    tfa2Init();
}
#endif

It works so far, but up to now, I've only found out the meaning of some of the bits sent by the sensor, but not all.

Following, I post a couple of bit-streams from the sensor with corresponding temperature and humidity values. The last 3 digit is the long footer pulse.
I assume, that the first 8 bits are the id, since they change whenever I put out the batteries.
After that, there are 8 bits, I have no clue about. They change every telegram seemingly random, so it could be a checksum. On the other hand, Pearl claims, that this sensor also measures the pressure. While I actually doubt it, it could have to do something with that.
The next 8 bits represent the temperature+90.0°F in Fahrenheit as a binary number with one decimal place, so you need to subtract 90.0°F from it.
After that there are 8 bits for the humidity, divided in 2 groups of 4, each one presenting one digit of the decimal value in binary.
The next two bits could be the battery state.
The last two bits represent the channel 1-3, which can be selected by a switch.

Code:
00010000100010010110011101100100001100013 - 24.1°C 43%
00010000000110010110011101010100010100013 - 24.0°C 45%
0001000001??10100110011010110100010000013 - 23.5°C 44%
00010000011010100110011010110?00010000013 - 23.5°C 44%
00010000110010100110010111000100010000013 - 22.6°C 44%
00010000111110100110001101100100011100013 - 20.5°C 47%
00010000000010100110001101100100100000013 - 20.5°C 48%
00010000011010100110001110010100100000013 - 20.7°C 48%
00010000001110100110001110000100100000013 - 20.6°C 48%
00010000000010100110001101100100100000013 - 20.5°C 48%
00010000101010100110001101000100100000013 - 20.4°C 48%
00010000011010100110001110010100100000013 - 20.7°C 48%
00010000111110100110001101100100011100013 - 20.5°C 47%
00010111010110000110001000110100100100013 - 19.5°C 49%
00010111011010000110000110100101000000013 - 19.0°C 66.2°F 50%
00010111111110000110000111010101000100013 - 19.1°C 66.5°F 51%
00010111000010000110001000000101000000013 - 19.3°C 66.8°F 50%
00010111110010000110001001100101000100013 - 19.6°C 67.4°F 51%
00010111111110000110001010000101000100013 - 19.7°C 67.6°F 51%
00010111000010000110001010110101000100013 - 19.9°C 67.9°F 51%
00010111001010000110001011100101000100013 - 20.1°C 68.2°F 51%
00010111010100010110001110010101001000013 - 20.7°C 69.3°F 52%

Okay, I now need your help in deciphering the second group of 8 bits and further more, I need some hints as to how I get the code into a style, that it could appear in the official pilight version, and how to get it there.

Since this is my first post in this forum, please let me know, if I've violated some rules or whatever.

Cheers
 
Reply
  


Messages In This Thread
Pearl weatherstation sensor for FWS-686 & FWS-665 - by chklump - 06-08-2017, 01:55 PM

Possibly Related Threads...
Thread Author Replies Views Last Post
  [Fully Supported] KlikAanKlikUit Motion Sensor koen01 37 15,215 03-26-2020, 02:46 PM
Last Post: Gisto
  Digoo / Baldr / Nexus / Rubicson temperature/humidity sensor thielj 12 3,087 02-10-2020, 10:54 PM
Last Post: ha_username
  Support for Temperaturee sensor clona 4 470 02-10-2020, 02:52 PM
Last Post: clona
  gs-iwds07 window sensor Loggisch 48 17,348 12-09-2019, 07:14 PM
Last Post: curlymo
Lightbulb [Fully Supported] Kaku Door sensor (AMST-606) geerttttt 53 28,365 10-19-2019, 06:26 PM
Last Post: curlymo
  433MHz PIR sensor from Amazon ha_username 0 706 09-29-2019, 11:44 PM
Last Post: ha_username
  Weatherstation ADE WS 1503 Rschnauzer 5 2,288 01-19-2019, 07:53 PM
Last Post: Rschnauzer
  TFA 30.3160 Pool Sensor wseifert 4 1,479 05-30-2018, 09:24 AM
Last Post: wseifert
Lightbulb BH1750 i2c digital illuminance sensor marcm 14 4,885 02-25-2018, 08:40 PM
Last Post: morph027
  [Fully Supported] LM75 and LM76 temperature sensor horst_dieter 64 28,813 11-19-2017, 08:54 PM
Last Post: edepi

Forum Jump:


Browsing: 1 Guest(s)