Pages: [1] 2   Go Down
Author Topic: EHZ Zähler auslesen  (Read 3060 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo Zusammen,

ich habe es geschafft mit dem Lesekopf von Volkszähler.org meinen EMH EHZ Zähler auszulesen. Leider schaffe ich es nicht die Zahlen zu formatieren, die Ausgabe ist im Moment wie folgt:
10693877
3928
sollte aber 1069.3877 kWh und 392.8W sein. Wenn jemand mehr Ahnung hat als ich, wäre ich auch für eine effizientere Umwandlung von Hex nach INT bzw. Float dankbar.

Vielen Dank im voraus!

Gruss
Norbert

Code:
/**
 * Copyright (c) 2012 Volker Wegert <ehzy@volker-wegert.de>
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Volker Wegert - initial implementation
 *
 */
#include <stdarg.h>
#include <stdio.h>
#include <avr/pgmspace.h>
/***************************************************************************************************
 * CONSTANTS
 ***************************************************************************************************/

/**
 * size of the SML message buffer = meximum number of bytes that can be received
 */
#define SML_MSG_BUFFER_SIZE    500

/**
 * maximum time to wait for the end of a data packet received via IR
 */
#define SERIAL_READ_TIMEOUT_MS 100

/***************************************************************************************************
 * MESSAGES
 ***************************************************************************************************/

/**
 * maximum length of a message (entire text after variable substitution!)
 */
#define MAX_MESSAGE_LENGTH        50

/**
 * message numbers
 */
#define MSG_PROGRAM_STOPPED        0
#define MSG_NEXT_FILENAME          1
#define MSG_NO_FILE_NAMES_LEFT     2
#define MSG_INIT_HARDWARE          3
#define MSG_INIT_SD_CARD           4
#define MSG_INIT_SD_CARD_ERROR     5
#define MSG_BYTE_READ              6
#define MSG_BUFFER_OVERFLOW        7
#define MSG_SERIAL_OVERFLOW        8 
#define MSG_INVALID_HEADER         9
#define MSG_FILE_OPEN_FAILED      10
#define MSG_FILE_WRITTEN          11
#define MSG_FREE_MEMORY           12
#define MSG_NUM_BYTES_READ        13

/**
 * actual message texts - caution, adapt MAX_MESSAGE_LENGTH if required!
 *                               ....+....1....+....2....+....3....+....4....+....5
 */
prog_char msgText00[] PROGMEM = "Program stopped.";
prog_char msgText01[] PROGMEM = "Next output file name is '%s'";
prog_char msgText02[] PROGMEM = "No more file names left";
prog_char msgText03[] PROGMEM = "Initializing Hardware...";
prog_char msgText04[] PROGMEM = "Initializing SD Card...";
prog_char msgText05[] PROGMEM = "SD Card initialization failed";
prog_char msgText06[] PROGMEM = "Read byte %02hhu from IR receiver";
prog_char msgText07[] PROGMEM = "Message buffer overflow";
prog_char msgText08[] PROGMEM = "Serial buffer overflow";
prog_char msgText09[] PROGMEM = "Invalid escape sequence";
prog_char msgText10[] PROGMEM = "Unable to open output file";
prog_char msgText11[] PROGMEM = "%u bytes of data written to file '%s'";
prog_char msgText12[] PROGMEM = "%u bytes of memory available";
prog_char msgText13[] PROGMEM = "%u bytes read";

/**
 * table for easier access to the message texts
 */
PROGMEM const char *msgTextTable[] = {   
  msgText00,
  msgText01,
  msgText02,
  msgText03,
  msgText04,
  msgText05,
  msgText06,
  msgText07,
  msgText08,
  msgText09,
  msgText10,
  msgText11,
  msgText12,
  msgText13
};

/***************************************************************************************************
 * GLOBAL VARIABLES (YUCK!)
 ***************************************************************************************************/
/**
 * the global buffer to store the SML message currently being read
 */
unsigned char buffer[SML_MSG_BUFFER_SIZE];
unsigned char Aktuell[4];
float aktuell;

/***************************************************************************************************
 * SUBROUTINES
 ***************************************************************************************************/

/**
 * printMessage - reads a message text from the PROGMEM, performs variable substitution and
 *                writes the resulting text to the serial console. Use MSG_* constants for
 *                messageNumber.
 */
void printMessage(int messageNumber, ...) {
  va_list args;
  char format[MAX_MESSAGE_LENGTH];
  char buffer[MAX_MESSAGE_LENGTH];
  va_start(args, messageNumber);
  strncpy_P(format, (char*)pgm_read_word(&(msgTextTable[messageNumber])), MAX_MESSAGE_LENGTH);
  vsnprintf(buffer, MAX_MESSAGE_LENGTH, format, args);
  Serial.println(buffer);
  va_end(args);
}

void PrintHex8(uint8_t *data, uint8_t length) // prints 8-bit data in hex with leading zeroes
{
  char tmp[16];
  for (int i=0; i<length; i++) {
    sprintf(tmp, "0x%.2X",data[i]);
    Serial.print(tmp);
    Serial.print(" ");
  }
}


/**
 * stop - stops program execution in a controlled fashion (endless loop).
 */
void stop() {
  printMessage(MSG_PROGRAM_STOPPED);
  while(1);
}


/**
 * isValidHeader - returns true if the global message buffer begins with a valid SML escape sequence.
 */
inline boolean isValidHeader() {
  return ((buffer[0] == 0x1b) ||
    (buffer[1] == 0x1b) ||
    (buffer[2] == 0x1b) ||
    (buffer[3] == 0x1b) ||
    (buffer[4] == 0x01) ||
    (buffer[5] == 0x01) ||
    (buffer[6] == 0x01) ||
    (buffer[7] == 0x01));
}

/***************************************************************************************************
 * MAIN ROUTINES
 ***************************************************************************************************/

/**
 * setup - initialization routine run once after the device is powered up.
 */
void setup() {
  // set the serial console speed
  Serial.begin(9600);
  // set the pin configuration
  printMessage(MSG_INIT_HARDWARE);
}
/**
 * loop - main program, run in an endless loop
 */
void loop() {
  unsigned int nextBufferPosition = 0;
  unsigned long lastReadTime = 0;

  // clear the message buffer
  memset(buffer, 0, sizeof(buffer));
  // wait until actual data is available
  while (!Serial.available());

  // keep reading data until either the message buffer is filled or no more data was
  // received for SERIAL_READ_TIMEOUT_MS ms
  lastReadTime = millis();
  while (millis() - lastReadTime < SERIAL_READ_TIMEOUT_MS) {
    if (Serial.available()) {
      buffer[nextBufferPosition] = Serial.read();
      lastReadTime = millis();
      if (nextBufferPosition >= SML_MSG_BUFFER_SIZE) {
        printMessage(MSG_BUFFER_OVERFLOW);
        return;
      }
      nextBufferPosition += 1;
    }
  }

  // check the header
  printMessage(MSG_NUM_BYTES_READ, nextBufferPosition + 1);
  if (!isValidHeader()) {
    // not a valid header - notify the user...
    printMessage(MSG_INVALID_HEADER);
    // ...and empty the receiver buffer (wait for the end of the current data stream
    while (Serial.available() > 0) {
      Serial.read();
    }
  }
  else {
    unsigned char Stand[4];
    //PrintHex8(buffer,313);
    Serial.println();
    for(int i=0;i<4;i++){
      Stand[i]=(buffer[143+i]);
      Aktuell[i]=buffer[205+i];
    }
    PrintHex8(Stand,4);
    Serial.println();
    unsigned long stand =(int(buffer[144])*65536);
    unsigned long stand2=(int(buffer[145])*256);
    unsigned long stand3=(int(buffer[146]));
    unsigned long summe=stand+stand2+stand3;
    Serial.println(summe);

    unsigned long Verb =(int(Aktuell[1])*65536);
    unsigned long Verb2=(int(Aktuell[2])*256);
    unsigned long Verb3=(int(Aktuell[3]));
    unsigned long verbrauch=Verb+Verb2+Verb3;
    Serial.println(verbrauch);
    //    Serial.println(stand);
    //    aktuell=*((float*)Aktuell);
    //    Serial.println(aktuell,DEC);
    //    for(int i=0;i<4;i++){
    //      Serial.print(buffer[143+i],HEX);
    //    }
    //  Serial.println();
    //     for(int i=0;i<5;i++){
    //      Serial.print(buffer[205+i],HEX);
    //     }
    //    Serial.println();
    // PrintHex8(Aktuell,4);
    //    Serial.println();

  }
}
Logged

0
Offline Offline
Faraday Member
**
Karma: 23
Posts: 3482
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Ich würde die Zahlen mit "/" bzw. "%" in einen Vor- und einen Nachkommateil zerlegen und dann den Dezimalpunkt dazwischen ausgeben.
Logged

Check out my experiments http://blog.blinkenlight.net

Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Auf die Idee mit Teilen war ich auch schon gekommen, aber ich hatte ganz vergessen, dass ich die Nachkommastellen wieder hinzufügen muss  smiley-roll.
Hat jemand noch eine Idee zu der HEX-> Int Umwandlung, ich bin mir sicher, dass man das noch optimieren kann?!

Gruss
Logged

0
Offline Offline
Faraday Member
**
Karma: 23
Posts: 3482
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Klar gibt es da Ideen. Aber schreib doch bitte zuerst einmal genau was Du von einer HEX->INT Konvertierung erwartest. Also wie sieht der Input aus und wie der gewünschte Output? Und vor allem: warum ist Dir Deine aktuelle Lösung nicht gut genug?

Und bitte keien Spec der Art: "so wie ich es jetzt implementiert" habe. Das zwingt jeden Leser Deinen Code zu verstehen obwohl Du selber damit nicht zufrieden bist.
Logged

Check out my experiments http://blog.blinkenlight.net

Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Da hast du recht, wenn man selber weiß was man reinbekommt und raus haben will, übersieht man leicht, dass die anderen Nutzer da nicht so drin sind.
Ich bekomme jetzt serielle Hexwerte (char Array) in folgendem Format rein:
62 1E Unit 1E = Wh
52 FF Scale
56 000014FDEF Zählerstand, berechnet mit Scaler und Unit (14FDEF = 137572,7 kWh!)
die Umwandlung mache ich ja durch Multiplikation:
    unsigned long stand =(int(buffer[144])*65536);
    unsigned long stand2=(int(buffer[145])*256);
    unsigned long stand3=(int(buffer[146]));
    unsigned long summe=stand+stand2+stand3;
das hochwertigste Byte lasse ich schon weg, da die Multiplikation ja einen Overflow geben würde.

Was ich suche wäre eine direkte Umwandlung von den Hexwerten in den Kommawert, ohne die Zwischenvariablen.

Gruss
Norbert
Logged

0
Offline Offline
Faraday Member
**
Karma: 23
Posts: 3482
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Ich würde diese Methode nehmen: http://www.willemer.de/informatik/cpp/union.htm. Nur solltest Du für ein unsigned long eben 4 Bytes benutzen, also etwa so:

Code:
union {
    struct {
        unsigned char b0;
        unsigned char b1;
        unsigned char b2;
        unsigned char b3;
    } part;
    unsigned long value;
} converter;

   converter.part.b3 = 0;  
   converter.part.b2 = buffer[144];
   converter.part.b1 = buffer[145];
   converter.part.b0 = buffer[146];

  unsigned long result = converter.value;
}

