Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conflict when using UART and I2C in Arduino UNO #56

Open
vandat07 opened this issue Mar 13, 2019 · 0 comments
Open

Conflict when using UART and I2C in Arduino UNO #56

vandat07 opened this issue Mar 13, 2019 · 0 comments

Comments

@vandat07
Copy link

vandat07 commented Mar 13, 2019

Hello all,

I am using Arduino UNO, ESP8266 and PZEM-004T to measure electricity parameters and send to server via Wifi. The communication protocols are use as below:

PZEM-004T <=== UART(pin 10, pin 11) ===> UNO <=== I2C (pin A4, A5) ===> ESP8266 (Master)

UNO will read the parameters from PZEM-004T via UART, then send to ESP8266 via I2C.

When I use only UART or only I2C, everything works well. But when both are implemented in UNO, there will be a conflict, data read from PZEM-004T is correct but data received by ESP8266 is not correct.

I don't know how to overcome this issue. I have tried to modify the library to skip UART reading when I2C is ongoing (I2C_Flag = true). However, I could only read Voltage, Current, Power, and could not for Energy.

PZEM004T.h

#ifndef PZEM004T_H
#define PZEM004T_H

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

// #define PZEM004_NO_SWSERIAL

#if (not defined(PZEM004_NO_SWSERIAL)) && (defined(__AVR__) || defined(ESP8266))
#define PZEM004_SOFTSERIAL
#endif

#if defined(PZEM004_SOFTSERIAL)
#include <SoftwareSerial.h>
#endif

#include <IPAddress.h>

#define PZEM_DEFAULT_READ_TIMEOUT 1000
#define PZEM_ERROR_VALUE -1.0

struct PZEMCommand {
    uint8_t command;
    uint8_t addr[4];
    uint8_t data;
    uint8_t crc;
};

class PZEM004T
{
public:
    PZEM004T(uint8_t receivePin, uint8_t transmitPin);
    PZEM004T(HardwareSerial *port);
    ~PZEM004T();

    void setReadTimeout(unsigned long msec);
    unsigned long readTimeout() {return _readTimeOut;}

    float voltage(const IPAddress &addr, **bool I2C_Flag**);
    float current(const IPAddress &addr, **bool I2C_Flag**);
    float power(const IPAddress &addr, **bool I2C_Flag**);
    float energy(const IPAddress &addr, **bool I2C_Flag**);

    bool setAddress(const IPAddress &newAddr);
    bool setPowerAlarm(const IPAddress &addr, uint8_t threshold);

private:
    Stream *serial;

    bool _isSoft;
    unsigned long _readTimeOut = PZEM_DEFAULT_READ_TIMEOUT;

    void send(const IPAddress &addr, uint8_t cmd, **bool I2C_Flag = false**, uint8_t data = 0);	
    bool recieve(uint8_t resp, **bool I2C_Flag = false**, uint8_t *data = 0);

    uint8_t crc(uint8_t *data, uint8_t sz);
};

#endif // PZEM004T_H

PZEM004T.cpp

#include "PZEM004T.h"

#define PZEM_VOLTAGE (uint8_t)0xB0
#define RESP_VOLTAGE (uint8_t)0xA0

#define PZEM_CURRENT (uint8_t)0xB1
#define RESP_CURRENT (uint8_t)0xA1

#define PZEM_POWER   (uint8_t)0xB2
#define RESP_POWER   (uint8_t)0xA2

#define PZEM_ENERGY  (uint8_t)0xB3
#define RESP_ENERGY  (uint8_t)0xA3

#define PZEM_SET_ADDRESS (uint8_t)0xB4
#define RESP_SET_ADDRESS (uint8_t)0xA4

#define PZEM_POWER_ALARM (uint8_t)0xB5
#define RESP_POWER_ALARM (uint8_t)0xA5

#define RESPONSE_SIZE sizeof(PZEMCommand)
#define RESPONSE_DATA_SIZE RESPONSE_SIZE - 2

#define PZEM_BAUD_RATE 9600

#ifdef PZEM004_SOFTSERIAL    
PZEM004T::PZEM004T(uint8_t receivePin, uint8_t transmitPin)
{
    SoftwareSerial *port = new SoftwareSerial(receivePin, transmitPin);
    port->begin(PZEM_BAUD_RATE);
    this->serial = port;
    this->_isSoft = true;
}
#endif

PZEM004T::PZEM004T(HardwareSerial *port)
{
    port->begin(PZEM_BAUD_RATE);
    this->serial = port;
    this->_isSoft = false;
}

PZEM004T::~PZEM004T()
{
    if(_isSoft)
        delete this->serial;
}

void PZEM004T::setReadTimeout(unsigned long msec)
{
    _readTimeOut = msec;
}

float PZEM004T::voltage(const IPAddress &addr, **bool I2C_Flag**)
{
	
	uint8_t data[RESPONSE_DATA_SIZE];

	send(addr, PZEM_VOLTAGE, **I2C_Flag**);
	if(!recieve(RESP_VOLTAGE, **I2C_Flag**, data))
		return PZEM_ERROR_VALUE;

	return (data[0] << 8) + data[1] + (data[2] / 10.0);
	
}

float PZEM004T::current(const IPAddress &addr, **bool I2C_Flag**)
{	
	
	uint8_t data[RESPONSE_DATA_SIZE];

	send(addr, PZEM_CURRENT, **I2C_Flag**);
	if(!recieve(RESP_CURRENT, **I2C_Flag**, data))
		return PZEM_ERROR_VALUE;

	return (data[0] << 8) + data[1] + (data[2] / 100.0);
	
}

float PZEM004T::power(const IPAddress &addr, **bool I2C_Flag**)
{
	
	uint8_t data[RESPONSE_DATA_SIZE];

	send(addr, PZEM_POWER, **I2C_Flag**);
	if(!recieve(RESP_POWER, **I2C_Flag**, data))
		return PZEM_ERROR_VALUE;

	return (data[0] << 8) + data[1];
	
}

float PZEM004T::energy(const IPAddress &addr, **bool I2C_Flag**)
{
	
	uint8_t data[RESPONSE_DATA_SIZE];

	send(addr, PZEM_ENERGY, **I2C_Flag**);
	if(!recieve(RESP_ENERGY, **I2C_Flag**, data))
		return PZEM_ERROR_VALUE;

	return ((uint32_t)data[0] << 16) + ((uint16_t)data[1] << 8) + data[2];
	
}

bool PZEM004T::setAddress(const IPAddress &newAddr)
{
    send(newAddr, PZEM_SET_ADDRESS);
    return recieve(RESP_SET_ADDRESS);
}
/*
bool PZEM004T::setPowerAlarm(const IPAddress &addr, uint8_t threshold)
{
    send(addr, PZEM_POWER_ALARM, threshold);
    return recieve(RESP_POWER_ALARM);
}

*/

void PZEM004T::send(const IPAddress &addr, uint8_t cmd, bool I2C_Flag, uint8_t data)	
{
    PZEMCommand pzem;

    pzem.command = cmd;
    for(int i=0; i<sizeof(pzem.addr); i++)
        pzem.addr[i] = addr[i];
    pzem.data = data;

    uint8_t *bytes = (uint8_t*)&pzem;
    pzem.crc = crc(bytes, sizeof(pzem) - 1);

    while(serial->available()){
		**if (I2C_Flag) return;**
		serial->read();
	}
        

    serial->write(bytes, sizeof(pzem));
}

bool PZEM004T::recieve(uint8_t resp, bool I2C_Flag, uint8_t *data)
{
    uint8_t buffer[RESPONSE_SIZE];

#ifdef PZEM004_SOFTSERIAL    
    if(_isSoft)
        ((SoftwareSerial *)serial)->listen();
#endif
	
    unsigned long startTime = millis();
    uint8_t len = 0;
    while((len < RESPONSE_SIZE) && (millis() - startTime < _readTimeOut))
    {	**if (I2C_Flag) return false;** 
		
        if(serial->available() > 0)
        {
            uint8_t c = (uint8_t)serial->read();
            if(!c && !len)
                continue; // skip 0 at startup
            buffer[len++] = c;			
        }
        yield();	// do background netw tasks while blocked for IO (prevents ESP watchdog trigger)
    }	
	
    if(len != RESPONSE_SIZE)
        return false;

    if(buffer[6] != crc(buffer, len - 1))
        return false;

    if(buffer[0] != resp)
        return false;

    if(data)
    {		
        for(int i=0; i<RESPONSE_DATA_SIZE; i++){
			data[i] = buffer[1 + i];	
		}
		    
		
    }

    return true;
}

uint8_t PZEM004T::crc(uint8_t *data, uint8_t sz)
{
    uint16_t crc = 0;
    for(uint8_t i=0; i<sz; i++)
        crc += *data++;
    return (uint8_t)(crc & 0xFF);
}

Please let me know if you have any solutions for this!
Thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant