/*
Teile aus ESPiLight v0.17.0
pilight 433.92 MHz protocols library for Arduino. With this port of pilight 433.92 MHz protocols,
you can transmit, receive and parse all 434 MHz protocols (e.g., rc switches or weather stations)
supported by pilight. This should help to implement IoT bridges between the 434MHz-RF band and
internet protocols. It is developed and tested on ESP8266.
*/
#define PULSE_DIV 34
/*
Macros to help convertion of a value from and to bits.
The value-variable passed to VALUE_TO_BITS_MSB_FIRST()
and VALUE_TO_BITS_LSB_FIRST() MUST be an unsigned type!
Macros have side effects to almost all variables passed!
*/
#define BITS_LSB_FIRST_TO_VALUE(bits, s, e, result) \
unsigned long long mask = 1; \
result = 0; \
for(; s<=e; mask <<= 1) \
if(bits[s++] != 0) \
result |= mask
#define BITS_MSB_FIRST_TO_VALUE(bits, s, e, result) \
unsigned long long mask = 1; \
result = 0; \
e++; \
for(; e > 0 && s<e; mask <<= 1) \
if(bits[--e] != 0) \
result |= mask
#define VALUE_TO_BITS_MSB_FIRST(value, bits, length) \
unsigned long long mask = value; \
do bits++; while (mask >>= 1); \
int *start = bits; \
do *--start = value & 1; while (value >>= 1); \
length = bits - start
#define VALUE_TO_BITS_LSB_FIRST(value, bits, length) \
int *start = bits; \
do *bits++ = value & 1; while (value >>= 1); \
length = bits - start
/*
Copyright (C) 2013 - 2016 CurlyMo
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef _MEM_H_
#define _MEM_H_
#define OUT_OF_MEMORY fprintf(stderr, "out of memory in %s #%d\n", __FILE__, __LINE__),exit(EXIT_FAILURE);
#ifdef __GNUC__
#define atomic_inc(ptr) __sync_add_and_fetch(&(ptr), 1)
#define atomic_dec(ptr) __sync_add_and_fetch(&(ptr), -1)
#elif defined (_WIN32)
#define atomic_inc(ptr) InterlockedIncrement(&(ptr))
#define atomic_dec(ptr) InterlockedDecrement(&(ptr))
#else
#error "Need some more porting work here"
#endif
int xfree(void);
void memtrack(void);
void *__malloc(unsigned long, const char *, int);
void *__realloc(void *, unsigned long, const char *, int);
void *__calloc(unsigned long a, unsigned long b, const char *, int);
void __free(void *, const char *, int);
/*
Windows already has a _strdup, libpcap uses __strdup
*/
char *___strdup(char *, const char *, int);
// #define MALLOC(a) __malloc(a, __FILE__, __LINE__)
// #define REALLOC(a, b) __realloc(a, b, __FILE__, __LINE__)
// #define CALLOC(a, b) __calloc(a, b, __FILE__, __LINE__)
// #define STRDUP(a) ___strdup(a, __FILE__, __LINE__)
// #define FREE(a) __free((void *)(a), __FILE__, __LINE__),(a)=NULL
#define MALLOC malloc
#define REALLOC realloc
#define CALLOC calloc
#define STRDUP strdup
#define FREE(a) free((void *)(a)),(a)=NULL
// #define _MALLOC malloc
// #define _REALLOC realloc
// #define _CALLOC calloc
// #define _STRDUP strdup
// #define _FREE free
#endif
typedef struct protocol_t {
char *id;
uint8_t rawlen;
uint16_t *raw;
} protocol_t;
int binToDecRev(const int *binary, int s, int e) { // 0<=s<=e, binary[s(msb) .. e(lsb)]
int result;
BITS_MSB_FIRST_TO_VALUE(binary, s, e, result);
return result;
}
int binToSignedRev(const int *binary, int s, int e) { // 0<=s<=e, binary[s(msb) .. e(lsb)]
int result = binToDecRev(binary, s, e);
if (binary[s]) {
result -= 1 << (e - s + 1);
}
return result;
}
/*
Ende der Teile aus ESPiLight v0.17.0
*/
/*
Beginn 433.92 MHz Protokoll für Aldi Funkwecker GT-TS-04 mit Aussentemperarur SKL-W1B.
*/
protocol_t *skl;
#define MIN_STOP_PULSE_LENGTH 72
#define MAX_STOP_PULSE_LENGTH 74
#define MIN_FOOTER_PULSE_LENGTH 261
#define MAX_FOOTER_PULSE_LENGTH 263
#define PULSE_LENGTH 481
#define LOW_PULSE_MULTIPLIER 4.15
#define HIGH_PULSE_MULTIPLIER 8.26
#define STOP_PULSE_MULTIPLIER 5.16
#define FOOTER_PULSE_MULTIPLIER 18.54
#define RAW_LENGTH 76
enum bits
{
UNKNOWN_BIT,
LOW_BIT,
HIGH_BIT,
STOP_BIT,
FOOTER_BIT
};
uint8_t getBitType(uint16_t len)
{
if (len >= (MIN_STOP_PULSE_LENGTH * PULSE_DIV) &&
len <= (MAX_STOP_PULSE_LENGTH * PULSE_DIV))
return STOP_BIT;
if (len >= (MIN_FOOTER_PULSE_LENGTH * PULSE_DIV) &&
len <= (MAX_FOOTER_PULSE_LENGTH * PULSE_DIV))
return FOOTER_BIT;
if (len > (PULSE_LENGTH * STOP_PULSE_MULTIPLIER))
return HIGH_BIT;
if (len > (PULSE_LENGTH * 2))
return LOW_BIT;
return UNKNOWN_BIT;
}
void initRaw(void) {
/*
Gemessene Pulsetrain.
*/
skl->rawlen = 76;
uint16_t newRaw[255];
skl->raw = newRaw;
skl->raw[0] = 485;
skl->raw[1] = 3959;
skl->raw[2] = 505;
skl->raw[3] = 1969;
skl->raw[4] = 505;
skl->raw[5] = 1984;
skl->raw[6] = 486;
skl->raw[7] = 3957;
skl->raw[8] = 485;
skl->raw[9] = 1999;
skl->raw[10] = 485;
skl->raw[11] = 3977;
skl->raw[12] = 485;
skl->raw[13] = 3977;
skl->raw[14] = 465;
skl->raw[15] = 3976;
skl->raw[16] = 485;
skl->raw[17] = 3973;
skl->raw[18] = 485;
skl->raw[19] = 3975;
skl->raw[20] = 465;
skl->raw[21] = 3975;
skl->raw[22] = 485;
skl->raw[23] = 3974;
skl->raw[24] = 485;
skl->raw[25] = 1998;
skl->raw[26] = 493;
skl->raw[27] = 1978;
skl->raw[28] = 485;
skl->raw[29] = 1977;
skl->raw[30] = 484;
skl->raw[31] = 2000;
skl->raw[32] = 485;
skl->raw[33] = 1985;
skl->raw[34] = 485;
skl->raw[35] = 2000;
skl->raw[36] = 465;
skl->raw[37] = 2000;
skl->raw[38] = 485;
skl->raw[39] = 1999;
skl->raw[40] = 465;
skl->raw[41] = 3979;
skl->raw[42] = 485;
skl->raw[43] = 3978;
skl->raw[44] = 465;
skl->raw[45] = 3978;
skl->raw[46] = 484;
skl->raw[47] = 3966;
skl->raw[48] = 485;
skl->raw[49] = 1999;
skl->raw[50] = 465;
skl->raw[51] = 3977;
skl->raw[52] = 484;
skl->raw[53] = 3977;
skl->raw[54] = 485;
skl->raw[55] = 1998;
skl->raw[56] = 464;
skl->raw[57] = 1998;
skl->raw[58] = 485;
skl->raw[59] = 1998;
skl->raw[60] = 465;
skl->raw[61] = 2003;
skl->raw[62] = 485;
skl->raw[63] = 2003;
skl->raw[64] = 464;
skl->raw[65] = 3975;
skl->raw[66] = 485;
skl->raw[67] = 1997;
skl->raw[68] = 464;
skl->raw[69] = 3975;
skl->raw[70] = 484;
skl->raw[71] = 2000;
skl->raw[72] = 485;
skl->raw[73] = 2465;
skl->raw[74] = 505;
skl->raw[75] = 8907;
delay(1); // ohne das ist raw nicht gefüllt.
}
static int validate(void) {
if (skl->rawlen == RAW_LENGTH) {
if (getBitType(skl->raw[skl->rawlen - 3]) == STOP_BIT &&
getBitType(skl->raw[skl->rawlen - 1]) == FOOTER_BIT)
{
return 0;
}
}
return -1;
}
static void parseCode(void) {
float temperature = 0.0;
int binary[RAW_LENGTH / 2 - 2];
uint16_t id = 0;
uint8_t battery = 0;
uint8_t code = 0;
uint8_t i = 0, x = 0;
if (skl->rawlen > RAW_LENGTH) {
Serial.println("skl: parsecode - invalid parameter passed " + String(skl->rawlen));
return;
}
/*
Nur jeder zweite Puls enthält Informationen. Getrennt durch und beginnend mit einem Taktpuls.
Die Informationen werden als ein vielfaches der einfachen Taktpulslänge abgebildet und
können wie folgt interpretiert werden:
4 X Pulslänge = logisch 0
8 X Pulslänge = logisch 1
5 X Pulslänge = Ende des Codes
18 X Pulslänge = Footer, dient als Trigger für ESPiLight,
damit die Pulstrain geparsed wird.
*/
for (x = 1; x < skl->rawlen - 4; x += 2) {
uint8_t bitType = getBitType(skl->raw[x]);
if (bitType == HIGH_BIT)
binary[i++] = 1;
if (bitType == LOW_BIT)
binary[i++] = 0;
}
id = binToDecRev(binary, 0, 11);
battery = !binary[12];
temperature = binToSignedRev(binary, 16, 27) / 10.0;
code = binToDecRev(binary, 28, 35);
printProtocolDetail(binary);
printCodeData(id, temperature, battery, code);
}
int createCode(uint16_t id, float temperature, uint8_t battery, uint8_t code)
{
if (id == -1 || temperature == -999)
{
Serial.println("skl: insufficient number of arguments");
return EXIT_FAILURE;
}
else
{
uint16_t newRaw[255];
skl->raw = newRaw;
skl->rawlen = 0;
binaryToRaw(id, 12);
if (battery == 1)
battery = 8;
binaryToRaw(battery, 4);
binaryToRaw((int)(temperature * 10), 12);
binaryToRaw(code, 8);
completeRaw();
}
return EXIT_SUCCESS;
}
void binaryToRaw(int value, uint8_t len) {
char binary[32];
itoa(value, binary, 2);
uint8_t binaryLen = 1;
if (binary != "0")
binaryLen = strlen(binary);
for (uint8_t i = 0; i < len; i++) {
if (len - i > binaryLen) {
writeRawBit(0);
} else if (binary[binaryLen - (len - i)] == '1') {
writeRawBit(1);
} else {
writeRawBit(0);
}
}
}
void writeRawBit(bool value) {
writeRaw(PULSE_LENGTH);
if (value)
writeRaw(PULSE_LENGTH * HIGH_PULSE_MULTIPLIER);
else
writeRaw(PULSE_LENGTH * LOW_PULSE_MULTIPLIER);
}
void writeRaw(uint16_t pulseLen) {
skl->raw[skl->rawlen] = pulseLen;
//Serial.println("[" + String(skl->rawlen) + "] " + String(skl->raw[skl->rawlen]));
skl->rawlen++;
}
void completeRaw(void) {
writeRaw(PULSE_LENGTH);
writeRaw(PULSE_LENGTH * STOP_PULSE_MULTIPLIER);
writeRaw(PULSE_LENGTH);
writeRaw(PULSE_LENGTH * FOOTER_PULSE_MULTIPLIER);
}
uint8_t multiplierFromPulse(uint16_t pulse)
{
/*
Puls glätten, runden auf 100er Werte
*/
return round(round((uint16_t)pulse / 100.0) * 100 / (float)PULSE_LENGTH);
}
void printProtocolDetail(int binary[]) {
Serial.println("Protocol: skl");
Serial.println("Model: " + String(skl->id));
Serial.println("pulsetrain:");
for (int x=1;x<RAW_LENGTH;x+=2){
Serial.print("|" + String(skl->raw[x]));
}
Serial.println();
Serial.println("Signal:");
// ┌─┐
// ┘ └
String line1 = "┌";
String line2 = "┘";
uint8_t cnt = 0;
for (int x = 0; x < skl->rawlen; x++) {
uint8_t isHigh = (x % 2 == 0);
String addChar = "";
uint8_t j = 0;
uint8_t bitType = getBitType(skl->raw[x]);
String label = "UNKNOWN";
if (bitType == HIGH_BIT)
label = " 1";
if (bitType == LOW_BIT)
label = " 0";
if (bitType == STOP_BIT)
label = "STOP";
if (bitType == FOOTER_BIT)
label = "FOOTER";
for (uint8_t i = 0; i < multiplierFromPulse(skl->raw[x]); i++) {
if (i < label.length()) {
addChar = label.substring(j, j + 1);
j++;
} else {
j = 0;
addChar = " ";
}
if (!isHigh) {
line1 += addChar;
line2 += "─";
}
else {
line1 += "─";
line2 += " ";
}
}
if (!isHigh) {
cnt++;
if (cnt == 8) {
Serial.println(line1);
Serial.println(line2);
line1 = "";
line2 = "";
cnt = 0;
}
}
if (isHigh) {
line1 += "┐";
line2 += "└";
}
else {
line1 += "┌";
line2 += "┘";
}
}
Serial.println(line1);
Serial.println(line2);
Serial.println("Pulse: " + String(skl->rawlen));
Serial.println("Daten Pulse: " + String(skl->rawlen / 2) + " LOW");
Serial.println("Pulslänge (PL): " + String(PULSE_LENGTH) + ", gerundet auf 100er");
Serial.println("Definition: logisch 1 = 8PL, logisch 0 = 4PL, STOP = 5PL, FOOTER = 18PL");
Serial.println("Code Trigger: > STOP - 1PL");
Serial.println("Bits: " + String((skl->rawlen / 2) - 2));
Serial.println();
Serial.println("Interpretation Binärcode:");
for (int i = 0; i < RAW_LENGTH / 2 - 2; i++) {
Serial.print(String(binary[i]));
if (i == 11 || i == 15 || i == 27)
Serial.print(" ");
}
Serial.println();
Serial.println("^----------^|^ |^----------^|^------^");
Serial.println("Sensor Id |Bat.|Temperatur |Unbekannt");
Serial.println();
}
void printCodeData(uint16_t id, float temperature, uint8_t battery, uint8_t code) {
Serial.println("Daten:");
Serial.println("Sensor Id: " + String(id));
if (battery)
Serial.println("Batterie: OK");
else
Serial.println("Batterie: SCHWACH");
Serial.println("Temperatur: " + String(temperature, 1) + " °C");
Serial.println("Unbekannt: " + String(code) + " - fester Wert?");
}
void setup() {
Serial.begin(115200);
skl = MALLOC(sizeof(struct protocol_t));
skl->id = "GT-TS-04 - SKL-W1B";
Serial.println("Empfangen, verwende gemessene Pulstrain");
Serial.println("───────────────────────────────────────");
initRaw();
if (validate() == 0)
parseCode();
Serial.println("Senden, verwende erzeugte Pulstrain");
Serial.println("───────────────────────────────────");
createCode(2431, -21.6, 1, 10);
if (validate() == 0)
parseCode();
}
void loop() {
}