Wenn die Werte andersrum im Speicher stehen würden, also niedrigstes Byte in buffer[144] und weiterhin eine 0 in buffer[147], dann könntest Du den Wert auch direkt auslesen, also etwas so:
Code:
unsigned long result = *((unsigned long *)(& buffer[144]));

gelesen werden. Falls Du das hochwertigstes Byte mitbenutzt gibt es bei der Methode garantiert keinen Overflow smiley-wink
Logged

Check out my experiments http://blog.blinkenlight.net

Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Guten morgen,

genau sowas hab ich gesucht, ich hab es jetzt so gemacht:
Code:
    for(int i=3;i>=0;i--){
      Stand[3-i]=(buffer[143+i]);
      Aktuell[3-i]=buffer[204+i];
    }
unsigned long result = *((unsigned long *)(& Stand[0]));
Serial.println(result);
double Result = result/10000;
Serial.println(Result);
int Rest = int(result);
Rest = Rest%10000;
Serial.println(Rest);

Mein Problem ist jetzt nur noch die Ausgabe mit Modulo, weil da immer Werte rauskommen, die ich mir nicht erklären kann. Könntest du mir da noch einen Tip geben? Es ist mir auch wichtig den Wert wirklich als Kommazahl zu haben, da dies die Nachverarbeitung erheblich vereinfacht.

