2024-12-26 08:08 PM
I have developed code in arduino IDE , I am only getting the output receive from UART 04000A075 . I understand that this is the default value in register DSP_CR1. Could someone please tell me what does this mean.
I hereby provide the code for receiving and transmission of signal from STPM34 to ESP32 using UART.
#include <HardwareSerial.h>
#include <math.h>
#include <stdint.h>
#include <string.h>
#include <Arduino.h>
const int STPM34_ENABLE_PIN = 4; //Enable pin for STPM3x
//UART2 Pins of ESP32 connected to STPM32
const int PIN_16 = 16; //UART Rx pin of STPM3x
const int PIN_17 = 17; //UART Tx pin of STPM3x
HardwareSerial stpm3xSerial(2);
hw_timer_t *timer;
const int baud_rate = 9600;
uint8_t CRC_u8Checksum = 0;
#define BUFFER_SIZE 1024
char STPM3x_RCV[BUFFER_SIZE];
volatile bool timer_call = false;
volatile uint8_t u2_chk=0;
uint16_t RawRms_volt;
uint16_t RawRms_curr;
float CalcRMS_volt;
float RMS_Volt;
float RMS_Curr;
float CalcRMS_curr;
float active_pwr;
float Active_Pwr;
float active_nrj;
float Active_Nrj;
float total_nrj;
float Total_Nrj;
int32_t energy;
int32_t energy_extension;
const int TIME_OUT =1000;// 1s timeout
uint32_t raw_RMS_Voltage = 0;
uint32_t raw_RMS_Current = 0;
uint64_t calc_RMS_Voltage = 0;
uint64_t calc_RMS_Current = 0;
int32_t BRR_REG1;
int32_t raw_ACT_power=0;
int64_t calc_ACT_power=0;
int32_t raw_apparent_pwr=0;
int64_t calc_apparent_pwr=0;
int32_t PH1_REG1=0;
int32_t TOT_REG1=0;
uint32_t DSP_CR2_LSB;
uint32_t DSP_CR2_MSB;
uint32_t DSP_CR3_LSB;
uint32_t DSP_CR5_LSB;
uint32_t DSP_CR5_MSB;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Serial.println("Starting ESP32.....");
stpm3xSerial.begin(baud_rate,SERIAL_8N1,PIN_16,PIN_17);
pinMode(STPM34_ENABLE_PIN,OUTPUT);
digitalWrite(STPM34_ENABLE_PIN,HIGH);
delay(100);
digitalWrite(STPM34_ENABLE_PIN,LOW);
Serial.println("STPM3x Enabled");
STPM_UART_init();//Establish STPM3x UART communication
stpm3x_init();
// timer = timerBegin(80);//timer frequency 80 Mhz
// timerAttachInterrupt(timer,&onTimer);//call function timer handler
// timerAlarm(timer,50000,true,0);
}
void loop() {
// put your main code here, to run repeatedly:")
RMS_Volt = readRMSvoltage();
Serial.print("RMS Voltage =");
Serial.print(RMS_Volt);
Serial.print("V");
Serial.println();
RMS_Curr=readRMScurrent();
Serial.print("RMS Current =");
Serial.print(RMS_Curr);
Serial.print("A");
Serial.println();
Active_Pwr = readActivePower();
Serial.print("Active Power =");
Serial.print(Active_Pwr);
Serial.print("kW");
Serial.println();
Active_Nrj = readActiveEnergy();
Serial.print("Active Energy =");
Serial.print(Active_Nrj);
Serial.print("kWh");
Serial.println();
delay(100);
read_stpm3x_init();
Serial.print("DSP_CR2_LSB=");
Serial.print(DSP_CR2_LSB,HEX);
Serial.println();
Serial.print("DSP_CR2_MSB=");
Serial.print(DSP_CR2_MSB,HEX);
Serial.println();
Serial.print("DSP_CR3_LSB=");
Serial.print(DSP_CR3_LSB,HEX);
Serial.println();
Serial.print("DSP_CR5_LSB=");
Serial.print(DSP_CR5_LSB,HEX);
Serial.println();
Serial.print("DSP_CR5_MSB=");
Serial.print(DSP_CR5_MSB,HEX);
Serial.println();
delay(100);
}
void STPM_UART_init()
{
snd_cmmnd_stpm3x(0xFF,0x24,0x2007);//US_REG1 LSB configuration
snd_cmmnd_stpm3x(0xFF,0x25,0x03E8);// US_REG1 MSB configuration Timeout = 1 ms
snd_cmmnd_stpm3x(0xFF,0x26,0x0683);// US_REG2 LSB config Buadrate = 9600 bps
snd_cmmnd_stpm3x(0xFF,0x27,0x00);// US_REG2 MSB config Frame delay = 0
snd_cmmnd_stpm3x(0xFF,0x28,0x00);// US_REG3
}
void stpm3x_init()
{
snd_cmmnd_stpm3x(0xFF,0x00,0xA0);// DSP_CR1 LSB
snd_cmmnd_stpm3x(0xFF,0x01,0x0400);
snd_cmmnd_stpm3x(0xFF,0x02,0x00A0);
snd_cmmnd_stpm3x(0xFF,0x03,0x2400);
snd_cmmnd_stpm3x(0xFF,0x04,0x04E0);
snd_cmmnd_stpm3x(0xFF,0x05,0x0000);
snd_cmmnd_stpm3x(0xFF,0x06,0x0000);
snd_cmmnd_stpm3x(0xFF,0x07,0x0000);
snd_cmmnd_stpm3x(0xFF,0x08,0xF800);
snd_cmmnd_stpm3x(0xFF,0x09,0x003F);
snd_cmmnd_stpm3x(0xFF,0x0A,0xF800);
snd_cmmnd_stpm3x(0xFF,0x0B,0x003F);
snd_cmmnd_stpm3x(0xFF,0x0C,0xF800); //Calibration register of secondary voltage channel
snd_cmmnd_stpm3x(0xFF,0x0D,0x003F); //Swell threshold of secondary voltage channel
snd_cmmnd_stpm3x(0xFF,0x0E,0xF800); //Calibration register of secondary current channel
snd_cmmnd_stpm3x(0xFF,0x0F,0x003F); //Swell threshold of secondary current channel
snd_cmmnd_stpm3x(0xFF,0x10,0x0FFF); //Primary channel RMS upper threshold (for AH)
snd_cmmnd_stpm3x(0xFF,0x11,0x0000);
snd_cmmnd_stpm3x(0xFF,0x12,0x0FFF); //Primary channel RMS lower threshold (for AH)
snd_cmmnd_stpm3x(0xFF,0x13,0x0000);
snd_cmmnd_stpm3x(0xFF,0x14,0x0FFF); //Secondary channel RMS upper threshold (for AH
snd_cmmnd_stpm3x(0xFF,0x15,0x0000);
snd_cmmnd_stpm3x(0xFF,0x16,0x0FFF); //Secondary channel RMS lower threshold (for AH)
snd_cmmnd_stpm3x(0xFF,0x17,0x0000);
snd_cmmnd_stpm3x(0xFF,0x18,0x0327); //current 1 gain
snd_cmmnd_stpm3x(0xFF,0x19,0x0327);
snd_cmmnd_stpm3x(0xFF,0x1A,0x0327); //current 2 gain
snd_cmmnd_stpm3x(0xFF,0x1B,0x0327);
snd_cmmnd_stpm3x(0xFF,0x1C,0x0000);
snd_cmmnd_stpm3x(0xFF,0x1D,0x0000);
snd_cmmnd_stpm3x(0xFF,0x1E,0x0000);
snd_cmmnd_stpm3x(0xFF,0x1F,0x0000);
snd_cmmnd_stpm3x(0xFF,0x20,0x777F);
snd_cmmnd_stpm3x(0xFF,0x21,0x0204);
snd_cmmnd_stpm3x(0xFF,0x22,0x0000);
snd_cmmnd_stpm3x(0xFF,0x23,0x0200);
}
void read_stpm3x_init()
{
rcvData(0x02);
memcpy(&DSP_CR2_LSB,STPM3x_RCV,4);
rcvData(0x03);
memcpy(&DSP_CR2_MSB,STPM3x_RCV,4);
rcvData(0x04);
memcpy(&DSP_CR3_LSB,STPM3x_RCV,4);
rcvData(0x08);
memcpy(&DSP_CR5_LSB,STPM3x_RCV,4);
rcvData(0x09);
memcpy(&DSP_CR5_MSB,STPM3x_RCV,4);
}
void snd_cmmnd_stpm3x(uint8_t READ_ADDRESS,uint8_t WRITE_ADDRESS,uint16_t DATA)
{
uint8_t LSB_DATA = 0;
uint8_t MSB_DATA = 0;
uint8_t CRC_DATA = 0;
uint8_t DATA_WITHOUT_CRC[4] = {0,0,0,0};
LSB_DATA = DATA & 0xFF;
MSB_DATA = (DATA>>8) & 0xFF;
DATA_WITHOUT_CRC[0]=u8ByteReverse(READ_ADDRESS);
DATA_WITHOUT_CRC[1]=u8ByteReverse(WRITE_ADDRESS);
DATA_WITHOUT_CRC[2]=u8ByteReverse(LSB_DATA);
DATA_WITHOUT_CRC[3]=u8ByteReverse(MSB_DATA);
CRC_DATA=CalcCRC8(DATA_WITHOUT_CRC);
CRC_DATA=u8ByteReverse(CRC_DATA);
memset(STPM3x_RCV,0,sizeof(STPM3x_RCV));// Clear the uart buffer for stpm3x
//Write to UART
if(stpm3xSerial.write(READ_ADDRESS)!=1)
{
//Handle UART write error
Serial.println("UART Write error for READ_ADDRESS");
return;
}
if(stpm3xSerial.write(WRITE_ADDRESS)!=1)
{
Serial.println("UART Write error for WRITE_ADDRESS");
return;
}
if(stpm3xSerial.write(LSB_DATA)!=1)
{
Serial.println("UART Write error for LSB_DATA");
return;
}
if(stpm3xSerial.write(MSB_DATA)!=1)
{
Serial.println("UART Write error for MSB_DATA");
return;
}
if(stpm3xSerial.write(CRC_DATA)!=1)
{
Serial.println("UART Write error for CRC_DATA");
return;
}
}
void rcvData(uint8_t read_addr)
{
//Send data to read from register
snd_cmmnd_stpm3x(read_addr,0xFF,0xFFFF);
//Serial.println(read_addr,HEX);
while(stpm3xSerial.available())
{
char u2_data = stpm3xSerial.read();//Read from UART2
if(u2_chk<BUFFER_SIZE -1){
STPM3x_RCV[u2_chk++]=u2_data;
// Serial.print("Data received by ESP32:");
// Serial.println(u2_data,HEX);
}
else
{
Serial.println("Buffer overflow reset");
u2_chk=0;
}
//STPM3x_RCV[u2_chk] = '\0';
//Serial.print("Data received by ESP32:");
//Serial.println(STPM3x_RCV);
}
}
/*
uint8_t rcvData(uint8_t read_addr)
{
uint8_t bytesRead=0;
unsigned long start_time = millis();
while(bytesRead<BUFFER_SIZE)
{
if(stpm3xSerial.available()){
STPM3x_RCV[bytesRead++] = stpm3xSerial.read();
}
if(millis() - start_time>100)
{
Serial.print("Waiting for UART data");
break;
}
}
}
*/
int32_t nrj_calc(uint32_t raw_nrj)
{
int64_t calc_nrj = 0;
/* manage the 2 U32 to have enougth place to save energy cumulated */
/* Make sure between two reads inside hardware registers if we have to add carry inside ext U32 */
if (((uint32_t)energy>0xA0000000) && ((uint32_t)raw_nrj<0x60000000))
{
energy_extension++;
}
if (((uint32_t)energy<0x60000000) && ((uint32_t)raw_nrj>0xA0000000))
{
energy_extension--;
}
/* save the new result cumulated come from register inside internal structure */
energy = raw_nrj;
/* calculate the nrj value and add the 32 bits extension */
calc_nrj = (uint64_t)raw_nrj + ((int64_t)energy_extension<< 32);
calc_nrj *= (int64_t)35145;
/* multiply by 10 to have in milli- */
calc_nrj *= 10;
/* Put the result in 32 bits format */
calc_nrj >>= 32;
/* return the nrj value */
return((int32_t)calc_nrj);
}
/* CRC CALUCULATIOPN */
uint8_t CalcCRC8(uint8_t *pBuf)
{
void Crc8Calc(uint8_t in_Data);
uint8_t i;
uint8_t CRC_u8Checksum = 0x00;
for (i=0; i<4; i++)
{
Crc8Calc(pBuf[i]);
}
return CRC_u8Checksum;
}
void Crc8Calc(uint8_t in_Data)
{
uint8_t loc_u8Idx;
uint8_t loc_u8Temp=0;
for(loc_u8Idx=0;loc_u8Idx<8;loc_u8Idx++)
{
loc_u8Temp=in_Data^CRC_u8Checksum;
CRC_u8Checksum<<=1;
if(loc_u8Temp&0x80)
{
CRC_u8Checksum^=0x7;
}
in_Data<<=1;
}
}
/* BYTE REVERSE FUNCTION */
uint8_t u8ByteReverse(uint8_t in_byte)
{
in_byte = ((in_byte >> 1) & 0x55) | ((in_byte << 1) & 0xaa);
in_byte = ((in_byte >> 2) & 0x33) | ((in_byte << 2) & 0xcc);
in_byte = ((in_byte >> 4) & 0x0F) | ((in_byte << 4) & 0xF0);
return in_byte;
}
float readRMSvoltage()
{
/******** LATCHING ********/
snd_cmmnd_stpm3x(0xFF,0x05,0x0060);
/******** Vrms and Irms ********/
rcvData(0x48);
uint32_t raw_RMS_Voltage = 0;
uint64_t calc_RMS_Voltage = 0;
memcpy(&raw_RMS_Voltage,STPM3x_RCV,4);
RawRms_volt=raw_RMS_Voltage;
calc_RMS_Voltage = (uint64_t)raw_RMS_Voltage*116274;
calc_RMS_Voltage *= 10;
calc_RMS_Voltage >>= 15;
CalcRMS_volt=calc_RMS_Voltage/1000000;
return CalcRMS_volt;
}
float readRMScurrent()
{
/******** LATCHING ********/
snd_cmmnd_stpm3x(0xFF,0x05,0x0060);
rcvData(0x48);
memcpy(&raw_RMS_Current,STPM3x_RCV,4);
raw_RMS_Current=(raw_RMS_Current>>15);
RawRms_curr=raw_RMS_Current;
calc_RMS_Current = (uint64_t)raw_RMS_Current*25934;
calc_RMS_Current *= 10;
calc_RMS_Current >>= 17;
CalcRMS_curr=calc_RMS_Current/1000000;
return CalcRMS_curr;
}
float readActivePower()
{
/******** LATCHING ********/
snd_cmmnd_stpm3x(0xFF,0x05,0x0060);
rcvData(0x5C);
memcpy(&raw_ACT_power,STPM3x_RCV,4);
raw_ACT_power=raw_ACT_power&(uint32_t)0x1FFFFFFF;
raw_ACT_power <<= 4; // handle sign extension as power is on 28 bits
raw_ACT_power >>= 4;
calc_ACT_power=((int64_t)raw_ACT_power*30154605);
calc_ACT_power *= 10;
calc_ACT_power >>= 28;
active_pwr=(int32_t)calc_ACT_power/1000;//kW
return active_pwr;
}
float readApparentRMSpwr()
{
/******** LATCHING ********/
snd_cmmnd_stpm3x(0xFF,0x05,0x0060);
rcvData(0x62);
memcpy(&raw_apparent_pwr,STPM3x_RCV,4);
raw_apparent_pwr=raw_apparent_pwr&(uint32_t)0x1FFFFFFF;
raw_apparent_pwr<<=4;
raw_apparent_pwr>>=4;
calc_apparent_pwr=((int64_t)raw_apparent_pwr*30154605);
}
float readActiveEnergy()
{
/******** LATCHING ********/
snd_cmmnd_stpm3x(0xFF,0x05,0x0060);
rcvData(0x54);
memcpy(&PH1_REG1,STPM3x_RCV,4);
active_nrj=nrj_calc(PH1_REG1);
active_nrj = active_nrj/1000;// dividing by 1000 to get watt hour mWh to Wh
return active_nrj;
}
float readTotalEnergy()
{
/******** LATCHING ********/
snd_cmmnd_stpm3x(0xFF,0x05,0x0060);
rcvData(0x84);
memcpy(&TOT_REG1,STPM3x_RCV,4);
total_nrj=nrj_calc(TOT_REG1);
total_nrj=total_nrj/1000;//dividing by 1000 to get watt hour mWh to Wh
return total_nrj;
}
2025-01-06 02:57 AM
What testing/investigation have you done to determine whether the problem is with the STPM34 or the ESP32 - or both?
Show your connections.
See this for some tips on debugging serial comms.
2025-01-06 10:14 PM
I have tried using Real term to send command frames in the format 0xFF,0x00,0x60,0x00 to write data in to registers of STPM34. Also checked if it gave back any response if I send 0x0A,0xFF as in the datasheet of STPM34 it is mentioned that when we are sending a data to read a register of STPM34. If it receives an address value first it gets ready that register to send a data after receiving next value. When I checked STPM34 using Real term I connected Rx and Tx of STPM34 to a TTL. But I was unable to receive any response.
When I used ESP32 to write and read data from STPM34 I used the method used in the code given above and also tried to read the data byte by byte, but this was also unsuccessful.
2025-01-07 01:41 AM
Hello,
If you always receive the content of the first register (0x04 00 00 A0), it probably means that the STPM3x does not understand your command.
Please refer to the starting guide attached, it gives important tips for UART/SPI communication, including:
- the init sequence at power up: the SCS signal must be at high level when power supply and EN signal rise (to select UART mode) and then, you must correctly reset the chipset, using SYN and SCS signals
- the UART access mode: at first command, the chipset always sends back the first register content. With the second command, it sends back the answer to the first command. With the third command, it sends back the answer to the second command, and so on.
- you will find example of the CRC calculation for UART and SPI, you can check that your CRC calculation is correct
Finally, if you can share a snapshot of the UART communication (TX and RX bit), it would be helpful.
Best regards,
Didier
2025-01-07 03:59 AM
2025-01-07 04:01 AM
Here I connect STPM34 Rx and Tx to a TTL and communicate using Real term, and I send data in this format to get a response. Correct me If it is wrong approach to debug the issue.
2025-01-07 05:34 AM - edited 2025-01-07 07:39 AM
2025-01-08 01:12 AM
Could you please help me to clarify what response will I get back if I tried to read register 0x48 which has both RMS voltage and current. Does I receive the value in the first address or from 0x48 itself.
2025-01-08 03:12 AM
If you want to read the 0x48 register:
- First, you must send the command with the targeted reading address (for instance : 0x48 FF FF FF + CRC): you will receive on RXD the response relative to youir previous command (0xA0 00 00 04 75 if it is the first command)
- Then, you must send the writing command of your next reading (for instance : 0xFF FF FF FF + CRC for incremental reading): you will receive on RXD the contents of the 0x48 register.
Regards,
Didier
2025-01-09 12:49 AM
float readRMSvoltage()
{
uint32_t raw_RMS_Voltage = 0;
uint64_t calc_RMS_Voltage = 0;
uint16_t v1;
uint8_t received_data[4] = {0};//array to store receeived bytes
/******** LATCHING ********/
snd_cmmnd_stpm3x(0xFF,0x05,0x0060);
/******** Vrms and Irms ********/
snd_cmmnd_stpm3x(0x48,0xFF,0xFFFF);
snd_cmmnd_stpm3x(0xFF,0xFF,0xFFFF);
for(int i=0;i<4;i++)
{
while(!stpm3xSerial.available())
{
received_data[i] = stpm3xSerial.read();
Serial.print("Received Data Byte:0x");
Serial.print(received_data[i],HEX);
Serial.println();
}
}
for(int i=0;i<4;i++)
{
raw_RMS_Voltage |= (uint32_t)u8ByteReverse(received_data[i])<<(8*i);
}
RawRms_volt=raw_RMS_Voltage;
calc_RMS_Voltage = (uint64_t)raw_RMS_Voltage*116274;
calc_RMS_Voltage *= 10;
calc_RMS_Voltage >>= 15;
CalcRMS_volt=calc_RMS_Voltage/1000000;
return CalcRMS_volt;
}
float readRMScurrent()
{
uint8_t received_data[4] = {0};//array to store receeived bytes
uint32_t raw_RMS_Current = 0;
uint64_t calc_RMS_Current = 0;
/******** LATCHING ********/
snd_cmmnd_stpm3x(0xFF,0x05,0x0060);
snd_cmmnd_stpm3x(0x48,0xFF,0xFFFF);
snd_cmmnd_stpm3x(0xFF,0xFF,0xFFFF);
for(int i=0;i<4;i++)
{
while(!stpm3xSerial.available())
{
received_data[i] = stpm3xSerial.read();
Serial.print("Received Data Byte:0x");
Serial.print(received_data[i],HEX);
Serial.println();
}
}
for(int i=0;i<4;i++)
{
raw_RMS_Current |= (uint32_t)u8ByteReverse(received_data[i])<<(8*i);
}
raw_RMS_Current=(raw_RMS_Current>>15);
RawRms_curr=raw_RMS_Current;
calc_RMS_Current = (uint64_t)raw_RMS_Current*25934;
calc_RMS_Current *= 10;
calc_RMS_Current >>= 17;
CalcRMS_curr=calc_RMS_Current/1000000;
return CalcRMS_curr;
}
void snd_cmmnd_stpm3x(uint8_t READ_ADDRESS,uint8_t WRITE_ADDRESS,uint16_t DATA)
{
uint8_t LSB_DATA = 0;
uint8_t MSB_DATA = 0;
uint8_t CRC_DATA = 0;
uint8_t DATA_WITHOUT_CRC[4] = {0,0,0,0};
LSB_DATA = DATA & 0xFF;
MSB_DATA = (DATA>>8) & 0xFF;
DATA_WITHOUT_CRC[0]=u8ByteReverse(READ_ADDRESS);
DATA_WITHOUT_CRC[1]=u8ByteReverse(WRITE_ADDRESS);
DATA_WITHOUT_CRC[2]=u8ByteReverse(LSB_DATA);
DATA_WITHOUT_CRC[3]=u8ByteReverse(MSB_DATA);
CRC_DATA = CalcCRC8(DATA_WITHOUT_CRC);
CRC_DATA = u8ByteReverse(CRC_DATA);
for(int i=0;i<4;i++)
{
if(stpm3xSerial.write(DATA_WITHOUT_CRC[i])!=1)
{
Serial.print("UART write error");
return;
}
}
stpm3xSerial.write(CRC_DATA);
}
This is the method how I use to read the RMS voltage and current. I use two separate user defined functions readRMSvoltage and readRMScurrent. Sending commands to STPM34 is done using snd_cmmd_stpm3x. Could you please tell me whether I am going wrong anywhere here in this code on sending or receiving through UART.