W poprzednim wpisie opisałem łamanie protokołu termometru ThermoPro TP-08. W tym umieszczam kawałek kodu, pozwalający skorzystać ze zdobytej wiedzy!
Kod wypróbowałem z odbiornikiem MX-RM-5V, który jest naprawdę słaby, zasięg całości w pomieszczeniu to ledwie kilka metrów, myślę że odbiornik superheterodynowy sprawdził by się lepiej (sprawdzę jak tylko listonosz przyniesie).
Oto schemat podłączenia do Arduino nano:
Antena o długości ok 170mm jest konieczna.
Kod korzysta z biblioteki TimerOne, odpowiedzialnej za generowanie przerwań z timera1.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
#include <TimerOne.h> #define DATA_IN 4 #define TICK_LENGTH 50 #define PAUSE_TICKS 3500/TICK_LENGTH #define HIGH_TICK_TIME 500/TICK_LENGTH #define ONE_TICK_TIME 1500/TICK_LENGTH #define ZERO_TICK_TIME 500/TICK_LENGTH #define MIN_DATA_SENDING_INTERVAL 100 #define LOWER_LIMIT 0.7 #define UPPER_LIMIT 1.3 volatile unsigned long lastDataSendTime = 0; volatile byte dataInState; volatile byte currentPulseTicks = 0; struct RawData { bool receivingData = false; byte currentPulse = 0; byte currentBit = 0; byte data[5]; bool finished = false; }; volatile RawData rawData; void setup() { pinMode(DATA_IN, INPUT_PULLUP); pinMode(LED_BUILTIN, OUTPUT); Timer1.initialize(TICK_LENGTH); Timer1.attachInterrupt(timerTick); dataInState = digitalRead(DATA_IN); lastDataSendTime = millis(); Serial.begin(9600); } void loop() { digitalWrite(LED_BUILTIN, dataInState); if (!rawData.finished) return; //nothing to do now if (millis() - lastDataSendTime > MIN_DATA_SENDING_INTERVAL) { float temperatures[2]; decodeTemperatures(temperatures); Serial.print("Temp1: "); Serial.print(temperatures[0]); Serial.print("; Temp2: "); Serial.println(temperatures[1]); lastDataSendTime = millis(); } clearRawData(); } void decodeTemperatures(float temperatures[]) { temperatures[0] = rawData.data[1]; temperatures[0] += (rawData.data[2] & 0xF0) << 4; temperatures[0] = temperatures[0] / 10 - 20; temperatures[1] = rawData.data[3]; temperatures[1] += (rawData.data[2] & 0x0F) << 8; temperatures[1] = temperatures[1] / 10 - 20; } void timerTick(void) { if (rawData.finished) return; //data received, wait untill data is sent currentPulseTicks++; if (dataInState != digitalRead(DATA_IN)) { //state of DATA_IN pin has changed handleInputPinStateChanged(); } else { if (currentPulseTicks > 250) { clearRawData(); currentPulseTicks = 0; } } } void handleInputPinStateChanged() { dataInState = !dataInState; if (!rawData.receivingData) { // not receiving data, but waiting for beginning if (curentPulseIsBeginning()) { rawData.receivingData = true; } else { clearRawData(); } currentPulseTicks = 0; return; } if (itsTimeForIntervalPulse()) { if (currentPulseIsInterval()) { rawData.currentPulse++; } else { clearRawData(); } currentPulseTicks = 0; return; } if (currentPulseMeansOne()) { setCurrentBit(1); } else if (currentPulseMeansZero()) { setCurrentBit(0); } else { clearRawData(); currentPulseTicks = 0; return; } rawData.currentBit++; rawData.currentPulse++; currentPulseTicks = 0; if (rawData.currentBit > 39) { rawData.finished = true; } } bool curentPulseIsBeginning() { return currentPulseTicks > PAUSE_TICKS * LOWER_LIMIT && currentPulseTicks < PAUSE_TICKS * UPPER_LIMIT; } bool itsTimeForIntervalPulse() { //even impulses are intervals between data impulses return rawData.currentPulse % 2 == 0; } bool currentPulseMeansZero() { return currentPulseTicks > ZERO_TICK_TIME * LOWER_LIMIT && currentPulseTicks < ZERO_TICK_TIME * UPPER_LIMIT; } bool currentPulseMeansOne() { return currentPulseTicks > ONE_TICK_TIME * LOWER_LIMIT && currentPulseTicks < ONE_TICK_TIME * UPPER_LIMIT; } bool currentPulseIsInterval() { return currentPulseTicks > HIGH_TICK_TIME * LOWER_LIMIT && currentPulseTicks < HIGH_TICK_TIME * UPPER_LIMIT; } void setCurrentBit(int value) { if (value == 1) { rawData.data[(rawData.currentBit) / 8] |= 1 << 7 - (rawData.currentBit % 8); } else { rawData.data[rawData.currentBit / 8] &= ~(1 << 7 - (rawData.currentBit % 8)); } } void clearRawData() { rawData.currentBit = 0; rawData.currentPulse = 0; rawData.finished = false; rawData.receivingData = false; } |
Dane przesyłane są do terminala w następującym formacie:
Temp1: 20.70; Temp2: 20.40
Temp1: 20.60; Temp2: 20.40
Temp1: 20.70; Temp2: 20.40
Temp1: 20.60; Temp2: 20.40
Temp1: 20.70; Temp2: 20.40
Aby z nich wygodnie skorzystać, użyłem terminala RealTerm z opcją logowania do pliku wraz ze znacznikiem czasu, następnie dane wyeksportowałem do Excela i utworzyłem wykres temperatury w funkcji czasu dla przebiegu zacierania mojego Oatmeal Stouta:
I to by było na tyle dzisiaj!
Tymczasem!
[…] [Tutaj kolejna część zawierająca kod arduino do odbioru danych] […]
Wow!
Nowe wpisy po 5 latach!
A u mnie blog wciąż na liście RSS 🙂
Wspaniale, mam nadzieję, ze warto było poczekać 😉
Patrze, patrze na ten kod i napatrzyć się nie mogę.
Prawdziwy kosmos