User Tools

Site Tools

projects:kamstrup

This is an old revision of the document!


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.

Komponenter

Det er muligt at købe et kit med ovenstående dele (dog ikke kabel) ved at kontakte Hal9k f. eks. på Facebook. Kittet koster 100 kr (inkl porto til Danmark, Norge eller Sverige) for 1 stk, med mulighed for rabat ved større styktal. Kittet ser således ud:

Indhold af kit

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.

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;
}

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. Digital-kameraer kan se infrarødt lys.

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 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.

Permalink projects/kamstrup.1456002755.txt.gz · Last modified: 2016/02/20 22:12 by Torsten Martinsen

oeffentlich