Arduino Uno I2C HDL3000 PSU

I have access to a HDL3000 PSU and it is connected to an Arduino Uno via I2C.
I found this documentation that explains how to communicate with the HDL3000 from an Arduino using I2C HDS_Series_I2C.pdf .

The code I use:

#include <Wire.h>

const int i2cAdd = 0x50;   // I2C address

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

void loop() {
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x60);      // Send command
  Wire.endTransmission(false);
  Wire.requestFrom(i2cAdd,2); 

  while(Wire.available()){
    char c = Wire.read();
    Serial.print(c);
  }
  delay (100);
  Serial.println();
}

When I send the command "0x00" to get "Unit Manufacturer" the response is "XP Power Ltd".

But when I send the command "0x60" to get "Output Voltage Measurement" the response is empty.
The same result is for "Output Current Measurement", "Output Voltage Setting" and "Output Current Setting".

I don't understand why.

Any idea?

Is this the correct command? Shouldn't the argument be "true"?

  Wire.endTransmission(false);

No, the datasheet shows no stop condition after the write.

What does empty mean? The result won't be in ASCII, so you might have to change the code line

to

Serial.print((uint8_t) c, HEX);

to get a readable version of the return value.

Great!

I tried to read "Output Voltage Measurement" from the PSU.
I wrote:

  Wire.beginTransmission(i2cAdd);
  Wire.write(0x60);      // Send command
  Wire.endTransmission(false);
  Wire.requestFrom(i2cAdd,1); 

I got "0xFF"

And then:

  Wire.beginTransmission(i2cAdd);
  Wire.write(0x61);      // Send command
  Wire.endTransmission(false);
  Wire.requestFrom(i2cAdd,1); 

I got "0xB"

But this doesn't sound right.

In the documentation it states that to get "Output Voltage Measurement" the following command is sent to the PSU:

[S_][A0][60][RS][A1][02][P_]

I found some information on this document : DV164122_serial_analyzer_eng_users.pdf

It states on page 36:
• Start bit (S_)
• Slave Address[W] – Enter the slave address of the device to communicate with.
The Write bit should be cleared to indicate a write operation.
• Word Address – Enter the word address
• Restart (RS),
• Slave Address[R] – The slave address with the Write bit set will be automatically
entered when the Slave Address[W] has been entered.
• Byte Count – Enter the number of bytes to be read
• Stop bit (P_)

How to translate this command in Arduino code?

That means the device measures a voltage of 0x0BFF cV = 30.71V. Does that make sense?

The second half of page 5 explains how to interpret the bytes.

You almost had it:

  Wire.beginTransmission(i2cAdd);
  Wire.write(0x60);      // Send command
  Wire.endTransmission(false);
  Wire.requestFrom(i2cAdd,2);
  uint16_t l = Wire.read();
  uint16_t h = Wire.read();
  uint16_t v = h << 8 | l;
  float voltage = (float) v / 100.0;

When I try your code I get 655.35.
l = 255 (0xFF)
h = 255 (0xFF)
v = 65535 (0xFFFF)

The voltmeter shows cV=29.90V.

This is the more explicit version. If that doesn't work, post a complete wiring diagram and a sharp picture of your setup.

  Wire.beginTransmission(i2cAdd);
  Wire.write(0x60);      // Send command
  Wire.endTransmission(false);
  Wire.requestFrom(i2cAdd,1);
  uint16_t l = Wire.read();
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x61);      // Send command
  Wire.endTransmission(false);
  Wire.requestFrom(i2cAdd,1);
  uint16_t h = Wire.read();
  uint16_t v = h << 8 | l;
  float voltage = (float) v / 100.0;

Your code works as expected.
It shows 30.71
But when I check with the voltmeter I get 29.90V
A difference of 0.81V.

I believe that the code is correct.
I will try to set another voltage value and then read the it.

Anyway I appreciate your help.
Thanks!

Is that a high quality meter? According to the datasheet the output voltage has an accuracy of +/-1%, so about a half of the difference might be on the PSU side, cheap voltage meters often have an accuracy in the same range.

In fact I tested with a Fluke 115 and also with a UNI-T UT33A and both show 29.9 V.
I don't understand why it is so.

