add configuration request and decode

This commit is contained in:
King Kévin 2016-09-17 14:22:11 +02:00
parent d44bbfe4f9
commit 8c9f5afaaf
2 changed files with 61 additions and 19 deletions

View File

@ -72,6 +72,18 @@ static const uint16_t register_input[] = {
0x0158 // 30345 total reactive energy (in kVArh)
};
/** SDM120 4xxxx holding register start addresses for the configuration types */
static const uint16_t register_holding[] = {
0x000c, // relay pulse width (60, 100, or 200 ms)
0x0012, // network parity stop (0: 1 stop bit no parity, 1: one stop bit even parity, 2: one stop bit odd parity, 3: two stop bits no parity)
0x0014, // meter slave address (1-247)
0x001c, // baud rate (0: 2400 bps, 1: 4800 bps, 2: 9600 bps, 5: 1200 bps)
0x0056, // pulse 1 output mode (1: import active energy, 2: import+export active energy, 4: export active energy, 5: import reactive energy, 6: import+export reactive energy, 8: export reactive energy)
0xf900, // time of scroll display (0-30 s)
0xf910, // pulse 1 output (0: 0.001 kWh/imp, 1: 0.01 kWh/imp, 2: 0.1 kWh/imp, 3: 1 kWh/imp)
0xf920 // measurement mode (1: total=import, 2: total=import+export, 3: total=import-export)
};
/** compute CRC for ModBus
* @note ModBus uses ANSi/IBM 16-bits CRC (with normal polynomial 0x8005, reverse polynomial 0xA001, start value 0xfff)
* @param[in] buffer data on which to compute the CRC for
@ -128,18 +140,18 @@ void sensor_sdm120_setup(void)
}
/** send request to electricity meter
* @param[in] slave electricity meter slave device address
* @param[in] meter_id electricity meter device id (ModBus salve address)
* @param[in] function ModBus function: 0x03 read two 16 bits holding registers, 0x04 read two 16 bits input registers, 0x10 write two 16 bits holding registers
* @param[in] address register start point address
* @param[in] value value to store in holding register (if function 0x10 is used)
* @return if request is correct and transmission started
*/
static bool sensor_sdm120_transmit_request(uint8_t slave, uint8_t function, uint16_t address, float value)
static bool sensor_sdm120_transmit_request(uint8_t meter_id, uint8_t function, uint16_t address, float value)
{
if (tx_used!=0) { // transmission is ongoing
return false;
}
if (slave==0) { // broadcast request are not supported
if (meter_id==0) { // broadcast request are not supported
return false;
}
if (function!=0x03 && function!=0x04 && function!=0x10) { // function not supported
@ -150,7 +162,7 @@ static bool sensor_sdm120_transmit_request(uint8_t slave, uint8_t function, uint
}
uint8_t packet[11]; // buffer to build ModBus message (without error check)
uint8_t packet_size = 0; // ModBus message size (without error check)
packet[0] = slave; // set slave device address
packet[0] = meter_id; // set slave device address
packet[1] = function; // set function
packet[2] = address>>8; // set high register address
packet[3] = address; // set low register address
@ -184,15 +196,21 @@ static bool sensor_sdm120_transmit_request(uint8_t slave, uint8_t function, uint
return true;
}
bool sensor_sdm120_measurement_request(uint8_t slave, enum sensor_sdm120_measurement_type_t type)
bool sensor_sdm120_measurement_request(uint8_t meter_id, enum sensor_sdm120_measurement_type_t type)
{
if (type>=SENSOR_SDM120_MAX) { // invalid type
if (type>=SENSOR_SDM120_MEASUREMENT_MAX) { // invalid type
return false;
}
return sensor_sdm120_transmit_request(slave, 0x04, register_input[type], 0);
return sensor_sdm120_transmit_request(meter_id, 0x04, register_input[type], 0);
}
bool sensor_sdm120_configuration_request(uint8_t meter_id, enum sensor_sdm120_configuration_type_t type)
{
if (type>=SENSOR_SDM120_CONFIGURATION_MAX) { // invalid type
return false;
}
return sensor_sdm120_transmit_request(meter_id, 0x03, register_holding[type], 0);
}
float sensor_sdm120_measurement_decode(void)
{
@ -205,8 +223,14 @@ float sensor_sdm120_measurement_decode(void)
if (rx_used<5) { // not a complete response (minimum is address, function, size/error, error check low, error check high)
return NAN;
}
if (crc_modbus(rx_buffer,rx_used)==0 && (rx_buffer[1]&0x80)==0) { // no checksum error, no error condition
// a complete message has been received
if (crc_modbus(rx_buffer,rx_used)) { // checksum error, error check failed
measurement = NAN;
} else if (rx_buffer[1]&0x80) { // error condition received
measurement = INFINITY; // indicate we received and error
} else {
switch (rx_buffer[1]) {
case 0x03: // read 4xxx holding register response received
case 0x04: // read 3xxxx input register response received
if (rx_buffer[2]==0x04 && rx_used>=(4+5)) { // 2 registers received, corresponds to implemented request
// convert big endian received float value to little endian return value
@ -217,11 +241,11 @@ float sensor_sdm120_measurement_decode(void)
convert[3] = rx_buffer[3];
}
break;
case 0x03: // read 4xxx holding register response received
break; // not supported currently
case 0x10: // write 4xxx holding register response received
measurement = (rx_buffer[4]<<8)+rx_buffer[5]; // number of registers written
break; // not supported currently
default: // unknown function response received
measurement = INFINITY;
break; // nothing to do
}
}
@ -251,7 +275,7 @@ void USART_ISR(SENSOR_SDM120_USART)(void)
USART_SR(USART(SENSOR_SDM120_USART)) &= ~USART_SR_RXNE; // clear flag, ignore received data
} else if (rx_used<LENGTH(rx_buffer)) { // receiving response
rx_buffer[rx_used++] = usart_recv(USART(SENSOR_SDM120_USART)); // put received byte in buffer (clears flag)
if (rx_used==1 && rx_buffer[0]==0) { // this is wrong decoding because the signal is low on idle (and the 0 broadcast device address is not supported)
if (rx_used==1 && rx_buffer[0]==0) { // this is wrong decoding because the signal is going low on idle, which is misinterpreted as start bit (and the 0 broadcast device address is not supported by this device)
rx_used = 0; // reset buffer
} else if (rx_used>=5 && (rx_buffer[1]&0x80)) { // error condition response received
sensor_sdm120_measurement_received = true; // notify used response has been received
@ -261,8 +285,7 @@ void USART_ISR(SENSOR_SDM120_USART)(void)
sensor_sdm120_measurement_received = true; // notify used response has been receive
}
} else { // buffer full and unknown response received
USART_SR(USART(SENSOR_SDM120_USART)) &= ~USART_SR_RXNE; // clear flag (reading measurement will clear buffer)
//rx_used = 0; // clear buffer (not clearing the flag will restart this ISR)
USART_SR(USART(SENSOR_SDM120_USART)) &= ~USART_SR_RXNE; // clear flag (wait for user to read measurement, this clears the buffer)
}
}
}

View File

@ -23,7 +23,7 @@
/** a measurement response has been received */
extern volatile bool sensor_sdm120_measurement_received;
/** measurements offered by electricity meter */
/** measurement types offered by electricity meter in 3xxx input registers */
enum sensor_sdm120_measurement_type_t {
SENSOR_SDM120_VOLTAGE = 0,
SENSOR_SDM120_CURRENT,
@ -38,18 +38,37 @@ enum sensor_sdm120_measurement_type_t {
SENSOR_SDM120_ENERGY_REACTIVE_EXPORT,
SENSOR_SDM120_ENERGY_ACTIVE_TOTAL,
SENSOR_SDM120_ENERGY_REACTIVE_TOTAL,
SENSOR_SDM120_MAX
SENSOR_SDM120_MEASUREMENT_MAX
};
/** configuration types for electricity meter in 4xxx holding registers */
enum sensor_sdm120_configuration_type_t {
SENSOR_SDM120_RELAY_PULSE_WIDTH = 0,
SENSOR_SDM120_NETWORK_PARITY_STOP,
SENSOR_SDM120_METER_ID,
SENSOR_SDM120_BAUD_RATE,
SENSOR_SDM120_PULSE_1_OUTPUT_MODE,
SENSOR_SDM120_TIME_OF_SCROLL_DISPLAY,
SENSOR_SDM120_PULSE_1_OUTPUT,
SENSOR_SDM120_MEASUREMENT_MODE,
SENSOR_SDM120_CONFIGURATION_MAX
};
/** setup peripherals to communicate with electricity meter */
void sensor_sdm120_setup(void);
/** request measurement from electricity meter
* @param[in] slave electricity meter slave device address
* @param[in] mater_id electricity meter device ID
* @param[in] type measurement type to request
* @return if transmission started
*/
bool sensor_sdm120_measurement_request(uint8_t slave, enum sensor_sdm120_measurement_type_t type);
bool sensor_sdm120_measurement_request(uint8_t meter_id, enum sensor_sdm120_measurement_type_t type);
/** request configuration from electricity meter
* @param[in] mater_id electricity meter device ID
* @param[in] type configuration: type to request
* @return if transmission started
*/
bool sensor_sdm120_configuration_request(uint8_t meter_id, enum sensor_sdm120_configuration_type_t type);
/** decode received measurement
* @return decoded measurement (NaN if invalid or no new measurement has been received)
* @return decoded measurement or number of registers written, NaN if message has error or no new measurement has been received, infinity if an error or unknown message has been received
*/
float sensor_sdm120_measurement_decode(void);