From 599027ac39f44983c5266ef7b0cb0fb4ca5e3c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Sun, 11 Sep 2016 17:21:15 +0200 Subject: [PATCH] add library to communicate with PZEM-004 electricity meter --- lib/sensor_pzem.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++ lib/sensor_pzem.h | 57 ++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100644 lib/sensor_pzem.c create mode 100644 lib/sensor_pzem.h diff --git a/lib/sensor_pzem.c b/lib/sensor_pzem.c new file mode 100644 index 0000000..5780cac --- /dev/null +++ b/lib/sensor_pzem.c @@ -0,0 +1,166 @@ +/* This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +/** library to query measurements from peacefair PZEM-004 and PZEM-004T electricity meter (code) + * @file sensor_pzem.c + * @author King Kévin + * @date 2016 + * @note peripherals used: USART @ref sensor_pzem_usart + */ + +/* standard libraries */ +#include // standard integer types +#include // general utilities + +/* STM32 (including CM3) libraries */ +#include // real-time control clock library +#include // general purpose input output library +#include // universal synchronous asynchronous receiver transmitter library +#include // interrupt handler +#include // Cortex M3 utilities + +#include "sensor_pzem.h" // PZEM electricity meter header and definitions +#include "global.h" // common methods + +/** @defgroup sensor_pzem_usart USART peripheral used for communication with electricity meter + * @{ + */ +#define SENSOR_PZEM_USART 2 /**< USART peripheral */ +/** @} */ + +#define USART_BAUDRATE 115200 /**< serial baudrate, in bits per second (with 8N1 8 bits, no parity bit, 1 stop bit settings) */ + +/* input and output ring buffer, indexes, and available memory */ +static uint8_t rx_buffer[7] = {0}; /**< buffer for received response */ +static volatile uint8_t rx_i = 0; /**< current position of read received data */ +static uint8_t tx_buffer[7] = {0}; /**< buffer for request to transmit */ +static volatile uint8_t tx_i = 0; /**< current position if transmitted data */ + +volatile bool sensor_pzem_measurement_received = false; + +void sensor_pzem_setup(void) +{ + /* enable USART I/O peripheral */ + rcc_periph_clock_enable(USART_PORT_RCC(SENSOR_PZEM_USART)); // enable clock for USART port peripheral + rcc_periph_clock_enable(USART_RCC(SENSOR_PZEM_USART)); // enable clock for USART peripheral + gpio_set_mode(USART_PORT(SENSOR_PZEM_USART), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_PIN_TX(SENSOR_PZEM_USART)); // setup GPIO pin USART transmit + gpio_set_mode(USART_PORT(SENSOR_PZEM_USART), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_PIN_RX(SENSOR_PZEM_USART)); // setup GPIO pin USART receive + gpio_set(USART_PORT(SENSOR_PZEM_USART), USART_PIN_RX(SENSOR_PZEM_USART)); // pull up to avoid noise when not connected + + /* setup USART parameters for electricity meter: 9600 8N1 */ + usart_set_baudrate(USART(SENSOR_PZEM_USART), 9600); // the electricity meter uses a fixed baud rate of 9600 bps + usart_set_databits(USART(SENSOR_PZEM_USART), 8); + usart_set_stopbits(USART(SENSOR_PZEM_USART), USART_STOPBITS_1); + usart_set_mode(USART(SENSOR_PZEM_USART), USART_MODE_TX_RX); + usart_set_parity(USART(SENSOR_PZEM_USART), USART_PARITY_NONE); + usart_set_flow_control(USART(SENSOR_PZEM_USART), USART_FLOWCONTROL_NONE); + + nvic_enable_irq(USART_IRQ(SENSOR_PZEM_USART)); // enable the USART interrupt + usart_enable_rx_interrupt(USART(SENSOR_PZEM_USART)); // enable receive interrupt + usart_enable(USART(SENSOR_PZEM_USART)); // enable USART + + /* reset buffer states */ + tx_i = 0; + rx_i = 0; + sensor_pzem_measurement_received = false; +} + +void sensor_pzem_measurement_request(uint32_t address, enum sensor_pzem_measurement_type_t type) +{ + if (tx_i!=0) { // transmission is ongoing + return; + } + if (type>=MAX) { // invalid type + return; + } + tx_buffer[0] = 0xB0+type; // set request nibble and type nibble + tx_buffer[1] = (address>>24)&0xff; // set address + tx_buffer[2] = (address>>16)&0xff; // set address + tx_buffer[3] = (address>>8)&0xff; // set address + tx_buffer[4] = (address>>0)&0xff; // set address + tx_buffer[5] = 0; // only used to set alarm + tx_buffer[6] = 0; // to calculate checksum (sum of all previous bytes) + for (uint8_t i=0; i=MAX) { // not a valid response type received (actually 4 and 5 are valid, but should not happen when using this code + return measurement; + } + uint8_t checksum = 0; // calculate checksum (sum of all other bytes) + for (uint8_t i=0; i=LENGTH(rx_buffer)) { // buffer full + sensor_pzem_measurement_received = true; // notify used response has been received + } + } else { // previous response not read before receiving the next + usart_recv(USART(SENSOR_PZEM_USART)); // drop received buffer + } + } +} diff --git a/lib/sensor_pzem.h b/lib/sensor_pzem.h new file mode 100644 index 0000000..de41a62 --- /dev/null +++ b/lib/sensor_pzem.h @@ -0,0 +1,57 @@ +/* This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +/** library to query measurements from peacefair PZEM-004 and PZEM-004T electricity meter (API) + * @file sensor_pzem.h + * @author King Kévin + * @date 2016 + * @note peripherals used: USART @ref sensor_pzem_usart + */ +#pragma once + +/** a measurement response has been received */ +extern volatile bool sensor_pzem_measurement_received; + +/** measurements offered by energy meter */ +enum sensor_pzem_measurement_type_t { + VOLTAGE = 0, + CURRENT = 1, + POWER = 2, + ENERGY = 3, + MAX +}; + +/** measurement returned by energy meter */ +struct sensor_pzem_measurement_t { + enum sensor_pzem_measurement_type_t type; /**< measurement type */ + bool valid; /**< is the measurement valid (e.g. format and checksum are correct) */ + union measurement_t { /**< measurement value */ + float voltage; /**< measured voltage in volts */ + float current; /**< measured current in amperes */ + float power; /**< measured power in watts */ + uint32_t energy; /**< measured energy in watts/hour (24 bits) */ + } value; +}; + +/** setup peripherals to communicate with electricity meter */ +void sensor_pzem_setup(void); +/** request measurement from electricity meter + * @param[in] address electricity meter device address + * @param[in] sensor_pzem_measurement_type measurement type to request + */ +void sensor_pzem_measurement_request(uint32_t address, enum sensor_pzem_measurement_type_t type); +/** decode received measurement + * @return decoded measurement (invalid if no new measurement has been received) + */ +struct sensor_pzem_measurement_t sensor_pzem_measurement_decode(void);