I am trying to sett the voltage according to the document on page 6.
First we send [S_][50][70][E8][03][P_] and then [S_][50][7C][85][P_]

My code looks like this:

  // Write command
  // Output Voltage Setting
  // 10V
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x70); 
  Wire.write(0xE8);        
  Wire.write(0x03);          
  Wire.endTransmission(false);

  // Control Register Setting
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x7C);     
  Wire.write(0x85);        
  Wire.endTransmission(false);

On the PSU the status LED turns Solid (Orange) meaning status "DC Output OK in remote control mode".
But when I read the output voltage I get this:

25.59
0.11
0.00
0.00
0.00
0.00

I use the same code as earlier to read the voltage.
Apparently the code to set the voltage is not correct.

Here the false parameter is wrong, it needs a stop condition. So either set it to true or don't set it (to use the default which is true).

1 Like

The same result!

Here is the updated code:

  // Write command
  // Output Voltage Setting
  // 10.00V: 1000 = 0x03E8
  // Write 0xE8 to address 0x70  
  // Write 0x03 to address 0x71   
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x70); 
  Wire.write(0xE8);        
  Wire.write(0x03);          
  Wire.endTransmission();

  // Control Register Setting
  // Write 0x85 to address 0x7C
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x7C);     
  Wire.write(0x85);        
  Wire.endTransmission();

Did i miss something?

Maybe a consecutive write is also not possible (as consecutive reads), so you might have to do three writes:

// Write command
  // Output Voltage Setting
  // 10.00V: 1000 = 0x03E8
  // Write 0xE8 to address 0x70  
  // Write 0x03 to address 0x71   
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x70); 
  Wire.write(0xE8);        
  Wire.endTransmission();
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x71); 
  Wire.write(0x03);          
  Wire.endTransmission();

  // Control Register Setting
  // Write 0x85 to address 0x7C
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x7C);     
  Wire.write(0x85);        
  Wire.endTransmission();

No, but a full sketch would be nice.
Are you talking to I2C address 0x50 ? Did you run a I2C Scanner sketch ?

I assume that multiple data bytes can be read and written, but pylon is right, it is somewhat vague.
There are two registers that are 16-bit and also Read/Write. You can do a test if you can write two bytes and read two bytes and then check with other values.

Here is the entire code:

#include <Wire.h>

const int i2cAdd = 0x50;

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

  setOutputVoltage();
  readOutputVoltage();
}

void readOutputVoltage(){
  // Read command
  // Read Output Voltage Measurement
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x60);      // Send command
  Wire.endTransmission(false);
  Wire.requestFrom(i2cAdd,1);
  uint16_t l = Wire.read();
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x61);      // Send command
  Wire.endTransmission(false);
  Wire.requestFrom(i2cAdd,1);
  uint16_t h = Wire.read();
  uint16_t v = h << 8 | l;
  float voltage = (float) v / 100.0;
  Serial.print(voltage);  
  Serial.println();
}

void setOutputVoltage(){
  // Write command
  // Output Voltage Setting
  // 10V: 1000 = 0x03E8
  // Write 0xE8 to address 0x70  
  // Write 0x03 to address 0x71   
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x70); 
  Wire.write(0xE8);        
  Wire.endTransmission();
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x71); 
  Wire.write(0x03);          
  Wire.endTransmission();

  // Control Register Setting
  // Write 0x85 to address 0x7C
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x7C);     
  Wire.write(0x85);        
  Wire.endTransmission();
}

void loop() {
}

After a couple of readings I get this:

30.71
25.59
0.15
0.00
0.00
0.00

How do you get a couple of readings ? Do you reset the Arduino board ?

It might need a delay between I2C sessions.
Can you add a delay of 1ms after each I2C session ?

You could try a lower or higher clock, for example 50kHz and 200kHz

Wire.begin();
Wire.setClock(50000UL);       // 50kHz as a Unsigned Long

