Läsa av trådlösa temperatursensorer, 433,92 MHz

Elektronikrelaterade (på komponentnivå) frågor och funderingar.
Användarvisningsbild
jadler
EF Sponsor
Inlägg: 407
Blev medlem: 28 maj 2009, 12:03:43
Ort: Vidja, Huddinge, Stockholm
Kontakt:

Läsa av trådlösa temperatursensorer, 433,92 MHz

Inlägg av jadler »

Jag vet inte om någon är intresserad, men jag har tre trådlösa termometrar från Kjell & Co, var och en med en trådlös sensor för utomhusbruk. Med mottagare, också från K&C, och hembyggd ooptimerad antenn kan jag ta emot signalerna från mina tre sensorer som är upp till 50 meter från mottagaren och antennen. Med information från http://thomaspfeifer.net/funk_temperatu ... tokoll.htm och egna mätningar med logikanalysator och senare med µC har jag fått ihop ett program som verkar kunna läsa av signalerna och ge mig korrekt temperatur för respektive sensor.

Programmet är långt ifrån färdigt, nuvarande version använder jag för att ta reda på dels vilka pulslängder som sensorerna faktiskt använder (eller mottagaren känner av) och för att försöka få stabila avläsningar. Jag får ibland signaler som har korrekt format men inte hör till mina sensorer, jag gissar att det är någon av grannarnas sensorer (minst 100 m bort) som inte använder riktigt samma format som de sensorer jag har. Programmet kör jag för ögonblicket på en Arduino Mega, med den syntax som dessa kort har. Jag har ändrat de pulslängder programmet skall acceptera utan att ändra kommentarerna i början, utveckling pågår som sagt.

Kod: Markera allt

/*
  Receiver for temperature probes for wireless thermometers "TFA" brand
  Long high signal measured as 1385 to 1445 µs stands for logic 0
  Short high signal measured as 595 to 635 µs stands for logic 1
  Signal train preceded by about 32 ms low (radio silence)
  Between high signals the low part is measured as 970 to 1050 µs
  
  See also http://thomaspfeifer.net/funk_temperatur_sensor_protokoll.htm for some details on the protocol
*/

#include "string.h"

//#define sigShort 615
//#define sigShortTolerance  150
//#define sigShortLow (sigShort - sigShortTolerance)
//#define sigShortHigh (sigShort + sigShortTolerance)
#define sigShortLow 350
#define sigShortHigh 850

//#define sigLong 1415
//#define sigLongTolerance 150
//#define sigLongLow (sigLong - sigLongTolerance)
//#define sigLongHigh (sigLong + sigLongTolerance)
#define sigLongLow 1150
#define sigLongHigh 1800

#define output Serial
#define rxPin 8

struct structTempSens {  // Struct with information regarding each sensor
  uint16_t id;           // Unit ID (random at each reset/battery change of sensor unit)
  uint8_t tA, tB, tC;    // Transmitted temperature values, temperature = (A-5)*10 + B + C/10
  char tempStr[6];       // Temperature as ASCII string
  char name[16];         // Human comprehensible name
  unsigned long values;  // Received packages from this sensor
} tempSens[8];

uint8_t numberSensors = 0;  // Number of sensors detected
uint8_t lastUnit;
byte data[44];  // Raw data from sensor via radio
unsigned long now;
uint16_t min0, max0, min1, max1;
unsigned long avg0, avg1;

uint8_t sensorNumber(uint16_t unitId) {  // Find position in array of struct of this unitId, or create new
  uint8_t i = 0, thisUnit;
  
  while(i < numberSensors) {
    if(tempSens[i].id == unitId) return(i);  // Found a matching unit ID, return its number in the array
    else i++;
  }
  thisUnit = numberSensors;
  numberSensors++;
  tempSens[thisUnit].id = unitId;  // Store ID
  switch (unitId) {  // Store human comprehensible name, if available
    case 0x0a06:
      strcpy(tempSens[thisUnit].name, "inomhus");
      break;
    case 0x0a01:
      strcpy(tempSens[thisUnit].name, "husvagg");
      break;
    case 0x0a08:
      strcpy(tempSens[thisUnit].name, "garaget");
      break;
    default:
      strcpy(tempSens[thisUnit].name, "unknown");
      break;
  }
  tempSens[thisUnit].values = 0;
  return(thisUnit);  // Return the new number given to this unit ID
}

