Table of Contents

IR-øje til Kamstrup og andre målere

De fleste Kamstrup målere (og visse andre fabrikater) kan aflæses via infrarød kommunikation. Det kræver dog et optisk læseøje, der konverterer lysglimtene til serielt data. Her er beskrivelsen og samlevejledning til sådan et øje.

Bemærk: Vi sælger ikke længere kits.

Indhold af kit

Komponenter

Se også projektets git.

Samle-vejledning

Det enkeltsidede print er lidt besværligt at lave første gang: Alle komponenter (undtaget de 2 dioder) skal nemlig sidde på BAGSIDEN, hvor print-banerne og lodningerne også er. Dette er et mindre problem med det dobbeltsidede print (som er i vores kit).

Layoutet er set fra forsiden (dvs. fra siden uden printbaner):

Layout set forfra

Opad kan findes ved at der er 4 huller på række.

Lod dioderne på forsiden

kamstrup-dioder.jpg

Transmit skal sidde til venstre, receive til højre (set forfra)

ASCII

Bor de 2 midterste huller af hylsteret ud til 5mm, og check at dioderne passer i hullerne:

dsc_0030.jpg

Lod 39k ohm på mellem de 2 dioder

kamstrup39k.jpg

Lod de resterende komponenter på

Pas meget på at tænke fremad, så du ikke får blokeret for at et andet komponent kan loddes på bagefter.

kamstrupmodstande.jpg

kamstrupbc547.jpg

kamstrupbc557.jpg

kamstrupheader.jpg

Sæt magneter fast i hylsteret

Bor hullerne ud til 6mm, og vær sikker på at bore dem dybt nok til magneterne. Klem en magnet så meget i som muligt, og brug en gummihammer til at slå den helt i:

dsc_0037.jpg

dsc_0039.jpg

dsc_0040.jpg

PAS PÅ! Magneter kan nemt splintre, hvis man slår på dem med en metalhammer!

Sæt printet i hylsteret, og forbind til Arduino

Benene er (fra venstre mod højre):

  1. Receive ⇒ Arduino ben 9
  2. Transmit ⇒ Arduino ben 10
  3. VCC (5V) ⇒ Arduino 5V
  4. Ground ⇒ Arduino GND

Hvis din måler er påtrykt “DLMS” (se billede nedenfor) kan du bruge nedenstående Arduino sketch eller (hvis du forbinder øjet til en PC, Raspberry Pi eller lignende) PyKamstrup. Hvis din måler ikke er mærket DLMS, bruger den formodentlig en anden protokol ved navn IEC 62056 - i dette tilfælde skal der bruges noget andet software, for eksempel PyDLMS.

Til Kamstrup MULTICAL 66 kan du bruge denne software, som Kenan Vilic har lavet.

Læg Arduino SerialKamstrupReadout programmet på Arduino'en:

#include <SoftwareSerial.h>

//Kamstrup setup
// Kamstrup 382Jx3
word const kregnums[] = { 0x0001,0x03ff,0x0027,0x041e,0x041f,0x0420 };
char* kregstrings[]   = { "Energy in","Current Power","Max Power","Voltage p1","Voltage p2","Voltage p3" };
#define NUMREGS 6 		// Number of registers above
#define KAMBAUD 9600

// Units
char*  units[65] = {"","Wh","kWh","MWh","GWh","j","kj","Mj",
	"Gj","Cal","kCal","Mcal","Gcal","varh","kvarh","Mvarh","Gvarh",
        "VAh","kVAh","MVAh","GVAh","kW","kW","MW","GW","kvar","kvar","Mvar",
        "Gvar","VA","kVA","MVA","GVA","V","A","kV","kA","C","K","l","m3",
        "l/h","m3/h","m3xC","ton","ton/h","h","hh:mm:ss","yy:mm:dd","yyyy:mm:dd",
        "mm:dd","","bar","RTC","ASCII","m3 x 10","ton xr 10","GJ x 10","minutes","Bitfield",
        "s","ms","days","RTC-Q","Datetime"};

// Pin definitions
#define PIN_KAMSER_RX  9  // Kamstrup IR interface RX
#define PIN_KAMSER_TX  10  // Kamstrup IR interface TX
#define PIN_LED        13  // Standard Arduino LED

// Kamstrup optical IR serial
#define KAMTIMEOUT 300  // Kamstrup timeout after transmit
SoftwareSerial kamSer(PIN_KAMSER_RX, PIN_KAMSER_TX, false);  // Initialize serial


void setup () {
  Serial.begin(57600);
  
  pinMode(PIN_LED, OUTPUT);
  digitalWrite(PIN_LED, 0);
  
  // setup kamstrup serial
  pinMode(PIN_KAMSER_RX,INPUT);
  pinMode(PIN_KAMSER_TX,OUTPUT);
  kamSer.begin(KAMBAUD);

  delay(200);
  
  //Serial.println("\n[testKamstrup]");
  // poll the Kamstrup registers for data 
    for (int kreg = 0; kreg < NUMREGS; kreg++) {
      kamReadReg(kreg);
      delay(100);
  }
}

void loop () {
  // poll the Kamstrup registers for data 
  for (int kreg = 0; kreg < NUMREGS; kreg++) {
    kamReadReg(kreg);
    delay(100);
  }
  
  digitalWrite(PIN_LED, digitalRead(PIN_KAMSER_RX));
  delay(1000);
}

// kamReadReg - read a Kamstrup register
float kamReadReg(unsigned short kreg) {

  byte recvmsg[40];  // buffer of bytes to hold the received data
  float rval;        // this will hold the final value

  // prepare message to send and send it
  byte sendmsg[] = { 0x3f, 0x10, 0x01, (kregnums[kreg] >> 8), (kregnums[kreg] & 0xff) };
  kamSend(sendmsg, 5);

  // listen if we get an answer
  unsigned short rxnum = kamReceive(recvmsg);

  // check if number of received bytes > 0 
  if(rxnum != 0){
    
    // decode the received message
    rval = kamDecode(kreg,recvmsg);
    
    // print out received value to terminal (debug)
    Serial.print(kregstrings[kreg]);
    Serial.print(": ");
    Serial.print(rval);
    Serial.print(" ");
    Serial.println();
    
    return rval;
  }
}

// kamSend - send data to Kamstrup meter
void kamSend(byte const *msg, int msgsize) {

  // append checksum bytes to message
  byte newmsg[msgsize+2];
  for (int i = 0; i < msgsize; i++) { newmsg[i] = msg[i]; }
  newmsg[msgsize++] = 0x00;
  newmsg[msgsize++] = 0x00;
  int c = crc_1021(newmsg, msgsize);
  newmsg[msgsize-2] = (c >> 8);
  newmsg[msgsize-1] = c & 0xff;

  // build final transmit message - escape various bytes
  byte txmsg[20] = { 0x80 };   // prefix
  int txsize = 1;
  for (int i = 0; i < msgsize; i++) {
    if (newmsg[i] == 0x06 or newmsg[i] == 0x0d or newmsg[i] == 0x1b or newmsg[i] == 0x40 or newmsg[i] == 0x80) {
      txmsg[txsize++] = 0x1b;
      txmsg[txsize++] = newmsg[i] ^ 0xff;
    } else {
      txmsg[txsize++] = newmsg[i];
    }
  }
  txmsg[txsize++] = 0x0d;  // EOF

  // send to serial interface
  for (int x = 0; x < txsize; x++) {
    kamSer.write(txmsg[x]);
  }

}

// kamReceive - receive bytes from Kamstrup meter
unsigned short kamReceive(byte recvmsg[]) {

  byte rxdata[50];  // buffer to hold received data
  unsigned long rxindex = 0;
  unsigned long starttime = millis();
  
  kamSer.flush();  // flush serial buffer - might contain noise

  byte r;
  
  // loop until EOL received or timeout
  while(r != 0x0d){
    
    // handle rx timeout
    if(millis()-starttime > KAMTIMEOUT) {
      Serial.println("Timed out listening for data");
      return 0;
    }

    // handle incoming data
    if (kamSer.available()) {

      // receive byte
      r = kamSer.read();
      if(r != 0x40) {  // don't append if we see the start marker
        // append data
        rxdata[rxindex] = r;
        rxindex++; 
      }

    }
  }

  // remove escape markers from received data
  unsigned short j = 0;
  for (unsigned short i = 0; i < rxindex -1; i++) {
    if (rxdata[i] == 0x1b) {
      byte v = rxdata[i+1] ^ 0xff;
      if (v != 0x06 and v != 0x0d and v != 0x1b and v != 0x40 and v != 0x80){
        Serial.print("Missing escape ");
        Serial.println(v,HEX);
      }
      recvmsg[j] = v;
      i++; // skip
    } else {
      recvmsg[j] = rxdata[i];
    }
    j++;
  }
  
  // check CRC
  if (crc_1021(recvmsg,j)) {
    Serial.println("CRC error: ");
    return 0;
  }
  
  return j;
  
}

// kamDecode - decodes received data
float kamDecode(unsigned short const kreg, byte const *msg) {

  // skip if message is not valid
  if (msg[0] != 0x3f or msg[1] != 0x10) {
    return false;
  }
  if (msg[2] != (kregnums[kreg] >> 8) or msg[3] != (kregnums[kreg] & 0xff)) {
    return false;
  }
    
  // decode the mantissa
  long x = 0;
  for (int i = 0; i < msg[5]; i++) {
    x <<= 8;
    x |= msg[i + 7];
  }
  
  // decode the exponent
  int i = msg[6] & 0x3f;
  if (msg[6] & 0x40) {
    i = -i;
  };
  float ifl = pow(10,i);
  if (msg[6] & 0x80) {
    ifl = -ifl;
  }

  // return final value
  return (float )(x * ifl);

}

// crc_1021 - calculate crc16
long crc_1021(byte const *inmsg, unsigned int len){
  long creg = 0x0000;
  for(unsigned int i = 0; i < len; i++) {
    int mask = 0x80;
    while(mask > 0) {
      creg <<= 1;
      if (inmsg[i] & mask){
        creg |= 1;
      }
      mask>>=1;
      if (creg & 0x10000) {
        creg &= 0xffff;
        creg ^= 0x1021;
      }
    }
  }
  return creg;
}

Flere kode eksempler er at finde på github her

Test

Hvis alt er samlet rigtigt virker IR-øjet nu. Det er dog en god ide at lave et par tests, da det er nemt at lave fejl.

Transmit test

Den første test er at transmit øjet blinker. Find en mobil-telefon og brug dens kamera til at kigge på transmit øjet, mens Arduino'en kører. Nogle digital-kameraer kan se infrarødt lys -vi har fået at vide at nogle mobiler, f.eks. iPhone 6 og Samsung Galaxy 4, ikke kan se infrarødt lys (men for iPhone er det tilsyneladende sådan, at frontkameraet kan se infrarødt lys, mens kameraet på bagsiden ikke kan). Så det kan være nødvendigt at prøve mere end en - find evt. en fjernbetjening og kontrollér at du kan se signalet fra den med dit kamera.

Den skulle gerne blinke i en lilla farve:

Hvis det ikke virker er der en fejl på printet. Check alt igennem igen, og check for kortslutninger mellem baner — også under komponenter!

Receive test

På Arduino'en sidder der en LED på ben 13. Test-programmet lyser med LED på ben 13 hvis IR-recieveren er i mørke, og slukker hvis IR-receiveren er i lys.

Lys på IR-receiveren. LED på Arduino ben 13 skal slukke, indenfor 2 sekunder.

Hvis det ikke virker er der en fejl på printet. Check alt igennem igen, og check for kortslutninger mellem baner — også under komponenter!

Kamstrup test

Sæt læseøjet fast på Kamstrup måleren. På Arduino seriel konsollen (som skal sættes til 57600 baud) burde der komme data:

Hvis der i stedet står Timed out listening for data er der ikke forbindelse. Prøv at dreje IR-hovedet 180 grader. Hvis det ikke virker, så drej det langsomt hele vejen rundt.

Hvis der stadig ikke er forbindelse: Lav de to forrige tests, check alt igennem, specielt for kortslutninger!

Bemærk: På nogle målere aktiveres IR-forbindelsen af de magneter der sidder på plasthuset. Så hvis du tester uden, kan du risikere at du ikke får noget signal.

Alternativ hus

Hvis man vil bruge en esp8266 til at aflæse sin måler med kan man i stedet printe dette hus hvor espen sidder lige over øjet.

Målere

Navn Model Status Noter
Kamstrup 685-392-OK-40 Begrænset output https://github.com/anderskvist/PyDLMS
Kamstrup Multical 402 Fungerer https://github.com/Hal9k-dk/kamstrup/tree/master/Software%20eksempler/kamstrup_multical402
Kamstrup Multical 21 Ingen output Det lader til at den ikke tænder for ir porten.
Kamstrup 382Lx7 (684-38B-L4-73-010) Fungerer https://github.com/Hal9k-dk/kamstrup/tree/master/Software%20eksempler/kamstrup_powermeter
https://github.com/bipsendk/kamstrup-382Lx7