You could test the error returned by Wire.endTransmission() and the number by Wire.requestFrom();

  Wire.beginTransmission(i2cAdd);
  Wire.write(0x60);      // Send command
  int error = Wire.endTransmission(false);
  if(error != 0)
  {
    Serial.println("O no, communication lost with the sensor");
  }
  int n = Wire.requestFrom(i2cAdd,1);
  if(n == 1)         // same amount as requested ?
  {
    uint16_t l = Wire.read();
  }
  else
  {
    Serial.println("Something wrong, communication lost with the sensor");
  }

How is your bus ? What are your pullup resistors ? What kind of cable or wires do you use ?

How do you get a couple of readings ? Do you reset the Arduino board ?

Yes I reset the Arduino board after that I commented out setOutputVoltage();

It might need a delay between I2C sessions.
Can you add a delay of 1ms after each I2C session ?

Yes I did, no difference.

You could try a lower or higher clock, for example 50kHz and 200kHz

Yes I did, no difference.

You could test the error returned by Wire.endTransmission() and the number by Wire.requestFrom();

Yes I did, no error.

How is your bus ? What are your pullup resistors ? What kind of cable or wires do you use ?

As I know the Uno has pullup resistors built in that pull the SDA and SCK to 5 volts.

Wiring:

#include <Wire.h>

const int i2cAdd = 0x50;

void setup() {
  Serial.begin(9600); 
  Wire.begin();
  //Wire.setClock(100000);
  //Wire.setClock(200000UL); 
  
  setOutputVoltage();
  readOutputVoltage();
}

void readOutputVoltage(){
  // Read command
  // Read Output Voltage Measurement
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x60);      // Send command
  int result1 = Wire.endTransmission(false);
  delay(10);    // Delay 10ms
  Serial.print("Result1: ");
  Serial.print(result1);
  Serial.println();
  
  Wire.requestFrom(i2cAdd,1);
  uint16_t l = Wire.read();
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x61);      // Send command
  int result2 = Wire.endTransmission(false);
  delay(10);    // Delay 10ms
  Serial.print("Result2: ");
  Serial.print(result2);
  Serial.println();
  
  int result3 = Wire.requestFrom(i2cAdd,1);
  Serial.print("Result3: ");
  Serial.print(result3);
  Serial.println();
  
  uint16_t h = Wire.read();
  uint16_t v = h << 8 | l;
  float voltage = (float) v / 100.0;
  Serial.print(voltage);  
  Serial.println();
}

void setOutputVoltage(){
  // Write command
  // Output Voltage Setting
  // 10V: 1000 = 0x03E8
  // Write 0xE8 to address 0x70  
  // Write 0x03 to address 0x71   
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x70); 
  Wire.write(0xE8);        
  Wire.endTransmission();
  delay(10);    // Delay 10ms
  
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x71); 
  Wire.write(0x03);          
  Wire.endTransmission();
  delay(10);    // Delay 10ms

  // Control Register Setting
  // Write 0x85 to address 0x7C
  Wire.beginTransmission(i2cAdd);
  Wire.write(0x7C);     
  Wire.write(0x85);        
  Wire.endTransmission();
  delay(10);    // Delay 10ms
 }

void loop() {
}

Output:

Result1: 0
Result2: 0
Result3: 1
25.19
Result1: 0
Result2: 0
Result3: 1
1.26
Result1: 0
Result2: 0
Result3: 1
0.03
Result1: 0
Result2: 0
Result3: 1
0.00

It seems that the communication is okay, the device is really returning zeros.
Result1 and 2 are the error codes and are zero, that is okay.
Result3 is the return value of Wire.requestFrom(), that is also okay since 1 byte was requested, so it should return 1.

Somehow the device gets tired of sending data.
I did not read everything, can you find example of others ?
Can you put it in the loop with a 5 second delay at the end of the loop() ?
Maybe try an other register, maybe include reading the manufacturer string, so you know that everything is working.

They say that a pullup resistors of 2k are needed.

When I send the command "0x00" to get "Unit Manufacturer" the response is "XP Power Ltd".

I also added pullup resistors 2k with no change.

I ordered the same interface equipment that is used in the document, PICkit serial analyzer.
I am wandering if this PSU really works.

Hello,
How did it end up ? were you able to program the power supply ? In my case, I am trying to use Rasberry Pi I2C to communicate and still no luck. I don't detect any device at all. Should the wiring be the same ?