void process(void) {
  uint16_t unitId = 0;
  uint8_t unitA = 0, unitB = 0, unitC = 0;
  uint8_t currentSensor;
  uint8_t i;
  
  for(uint8_t i=0; i<16; i++) {
    unitId += data[i] << (15 - i);
  }
  for(uint8_t i=0; i<4; i++) {
    unitA += data[i + 20] << (3 - i);
    unitB += data[i + 24] << (3 - i);
    unitC += data[i + 28] << (3 - i);
  }
  
  currentSensor = sensorNumber(unitId);
  tempSens[currentSensor].tA = unitA;
  tempSens[currentSensor].tB = unitB;
  tempSens[currentSensor].tC = unitC;
  
  i = 0;
  if(unitA < 5) {
    tempSens[currentSensor].tempStr[i++] = '-';
    tempSens[currentSensor].tempStr[i++] = '0' + 5 - unitA;
  } else if (unitA > 5) {
    tempSens[currentSensor].tempStr[i++] = '0' + unitA - 5;
  }
  tempSens[currentSensor].tempStr[i++] = '0' + (unitB % 10);
  tempSens[currentSensor].tempStr[i++] = '.';
  tempSens[currentSensor].tempStr[i++] = '0' + unitC;
  tempSens[currentSensor].tempStr[i++] = 0;
  
  tempSens[currentSensor].values++;
  lastUnit = currentSensor;
}

void reportBinData(void) {
  for(uint8_t i=0; i<44; i++) {
    output.print(data[i], DEC);
    if((i >= 15) && (i+1) % 4 == 0) output.print(" ");
  }
}
  
void reportTime(unsigned long now) {
  char nowH, nowM, nowS;
  
  now /= 1000UL;
  nowS = now % 60UL;
  now /= 60UL;
  nowM = now % 60UL;
  nowH = now / 60UL;
  
  if(nowH < 10) output.print("0");
  output.print(nowH, DEC);
  output.print(":");
  if(nowM < 10) output.print("0");
  output.print(nowM, DEC);
  output.print(":");
  if(nowS < 10) output.print("0");
  output.print(nowS, DEC);
}

void reportUnitId(uint16_t unitId) {
  output.print("\"");
  if(unitId < 0x1000) output.print("0");
  if(unitId < 0x100) output.print("0");
  if(unitId < 0x10) output.print("0");
  output.print(unitId, HEX);
  output.print("\"");
}

void reportUnit(uint8_t unitNumber) {
  output.print("\"");
  output.print(tempSens[unitNumber].name);
  output.print("\";");
  output.print(tempSens[unitNumber].tempStr);
  output.print(";");
  output.print(tempSens[unitNumber].values, DEC);
}

void report() {
  reportTime(now);
  output.print(";");
  output.print(min0);
  output.print(";");
  output.print(avg0);
  output.print(";");
  output.print(max0);
  output.print(";");
  output.print(min1);
  output.print(";");
  output.print(avg1);
  output.print(";");
  output.print(max1);
  for(uint8_t i=0; i<numberSensors; i++) {
    output.print(";");
    reportUnit(i);
  }
  output.println();
  if(strcmp(tempSens[lastUnit].name, "unknown") == 0) {
    reportBinData();
    output.println();
    output.print(tempSens[lastUnit].tA, DEC);
    output.print(";");
    output.print(tempSens[lastUnit].tB, DEC);
    output.print(";");
    output.print(tempSens[lastUnit].tC, DEC);
    output.println();
  }
}

void setup() {
  pinMode(rxPin, INPUT);
  output.begin(57600);
}

void loop() {
  uint8_t i = 0, count0 = 0, count1 = 0, j;
  uint16_t sample0[44], sample1[44];
  unsigned long t;
  boolean sigCheck = true;
  
  while (i < 44) {
    t = pulseIn(rxPin, HIGH, sigLongHigh);
    if (sigShortLow < t && t < sigShortHigh) {
      data[i++] = 1;
      sample1[count1++] = int(t);
    } else if(sigLongLow < t && t < sigLongHigh) {
      data[i++] = 0;
      sample0[count0++] = int(t);
    } else {
      i = 0;
      count0 = 0;
      count1 = 0;
      continue;
    }
  }
  
  j = 0;
  while(sigCheck && j<4) {
    sigCheck = sigCheck && (data[j+20] == data[j+32]);
    sigCheck = sigCheck && (data[j+24] == data[j+36]);
    j++;
  }
  
  if(sigCheck) {
    avg0 = 0;
    min0 = max0 = sample0[0];
    for(j=0; j<count0; j++) {
      avg0 += sample0[j];
      min0 = min(min0, sample0[j]);
      max0 = max(max0, sample0[j]);
    }
    avg0 /= count0;
    
    avg1 = 0;
    min1 = max1 = sample1[0];
    for(j=0; j<count1; j++) {
      avg1 += sample1[j];
      min1 = min(min1, sample1[j]);
      max1 = max(max1, sample1[j]);
    }
    avg1 /= count1;

    now = millis();
    process();
    report();
  }
}
Användarvisningsbild
treton
Inlägg: 57
Blev medlem: 15 oktober 2008, 10:34:21
Ort: Solna

Re: Läsa av trådlösa temperatursensorer, 433,92 MHz

Inlägg av treton »

Helt klart intressant projekt!

Jag har sändare och mottagare från Kjell&Co samt trådlösa temperaturgivare från dem, men planen att göra något kul när jag betat av alla andra idéer i pipen. Någon föreslog att samla information om olika produkters protokoll i en wiki här på forumet och jag kan bara hålla med om att det skulle bli en veritabel guldgruva för alla oss hemmapulare.
jbulow
Inlägg: 114
Blev medlem: 22 juni 2006, 21:35:26
Ort: Malmö

Re: Läsa av trådlösa temperatursensorer, 433,92 MHz

Inlägg av jbulow »

Vilken trådlös termometer är det du använder? Artikelnummer?
Användarvisningsbild
jadler
EF Sponsor
Inlägg: 407
Blev medlem: 28 maj 2009, 12:03:43
Ort: Vidja, Huddinge, Stockholm
Kontakt:

Re: Läsa av trådlösa temperatursensorer, 433,92 MHz

Inlägg av jadler »

På kartongen står det "TFA Easy Funk-Thermometer", Kjell&Co artikelnummer 48640. Min modell ser helt annourlunda ut än den som Tomas Pfeifer testade, och jag vet att K&C idag har modeller från samma företag men med annan design.

Nu har jag justerat de tidsintervall jag använder för att acceptera korta och långa höga pulser, och lagt till lite mer tester för att utesluta felaktiga signaler, och jag verkar få helt rimliga värden under ett dygn. Mottagaren, med hembyggd dipolantenn som inte borde fungera särskilt bra enligt bedömningar här, står i ett fönster åt öster och läser av en sensor på husets norra gavel, en i källaren mot norrgaveln och en på garageväggen ca 50 meter bort. Antennläget är lite känsligt, så något gör antennen trots allt, men en bättre antenn med bättre läge kan nog tillföra mycket.

Jag vet att man kan slänga ut en bunt DS1820 eller andra temperatursensorer, men detta känns ändå som en enkel och smidig lösning. Dessa sensorer kostar omkring 99 kronor på K&C eller CO, färdigbyggda i utomhuståliga burkar, kalibrerade och med adekvat strömförsörjning. Man löser bara avläsningen med en radiomodul så kan man ställa ut så många sensorer man orkar (de använder en 16-bitars kod som enhets-ID, så teoretiskt sett kunde man väl ha 2^16 sensorer) och läsa av trådlöst. Känns rimligt och smidigt för mig.

Jag skrev förut att jag trodde att jag fick in grannens sensorer, det var nog snarare delvis felaktiga signaler från mina egna sensorer, där delar av signalsekvensen var förstörd av brus. Det är det jag verkar ha kommit till rätta med nu genom att analysera ett större antal korrekta signalers pulslängder.
bos
Inlägg: 2308
Blev medlem: 24 februari 2007, 23:29:15
Kontakt:

Re: Läsa av trådlösa temperatursensorer, 433,92 MHz

Inlägg av bos »

Användarvisningsbild
jadler
EF Sponsor
Inlägg: 407
Blev medlem: 28 maj 2009, 12:03:43
Ort: Vidja, Huddinge, Stockholm
Kontakt:

Re: Läsa av trådlösa temperatursensorer, 433,92 MHz

Inlägg av jadler »

OK, där ser man. oJsan verkar ha samma termometer som jag har tre av, och i den tråden hade man listat ut paritetsbiten.

Utifrån mina data verkar det som att oJsan kan ha rätt och att det är ett 19-bitars ID, inte 16 bitar som Pfeifer tror.

Jag har samlat data över natten från mina tre sensorer, här är alla unika rader, först sorterat som vanligt, sedan sorterat på det sista och hittills okända fältet. Jag har inte hunnit kika på vad det sista fältet kan betyda. Mina data är för övrigt, som ni förstår, inhämtade över radio, och temperaturerna stämmer bra med mina tre mottagare. Datainsamlingen fortsätter, så det kommer mer rådata med tiden om det behövs.

Kod: Markera allt

       ID           P  A    B    C    A    B    x
0000101000000001110 0 0101 0011 1001 0101 0011 0000
0000101000000001110 0 0101 0100 0001 0101 0100 1010
0000101000000001110 0 0101 0100 0010 0101 0100 1011
0000101000000001110 0 0101 0100 0100 0101 0100 1101
0000101000000001110 0 0101 0100 0111 0101 0100 0000
0000101000000001110 0 0101 0100 1000 0101 0100 0001
0000101000000001110 0 0101 0101 0000 0101 0101 1011
0000101000000001110 0 0101 0101 0011 0101 0101 1110
0000101000000001110 0 0101 0101 0101 0101 0101 0000
0000101000000001110 0 0101 0101 0110 0101 0101 0001
0000101000000001110 1 0101 0011 0111 0101 0011 1111
0000101000000001110 1 0101 0011 1000 0101 0011 0000
0000101000000001110 1 0101 0100 0000 0101 0100 1010
0000101000000001110 1 0101 0100 0011 0101 0100 1101
0000101000000001110 1 0101 0100 0101 0101 0100 1111
0000101000000001110 1 0101 0100 0110 0101 0100 0000
0000101000000001110 1 0101 0100 1001 0101 0100 0011
0000101000000001110 1 0101 0101 0001 0101 0101 1101
0000101000000001110 1 0101 0101 0010 0101 0101 1110
0000101000000001110 1 0101 0101 0100 0101 0101 0000
0000101000000001110 1 0101 0101 0111 0101 0101 0011
0000101000000110011 0 0110 0101 0011 0110 0101 1111
0000101000000110011 1 0110 0101 0010 0110 0101 1111
0000101000000110011 1 0110 0101 0100 0110 0101 0001
0000101000000110011 1 0111 0000 0000 0111 0000 0101
0000101000001000110 0 0101 0011 0101 0101 0011 0011
0000101000001000110 0 0101 0011 0110 0101 0011 0100
0000101000001000110 0 0101 0011 1001 0101 0011 0111
0000101000001000110 0 0101 0100 0001 0101 0100 0001
0000101000001000110 0 0101 0100 0010 0101 0100 0010
0000101000001000110 0 0101 0100 0100 0101 0100 0100
0000101000001000110 0 0101 0100 0111 0101 0100 0111
0000101000001000110 0 0101 0100 1000 0101 0100 1000
0000101000001000110 0 0101 0101 0000 0101 0101 0010
0000101000001000110 0 0101 0101 0011 0101 0101 0101
0000101000001000110 0 0101 0101 0101 0101 0101 0111
0000101000001000110 0 0101 0101 0110 0101 0101 1000
0000101000001000110 1 0101 0011 0100 0101 0011 0011
0000101000001000110 1 0101 0011 0111 0101 0011 0110
0000101000001000110 1 0101 0011 1000 0101 0011 0111
0000101000001000110 1 0101 0100 0000 0101 0100 0001
0000101000001000110 1 0101 0100 0011 0101 0100 0100
0000101000001000110 1 0101 0100 0101 0101 0100 0110
0000101000001000110 1 0101 0100 0110 0101 0100 0111
0000101000001000110 1 0101 0100 1001 0101 0100 1010
0000101000001000110 1 0101 0101 0001 0101 0101 0100
0000101000001000110 1 0101 0101 0010 0101 0101 0101
0000101000001000110 1 0101 0101 0100 0101 0101 0111
0000101000001000110 1 0101 0101 0111 0101 0101 1010

Kod: Markera allt

       ID           P  A    B    C    A    B    x
0000101000000001110 0 0101 0011 1001 0101 0011 0000
0000101000000001110 0 0101 0100 0111 0101 0100 0000
0000101000000001110 0 0101 0101 0101 0101 0101 0000
0000101000000001110 1 0101 0011 1000 0101 0011 0000
0000101000000001110 1 0101 0100 0110 0101 0100 0000
0000101000000001110 1 0101 0101 0100 0101 0101 0000
0000101000000001110 0 0101 0100 1000 0101 0100 0001
0000101000000001110 0 0101 0101 0110 0101 0101 0001
0000101000000110011 1 0110 0101 0100 0110 0101 0001
0000101000001000110 0 0101 0011 0011 0101 0011 0001
0000101000001000110 0 0101 0100 0001 0101 0100 0001
0000101000001000110 1 0101 0100 0000 0101 0100 0001
0000101000001000110 0 0101 0100 0010 0101 0100 0010
0000101000001000110 0 0101 0101 0000 0101 0101 0010
0000101000000001110 1 0101 0100 1001 0101 0100 0011
0000101000000001110 1 0101 0101 0111 0101 0101 0011
0000101000001000110 0 0101 0011 0101 0101 0011 0011
0000101000001000110 1 0101 0011 0100 0101 0011 0011
0000101000001000110 0 0101 0011 0110 0101 0011 0100
0000101000001000110 0 0101 0100 0100 0101 0100 0100
0000101000001000110 1 0101 0100 0011 0101 0100 0100
0000101000001000110 1 0101 0101 0001 0101 0101 0100
0000101000000110011 1 0111 0000 0000 0111 0000 0101
0000101000001000110 0 0101 0101 0011 0101 0101 0101
0000101000001000110 1 0101 0101 0010 0101 0101 0101
0000101000001000110 1 0101 0011 0111 0101 0011 0110
0000101000001000110 1 0101 0100 0101 0101 0100 0110
0000101000001000110 0 0101 0011 1001 0101 0011 0111
0000101000001000110 0 0101 0100 0111 0101 0100 0111
0000101000001000110 0 0101 0101 0101 0101 0101 0111
0000101000001000110 1 0101 0011 1000 0101 0011 0111
0000101000001000110 1 0101 0100 0110 0101 0100 0111
0000101000001000110 1 0101 0101 0100 0101 0101 0111
0000101000001000110 0 0101 0100 1000 0101 0100 1000
0000101000001000110 0 0101 0101 0110 0101 0101 1000
0000101000000001110 0 0101 0100 0001 0101 0100 1010
0000101000000001110 1 0101 0100 0000 0101 0100 1010
0000101000001000110 1 0101 0100 1001 0101 0100 1010
0000101000001000110 1 0101 0101 0111 0101 0101 1010
0000101000000001110 0 0101 0100 0010 0101 0100 1011
0000101000000001110 0 0101 0101 0000 0101 0101 1011
0000101000000001110 0 0101 0011 0101 0101 0011 1100
0000101000000001110 0 0101 0011 0110 0101 0011 1101
0000101000000001110 0 0101 0100 0100 0101 0100 1101
0000101000000001110 1 0101 0100 0011 0101 0100 1101
0000101000000001110 1 0101 0101 0001 0101 0101 1101
0000101000000001110 0 0101 0101 0011 0101 0101 1110
0000101000000001110 1 0101 0101 0010 0101 0101 1110
0000101000000001110 1 0101 0011 0111 0101 0011 1111
0000101000000001110 1 0101 0100 0101 0101 0100 1111
0000101000000110011 0 0110 0101 0011 0110 0101 1111
0000101000000110011 1 0110 0101 0010 0110 0101 1111
Användarvisningsbild
jadler
EF Sponsor
Inlägg: 407
Blev medlem: 28 maj 2009, 12:03:43
Ort: Vidja, Huddinge, Stockholm
Kontakt:

Re: Läsa av trådlösa temperatursensorer, 433,92 MHz

Inlägg av jadler »

Vad gäller hur sändare och mottagare paras ihop (se även oJsans tankar) har jag märkt att om mina mottagare (som klarar tre sändare) står på och inte har parats med tre sändare än så spelar det ingen roll hur länge mottagaren har varit på när den introduceras för en ny sändare. Hittar den en sändare den inte känner till så läggs den till på nästa position i listan. Jag tror att sändarna slumpar fram ett ID, kanske inte helt slumpmässigt, men ändå annat än gången innan, när man byter batteri eller på annat sätt återställer den. Byter jag batteri i en sändare så kommer en mottagare den tidigare var hopparad med inte att känna igen den längre. Jag har inte analyserat om det är så att den skickar annorlunda paket just de första två minuterna efter att den får ström, men det kan jag ganska enkelt göra. Återkommer när jag vet mer om detta.

Jag vet inte hur sändare och mottagare gör för att inte överföringen från olika sändare skall kollidera tidsmässigt. Det är inga långdragna överföringar, men jag har svårt att tänka mig att det skulle finnas någon tvåvägskommunikation där mottagaren kan bekräfta mottagningen. När det bara skickas en mätning som två paket med 30 ms mellanrum en gång per minut så borde det finnas en liten risk för kollision. Å andra sidan har jag sett att perioden verkar variera med några sekunder kring de tänkta 60 sekunderna, kanske är det för att minska risken för kollision.

Jag tänkte ändra lite i mitt program för att låta det registrera nya sändare automagiskt. Första tanken är att preliminärt godkänna paket med korrekt format, 44 bitar, tiotal och heltal för temperatur upprepas, pariteten för temperaturangivelsen är korrekt. Kan man så ta emot t.ex. tio korrekta sådana paket från samma enhets-ID inom 15-20 minuter (för att tillåta svårare överföringsförhållanden) och temperaturen under denna tid håller sig inom ett intervall på kanske två grader Celsius (vilket borde vara normalt för många placeringar av mätarna) så godkänns detta enhets-ID permanent och läggs till listan. Skulle det visa sig att sändarna skickar någon form av "parningspaket" i början kan man ju lyssna efter det också om man inte vill få extra information från grannarnas termometrar.

De två listorna i min förra text är alltså gjorda med 'sort|uniq' respektive 'sort|uniq|sort -k8' (efter en 'grep' för att rensa bort mer läsbar data om var sändarna finns och temperaturen i klartext.)
Användarvisningsbild
jadler
EF Sponsor
Inlägg: 407
Blev medlem: 28 maj 2009, 12:03:43
Ort: Vidja, Huddinge, Stockholm
Kontakt:

Re: Läsa av trådlösa temperatursensorer, 433,92 MHz

Inlägg av jadler »

Jag har inte varit aktiv här på forumet eller hemma med lödkolven på ett tag, delvis på grund av kärleken. (Singel flera år efter skilsmässa, nu nyförälskad i underbar kollega.)

Svängningarna i yttertemperatur sista tiden, och de låga temperaturerna, har fått mig att vilja få lite mer fart på min temperaturloggning, och ett PM fick mig att logga in här och kika på min tråd igen.

Ur mitt svar på PM:
Det går bra, jag har inte vidareutvecklat µC-programmet mer, inte implementerat de idéer jag nog nämnde i tråden, men grundversionen fungerar bra och tuffar på. Jag har inte lyckats göra något vettigt med informationen på datorsidan än, försökte ett tag få till ett Perl-script som skulle plocka ut relevant information och mata det till rrd, men även om jag har hackat en del Perl tidigare har jag inte försökt mig på seriekommunikation, och vad gäller rrd är jag helt grön.
...
Jag tänkte istället låta min AVR mata värden kontinuerligt, dels för att göra det enkelt på µC-sidan, dels för att enkelt kunna få korrekt avläsningstidpunkt för varje mätvärde utan att lägga till en RTC som skall hållas synkad till korrekt tid.
Skriv svar