Skip to content

Instantly share code, notes, and snippets.

@onetransistor
Created January 1, 2024 11:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save onetransistor/773ceee205a55e7e74146d75d93300b5 to your computer and use it in GitHub Desktop.
Save onetransistor/773ceee205a55e7e74146d75d93300b5 to your computer and use it in GitHub Desktop.
Arduino test sketch to capture and decode data from a weather station. A 433 MHz receiver module is required
#define BITS_PER_PACKET 36
#define PACKETS_PER_STREAM 10
const int decPin = 3; /* signal input pin; must be interrupt capable */
volatile unsigned long lastInt = 0; /* the most recent interrupt time */
volatile unsigned long currInt, currMicros; /* current interrupt time and current time for main loop */
volatile unsigned long timeInt1 = 0, timeInt2 = 0; /* These variables store the last two pulse timings */
volatile bool keepWaiting = true; /* this is reset when the signal is idle and we can start processing sampled data */
bool rawBits[PACKETS_PER_STREAM][BITS_PER_PACKET + 1]; /* two dimension array for raw bitstream; number of rows and columns is set for expected signal */
/* there is one extra bit at the end which will be used as complete flag (i.e. if the row has been filled by exactly 36 bits) */
uint8_t i = 0, j = 0; /* i is the column, j is the row */
uint8_t deviceID;
bool batteryState;
uint8_t channelNo;
float temperature;
uint8_t humidity;
void setup() {
Serial.begin(115200);
Serial.println("Decoder test...");
pinMode(decPin, INPUT);
attachInterrupt(digitalPinToInterrupt(decPin), mapPulseTimingsToBits, CHANGE);
}
void loop() {
currMicros = micros();
// if no interrupt has occured 10 ms after last change of signal
if (currMicros > lastInt + 10000 && keepWaiting == true) {
// and the current state of the signal is LOW (this is an optional check)
// current code gets pulse timings; it doesn't care about polarity
// only this state check is a basic polarity test, expecting LOW when idle
if (digitalRead(decPin) == LOW) {
// disable sampling pulses and process existing data
keepWaiting = false;
// if at least one packet received
if (j > 0) processRawBitstream();
j = 0;
i = 0; // reset indexes
//Serial.println("\nData end");
}
}
}
void mapPulseTimingsToBits() {
currInt = micros();
// Copy the second pulse timing to the first variable
timeInt1 = timeInt2;
// Update the second pulse timing with the last measured pulse
timeInt2 = currInt - lastInt;
// Update the most recent interrupt time
lastInt = currInt;
// if the first pulse timing fits the ON state
if (timeInt1 > 400 && timeInt1 < 600) {
// continue sampling the signal
keepWaiting = true;
// check if the pulse that followed matches bit 0
if (timeInt2 > 900 && timeInt2 < 1100) {
rawBits[j][i] = 0;
if (i < BITS_PER_PACKET)
i += 1;
else rawBits[j][BITS_PER_PACKET] = 0; // more than expected bits received; set error flag
// DEBUG: Serial.print("0");
}
// otherwise if it maches bit 1
else if (timeInt2 > 1900 && timeInt2 < 2100) {
rawBits[j][i] = 1;
if (i < BITS_PER_PACKET)
i += 1;
else rawBits[j][BITS_PER_PACKET] = 0; // more than expected bits received; set error flag
// DEBUG: Serial.print("1");
}
// if it is longer than that is probably a sync pulse
else if (timeInt2 < 4500) {
// if less than 36 bits received, set the flag to false
if (i < (BITS_PER_PACKET - 1)) {
rawBits[j][BITS_PER_PACKET] = 0;
} else {
rawBits[j][BITS_PER_PACKET] = 1;
}
i = 0; // reset column
// start filling the next row; ignore extra streams
if (j < PACKETS_PER_STREAM)
j += 1;
// DEBUG: Serial.println();
}
}
}
void processRawBitstream() {
bool finalBits[BITS_PER_PACKET] = { 0 };
int8_t bitLowCnt = 0, bitHighCnt = 0;
for (int8_t b = 0; b < BITS_PER_PACKET; b++) {
for (int8_t a = 0; a < PACKETS_PER_STREAM; a++) {
// only if packet is complete
if (rawBits[a][BITS_PER_PACKET] == 1) {
// take each bit in the same position over all packets
// in theory all should be identical
// assuming poor signal strength, go with the majority
// by counting packets with each of the two bit states
if (rawBits[a][b] == 1) bitHighCnt += 1;
else bitLowCnt += 1;
if (bitHighCnt >= bitLowCnt) finalBits[b] = 1;
else finalBits[b] = 0;
bitLowCnt = 0;
bitHighCnt = 0;
}
}
}
Serial.print("\nProcessed bitstream: ");
for (int8_t i = 0; i < BITS_PER_PACKET; i++) {
Serial.print(finalBits[i], BIN);
}
Serial.println();
// another sanity check
if (finalBits[24] && finalBits[25] && finalBits[26] && finalBits[27]) {
// get device ID
deviceID = 0;
for (int8_t i = 0; i < 8; i++) {
if (finalBits[i] == 1) deviceID |= 1 << (7 - i);
}
// get battery state
batteryState = finalBits[8];
// get channel number
channelNo = (int8_t(finalBits[10]) << 1) | int8_t(finalBits[11]);
// get temperature
int16_t t12 = 0;
for (int8_t i = 0; i < 12; i++) {
if (finalBits[i + 12] == 1) t12 |= 1 << (11 - i);
}
temperature = t12 / 10.0;
// get humidity
humidity = 0;
for (int8_t i = 0; i < 8; i++) {
if (finalBits[i + 28] == 1) humidity |= 1 << (7 - i);
}
processDecodedData();
}
}
void processDecodedData() {
Serial.println("\nDecoded data:");
Serial.print("Device ID: ");
Serial.println(deviceID, DEC);
Serial.print("Battery State: ");
Serial.println(batteryState ? "Normal" : "Low");
Serial.print("Channel: ");
Serial.println(channelNo);
Serial.print("Temperature: ");
Serial.println(temperature, 2);
Serial.print("Humidity: ");
Serial.println(humidity, DEC);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment