S0 Outputgenerator - Energymeter

Hi all,
I'm searching for a solution to generate S0 Signals. I'm reading the energy consumption from the Smartmeter (EHZ) and want to send the current energyconsumption with S0 signals to another device.

Reading the EHZ is no problem, but It's getting a bit tricky to generate the S0 output:

The Basic:
One positive pulse is always 30ms long. The low time depends on the current energy consumption, but must be >30ms also, plus 5ms each slope change gives 70ms min. periodlength. This gives a max. frequency of about ~15Hz.

The S0 pulses are weightet. This is individual from device to device like 1000 Pulses per kWh.

Example:
Current consumption: 0,3kWh
S0 Weight: 1000 Pulses per kWh

5 Pulses per Minute
0,083 Hz
1 Pulse per 12 Sec.

I think it would be possible to do this with delay loops, but I want the arduino also doing other things, so I would like to solve it with timers and interrupts if this is possible. I have not worked with the timers except the arduino based basic funktions like tone() PWM and so on. But these seems to be very specific.

Could you maybee give me a hint how to do this with timers? or is there a other way to let this the arduino do in background?

Thank you very much in advanced!

BR
Chris

Current consumption: 0,3kWh
S0 Weight: 1000 Pulses per kWh

5 Pulses per Minute
0,083 Hz
1 Pulse per 12 Sec.

Based from this, you have to generate 300 pulses (0.3 * 1000). But what determines the time it takes to generate those 300 pulses? You had it at 5 per minute, so it will take you 1 hr to generate those 300 pulses. Why not generate the 300 pulses in lets say 30 minutes instead? This looks like solving two variables with one equation.

Look at the example Several Things at the same time to see how to break your code up into small bits so you can be transmitting this information and doing other things.

SO.. are you looking for a binary output? No? It sounds like you are looking for something that could follow a timed voltage profile as a signal.

Could you maybe sketch/post/link a picture of the profile you are looking for? I [think] I may have just what you are looking for.

-jim lee

Here's something that seems to work on an Uno, if I interpret your description properly.

It's basic S0 output on the LED for visuals. You'd call the S0Signal(...) function frequently with the latest kWh value as the parameter.

#define S0_METER_WEIGHT     1000.0      //pulses per hour per kWh
#define S0_HIGH_PULSE       30ul        //mS high pulse
#define F_MS_PER_SEC        1000.0f
#define F_SEC_PER_HOUR      3600.0f     

#define MIN_KWH             0.001f      //min allowed kWh: 1Wh would give 1 pulse per hour
#define MAX_KWH             54.0f       //max allowed kWh: 54kWh would give 

const uint8_t pinS0 = LED_BUILTIN;

enum eStatesS0
{
    ST_S0_PULSEHI=0,
    ST_S0_PULSELO
};

void setup( void )
{
    pinMode( pinS0, OUTPUT );
        
}//setup

void loop( void )
{
    //S0Signal( 0.01 );    //10Wh should give 6-min between pulses
    S0Signal( 0.3 );    //0.3kWh should give 12-sec between pulses
    //S0Signal( 1.0 );    //1.0kWh should give 3.6-sec between pulses
    //S0Signal( 10.0 );    //10.0kWh should give 360mS between pulses
    //S0Signal( 54.0 );    //54.0kWh should give 66.67mS between pulses
    
}//loop

void S0Signal( float fkWh )
{
    static uint8_t
        stateS0 = ST_S0_PULSELO;
    static uint32_t
        timeS0;
    uint32_t
        timeNow = millis();

    //defend against potential DIV0 error and too high a reading by constraining fkWh
    fkWh = constrain( fkWh, MIN_KWH, MAX_KWH );

    //compute the pulse low time
    //is period minus fixed width high-pulse
    uint32_t timeS0Low = (uint32_t)(F_MS_PER_SEC * (F_SEC_PER_HOUR / (S0_METER_WEIGHT * fkWh) )) - S0_HIGH_PULSE;

    switch( stateS0 )
    {
        case    ST_S0_PULSEHI:
            //fixed width high pulse timing
            if( (timeNow - timeS0) >= S0_HIGH_PULSE )
            {
                //when done, set pin low and move to time low-width
                digitalWrite( pinS0, LOW );
                timeS0 = timeNow;
                stateS0 = ST_S0_PULSELO;                
                
            }//if
            
        break;

        case    ST_S0_PULSELO:
            //variable width low-width timing
            //timeS0Low is adjusted each time we enter
            if( (timeNow - timeS0) >= timeS0Low )
            {
                //when done, set pin high again
                digitalWrite( pinS0, HIGH );
                timeS0 = timeNow;
                stateS0 = ST_S0_PULSEHI;                
                
            }//if
        
        break;
        
    }//switch
    
}//S0Signal

OK, I give up. What are "S0 Signals"?

Hi all,
first of all thanks for your effort.

I try to explain the S0 signal. It's used to read out energymeters. It's digital (high/ low). The high pulse is always 30ms long. The low pulse is also minimum 30ms long. Just for example this would indicate 60kWh usage at time. (1/0,06 * 60sec * 60min / 1000 Pulses/kWh). The longer the low pulse, the lower the indicated power consumtion at the moment.

@hzrnbgy: I'm not sure if I understood you correct, but It's not like I've got 0,3kw to "transfere" and then need to wait 5 Min. to do that. It's like the pulses are indicating the power consumtion right now in presence. Only at a constant power consumtion for one hour of maybee 0,3 kWh it would be static 5 Pulses per Minute..

The other thing is, my loop() is not as fast as the "statemachine" examples sugest. There is also very delaying code for sending on LAN and waiting sometimes. So I can't call the S0Signal() like @Blackfin expected within the loop() because I expect the loop() to be sometimes slower than 30ms.
I'm thinking of a solution like from @Backfin but beeing called from timer/ interrupt driven. I suggest, it will then be independent of what's loop() doing.

I started to sketch it, but the ISR_S0() is not called yet. There I'd like to use timer2 to do this.

#define S0Pin 6
#define Scaler2 16					
#define HighTime 30			// S0 HighTime in ms
#define PulsePerkWh 1000		// S0 Weight
boolean status = false;			// true = S0 high; false = S0 low
unsigned int LowTime = 0;		// Low Time for S0 in ms
unsigned int Power = 0;			// Power from EHZ in Wh
unsigned int ISRCount1 = 0;		// Counter to prescale Timer again
unsigned long ISRCount2 = 0;		// Counter each millisec

void setup()
{
	pinMode(S0Pin, OUTPUT);
}

void loop()
{
	LowTime = (long)100000/((long)Power/(long)36);
}

void ISR_S0()
{
	ISRCount1++;					// Call each 0,000064 sec (16MHz / 1024)
	if (ISRCount1 >= Scaler2)			// Call each 0,001024 sec (millisec)
	{
		ISRCount1 = 0;
		ISRCount2++;

		if (status)
		{
			digitalWrite(S0Pin, true);
			if (ISRCount > HighTime)
			{
				status = !status;
				ISRCount = 0;
			}		
		}
		else
		{
			digitalWrite(S0Pin, false);
			if (ISRCount > LowTime)
			{
				status = !status;
				ISRCount = 0;			
			}		
		}	
	}
};

Do you think this will be a solid solution?
BR
Chris

Which Arduino are you using?

I don't suppose that the OP did a internet thingy using the words "c++ read smart meter ehz"?

@Blackfin: I'm using the arduino nano

This is a timer 2-based approach. It uses output compares (2A) so the S) signal is on pin 11.

The interrupt for the timer fires roughly every 4mS and the ISR execution time measures out at around 5uS so it's taking 0.12% of the processor time.

This example uses a fixed value for the kWh reading. It is "updated" in ReadEnergy() once every 100mS. You'll have to add your own EHZ reading logic in place of the fixed test value.

It doesn't update the S0 signal with the new energy value until the next falling edge of the 30mS start pulse. It can be modified to update on the fly but it would add complexity to the ISR.

Tested on an Uno. You'd use pin D11 on a Nano.

#define S0_METER_WEIGHT     1000.0      //pulses per hour per kWh
//#define S0_HIGH_PULSE       30ul        //mS high pulse
#define TICKS_30MS          1875ul
#define F_T2_TICKRATE       62500.0
#define F_SEC_PER_HOUR      3600.0f     

#define MIN_KWH             0.001f      //min allowed kWh: 1Wh would give 1 pulse per hour
#define MAX_KWH             54.0f       //max allowed kWh: 54kWh would give

#define INIT_KWH_TICKS      20000ul

//10Wh should give 6-min between pulses
//0.3kWh should give 12-sec between pulses
//1.0kWh should give 3.6-sec between pulses
//10.0kWh should give 360mS between pulses
//54.0kWh should give 66.67mS between pulses

const uint8_t pinDBG = LED_BUILTIN;
const uint8_t pinS0 = 11;   //output compare 2A

enum eStatesS0
{
    ST_S0_30RE=0,
    ST_S0_TIME_30MSH,
    ST_S0_30FE,
    ST_S0_TIMELOW

};

volatile uint32_t
    g_kWhTicks;

void setup( void )
{
    Serial.begin(115200);
    pinMode( pinDBG, OUTPUT );
    pinMode( pinS0, OUTPUT );
    Timer2Setup();
    g_kWhTicks = INIT_KWH_TICKS;
          
}//setup

void loop( void )
{
    ReadEnergy();
   
}//loop

void ReadEnergy( void )
{
    static uint32_t
        timeReadEnergy = 0;
    uint32_t    
        timeNow = millis();

    //update reading every 100mS
    if( (timeNow - timeReadEnergy) >= 100ul )
    {
        timeReadEnergy = timeNow;
        
        //read the meter        
        float fkWh = 10.0;     //sub fixed value for testing
        fkWh = constrain( fkWh, MIN_KWH, MAX_KWH );

        //convert energy value to Timer2 ticks
        uint32_t kWhTicks = (uint32_t)(F_T2_TICKRATE * (F_SEC_PER_HOUR / (S0_METER_WEIGHT * fkWh) ) );        
        //Serial.println( kWhTicks );
        //assign global used in ISR
        noInterrupts();
        g_kWhTicks = kWhTicks;
        interrupts();
        
    }//if
    
}//ReadEnergy

void Timer2Setup( void )
{    
    TCCR2A = _BV(COM2A1) | _BV(COM2A0);
    TCNT2 = 1;
    OCR2A = 0;
    TIFR2 = _BV(OCF2A);
    TIMSK2 = _BV(OCIE2A);    
    TCCR2B = _BV(CS22) | _BV(CS21);               
        
}

ISR( TIMER2_COMPA_vect )
{
    static uint8_t
        _stateS0 = ST_S0_30RE;
    static uint32_t
        _numTicks;

    digitalWrite( pinDBG, HIGH );
    switch( _stateS0 )
    {
        case    ST_S0_30RE:
            //pin has gone high for start of 30mS period            
            _numTicks = TICKS_30MS - 256ul;
            _stateS0 = ST_S0_TIME_30MSH;
            
        break;

        case    ST_S0_TIME_30MSH:
            if( _numTicks <= 255ul )                        
            {
                //config next OC to set pin low
                TCCR2A = _BV(COM2A1);
                if( _numTicks == 0 )
                    OCR2A = OCR2A + 1;
                else
                    OCR2A = OCR2A + (uint8_t)_numTicks;
                    
                _stateS0 = ST_S0_30FE;
                
            }//if                       
            else
                _numTicks -= 256ul;
            
        break;

        case    ST_S0_30FE:
            //pin has just fallen from 30mS period.
            //leave OC pin control as is (so subsequent OCs just keep driving it low)
            //and update the numTicks for the balance of the period
            _numTicks = g_kWhTicks - TICKS_30MS - 256ul;
            _stateS0 = ST_S0_TIMELOW;
            
        break;

        case    ST_S0_TIMELOW:
            if( _numTicks <= 255ul )                        
            {
                //set OC to set pin high
                TCCR2A = _BV(COM2A1) | _BV(COM2A0);
                if( _numTicks == 0 )
                    OCR2A = OCR2A + 1;
                else
                    OCR2A = OCR2A + (uint8_t)_numTicks;
                    
                _stateS0 = ST_S0_30RE;
                
            }//if                       
            else
                _numTicks -= 256ul;
            
        break;

    }//switch

    digitalWrite( pinDBG, LOW );
    
}//ISR( TIMER2_COMPA_vect )
1 Like

Hi Blackfin,
first thank you so much! Your solution is even better than I was thinking about! It's hard to follow your code, but It's greate for me to learn how to make things better.
I was thinging of using the timer just as periodic trigger. But you're using the timer directly for high and low time individually...

I checked the output on the oscilloscope (picture attached)

I set fkW to 20,0 what should give a pulsetime of 180ms. It's perfect!!!

Just for interest, you're using static variables within functions. Is this different to using global variables? Are there advantages/ disadvantages?

Thanks again and
BR
Chris

ard24:
Just for interest, you're using static variables within functions. Is this different to using global variables? Are there advantages/ disadvantages?

From a memory usage perspective, it's no different. Variables declared as global (i.e. outside a function) and those declared static within functions occupy memory in the same way; they're persistent during the execution of the program.

But those declared as static within a function are "visible" only to that function so there's a degree of protection against them being inadvertently modified somewhere else in the code. This can be beneficial in bigger programs with multiple source files. So you get the benefit of persistence when a function exits (to maintain a state or a time/count value etc) but also that protection. On a personal note code -- especially larger more complex code -- is easier to follow if variables used in a specific function are declared within that function.

If you zoom in to a function level you may see furtherance of this thinking where a variable is declared within a specific part of a function. For example, in the ReadEnergy function:

void ReadEnergy( void )
{
    static uint32_t
        timeReadEnergy = 0;
    uint32_t    
        timeNow = millis();

    //update reading every 100mS
    if( (timeNow - timeReadEnergy) >= 100ul )
    {
        timeReadEnergy = timeNow;
        
        //read the meter        
        float fkWh = 10.0;     //sub fixed value for testing
        fkWh = constrain( fkWh, MIN_KWH, MAX_KWH );

        //convert energy value to Timer2 ticks
        uint32_t kWhTicks = (uint32_t)(F_T2_TICKRATE * (F_SEC_PER_HOUR / (S0_METER_WEIGHT * fkWh) ) );        
        //Serial.println( kWhTicks );
        //assign global used in ISR
        noInterrupts();
        g_kWhTicks = kWhTicks;
        interrupts();
        
    }//if
    
}//ReadEnergy

you'll see that two variables are not declared as "global" within the scope of the function but rather only within the scope of an if statement:

.
.
.
    if( (timeNow - timeReadEnergy) >= 100ul )
    {
        timeReadEnergy = timeNow;
        
        //read the meter        
        float fkWh = 10.0;     //sub fixed value for testing
        fkWh = constrain( fkWh, MIN_KWH, MAX_KWH );

        //convert energy value to Timer2 ticks
        uint32_t kWhTicks = (uint32_t)(F_T2_TICKRATE * (F_SEC_PER_HOUR / (S0_METER_WEIGHT * fkWh) ) );        
.
.
.

Those being fkWh and kWhTicks. Those two are visible only within the if( (timeNow... ) statement braces. In this particular function there's not really a critical nor protective need to isolate these variables this way, it just makes the code a bit clearer to the reader.

One could argue, I guess, that if declared globally (within the scope of ReadEnergy()) the processor must make space on the stack for these two variables each time the code is called which occupies a few machine cycles which are needlessly wasted cycles that are really needed only once every 100mS.

Anyway, if you need any help understanding the code feel free to PM me. I usually try to be more verbose in my comments but sometimes just get lazy.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.