Communicating with the ADE7953 chip via I2C to read voltage

I am trying to program the ADE7953 to read RMS voltage. It communicates with an Arduino via I2C. I have been working with Arduino for a while, but this is the first sensor I am using that communicates via I2C (hence I am a newbie at I2C).

Below is the code I wrote:

#include “Wire.h”

void setup() {
Wire.begin();
Serial.begin(9600);
}

void loop() {
getVoltageRMS();
delay(1000);
}

void getVoltageRMS()
{
//The 24-bit unsigned voltage channel rms measurement is available in the VRMS register (Address 0x21C and Address 0x31C)
Wire.beginTransmission(0x71); //not sure if 0x70 or 0x71. please see below
Wire.write(0x21C);
Wire.endTransmission();
Wire.requestFrom(0x70, 3); //not sure if 0x70 or 0x71. please see below
//Serial.println(“Here”);
int a = Wire.read();
int b = Wire.read();
int c = Wire.read();
Serial.println(a);
Serial.println(b);
Serial.println(c);
Serial.println("---------");
}

The datasheet is here: http://www.analog.com/media/en/technical-documentation/data-sheets/ADE7953.pdf
From the datasheet, the address of the ADE7953 is 0111000X (0x70 or 0x71). Bit 7 in the address byte indicates whether a read or a write is required: 0 indicates a write, and 1 indicates a read. The 24-bit address for VRMS measurement is 0x21C, and the 32-bit address for VRMS measurement is 0x31C.

When I upload the code, I just see -1 for all a,b,c on the serial monitor regardless of the voltage source I connect for measuring. Another information that I found on the datasheet, which may be useful is that, for maximum accuracy and dynamic range, the following sequence should be followed each time the ADE7953 is powered up:- Write 0xAD to Register Address 0xFE: This unlocks Register 0x120. Then write 0x30 to Register Address 0x120. This configures the optimum settings. I am not sure how to do this, but I think this needs to be written inside void setup.

Thank you in advance for any help.

sketch_nov02a.ino (628 Bytes)

Hi, Welcome to the forum.

Please read the first post in any forum entitled how to use this forum. http://forum.arduino.cc/index.php/topic,148850.0.html then look down to item #7 about how to post your code. It will be formatted in a scrolling window that makes it easier to read.

Thanks.. Tom.. :)

I think you are making two errors.
First, using the wire library all the device addresses are 7 bits, and for ADE7953 the 7 bit device address will be 0x38. The wire library will append the read/write bit.
Second, the register address is two bytes, and must be sent MSB,LSB.

