Skip to content
reading Berg BZ40i modbus energy meters from esp8266 arduino
C++ Objective-C
Branch: master
Clone or download
Pull request Compare This branch is 5 commits ahead, 19 commits behind reaper7:master.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
examples Fixed examples and README Jul 27, 2019
.gitignore update Jun 22, 2018
BZ40i.cpp Adapt to BZ40i protocol Apr 21, 2019
BZ40i.h Adapt to BZ40i protocol Apr 21, 2019
BZ40i_Config_User.h Adapt to BZ40i protocol Apr 21, 2019
README.md Fixed examples and README Jul 27, 2019
library.properties rename library Apr 20, 2019

README.md

Library for reading Berg BZ40i Modbus Energy meters.

SECTIONS:

1. INTRODUCTION

2. SCREENSHOTS

3. CONFIGURING

4. INITIALIZING

5. READING

6. DEBUGING

7. CREDITS


Introduction:

This library allows you reading Berg BZ40i energy meter(s) using:

  • Hardware Serial (recommended option, smallest number of reads errors) or
  • Software Serial (library for ESP8266)

you also need rs232<->rs485 converter:

  • with automatic flow direction control (look at images below) or
  • with additional pins for flow control, like MAX485
    (in this case MAX485 DE and RE pins must be connected together to one of uC pin
    and this pin must be passed when initializing the library
    )

Screenshots:


Configuring:

Default configuration is specified in the BZ40i.h file, and parameters are set to:
Software Serial, baud 4800, uart config SERIAL_8N1, without DE/RE pin.

User can set the parameters in two ways:

  • by editing the BZ40i_Config_User.h file
  • by passing values during initialization (section below)

NOTE for Hardware Serial mode: to force the Hardware Serial mode,
user must edit the corresponding entry in BZ40i_Config_User.h file.
adding #define USE_HARDWARESERIAL to the main ino file is not enough.

Keep in mind you need to set your BZ40i to RTU-mode with the same baudrate as defined here, for example "BAU r4.8k".


Initializing:

If the user configuration is specified in the BZ40i_Config_User.h file
or if the default configuration from the BZ40i.h file is suitable
initialization is limited to passing serial port reference (software or hardware)
and looks as follows:

//lib init when Software Serial is used:
#include <SoftwareSerial.h>
#include <BZ40i.h>

SoftwareSerial swSerBZ40i(D1, D2);

//              _software serial reference
//             |
BZ40i bz40i(swSerBZ40i);


//lib init when Hardware Serial is used:
#include <BZ40i.h>

//            _hardware serial reference
//           |
BZ40i bz40i(Serial);

If the user wants to temporarily change the configuration during the initialization process
then can pass additional parameters as below:

//lib init when Software Serial is used:
#include <SoftwareSerial.h>
#include <BZ40i.h>

SoftwareSerial swSerBZ40i(D1, D2);

//              ______________________software serial reference
//             |          ____________baudrate(optional, default from BZ40i_Config_User.h)   
//             |         |           _dere pin for max485(optional, default from BZ40i_Config_User.h)
//             |         |          |
BZ40i bz40i(swSerBZ40i, 9600, NOT_A_PIN);


//lib init when Hardware Serial is used:
#include <BZ40i.h>

//             _____________________________________hardware serial reference
//            |      _______________________________baudrate(optional, default from BZ40i_Config_User.h)
//            |     |           ____________________dere pin for max485(optional, default from BZ40i_Config_User.h)
//            |     |          |            ________hardware uart config(optional, default from BZ40i_Config_User.h)
//            |     |          |           |       _swap hw serial pins from 3/1 to 13/15(optional, default from BZ40i_Config_User.h)
//            |     |          |           |      |
BZ40i bz40i(Serial, 9600, NOT_A_PIN, SERIAL_8N1, false);

NOTE for ESP8266: when GPIO15 is used (especially for swapped hardware serial):
some converters have built-in pullup resistors on TX/RX lines from rs232 side,
connection this type of converters to ESP8266 pin GPIO15 block booting process.
In this case you can replace the pull-up resistor on converter with higher value (100k),
to ensure low level on GPIO15 by built-in in most ESP8266 modules pulldown resistor.


Reading:

List of available registers for Berg BZ40i:
https://github.com/adlerweb/BZ40i_Energy_Meter/blob/master/BZ40i.h#L59

//reading voltage from BZ40i with slave address 0x01 (default)
//                                      __________register name
//                                     |
float voltage = bz40i.readVal(BZ40i_U_SYSTEM);

//reading power from 1st BZ40i with slave address ID = 0x01
//reading power from 2nd BZ40i with slave address ID = 0x02
//useful with several meters on RS485 line
//                                      __________register name
//                                     |      ____BZ40i device ID  
//                                     |     |
float power1 = bz40i.readVal(BZ40i_P_SYSTEM, 0x01);
float power2 = bz40i.readVal(BZ40i_P_SYSTEM, 0x02);

NOTE: if you reading multiple BZ40i devices on the same RS485 line,
remember to set the same transmission parameters on each device,
only ID must be different for each BZ40i device.


Debuging:

Sometimes readVal return NaN value (not a number),
this means that the requested value could not be read from the BZ40i module for various reasons.

Please check out open and close issues, maybe the cause of your error is explained or solved there.

The most common problems are:

  • weak or poorly filtered power supply / LDO, causing NaN readings and ESP crashes
  • faulty or incorrectly prepared converter
  • faulty esp module
  • many users report that between each readings should be placed delay(50);
  • using GPIO15 without checking signal level (note above)
  • compilation error for hardware serial mode

You can get last error code using function:

//get last error code
//                                      __________optional parameter,
//                                     |          true -> read and reset error code
//                                     |          false or no parameter -> read error code
//                                     |          but not reset stored code (for future checking)
//                                     |          will be overwriten when next error occurs
uint16_t lasterror = bz40i.getErrCode(true);

//clear error code also available with:
bz40i.clearErrCode();

Errors list returned by getErrCode:
https://github.com/adlerweb/BZ40i_Energy_Meter/blob/master/BZ40i.h#L214

You can also check total number of errors using function:

//get total errors counter
//                                       _________optional parameter,
//                                      |         true -> read and reset errors counter
//                                      |         false or no parameter -> read errors counter
//                                      |         but not reset stored counter (for future checking)
uint16_t cnterrors = bz40i.getErrCount(true);

//clear errors counter also available with:
bz40i.clearErrCount();

Notes:

I wasn't able to get a proper timing with ESP8266 and SoftwareSerial 5.x. If you run into similar problems try downgrading to Version 4.0. Also keep in mind a lower baudrate is much more likely to work.


Credits:

👍 SDM_Energy_Meter library by Reaper7 (https://github.com/reaper7/SDM_Energy_Meter)
👍 ESP SoftwareSerial library by Peter Lerup (https://github.com/plerup/espsoftwareserial)
👍 crc calculation by Jaime García (https://github.com/peninquen/Modbus-Energy-Monitor-Arduino)

You can’t perform that action at this time.