Project: S0-Interface for energy counting with Homematic CCU.

S0-impulse interface for Homematic CCU.


Counts impulses of energy counters with 'S0' output, calculates used energy end sends value via LAN to CCU.


Hardware: Power:
mbed pin 1 to GND
mbed pin 2 to +5V
3V lithium cell + to mbed pin 3
3V lithium cell - to GND
Inputs:
mbed pin 5 = Input pin to energy meter S0 pin 9(open collector pin 9 = + pin 8 = GND).
mbed pin 5 to Pullup of 2.2k to 5v mbed pin 2 (or internal pullup, our 3,3V mbed out).
GND to pin 8 energy meter.
Future: use opto couplers
LAN:


Features:

  • count pulses from one or more energy meters "Swissnox SX-3L (800imp/kWh)" with S0-interface. ...ok
  • store logs in SD-Card or USB stick. ...not yet
  • Web interface to view impulses/day/month/year and calculated costs/day/month/year. ...not yet


For the future: Count with interrupt to 2 Variables

  • one counts from 0 to max (absolute impulses/800 = kWh absolute )
  • one counts during 1 minute.( x/800 * 60 ) => write to log or one timer to measure time between 2 impulses. (measured time [ms] * 1000 * 60 * 60 / 800/kWh) = actual kW => write to log every minute.




Dev Phase 1:

  • Count S0 impulses in counter 1. (InterruptIn) ...ok
  • Send to CCU to display ...ok
  • Calculate impulses/800 ...ok




Dev Phase 2:

  • Use non-volatile registers of RTC to store during power cut ...ok
  • add button to set to zero the RTC registers becausse they arent reset by reboot ...still to do see: http://mbed.org/users/4180_1/notebook/internet-lcd-clock/
  • Count from 06:00 till 22:00 in counter 1, otherwise in counter 2 (double tarif)
  • Menu (needs LCD) or ini-file for settings ?




Hardware:
/media/uploads/DerMich/_scaled_s0-interface_for_homematic_ccu.jpg

Source:

/**
 * =============================================================================
 * S0 to CCU Interface
 * =============================================================================
 * Copyright (c) 2013 Michael Jank
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * =============================================================================
 */



/*  S0 - Interface for HM-CCU.

 Features:
 * Count impulses (InterruptIn) in non-volatile register of RTC.
  (5 are possible) GPREG0…GPREG4 RTC-RAM battery buffered 5*32bit
 * Calculate count / 800 to get kwh.
 * Send result to CCU via DB-ACCESS direct into a "System Variable", replaced by xml-rpc in V9
 * User parameters are passed in firm.txt Firmwareupdater file.



 Pins:
 P5 - Counter1
 P6 - Counter2
 P7 - Counter3
 P8 - Counter4

  P9 - Reset Counter1
 P10 - Reset Counter2
 P11 - Reset Counter3
 P12 - Reset Counter4



 Status LEDs:
 LED 1: Power is on.
 LED 2: DHCP is ok.
 LED 3: -
 LED 4: Sending to CCU.
 


 ToDo:
 - not every counter must be divided by 800
 - send all in 1 variable?
 - Use all 4 Entries?
 - choose fix ethernet ip or dhcp
 - Thanks to ...
 - URL for firmware updater


Leistungsmessung:
=================

Messen der Zeit zwischen 2 Imp: z.B. 1.5 s  = 1.5/3600 h

3600/(800*1.5)      [kWh/h = kw]

Formel der Leistung in W:

 3600000/(Divisor*Abstand zwischen 2 Impulsen in Sekunden)

Beispiel: 1.5:
3600000/(800*1.5) = 3000W


- division durch 0 oder timer überlauf

*/

#include "mbed.h"
#include "FirmwareUpdater.h"         // Auto update this program
#include "EthernetNetIf.h"           // Ethernet 
#include "HTTPClient.h"              // HTTP Client.
#include "ConfigFile.h"              // File with config data on mbed stick: input.cfg

#define VERSION 11                   // for automatic update
#define HOSTNAME "S0CCU"


/*

Ex. for firm.txt
----------------

12

# First number is for FirmwareUpdater
# UpdateServerURL=

# Ethernet Settings CCU
CCU_IP=192.168.x.x
SEND_INTERVALL=120

# Ethernet Settings MBED
USE_DHCP=NO
MBED_IP=
MBED_MASK=
MBED_GATEWAY=

# Counters Settings
SYSVAR1=Zaehler1
SYSVAR_POWER1=Power1
DIVISOR1=800

SYSVAR2=S0-Zaehler2
SYSVAR_POWER2=Power2
DIVISOR2=800

SYSVAR3=S0-Zaehler3
SYSVAR_POWER3=Power3
DIVISOR3=800

SYSVAR4=S0-Zaehler4
SYSVAR_POWER4=Power4
DIVISOR4=800

*/

// Declar. variables here to be global.
int waitforresend = 120;
float divisor1 = 800;
float divisor2 = 800;
float divisor3 = 800;
float divisor4 = 800;
char CCU_IP[BUFSIZ];
char SYSVAR1[BUFSIZ];
char SYSVAR2[BUFSIZ];
char SYSVAR3[BUFSIZ];
char SYSVAR4[BUFSIZ];
char DIVISOR1[BUFSIZ];
char DIVISOR2[BUFSIZ];
char DIVISOR3[BUFSIZ];
char DIVISOR4[BUFSIZ];
char SEND_INTERVALL[BUFSIZ];
char SYSVAR_POWER1[BUFSIZ];
char SYSVAR_POWER2[BUFSIZ];
char SYSVAR_POWER3[BUFSIZ];
char SYSVAR_POWER4[BUFSIZ];

LocalFileSystem local("local");
ConfigFile cfg;

EthernetNetIf eth(HOSTNAME);
// EthernetNetIf eth;  // Instantiate the interface using DHCP:

HTTPClient http;
FirmwareUpdater fwup("http://192.168.1.4:90/mbedFirmwareUpdater/mbed3/", "firm", false);
// FirmwareUpdater fwup("http://192.168.1.1/mbedFirmwareUpdater/mbed3/", "firm", false);
// There are 2 files for the firmware.
//  1. firm.txt : firmware version file.
//  2. firm.bin : firmware binary file.

// counters 1-4 on pin 5-8
InterruptIn counter1(p5);
InterruptIn counter2(p6);
InterruptIn counter3(p7);
InterruptIn counter4(p8);

// buttons 1-4 for "Reset Counter" 1-4
InterruptIn button1(p9);
InterruptIn button2(p10);
InterruptIn button3(p11);
InterruptIn button4(p12);

// Timers for power measuring.
Timer t1; // measures time between 2 impulses.
Timer t2;
Timer t3;
Timer t4;
float power1,power2,power3,power4,time_s;
// int time_ms;
    
// Counter Functions declaration.
void inc_counter1() {
    LPC_RTC->GPREG0++;
    time_s = t1.read(); // read timer in seconds.
    t1.reset(); // restart timer from zero.
    if (time_s) {
        power1=3600000/time_s/divisor1; //calculate power
    } else {
        power1=0; 
      } 
}

void inc_counter2() {
    LPC_RTC->GPREG1++;
    time_s = t2.read(); // read timer in seconds.
    t2.reset(); // restart timer from zero.
    if (time_s) {
        power2=3600000/time_s/divisor2; //calculate power
    } else {
        power2=0; 
      } 
}

void inc_counter3() {
    LPC_RTC->GPREG2++;
    time_s = t3.read(); // read timer in seconds.
    t3.reset(); // restart timer from zero.
    if (time_s) {
        power3=3600000/time_s/divisor3; //calculate power
    } else {
        power3=0; 
      } 
}

void inc_counter4() {
    LPC_RTC->GPREG3++;
    time_s = t4.read(); // read timer in seconds.
    t4.reset(); // restart timer from zero.
    if (time_s) {
        power4=3600000/time_s/divisor4; //calculate power
    } else {
        power4=0; 
      } 
}

// Reset Buttons
void reset_counter1() {
    LPC_RTC->GPREG0 =0;
}
void reset_counter2() {
    LPC_RTC->GPREG1 =0;
}
void reset_counter3() {
    LPC_RTC->GPREG2 =0;
}
void reset_counter4() {
    LPC_RTC->GPREG3 =0;
}

void check_update() {
    // Check for new firmware
    if (fwup.exist() == 0) {
        printf("Found a new firmware.\n");
        if (fwup.execute() == 0) {
            printf("Update succeed.\n");
            printf("Resetting this system...\n\n\n\n\n");
            fwup.reset();
        } else {
            printf("Update failed!\n");
        }
    }
}

void send_CCU(char* ccu_ip, char* sysvar, float value) {
    // Update system variable in CCU via LAN. CCU_IP = 192.168.1.4
    // DB-ACCESS addon must be installed.
    char url[192] = {0};
    // sprintf(url, "http://%s/addons/db/state.cgi?item=%s&value=%f",ccu_ip,sysvar,value);
    sprintf(url, "http://%s:8181/loksoft.exe?ret=dom.GetObject(\"%s\").State(\"%f\")",ccu_ip,sysvar,value);
    printf("Sending: http://%s:8181/loksoft.exe?ret=dom.GetObject(\"%s\").State(\"%f\")",ccu_ip,sysvar,value);    
    HTTPText txt;
    HTTPResult r = http.get(url, &txt);
}

// Using the mbed LEDs to show status.
DigitalOut Power_Ok(LED1);
DigitalOut DHCP_Ok(LED2);
DigitalOut Sending_Ok(LED4);
 
int main()
{
    Power_Ok = 1;
    int Version = VERSION;
    printf("\r\n--- S0-Interface Counter by Michel Jank ---\r\n");
    printf("Version: %d\r\n",Version);
    
    // Setup Ethernet and print out IP got from DHCP.
    // eth.setup(); // This can be problematic after power down, when DHCP-Server isn't ready yet.
    DHCP_Ok = 0;
    EthernetErr ethErr;
    do {
        DHCP_Ok = 1;
        wait(0.4);
        DHCP_Ok = 0;
        wait(0.4);
        DHCP_Ok = 1;
        wait(0.4);
        DHCP_Ok = 0;
        printf("Setting up...\n");
        ethErr = eth.setup();
        if (ethErr) printf("Timeout\n", ethErr);
    } while (ethErr != ETH_OK);
    DHCP_Ok = 1;
    
    wait(3);
    IpAddr ethIp = eth.getIp();
    printf("Connected ok, IP : %d.%d.%d.%d\n", ethIp[0], ethIp[1], ethIp[2], ethIp[3]);
    const char* hwAddr = eth.getHwAddr();
    printf("HW address : %02x:%02x:%02x:%02x:%02x:%02x\n", hwAddr[0], hwAddr[1], hwAddr[2], hwAddr[3], hwAddr[4], hwAddr[5]);

    // ConfigFile - Read all needed keys and values.
    
    // Read a configuration file from a mbed.
    if (!cfg.read("/local/firm.txt")) {
        printf("Failure to read a configuration file.\n");
    } else {
        printf("Reading configuration file.\n");
    }
    // Get CCU IP.
    if (cfg.getValue("CCU_IP", &CCU_IP[0], sizeof(CCU_IP))) {
        printf("CCU_IP=%s\n", CCU_IP);
    }
    // Get Systemvariables for counter data.
    if (cfg.getValue("SYSVAR1", &SYSVAR1[0], sizeof(SYSVAR1))) {
        printf("SYSVAR1=%s\n", SYSVAR1);
    }
    if (cfg.getValue("SYSVAR2", &SYSVAR2[0], sizeof(SYSVAR2))) {
        printf("SYSVAR2=%s\n", SYSVAR2);
    }
    if (cfg.getValue("SYSVAR3", &SYSVAR3[0], sizeof(SYSVAR3))) {
        printf("SYSVAR3=%s\n", SYSVAR3);
    }
    if (cfg.getValue("SYSVAR4", &SYSVAR4[0], sizeof(SYSVAR4))) {
        printf("SYSVAR4=%s\n", SYSVAR4);
    }
    // Get SEND_INTERVALL.
    if (cfg.getValue("SEND_INTERVALL", &SEND_INTERVALL[0], sizeof(SEND_INTERVALL))) {
        printf("SEND_INTERVALL=%s\n", SEND_INTERVALL);
        waitforresend = atoi(SEND_INTERVALL);  // convert string to integer!
    }
    // Get DIVISORRS.
    if (cfg.getValue("DIVISOR1", &DIVISOR1[0], sizeof(DIVISOR1))) {
        printf("DIVISOR1=%s\n", DIVISOR1);
        divisor1 = atof(DIVISOR1);
    }
    if (cfg.getValue("DIVISOR2", &DIVISOR2[0], sizeof(DIVISOR2))) {
        printf("DIVISOR2=%s\n", DIVISOR2);
        divisor2 = atof(DIVISOR2);
    }
    if (cfg.getValue("DIVISOR3", &DIVISOR3[0], sizeof(DIVISOR3))) {
        printf("DIVISOR3=%s\n", DIVISOR3);
        divisor3 = atof(DIVISOR3);
    }
    if (cfg.getValue("DIVISOR4", &DIVISOR4[0], sizeof(DIVISOR4))) {
        printf("DIVISOR4=%s\n", DIVISOR4);
        divisor4 = atof(DIVISOR4);
    }
    // Get Systemvariables for power data.
    if (cfg.getValue("SYSVAR_POWER1", &SYSVAR_POWER1[0], sizeof(SYSVAR_POWER1))) {
        printf("SYSVAR_POWER1=%s\n", SYSVAR_POWER1);
    }
    if (cfg.getValue("SYSVAR_POWER2", &SYSVAR_POWER2[0], sizeof(SYSVAR_POWER2))) {
        printf("SYSVAR_POWER2=%s\n", SYSVAR_POWER2);
    }
    if (cfg.getValue("SYSVAR_POWER3", &SYSVAR_POWER3[0], sizeof(SYSVAR_POWER3))) {
        printf("SYSVAR_POWER3=%s\n", SYSVAR_POWER3);
    }
    if (cfg.getValue("SYSVAR_POWER4", &SYSVAR_POWER4[0], sizeof(SYSVAR_POWER4))) {
        printf("SYSVAR_POWER4=%s\n", SYSVAR_POWER4);
    }
    
    t1.start();
    t2.start();
    t3.start();
    t4.start();

    // attach the address of the function to the rising edge of a reset button
    button1.rise(&reset_counter1);
    button2.rise(&reset_counter2);
    button3.rise(&reset_counter3);
    button4.rise(&reset_counter4);

    // attach the address of the function to the rising edge of an interrupt entry.
    counter1.rise(&inc_counter1);
    counter2.rise(&inc_counter2);
    counter3.rise(&inc_counter3);
    counter4.rise(&inc_counter4);

    float c1, c2, c3, c4;

    while(1) {

        c1 = LPC_RTC->GPREG0/divisor1;
        c2 = LPC_RTC->GPREG1/divisor2;
        c3 = LPC_RTC->GPREG2/divisor3;
        c4 = LPC_RTC->GPREG3/divisor4;

        // Send to CCU
        Sending_Ok = 1;
        send_CCU(CCU_IP,SYSVAR1,c1);
        send_CCU(CCU_IP,SYSVAR2,c2);
        send_CCU(CCU_IP,SYSVAR3,c3);
        send_CCU(CCU_IP,SYSVAR4,c4);
        send_CCU(CCU_IP,SYSVAR_POWER1,power1);
        send_CCU(CCU_IP,SYSVAR_POWER2,power2);
        send_CCU(CCU_IP,SYSVAR_POWER3,power3);
        send_CCU(CCU_IP,SYSVAR_POWER4,power4);
        Sending_Ok = 0;

        // To avoid using ticker interrupts, make send to CCU and check for update in main loop.
        // Check for new firmware
        check_update();
        wait(waitforresend);

    }
}

txt file to include in the same dir like the bin file

# Ethernet Settings CCU
CCU_IP=xxx.xxx.xxx.xxx
SEND_INTERVALL=120
 
# Ethernet Settings MBED
USE_DHCP=YES
MBED_IP=
MBED_MASK=
MBED_GATEWAY=
 
# Counters Settings
SYSVAR1=EnergieverbrauchBoiler
SYSVAR_POWER1=Power1
DIVISOR1=800
 
SYSVAR2=S0-Zaehler2
SYSVAR_POWER2=Power2
DIVISOR2=800
 
SYSVAR3=S0-Zaehler3
SYSVAR_POWER3=Power3
DIVISOR3=800
 
SYSVAR4=S0-Zaehler4
SYSVAR_POWER4=Power4
DIVISOR4=800


The Source is still in beta test. There is still some work todo...


4 comments on Project: S0-Interface for energy counting with Homematic CCU.:

01 May 2013

Hi,

sieht echt interessant aus. Ich hab gerade mal versucht das bei mir zum laufen zu bekommen.Leider hab ich hier auf den MBed Seiten ihrenProjekt Source nicht gefunden und konnte nur auf den Homematic Forums Post zurückgreifen.

Könnten sie mir verraten welche weiteren LIBs im Projekt notwendig sind damit das kompiliert ?

Vielen Dank, Jens

02 May 2013

Hallo Jens,

Ich setz die Source bald mal hier rein.Wie findest du meine Lösung, die Daten direkt in die CCU-Systemvariablen zu schreiben?

02 May 2013

Hi,

danke für den aktuelleren Source. Ich find die Idee und die Umsetzung sehr gelungen. Ob ich das auf Dauer direkt in der CCU brauche muss ich erstmal ausprobieren. Aber Dein Projekt ist auf jedenfall sehr vollständig aufgezogen (Firmwareupdate, Netzwerk, Configfile). Das gibt einen sehr guten Start für mich in den MBed.

Leider hab ich immernoch keine Ahnung welche Libary Imports nötig sind damit das Projekt läuft. Allein für den HTTPClient werden mir mehr als drei verschiedene vorgeschlagen.

Mfg, Jens

02 May 2013

Nimm die von der Cookbook - Seite z.B.: http://mbed.org/users/donatien/code/HTTPClient/

Please log in to post comments.