void getVoltageRMS()
{
  unsigned long voltageRMS = 0;
  //The 24-bit unsigned voltage channel rms measurement is available in the VRMS register (Address 0x21C and Address 0x31C)
  // Wire.beginTransmission(0x70);
  Wire.beginTransmission(0x38);//use 7 bit device address
  // Wire.write(0x21C);// register address must be 2 bytes, msb,lsb
 //0x21c 24 bit VRMS register
  Wire.write(0x02);//MSB set register pointer
  Wire.write(0x1C);//LSB  set register pointer
  Wire.endTransmission();
  // Wire.requestFrom(0x71, 3);
  Wire.requestFrom(0x38, 3);
  //Serial.println("Here");
  int a = Wire.read();
  int b = Wire.read();
  int c = Wire.read();
  Serial.println(a);
  Serial.println(b);
  Serial.println(c);
  Serial.println("---------");

When I upload the code, I just see -1 for all a,b,c on the serial monitor regardless of the voltage source I connect for measuring. Another information that I found on the datasheet, which may be useful is that, for maximum accuracy and dynamic range, the following sequence should be followed each time the ADE7953 is powered up:- Write 0xAD to Register Address 0xFE: This unlocks Register 0x120. Then write 0x30 to Register Address 0x120. This configures the optimum settings. I am not sure how to do this, but I think this needs to be written inside void setup.

Follow the same scheme as explained with device address and register address. But you will use a write command after the register address and before the endTransmission.

Wire.beginTransmission(0x38);//use 7 bit device address
Wire.write(0x00);//MSB set register pointer
Wire.write(0xFE);//LSB set register pointer
Wire.write(0xAD);
Wire.endTransmission();

Wire.beginTransmission(0x38);//use 7 bit device address
Wire.write(0x01);//MSB set register pointer
Wire.write(0x20);//LSB set register pointer
Wire.write(0x30);
Wire.endTransmission();

Thank you cattledog for explaining the errors to me. It would be great if you could explain a couple other things to me.

  1. As you can see, I tried using the 24-bit register address to read Vrms. If I were to use the 32-bit address, would I have to change anything else in the code other than the address (particularly in the line Wire.requestFrom(0x38, 3); )? In other words, I want to understand what the 3 does in that line.

  2. In another forum, I found the following code for converting the raw data to Vrms. Could you please tell me what each line is doing?

if(Wire.available() >0){     
      voltageRMS=((unsigned long)Wire.read() << 16) & 0x00FF0000;
      voltageRMS=voltageRMS+(((unsigned long)Wire.read()<<8) & 0X0000FF00);
      voltageRMS=voltageRMS+((unsigned long)Wire.read()) & 0x000000FF;
    }
Serial.println(voltageRMS);

If I were to use the 32-bit address, would I have to change anything else in the code other than the address (particularly in the line Wire.requestFrom(0x38, 3); )? In other words, I want to understand what the 3 does in that line.

The 32 bit VRMS register is 0x31. Where you would use the 0x21 as a 24 bit register address, change that to the 0x31 for the 32 bit data location.

The Wire.requestFrom() asks for a number of consecutive bytes starting from the register address pointer, so you will need to request 4 bytes from 0x31 and not 3. I’m actually not sure is there is indeed an extra byte of data, and it sjust not 24 bits in a 32 bit channel. The data sheet about the voltage channel ADC output is not clear to me.

I found the following code for converting the raw data to Vrms

This code is using bit shifting << and masking to building a 24 bit unsigned long integer from the 3 bytes read. I’m not certain if it wants to be signed or unsigned if the device is reading AC input. I’m also not sure how you go about rescaling the giant number you get (ADC values I think) back to voltage.

See this arduino reference on bitshifting and the bitwise & as a mask.https://www.arduino.cc/en/Reference/Bitshift
https://www.arduino.cc/en/Reference/BitwiseAnd

My ADE7953 is communicating well with the Arduino through I2C, thanks to your help. However, I might need to make it work with SPI sometime soon. Any help with SPI would be great.

Below is the code I wrote:

#include <SPI.h>

const byte READ = 0b10000000;
const byte WRITE = 0b00000000;

void setup() {
  Serial.begin(9600);
  SPI.begin();
  delay(100);


  /*The SPI communication packet consists of two initial bytes that contain the address of the register that is to be read from or written to. This address should be transmitted MSB first. The third byte of the communication determines whether a read or a write is being issued. The most significant bit of this third byte should be set to 1 for a read operation and to 0 for a write operation.*/
  //Write 0xAD to Register Address 0xFE: This unlocks Register 0x120.
  SPI.transfer(0x00);
  SPI.transfer(0xFE);
  SPI.transfer(WRITE);
  SPI.transfer(0x00);
  SPI.transfer(0xAD);

  //Write 0x30 to Register Address 0x120: This configures the optimum settings.
  SPI.transfer(0x01);
  SPI.transfer(0x20);
  SPI.transfer(WRITE);
  SPI.transfer(0x00);
  SPI.transfer(0x30);
  delay(100); 
}

void loop() {
  getVoltageRMS();
  delay(250);
}

//The 24-bit unsigned voltage channel rms measurement is available in the VRMS register (Address 0x21C)
void getVoltageRMS()
{
  unsigned long voltageRMS = 0;
  SPI.transfer(0x02);
  SPI.transfer(0x1C);
  voltageRMS = ((unsigned long)SPI.transfer(READ) << 16) & 0x00FF0000;
  voltageRMS = voltageRMS+(((unsigned long)SPI.transfer(READ)<<8) & 0X0000FF00);
  voltageRMS = voltageRMS+((unsigned long)SPI.transfer(READ)) & 0x000000FF;
  //Not sure if I am doing the above reading of register 0x21C correctly.
  Serial.println("RMS Voltage:");
  Serial.println(voltageRMS);
  Serial.println("---------");
}

Once again, the datasheet is here: http://www.analog.com/media/en/technical-documentation/data-sheets/ADE7953.pdf
For your information, the SPI interface for it are described on page 52 of the datasheet. Thanks again in advance.

The first thing I notice in our SPI version is that there is no writing to the CS(Chip Select) Pin which is required to enable the transmission. From the data sheet, the hardware pin 28 is CS on the ADE7953. A pin on the uno needs to be connected to this, and need to be written LOW for a transmission and HIGH when no data is moving on the bus. I have used pin10 for this in my code below.

Nick Gammon has written a good tutorial on SPI Gammon Forum : Electronics : Microprocessors : SPI - Serial Peripheral Interface - for Arduino

You might as well confirm the other connections as well. On the uno
MOSI = pin 11 connect to ADE 7953 pin 27
MISO = pin 12 connect to ADE 7953 pin 26
SCK = pin 13 connect to ADE 7953 pin 25

I too am a little unclear about the reading of the voltage data. Your method of using SPI.transfer(READ) as a dummy write to get the return value may be correct, but I’m more familiar with SPI.transfer(0x00) as the way to get the return value. I also placed SPI.transfer(READ) after the address bytes.

I was also somewhat unsure of the bracketing to get the mask correct, but I think the formation of the 24 bit value can be written without the mask. Here’s my version of your code.

#include <SPI.h>

const byte READ = 0b10000000;
const byte WRITE = 0b00000000;
const byte CS = 10;

//const byte CS = what pin is connected to device pin 28?
//For this code demo, I will use Arduino pin 10


void setup() {
  Serial.begin(9600);
  SPI.begin();
  delay(100);
  pinMode(CS, OUTPUT);//chip select pin on Arduino
  digitalWrite(CS,HIGH);//no data


  /*The SPI communication packet consists of two initial bytes that contain the address of the register that is to be read from or written to. This address should be transmitted MSB first. The third byte of the communication determines whether a read or a write is being issued. The most significant bit of this third byte should be set to 1 for a read operation and to 0 for a write operation.*/
  //Write 0xAD to Register Address 0xFE: This unlocks Register 0x120.

  digitalWrite(CS, LOW);//enable data transfer
  SPI.transfer(0x00);
  SPI.transfer(0xFE);
  SPI.transfer(WRITE);
  SPI.transfer(0x00);
  SPI.transfer(0xAD);

  //Write 0x30 to Register Address 0x120: This configures the optimum settings.
  SPI.transfer(0x01);
  SPI.transfer(0x20);
  SPI.transfer(WRITE);
  SPI.transfer(0x00);
  SPI.transfer(0x30);
  digitalWrite(CS,HIGH);//end transfer
  delay(100);
}

void loop() {
  getVoltageRMS();
  delay(250);
}

//The 24-bit unsigned voltage channel rms measurement is available in the VRMS register (Address 0x21C)
void getVoltageRMS()
{
  unsigned long voltageRMS = 0;
  digitalWrite(CS, LOW);//enable data transfer
  SPI.transfer(0x02);
  SPI.transfer(0x1C);
  SPI.transfer(READ);

  //read back on dummy write,and form 24 bit long
  voltageRMS = (unsigned long)SPI.transfer(0x00) << 16;
  voltageRMS = voltageRMS + ((unsigned long)SPI.transfer(0x00) << 8);
  voltageRMS = voltageRMS + (unsigned long)SPI.transfer(0x00);
  
  digitalWrite(CS,HIGH);//end transfer
  Serial.println("RMS Voltage:");
  Serial.println(voltageRMS);
  Serial.println("---------");
}

Hi Everyone, I am building an energy meter using ADE7953.

I have done all the connections as per the document given. my I2C communication is set. and I could read all the default values of the registers.

The problem I am facing is that I am unable to read practical voltage applied.

I am using a 12v500ma transformer for input for testing purpose(directly connected to the concerned pin using 1Mohm resistor)

is this the correct way to test voltage?if not can u please suggest me one.or shall I do it with the shunt? can you please help

Thanking you.

Hello could you please provide the complete ADE7953 i2c communication code with the arduino