Gruss und schonmal vielen Dank

Norbert
Logged

Munich/Germany
Offline Offline
God Member
*****
Karma: 11
Posts: 643
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote from: Nobi link=topic=108865.msg819336#msg819336 date=1339137563
Code:
...
int Rest = int(result);
Rest = Rest%10000;
Serial.println(Rest);

Mein Problem ist jetzt nur noch die Ausgabe mit Modulo, weil da immer Werte rauskommen, die ich mir nicht erklären kann.

Wundert Dich das, wenn Du ein unsigned long in ein vorzeichenbehaftetes int zwängst?  smiley-wink
Logged

_______
Manfred

Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

aber der Wert ist doch so klein, dass er in dem INT noch Platz finden sollte, oder?  smiley-confuse
Logged

Munich/Germany
Offline Offline
God Member
*****
Karma: 11
Posts: 643
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Auf dem ATmega ist ein int nur 16 Bit groß und geht somit nur bis ca. 32000.
Logged

_______
Manfred

Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

aber der Rest ist doch <10000, damit müsste er doch passen
Logged

Munich/Germany
Offline Offline
God Member
*****
Karma: 11
Posts: 643
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
int Rest = int(result);
Rest = Rest%10000;

Du kopierst aber vorher den Wert von result nach Rest und machst erst danach die Division.
Probiers mal so:
Code:
int Rest = unsigned long(result)%10000;
Logged

_______
Manfred

Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Jetzt läuft's so wie ich es mir gewünscht hab, der Arduino ließt die Daten ein und stellt sie per HTML Seite zur Verfügung.
Ich hab's mal hier reingestellt, das können bestimmt auch andere noch gebrauchen.
Vielen Dank für die Unterstützung!

Gruss
Norbert

* eHZy_Arduino_1_HTML.ino (8.71 KB - downloaded 71 times.)
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 1
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo zusammen,

ich will das Projekt nun auch mal angehen.
Habe das script geladen und die IP angepasst, leider gibt mein Mega nichts aus, webseite kann nicht gefunden werden.
Des Weiteren habe ich noch die Frage an welcher stelle im Script die Daten eingelesen werden?
Ich habe einen Seriellen Lesekopf!

Danke und Grüße
Simon
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 12
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,

fangen wir mal vorne an, welchen Zähler hast Du, welchen Lesekopf?
Bekommst du etwas angezeigt, wenn du nur eine einfache HTML Seite ausgibst (siehe Beispiele)?
Ist der Arduino pingbar?

Gruss
Norbert
Logged

Pages: [1] 2   Go Up
Jump to: