B4R Code Snippet reading smart meter smr5.0 p1 port

MbedAndroid

Active Member
Licensed User
We should have all a smart meter, so such a beast is installed here in home. The smartmeter provides via a so called P1 port a detailed report from Electricity, gas, and sometimes water. Even the solarenergy is reported in the telegram.
For details about the telegram read https://www.netbeheernederland.nl/_upload/Files/Slimme_meter_15_a727fce1f1.pdf

The smr5.0 is one of many installed meters, but lucky there is documentation and the code can be changed easily.
With B4R I made a adapter which receives, decodes and transmits the data through Wifi to my home Domotica system running with java build with B4J

Using a WeMos mini D1 a small box is needed. Power is supplied via the P1 port. No adapters are needed. All packed in a small box
schematic : http://www.esp8266thingies.nl/wp/wp-content/uploads/2016/11/Screen-Shot-2017-01-03-at-16.43.23.png
Only i use different pins. The serial input is tied to P12 via serial software. Hardware serialport didnt work. (serial port 2)



In my case no watermeter is connected so i installed a flowmeter which gives pulses for every drop of water. No calibration how many pulses, but i found that 256 pulses is equivalent for aprox 1 liter of water.
We dont need a report of every CC, so the unit transmits only the liters.


And the final result in the webpage of the Domotics:
upload_2019-4-21_18-1-40.png


B4X:
#Region Project Attributes
   #AutoFlushLogs: True
   #CheckArrayBounds: True
   #StackBufferSize: 900
#End Region
'1-0:1.8.1(003808.351*kWh)
'1-0:1.8.2(002948.827*kWh)
'1-0:2.8.1(001285.951*kWh)
'1-0:2.8.2(002876.514*kWh)
'1-0:1.7.0(01.193*kW)
'1-0:2.7.0(00.000*kW)
'1-0:1.7Actual electricity powerdelivered (+P)in 1 Wattreso
'1-0:2.7.0.255Actual electricity Power received (-P) in 1 Wattresolution
'0-0:1.0.0(101209113020W time
'0-1:24.2.1(101209112500W)(12785.123*m3
'-0:96.14.0(0002)
'Serial0 - RX: GPIO3 , TX: GPIO1
'Serial1 - RX: GPIO9 , TX: GPIO10
'Serial2 - RX: GPIO16 , TX: GPIO17


Sub Process_Globals
   Type averageGas (timestamp As ULong, GasAtTime As Float)
   Public Serial1 As Serial
   Private SerialNative2 As Stream
   Private astream As AsyncStreams
   Private softserial As SoftwareSerial
   Private Timer1 As Timer
   Private Timer2 As Timer
'   Private server As WiFiServerSocket
'   Private serverport As UInt = 80
   Dim count=0 As Byte
   Private usocket As WiFiUDP
   Private wifi As ESP8266WiFi
   Dim firstpackage=False As Boolean
   #if test
   Private ip() As Byte = Array As Byte(192, 168, 1,64)
   #Else
   Private ip() As Byte = Array As Byte(192, 168, 1,22)
   #end if
   Private port As UInt = 5000
   Dim MacArray(6) As Byte
   Private bc As ByteConverter
   Dim Pwa=1000 As Float
   Private d1pins As D1Pins
   Dim Pwt1 As Float
   Dim Pwt2 As Float
   Dim Pwtr1 As Float
   Dim Pwtr2 As Float
   Dim Pwa As Float
   Dim Pwar As Float
   Dim Gast As Float
   Dim GasAverage As Float
   Dim Water=0 As Float
   Dim OneLiterBucket=0 As UInt
   Dim WateraverageCount=0 As ULong
   Dim WaterFlow=0 As ULong
   Dim tarif As Int
   Private HallSensor As Pin
   Private P1Data As Pin
   Private Flowsensor As Pin
   Dim avg As averageGas
End Sub

Private Sub AppStart
   Serial1.Initialize(115200)
    Log("AppStart")
   RunNative("SerialNative2", Null)
   HallSensor.Initialize(d1pins.D0,HallSensor.MODE_INPUT_PULLUP)
   HallSensor.AddListener("Change_state")
   P1Data.Initialize(d1pins.D1,P1Data.MODE_OUTPUT)
   Flowsensor.Initialize(d1pins.D2,Flowsensor.MODE_OUTPUT)
   P1Data.DigitalWrite(True)
   Flowsensor.DigitalWrite(True)
 
   astream.Initialize(SerialNative2, "astream_NewData", "astream_Error")
   softserial.Initialize(115200, 12, 13)
   astream.Initialize(softserial.Stream, "astream_newdata", Null)
   usocket.Initialize(5000,"usocket_PacketArrived")
   If wifi.Connect2("Sid","password") Then 'change to your network SSID (use Connect2 if a password is required).
       Log("Connected to wireless network.")
       Log("My ip: ", wifi.LocalIp)
   Else
       Log("Failed to connect.")
       Return
       End If
#if test
   Timer1.Initialize("Timer1_Tick", 1000) ''sample every 1 minutes for average
   Timer1.Enabled = True
#end if

   Timer2.Initialize("Timer2_Tick", 10)
   Timer2.Enabled = True

End Sub

Sub Timer2_Tick
   WateraverageCount=WateraverageCount+1
   If WateraverageCount>60000 Then 'wait for 10 minutes before resetting the counter
       Water=0
   End If

End Sub

Sub Change_state (State As Boolean)
   Select State
       Case True
           WaterFlow=WaterFlow+1           'accumulate counts just to 1 liter to send it up
           If WaterFlow>255 Then
                                   WaterFlow=0
                                   OneLiterBucket=OneLiterBucket+1 'increase 1 liter and send it for the next frame
           End If
           If WateraverageCount>0 Then
                                   Water=1200/WateraverageCount 'will give aprox liters/hour
                                   Else
                                       Water=0
                                   End If
           WateraverageCount=0
           Flowsensor.DigitalWrite(False)
#if test
           Log("state: Detected")
#end if
       Case False
           Flowsensor.DigitalWrite(True)
#if test
           Log("state: Not Detected")
#end if
   End Select
End Sub

Sub astream_Terminated
   Log("Connection is broken.")

End Sub
Sub astream_Error

End Sub
Sub astream_NewData (s() As Byte)
   ''Log(s)
   Dim temp As Float
 
   temp=bc.StringFromBytes(Parsing(s,"1-0:1.8.1"))
   If temp<>-1 Then
                       Pwt1=temp
                       P1Data.DigitalWrite(False)
   End If
   temp=bc.StringFromBytes(Parsing(s,"1-0:1.8.2"))
   If temp<>-1 Then Pwt2=temp
   temp=bc.StringFromBytes(Parsing(s,"1-0:2.8.1"))
   If temp<>-1 Then Pwtr1=temp
   temp=bc.StringFromBytes(Parsing(s,"1-0:2.8.2"))
   If temp<>-1 Then
                       Pwtr2=temp
                       P1Data.DigitalWrite(True)
   End If
'   1-0:1.7.0(00.000*kW)
   temp=bc.StringFromBytes(Parsing(s,"1-0:1.7.0"))
   If temp<>-1 Then Pwa=temp
   temp=bc.StringFromBytes(Parsing(s,"1-0:2.7.0"))
   If temp<>-1 Then Pwar=temp
   temp=(bc.StringFromBytes(Parsing(s,"0:96.14.0")))
   If temp<>-1 Then tarif=temp
   Dim sg()  As Byte
   sg=(Parsing(s,"0-1:24.2.1"))
   If sg<>"-1" Then
       Dim start As Int =bc.IndexOf(sg,"(")
       If start<>-1 Then
                   Dim time=(((sg(6)-48)*10+(sg(7)-48))*60+(sg(8)-48)*10+(sg(9)-48))*60+(sg(10)-48)*10+sg(11)-48 As ULong
                   If time>86400 Then time=0 'error condition, cant be > then 86400
                   count=count+1
                   Gast=bc.StringFromBytes(bc.SubString(sg,start+1))
                   End If
       If count=6 Then
         
           count=0 
           Dim deltaT= time-avg.timestamp As ULong
           avg.timestamp=time 'save new timestamp
           Dim deltaG=Gast-avg.GasAtTime As Float
           avg.GasAtTime=Gast
           If deltaG>0 And firstpackage Then
                               GasAverage=deltaG*3600/deltaT
                           Else
                               GasAverage=0
                               firstpackage=True
                       End If
           Dim buffer As String=JoinStrings(Array As String(wifi.LocalIp ,",","P1",",",bc.HexFromBytes(MacAddress),",",NumberFormat(Pwt1,1,1),",",NumberFormat(Pwt2,1,1),",",NumberFormat(Pwtr1,1,1),",",NumberFormat(Pwtr2,1,1),",",NumberFormat(Pwa,1,3),",",NumberFormat(Pwar,1,3),",",NumberFormat(Gast,1,3),",",NumberFormat(tarif,1,0),",",NumberFormat(GasAverage,1,3),",",NumberFormat(Water,1,3),",",NumberFormat(OneLiterBucket,1,0)))
           OneLiterBucket=0
           usocket.BeginPacket(ip, port)
           usocket.Write(buffer)
           usocket.SendPacket
#if test
           Log("packed send")
           Log(Pwt1)
           Log(Pwt2)
           Log(Pwtr1)
           Log(Pwtr2)
           Log(Pwa)
           Log(Pwar)
           Log(Gast)
           Log(tarif)
           Log(GasAverage)
           Log(Water)
#end if
           Pwa=-1
           Pwar=-1
       End If
   End If



End Sub
Sub Parsing(s() As Byte,search() As Byte) As  Byte()
   If bc.StartsWith(s,search)  Then
'       Dim index=bc.IndexOf(s,search) As Byte
       Dim start As Int =bc.IndexOf(s,"(")
       Dim stop As Int =bc.IndexOf(s,"*kW")
       Dim closingparenthesis As Int =bc.IndexOf(s,")")
       Dim gas As Int =bc.IndexOf(s,"*m3)")

'detect electricity lines
       If  stop<>-1 And start<>-1 Then
       Return (bc.SubString2(s,start+1,stop))
       Else

'detect gas lines
       If gas<>-1 And start<>-1 Then
               Return (bc.SubString2(s,start+1,gas))
           Else
   'Detect all other lines     
           If closingparenthesis<>-1 And start<>-1 Then
               Return (bc.SubString2(s,start+1,closingparenthesis))
               Else
                   Return "-1"
           End If
       End If
       End If 
       Else
           Return "-1"
   End If

End Sub
#if test
Private Sub Timer1_Tick
Select  count
       Case 0:       Dim s() As Byte ="0-1:24.2.1(101209112500W)(12785.123*m3)"
       Case 1:       Dim s() As Byte ="0-1:24.2.1(101209113000W)(12786.123*m3)"
       Case 2:       Dim s() As Byte ="0-1:24.2.1(101209113500W)(12787.123*m3)"
       Case 3:       Dim s() As Byte ="0-1:24.2.1(101209114500W)(12789.123*m3)"
       Case 4:       Dim s() As Byte ="0-1:24.2.1(101209120500W)(12790.123*m3)"
       Case 5:       Dim s() As Byte ="0-1:24.2.1(101209134500W)(12791.123*m3)"
End Select
Dim sg()=(Parsing(s,"0-1:24.2.1")) As Byte
Log(sg)
If sg<>"-1" Then
   Dim start As Int =bc.IndexOf(sg,"(")
   If start<>-1 Then
'           Log(sg(6))
'           Log(sg(7))
'           Log(sg(8))
'           Log(sg(9))
'           Log(sg(10))
'           Log(sg(11))
         
       Dim time=(((sg(6)-48)*10+(sg(7)-48))*60+(sg(8)-48)*10+(sg(9)-48))*60+(sg(10)-48)*10+sg(11)-48 As ULong
       If time>86400 Then time=0 'error condition, cant be > then 86400
       Log (time)
       count=count+1
       Gast=bc.StringFromBytes(bc.SubString(sg,start+1))
   End If
       Log(Gast)
       Dim deltaT= time-avg.timestamp As ULong
       Log (deltaT)
       If deltaT<0 Then deltaT=deltaT+86399
       avg.timestamp=time 'save new timestamp
       Dim deltaG=Gast-avg.GasAtTime As Float
       avg.GasAtTime=Gast
       If deltaG>0 And firstpackage Then
           GasAverage=deltaG*3600/deltaT
           Log(GasAverage)
       Else
           GasAverage=0
           firstpackage=True
       End If
   If count=6 Then
   count=0
   Dim deltaT= time-avg.timestamp As ULong
   Log (deltaT)
   If deltaT<0 Then deltaT=deltaT+86399
   avg.timestamp=time 'save new timestamp
   Dim deltaG=Gast-avg.GasAtTime As Float
   avg.GasAtTime=Gast
       If deltaG>0 And firstpackage Then
       GasAverage=deltaG*3600/deltaT
       Log(GasAverage)
   Else
       GasAverage=0
       firstpackage=True
   End If
     
           Dim buffer As String=JoinStrings(Array As String(wifi.LocalIp ,",","P1",",",bc.HexFromBytes(MacAddress),",",NumberFormat(Pwt1,1,1),",",NumberFormat(Pwt2,1,1),",",NumberFormat(Pwtr1,1,1),",",NumberFormat(Pwtr2,1,1),",",NumberFormat(Pwa,1,3),",",NumberFormat(Pwar,1,3),",",NumberFormat(Gast,1,3),",",NumberFormat(tarif,1,0),",",NumberFormat(GasAverage,1,3),",",NumberFormat(Water,1,3),",",NumberFormat(OneLiterBucket,1,0)))
           OneLiterBucket=0
'           Dim buffer As String=JoinStrings(Array As String(wifi.LocalIp ,",","P1",",",bc.HexFromBytes(MacAddress),",",NumberFormat(Pwt1,1,1),",",NumberFormat(Pwt2,1,1),",",NumberFormat(Pwtr1,1,1),",",NumberFormat(Pwtr2,1,1),",",NumberFormat(Pwa,1,3),",",NumberFormat(Pwar,1,3),",",NumberFormat(Gast,1,3),",",NumberFormat(tarif,1,0)))
           Log(buffer)
           usocket.BeginPacket(ip, port)
           usocket.Write(buffer)
           usocket.SendPacket
       End If

End If
End Sub
#end if


'Public Sub SendOutString(text As String)
'
'   Dim Buffer(6) As Byte
'   For i = 0 To text.Length-1
'       Buffer(i)=text.GetBytes(i)
'   Next
'   ''Log(Buffer)
''   Log("buffer send")
'   astream.Write(Buffer)
'End Sub
Private Sub usocket_PacketArrived (Data() As Byte, ip1() As Byte, port1 As UInt)
   'not used
   Log("Packet arrived")
End Sub

Sub MacAddress() As Byte()
   RunNative("getMac", Null)
   Return MacArray
End Sub


#if C
  #include <ESP8266WiFi.h>
  void getMac(B4R::Object* u) {
  WiFi.macAddress((Byte*)b4r_main::_macarray->data);
}
#end if
#if C
HardwareSerial Serial2(2); // Second Hardware Port


void SerialNative2(B4R::Object* unused)
{
::Serial2.begin(115200);
b4r_main::_serialnative2->wrappedStream = &::Serial2;
}
#End If
 
Top