Hex over TTL

I'm going to try and repost this as a new topic, I hope I'm not treading on toes here, I started a post about measuring 230v through my Arduino Uno analog pins but now I have discovered I have power meters that can provide me with the information I need through a TTL serial port. I tried opening a new topic as I thought it is now completely different to the original post but the two were combined and after 24 hours nobody has responded so I will try again as a new post (once again, sorry if I am annoying moderators.)...

I would like to know if the following (it is my first ever code) will send a 7 byte Hex request to the meter from pin 3 then read the reply from pin 2 and print it to the serial monitor. (Thanks to Nick Gammon for his great tutorials, I absolutely would not have gotten this far so quickly without them.)

I am getting no reply from the meter and the most obvious mistake I might have made is the sketch. If I know I have a good sketch I can then concentrate on trouble shooting wiring etc.

Any advice, comments, criticism of the sketch below is much appreciated.

Thanks

#include <SoftwareSerial.h>

SoftwareSerial sSerial(2, 3); // RX, TX pins

const unsigned int baudRate = 9600;
byte voltageRequest[] = {0xB0, 0xC0, 0xA8, 0x01, 0x01, 0x00, 0x1A}; // Hex request to send to meter
const unsigned int MAX_INPUT = 7; // how much serial data we expect before a newline
unsigned long oldMillis = millis();
int Try = 1;

void setup() {
  // initialize serial:
  Serial.begin(9600);
  sSerial.begin(baudRate);
}

// Print incoming sSerial data to Serial monitor
void process_data (const char * data)
{
  Serial.println (data);
}

void processIncomingByte (const byte inByte)
{
  static char input_line [MAX_INPUT];
  static unsigned int input_pos = 0;
  if (input_pos < (MAX_INPUT - 1))
    input_line [input_pos++] = inByte;
  else  process_data (input_line);
}  // end of processIncomingByte

void loop() {

  // Send voltage request every 2 seconds
  if (millis() >= oldMillis)
  {
    for (int i = 0; i <= sizeof(voltageRequest) - 1; i++)
    {
      sSerial.write(voltageRequest[i]); 
    }

    oldMillis = oldMillis + 2000;
  }


  // When Software serial is available
  while (sSerial.available() > 0)
  {
    processIncomingByte (sSerial.read ());

  }
}

Perhaps you get no replies because you don't supply information about what you are using. What meter and documentation on it and the TTL serial port connections and the data format and protocol for communicating? What tells you the meter is actually receiving your data transmission? Is there a LED in the meter blinking as it received data?

How do you have the Arduino connected to the meter? Is the meter powered up and working?

Paul

In the following you do not properly terminate the character string with a zero terminator, so Serial.println() in process_data() will malfunction.

{
  static char input_line [MAX_INPUT];
  static unsigned int input_pos = 0;
  if (input_pos < (MAX_INPUT - 1))
    input_line [input_pos++] = inByte;
  else  process_data (input_line);
}  // end of processIncomingByte

Paul_KD7HB:
Perhaps you get no replies because you don't supply information about what you are using. What meter and documentation on it and the TTL serial port connections and the data format and protocol for communicating? What tells you the meter is actually receiving your data transmission? Is there a LED in the meter blinking as it received data?

How do you have the Arduino connected to the meter? Is the meter powered up and working?

Paul

Thanks for the advice Paul. I was trying to keep the post simple as the first question was simply about the sketch. Perhaps I should have put it in the Programming section but I suspect it will most likely morph into more of a project guidance topic. I will include the document I have and a couple of pics in a seperate post and answer your suggestions as well as I can.

First the meter is a peacefair PZEM-004. The TTL port has 4 pins labeled VDD, RX, TX and GND. When the meter has power there is no voltage between VDD and GND (or any of the pins) so I have them connected to the Arduino 5V and GND. Also, I sometimes switch the TX and RX pins when I try to establish communication as I read somewhere that manufacturers sometimes label them backward.

The documentation I have contains communication instructions but no protocols and peacefair do not seem to have a web site. I emailed the supplier who confirmed a baud rate of 9600 and 5V although I did suggest both those figures in my email so it is possible they just confirmed them but didn't actually know, just a thought.

The microcontroller in the meter has SDIC RVSU SD3004 printed on it. A google search turned up this web site http://www.sdicmicro.com/products.html I sent them an email asking for TTL protocols etc.

The meter is powered while I am testing and showing voltage information (it also shows current, power and total power on 4 separate LED windows.

There is nothing on the meter (LED etc) to show any communication and I have received nothing in Serial Monitor to indicate anything has been received from it although this is possibly bad code on my part.

That's enough for now, I will concentrate on getting the sketch correct before I get too far into other options.

Thanks for your time.

Perry

Documents for the post above...

200127 PZEM-004.pdf (244 KB)

DSCF5186.JPG

jremington:
In the following you do not properly terminate the character string with a zero terminator, so Serial.println() in process_data() will malfunction.

{

static char input_line [MAX_INPUT];
  static unsigned int input_pos = 0;
  if (input_pos < (MAX_INPUT - 1))
    input_line [input_pos++] = inByte;
  else  process_data (input_line);
}  // end of processIncomingByte

Thanks jremington this is a great help.

I have seen code with zero terminator but I assumed (obviously incorrectly) it was only needed because the string length was unknown. My received string will be 7 bytes so I thought I would be able to process the data after 7 bytes are received (I'm not even sure the code I wrote does this correctly, sorry, I am in over my head here but getting there slowly)

I'm not sure how to terminate the character string and work has me pretty busy so my research on this will be slow for a day or two. Any pointers, hints or links to what is even meant by "terminate the character string" would be appreciated. In the mean time, I will go back and look at Mr. Gammons excellent work and then check with my good friend google.com.

Paul_KD7HB:
Perhaps you get no replies because you don't supply information about what you are using. What meter and documentation on it and the TTL serial port connections and the data format and protocol for communicating? What tells you the meter is actually receiving your data transmission? Is there a LED in the meter blinking as it received data?

How do you have the Arduino connected to the meter? Is the meter powered up and working?

Paul

Paul your post has been more helpful than you might have expected.

Sometimes when your new at something the seemingly obvious is not always so.

After answering your post I was looking for info about my meter and discovered an Arduino library for it on GitHub. This led to a new search of this forum using the make and model of my meter rather than a general question about HEX over TTL and I found I am not the first down this road.
https://forum.arduino.cc/index.php?topic=467640.0
I just needed the correct search!

I now have the meters values displaying on my serial monitor using the library and info from the other post.

Thanks for the help, no doubt more questions will follow!

Make your input_line one byte more; once you have received 7 bytes, add a '\0'. Just as an example

void processIncomingByte (const byte inByte)
{
  static char input_line [MAX_INPUT + 1];  <----- one byte more
  static unsigned int input_pos = 0;
  if (input_pos < (MAX_INPUT - 1))
  {
    input_line [input_pos++] = inByte;
  }
  else 
  {
    input_line[input_pos] = '\0'; <----- add nul terminator
    process_data (input_line);
  }
}  // end of processIncomingByte

Blueskies:
Paul your post has been more helpful than you might have expected.

Sometimes when your new at something the seemingly obvious is not always so.

After answering your post I was looking for info about my meter and discovered an Arduino library for it on GitHub. This led to a new search of this forum using the make and model of my meter rather than a general question about HEX over TTL and I found I am not the first down this road.
Using PZEM-004 with arduino to operate relays ON/OFF - Project Guidance - Arduino Forum
I just needed the correct search!

I now have the meters values displaying on my serial monitor using the library and info from the other post.

Thanks for the help, no doubt more questions will follow!

That is great news! I learned years ago how helpful it is to discuss what you are doing with someone else. Even if you can't help, it does help the other person to clarify their thoughts.

Good luck with your project. You might add all your productive links to this thread so the next person trying what you are doing will have shorter development time.

Paul

sterretje:
Make your input_line one byte more; once you have received 7 bytes, add a '\0'. Just as an example

If you understand an example, use it.
If you don't understand an example, don't use it.

Thanks, I can see from your code what I need to do there but taking advice from your signature I'm going to push this a little further...

I don't understand why the nul terminator needs to be there. I have been a googling and I find information on ending a string with a new line and/or carriage return and I find examples of using a nul terminator at the end of a string but nothing that explains why the nul terminator needs to be there. Do all strings need a nul terminator before the NL / CR?

Thanks again.

Paul_KD7HB:
Good luck with your project. You might add all your productive links to this thread so the next person trying what you are doing will have shorter development time.

Paul

Here are some links to what I found and a little info.

The library on GitHub

I have included the Library Zip as an attachment.

Examples of some code

Other projects using PZEM-004 and Arduino
https://forum.arduino.cc/index.php?topic=467640.0
https://wiki.cuvoodoo.info/doku.php?id=spark_counter

Wiring info
UART PZEM-004
5V VDD 3.3V is not enough
TX RX the UART pin needs to be able to sink ~4 mA
RX TX the UART pin needs to be idle high
GND GND

If your PZEM-004 has a buzzer disconnect it as it signals all com and can be anoying. Later versions don't have the buzzer. You can aire in an LED instead.

Keywords
#######################################

Datatypes (KEYWORD1)

#######################################

PZEM004T KEYWORD1

#######################################

Methods and Functions (KEYWORD2)

#######################################

voltage KEYWORD2
current KEYWORD2
power KEYWORD2
energy KEYWORD2
setAddress KEYWORD2
setPowerAlarm KEYWORD2

#######################################

Constants (LITERAL1)

#######################################

PZEM004T-master.zip (6.49 KB)

A string (lower case s) in the C language is sequence of characters terminated by the nul character. That way string functions can find the end of the string and know when to stop; e.g. Serial.print("hello") will print one character at a time till it finds the nul character (after the 'o').

Carriage return and newline come from keyboards (and typewriters before that); they are 'normal' characters.

Hi.
I have to thank the proposed code, having adapted to the ESP12 microcontroller. For which, I used the Pzem04 (TTL 5V). However, you need a converter from 5V to 3.3V.

#include <SoftwareSerial.h>
SoftwareSerial serial_(0, 2); // GPIO0 & GPIO2 RX, TX pins https://forum.arduino.cc/index.php?topic=492614.0
const unsigned int baudRate = 9600; // 115200 https://github.com/olehs/PZEM004T
float pzA=1, pzV=0, pzI=0, pzP=0, pzE=0;
byte reqVn[]={0xB0,0xC0,0xA8,0x01,0x01,0x00,0x1A}; // B0 C0 A8 01 01 00 1A : (Computer sends a request to read the voltage value) : Hex request to send to meter
byte reqIn[]={0xB1,0xC0,0xA8,0x01,0x01,0x00,0x1B}; // B1 C0 A8 01 01 00 1B : current value
byte reqPn[]={0xB2,0xC0,0xA8,0x01,0x01,0x00,0x1C}; // B2 C0 A8 01 01 00 1C : Active power
byte reqEn[]={0xB3,0xC0,0xA8,0x01,0x01,0x00,0x1D}; // B3 C0 A8 01 01 00 1D : Energy kWh
byte  reqA[]={0xB4,0xC0,0xA8,0x01,0x01,0x00,0x1E}; // B4 C0 A8 01 01 00 1E : address
byte  reqB[]={0xB5,0xC0,0xA8,0x01,0x01,0x00,0x1F}; //
byte  reqC[]={0xB6,0xC0,0xA8,0x01,0x01,0x00,0x1A}; //
byte  reqD[]={0xB7,0xC0,0xA8,0x01,0x01,0x00,0x1C}; //
byte  reqE[]={0xB7,0xC0,0xA8,0x01,0x01,0x00,0x1B}; //
byte  reqF[]={0xB7,0xC0,0xA8,0x01,0x01,0x00,0x1A}; //

// Set the module address Req B4 C0 A8 01 01 00 1E (Computer sends a request to set the address, the address is 192.168.1.1)
// Set the module address resp A4 00 00 00 00 00 A4 (Meter reply the address was successfully set)
// Set the power alarm threshold Req B5 C0 A8 01 01 14 33 (computer sends a request to set a power alarm threshold)
// Set the power alarm threshold Resp A5 00 00 00 00 00 A56 (Meter reply the power alarm threshold was successfully set)

const unsigned int max_sRx = 50; // how much serial data we expect before a newline
unsigned long mil = millis(); static unsigned int posRx=0; String s="", s1="";
int timer=185; // pode ir até 50ms, mas o ideal será 235ms(for 4units) or 190(for 5units) totalizando ~1s
byte tmp=5; // evitar erros a alta velocidade quando se aplica o Serial.println("");
unsigned long oldMil=mil+timer*4+500; byte sRx=0;

void setup() { delay(5000); // initialize serial:
  Serial.begin(115200); // Serial.begin(9600); comunicação com/usb
  serial_.begin(9600); // const unsigned int baudRate = 9600; comunicação com pzem
  wdt_enable(WDTO_500MS); // WDTO_15MS WDTO_30MS WDTO_250MS WDTO_500MS WDTO_1S WDTO_8S watchdog
  for (int i=0; i<=sizeof(reqA)-1; i++) { serial_.write(reqA[i]); s+=String(reqA[i],HEX)+":"; }
   s+=" "; posRx=0; sAvailable(timer); pzA=convNum(s,164); s+=" Adr="+String(pzA); Serial.println(s); delay(tmp); s="";
  if (pzA==164) {Serial.println(" PZEM04 exits "); } else Serial.println("PZEM04 no exits "); // 164(dec)=A4(hex)
}

float convNum(String st, byte adr) { // converter número
 float n=0; byte i0=0, i1=0, i2=0, i3=0, i4=0, i5=0, i6=0; // year = str.substring(2, str.indexOf("/")).toInt();
  st=st.substring(st.indexOf(";")-3,st.length()); i0=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 0)"+st+"["+i0+"]");
  st=st.substring(st.indexOf(";")+1,st.length()); i1=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 1)"+st+"["+i1+"]");
  st=st.substring(st.indexOf(";")+1,st.length()); i2=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 2)"+st+"["+i2+"]");
  st=st.substring(st.indexOf(";")+1,st.length()); i3=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 3)"+st+"["+i3+"]");
  st=st.substring(st.indexOf(";")+1,st.length()); i4=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 4)"+st+"["+i4+"]");
  st=st.substring(st.indexOf(";")+1,st.length()); i5=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 5)"+st+"["+i5+"]");
  st=st.substring(st.indexOf(";")+1,st.length()); i6=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 6)"+st+"["+i6+"]");
   // Serial.print("<"+String(i0)+";"+String(i1)+";"+String(i2)+";"+String(i3)+";"+String(i4)+";"+String(i5)+";"+String(i6)+">");
  if (i0==160) { n=i2+(float)i3/10; }           // tension=list[2]+list[3]/10
   else if (i0==161) { n=i2+(float)i3/100; }    // current=list[2]+list[3]/100
   else if (i0==162) { n=i1*256+i2; }             // power=list[1]*256+list[2]
   else if (i0==163) { n=i1*256*256+i2*256+i3; } // Energy=list[1]*256*256+list[2]*256+list[3]
   else if (i0==164) { n=i1+i2+i3+i4+i5+i6; } // Energy=list[1]*256*256+list[2]*256+list[3]
 return(n);
}

void sAvailable(int hora) { // serialAvailable
unsigned long mil_ = millis(); unsigned long milPre = mil_; String s0="", s1="";
 while (mil_-milPre<hora) {
  mil_=millis();
  while (serial_.available() > 0) // When Software serial is available
   { sRx=serial_.read();
   if (posRx < (max_sRx)) { s1+=String(sRx)+";"; posRx+=1; }
    s0+=String(sRx,HEX)+":";
   }
 } s="tx="+s+" rx="+s0+"=["+s1+"] ";
}

void loop() { mil=millis(); delay(1); // usar delay nas funções para evitar erros
  if (mil > oldMil) { s=""; oldMil=mil+timer*4+500; // min.total 2500=2.5s
    for (int i=0; i<=sizeof(reqA)-1; i++) { serial_.write(reqA[i]); s+=String(reqA[i],HEX)+":"; }
     s+=" "; posRx=0; sAvailable(timer); pzA=convNum(s,164); delay(tmp); s="";
     if (pzA==164) { /* Serial.println(" PZEM04 exits "); */ } else Serial.println("PZEM04 no exits, verify connection. "); // 164(dec)=A4(hex)
    for (int i=0; i<=sizeof(reqVn)-1; i++) { serial_.write(reqVn[i]); s+=String(reqVn[i],HEX)+":"; }
     s+=" "; posRx=0; sAvailable(timer); pzV=convNum(s,160); s+=" Vn="+String(pzV); Serial.println(s); delay(tmp); s="";
    for (int i=0; i<=sizeof(reqIn)-1; i++) { serial_.write(reqIn[i]); s+=String(reqIn[i],HEX)+":"; }
     s+=""; posRx=0; sAvailable(timer); pzI=convNum(s,161); s+="In="+String(pzI); Serial.println(s); delay(tmp); s="";
    for (int i=0; i<=sizeof(reqPn)-1; i++) { serial_.write(reqPn[i]); s+=String(reqPn[i],HEX)+":"; }
     s+=""; posRx=0;sAvailable(timer); pzP=convNum(s,162); s+="Pn="+String(pzP); Serial.println(s); delay(tmp); s="";
    for (int i=0; i<=sizeof(reqEn)-1; i++) { serial_.write(reqEn[i]); s+=String(reqEn[i],HEX)+":"; }
     s+=""; posRx=0; sAvailable(timer); pzE=convNum(s,163); s+="En="+String(pzE); Serial.println(s); delay(tmp); s="";
//  wdt_reset(); // reset o watchdog, ou seja se o programa craker e não ativar esse reset em 8segundos e o watchdog será ativado.
  Serial.println(" time="+String(long(millis()-mil))+" "); delay(tmp);
  }
}

It seems that the tension, current, power and energy are changed.

When you connect plug, having the following result:

report "lamp":
tx=b4:c0:a8:1:1:0:1e: rx=a4:0:0:0:0:0:a4:=[164;0;0;0;0;0;164;] Adr=164.00
PZEM04 exits
tx=b0:c0:a8:1:1:0:1a: rx=a0:0:ed:6:0:0:93:=[160;0;237;6;0;0;147;] Vn=237.60
tx=b1:c0:a8:1:1:0:1b: rx=a1:0:0:8:0:0:a9:=[161;0;0;8;0;0;169;] In=0.08
tx=b2:c0:a8:1:1:0:1c: rx=a2:0:c:0:0:0:ae:=[162;0;12;0;0;0;174;] Pn=12.00
tx=b3:c0:a8:1:1:0:1d: rx=a3:0:1:ad:0:0:51:=[163;0;1;173;0;0;81;] En=429.00
time=990

report "ventilation":

B0:C0:A8:1:1:0:1A: 160 0 230 70 0 0 150 Vn=160;0;230;7;0;0;150;
B1:C0:A8:1:1:0:1B: 161 0 7 5 0 0 142 In=161;0;7;50;0;0;142;
B2:C0:A8:1:1:0:1C: 162 0 6 174 0 0 150 Pn=162;0;6;174;0;0;150;
B3:C0:A8:1:1:0:1D: 163 0 0 70 0 0 152 En=163;0;0;70;0;0;152;

Vn=230+7/10=231.7V
In=7+50/100=7.50A
Pn=6256+174=1710W
En=0
256256+0256+70=70kWh

When you disconetc the plug, report the next descrition:

PZEM04 no exits, verify connection.
tx=b0:c0:a8:1:1:0:1a: rx==[] Vn=0.00
tx=b1:c0:a8:1:1:0:1b: rx==[] In=0.00
tx=b2:c0:a8:1:1:0:1c: rx==[] Pn=0.00
tx=b3:c0:a8:1:1:0:1d: rx==[] En=0.00