Added modbus registers and stuff.
This commit is contained in:
		| @@ -25,6 +25,7 @@ config_read(&config); | ||||
|  | ||||
| #include "stdint.h" | ||||
| #include "stm32l0xx.h" | ||||
| #include "ltr329.h" | ||||
| /* DESCRIPTION OF THE DATA STRUCTURE */ | ||||
| /* | ||||
|  * Data are divided into two groups: | ||||
| @@ -120,15 +121,11 @@ extern const uint8_t config_baudrates_length; | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
| 	/* LED CONFIG */ | ||||
| 	uint8_t led_on; | ||||
| 	uint16_t led_brightness; | ||||
| 	uint8_t led_smooth; | ||||
| 	uint16_t led_co2_alert_limit1; | ||||
| 	uint16_t led_co2_alert_limit2; | ||||
|  | ||||
| 	/* SCD4x Temperature sensor offset */ | ||||
| 	int16_t scd4x_t_offset; | ||||
| 	/* LTR329 CONFIG */ | ||||
| 	ltr329_gain_t ltr329_gain; | ||||
| 	ltr329_als_mode_t ltr329_mode; | ||||
| 	ltr329_integration_time_t ltr329_integ_time; | ||||
| 	ltr329_measurement_rate_t ltr329_meas_rate; | ||||
|  | ||||
| 	/* MODBUS CONFIG */ | ||||
| 	uint16_t modbus_addr; | ||||
| @@ -136,6 +133,7 @@ typedef struct | ||||
| } config_t; | ||||
|  | ||||
|  | ||||
|  | ||||
| /* const uint32_t baudrates [] = {19200, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 76800, 115200}; */ | ||||
|  | ||||
| int8_t config_read(config_t *config); | ||||
|   | ||||
| @@ -1,22 +0,0 @@ | ||||
| /* | ||||
|  * crc.h | ||||
|  * | ||||
|  *  Created on: Jun 9, 2021 | ||||
|  *      Author: user | ||||
|  */ | ||||
|  | ||||
| #ifndef INC_CRC8_H_ | ||||
| #define INC_CRC8_H_ | ||||
|  | ||||
| #include "stdint.h" | ||||
|  | ||||
| /* | ||||
|  * Definitions & macros | ||||
|  */ | ||||
|  | ||||
| #define CRC8_POLYNOMIAL ((uint8_t)0x31) | ||||
| #define CRC8_INIT ((uint8_t)0xFF) | ||||
|  | ||||
| uint8_t crc8_calculate(const uint8_t *data, uint16_t count); | ||||
|  | ||||
| #endif /* INC_CRC8_H_ */ | ||||
| @@ -1,93 +0,0 @@ | ||||
| /* | ||||
|  * ltr329.h | ||||
|  * | ||||
|  *  Created on: May 22, 2022 | ||||
|  *      Author: david | ||||
|  */ | ||||
|  | ||||
| #ifndef INC_LTR329_H_ | ||||
| #define INC_LTR329_H_ | ||||
|  | ||||
| #include "stdint.h" | ||||
| #include "stm32l0xx_ll_i2c.h" | ||||
| #include "stm32l0xx_ll_utils.h" | ||||
| #include "i2c.h" | ||||
|  | ||||
| #define LTR329_I2C_ADDRESS 0x29 | ||||
|  | ||||
| /* | ||||
|  * Return values | ||||
|  */ | ||||
|  | ||||
| #define LTR329_OK 0 | ||||
| #define LTR329_ERROR -1 // generic error | ||||
|  | ||||
| /* LTR-329ALS-01 Registers */ | ||||
| /* Datasheet: https://optoelectronics.liteon.com/upload/download/DS86-2014-0006/LTR-329ALS-01_DS_V1.pdf */ | ||||
| typedef enum | ||||
| { | ||||
| 	LTR329_ALS_CONTR 			= 0x80,	/* RW */ | ||||
| 	LTR329_ALS_MEAS_RATE 		= 0x85, /* RW */ | ||||
| 	LTR329_PART_ID 				= 0x86, /* R */ | ||||
| 	LTR329_MANUFAC_ID 			= 0x87, /* R */ | ||||
| 	LTR329_ALS_DATA_CH1_0 		= 0x88, /* R */ | ||||
| 	LTR329_ALS_DATA_CH1_1 		= 0x89, /* R */ | ||||
| 	LTR329_ALS_DATA_CH0_0 		= 0x8A, /* R */ | ||||
| 	LTR329_ALS_DATA_CH0_1 		= 0x8B, /* R */ | ||||
| 	LTR329_ALS_STATUS 			= 0x8C /* R */ | ||||
| } ltr329_cmd_t; | ||||
|  | ||||
| /* Bit masks for ALS Mode */ | ||||
| typedef enum | ||||
| { | ||||
| 	LTR329_MODE_STAND_BY		= 0b00000000, /* DEFAULT */ | ||||
| 	LTR329_MODE_ACTIVE			= 0b00000001 | ||||
| } ltr329_als_mode_t; | ||||
|  | ||||
| /* Bit masks for Gain settings */ | ||||
| typedef enum | ||||
| { | ||||
| 	LTR329_GAIN_1X 				= 0b00000000, /* DEFAULT */ | ||||
| 	LTR329_GAIN_2X				= 0b00000100, | ||||
| 	LTR329_GAIN_4X				= 0b00001000, | ||||
| 	LTR329_GAIN_8X				= 0b00001100, | ||||
| 	LTR329_GAIN_48X				= 0b00011000, | ||||
| 	LTR329_GAIN_96X				= 0b00011100, | ||||
| 	LTR329_GAIN_RESERVED1		= 0b00010000, | ||||
| 	LTR329_GAIN_RESERVED2		= 0b00010100 | ||||
| } ltr329_gain_t; | ||||
|  | ||||
| /* Bit masks for Integration Time settings */ | ||||
| typedef enum | ||||
| { | ||||
| 	LTR329_INTEGRATION_50MS		= 0b00001000, | ||||
| 	LTR329_INTEGRATION_100MS 	= 0b00000000, /* DEFAULT */ | ||||
| 	LTR329_INTEGRATION_150MS	= 0b00100000, | ||||
| 	LTR329_INTEGRATION_200MS	= 0b00010000, | ||||
| 	LTR329_INTEGRATION_250MS	= 0b00101000, | ||||
| 	LTR329_INTEGRATION_300MS	= 0b00110000, | ||||
| 	LTR329_INTEGRATION_350MS	= 0b00111000, | ||||
| 	LTR329_INTEGRATION_400MS	= 0b00011000 | ||||
| } ltr329_integration_time_t; | ||||
|  | ||||
| /* Bit masks for Measurement Rate settings */ | ||||
| typedef enum | ||||
| { | ||||
| 	LTR329_MEAS_RATE_50MS		= 0b00000000, | ||||
| 	LTR329_MEAS_RATE_100MS		= 0b00000001, | ||||
| 	LTR329_MEAS_RATE_200MS		= 0b00000010, | ||||
| 	LTR329_MEAS_RATE_500MS		= 0b00000011, /* DEFAULT */ | ||||
| 	LTR329_MEAS_RATE_1000MS		= 0b00000100, | ||||
| 	LTR329_MEAS_RATE_2000MS		= 0b00000111 | ||||
| } ltr329_measurement_rate_t; | ||||
|  | ||||
| static int8_t ltr329_read_register (ltr329_cmd_t register_addr, uint8_t *register_data ); | ||||
| static int8_t ltr329_write_register (ltr329_cmd_t register_addr, uint8_t register_data); | ||||
| int8_t ltr329_write_settings (ltr329_gain_t gain, ltr329_als_mode_t mode, ltr329_integration_time_t integ_time, ltr329_measurement_rate_t meas_rate); | ||||
| int8_t ltr329_read_settings (ltr329_gain_t *gain, ltr329_als_mode_t *mode, ltr329_integration_time_t *integ_time, ltr329_measurement_rate_t *meas_rate); | ||||
| int8_t ltr329_sw_reset( void ); | ||||
| int8_t ltr329_measure (uint16_t *data_ch0, uint16_t *data_ch1); | ||||
| int8_t ltr329_read_status_register(uint8_t *data_valid, uint8_t *new_data, ltr329_gain_t *gain); | ||||
| int8_t ltr329_read_device_info (uint8_t *manufacturer_id, uint8_t *part_id); | ||||
|  | ||||
| #endif /* INC_LTR329_H_ */ | ||||
| @@ -1,231 +0,0 @@ | ||||
| /* | ||||
|  * modbus.h | ||||
|  * | ||||
|  *  Created on: Jul 18, 2021 | ||||
|  *      Author: user | ||||
|  * | ||||
|  *  Modbus slave RTU library (does NOT support ASCII and TCP) | ||||
|  * | ||||
|  *  Useful links: | ||||
|  *  https://www.picotech.com/library/oscilloscopes/modbus-serial-protocol-decoding | ||||
|  *  https://ipc2u.com/articles/knowledge-base/modbus-rtu-made-simple-with-detailed-descriptions-and-examples/ | ||||
|  *  https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf | ||||
|  *  https://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf | ||||
|  * | ||||
|  *  Note that byte order is big endian. | ||||
|  * | ||||
|  * USAGE: | ||||
|  * | ||||
|  * 1) Implement functions modbus_callback_function() and modbus_uart_transmit_function() | ||||
|  *      - modbus_uart_transmit_function() sends data via UART | ||||
|  *      - modbus_callback_function() does the real work: read sensors, set outputs... | ||||
|  *        note that when filling buffers (e.g. input_registers[]) user must | ||||
|  *        ensure that all data is big-endian | ||||
|  *    These functions are implementation-specific. | ||||
|  * 2) Set device address (variable modbus_device_address); you can do this either | ||||
|  *      - setting modbus_device_address directly (modbus.h needs to be included, duh) | ||||
|  *      - using modbus_set_device_address(uint8_t address) function | ||||
|  *    Or you can leave address as-is (MODBUS_DEFAULT_SLAVE_ADDRESS) and set it via | ||||
|  *    Modbus during runtime | ||||
|  * 3) Call modbus_process_msg() after message reception; you need to observe Modbus RTU timing: | ||||
|  *      - pauses between chars in frame are less or equal to 1.5 char | ||||
|  *      - pauses between frames are at least 3.5 chars (of silence) | ||||
|  *    For more information see section 2.5.1.1 (MODBUS Message RTU Framing) | ||||
|  *    in "MODBUS over Serial Line: Specification and Implementation Guide" | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef SRC_MODBUS_H_ | ||||
| #define SRC_MODBUS_H_ | ||||
|  | ||||
| #include "stdint.h" | ||||
| #include "stddef.h" | ||||
| #include "string.h" | ||||
|  | ||||
| /* | ||||
|  * Defines & macros | ||||
|  */ | ||||
|  | ||||
| #define MODBUS_BROADCAST_ADDR 0 | ||||
| #define MODBUS_DEFAULT_SLAVE_ADDRESS 247 /* 255 may be used for bridge device */ | ||||
| /* minimal frame length is 4 bytes: 1 B slave address, 1 B function code, 2 B CRC */ | ||||
| #define MODBUS_MINIMAL_FRAME_LEN 4 | ||||
| #define MODBUS_MINIMAL_READWRITE_LEN 4 | ||||
| #define MODBUS_MINIMAL_WRITE_MULTIPLE_LEN 5 | ||||
| #define MODBUS_READ_DEVICE_ID_REQUEST_LEN 4 | ||||
| #define MODBUS_READ_DEVICE_ID_RESPONSE_HEADER_LEN 4 | ||||
| #define MODBUS_READ_DEVICE_ID_RESPONSE_OFFSET 3 | ||||
| #define MODBUS_MAX_RTU_FRAME_SIZE 256 | ||||
| #define MODBUS_BUFFER_SIZE MODBUS_MAX_RTU_FRAME_SIZE /* alias */ | ||||
| #define MODBUS_ERROR_FLAG 0x80 | ||||
| #define MODBUS_MAX_REGISTERS 125 | ||||
| /* read device id constants */ | ||||
| #define MODBUS_MEI 0x0E | ||||
| #define MODBUS_DEVICE_ID_INDIVIDUAL_ACCESS_FLAG 0x80 | ||||
| #define MODBUS_MORE_FOLLOWS 0xFF | ||||
| #define MODBUS_NO_MORE_FOLLOWS 0x00 | ||||
| #define MODBUS_BASIC_OBJECT_COUNT 3 | ||||
| #define MODBUS_REGULAR_OBJECT_COUNT 7 | ||||
|  | ||||
| /* | ||||
|  * Return values | ||||
|  */ | ||||
|  | ||||
| #define MODBUS_OK 0 | ||||
| #define MODBUS_ERROR -1 // generic error | ||||
| #define MODBUS_ERROR_CRC -2 // checksum failed | ||||
| #define MODBUS_ERROR_FRAME_INVALID -3 // invalid frame format / length | ||||
| #define MODBUS_ERROR_OUT_OF_BOUNDS -4 // requested register is out of bounds | ||||
| #define MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED -5 // function not implemented in callback | ||||
| #define MODBUS_ERROR_REGISTER_NOT_IMPLEMENTED -6 // register not implemented in callback | ||||
| #define MODBUS_ERROR_DEVICE_ID_NOT_IMPLEMENTED -7 | ||||
|  | ||||
| /* | ||||
|  * Data types | ||||
|  */ | ||||
|  | ||||
| /* Public functions codes (Modbus Application protocol specification, section 5.1) */ | ||||
| typedef enum { | ||||
| 	/* single bit access functions */ | ||||
| 	MODBUS_READ_COILS = 1, | ||||
| 	MODBUS_READ_DO = 1, // alias | ||||
| 	MODBUS_READ_DISCRETE_INPUTS = 2, | ||||
| 	MODBUS_READ_DI = 2, // alias | ||||
| 	MODBUS_WRITE_SINGLE_COIL = 5, | ||||
| 	MODBUS_WRITE_SINGLE_DO = 5, // alias | ||||
| 	MODBUS_WRITE_MULTIPLE_COILS = 15, | ||||
| 	MODBUS_WRITE_MULTIPLE_DO = 15, // alias | ||||
| 	/* 16-bit access functions */ | ||||
| 	MODBUS_READ_HOLDING_REGISTERS = 3, | ||||
| 	MODBUS_READ_AO = 3, // alias | ||||
| 	MODBUS_READ_INPUT_REGISTERS = 4, | ||||
| 	MODBUS_READ_AI = 4, // alias | ||||
| 	MODBUS_WRITE_SINGLE_REGISTER = 6, | ||||
| 	MODBUS_WRITE_SINGLE_AO = 6, // alias | ||||
| 	MODBUS_WRITE_MULTIPLE_REGISTERS = 16, | ||||
| 	MODBUS_WRITE_MULTIPLE_AO = 16, // alias | ||||
| 	MODBUS_MASK_WRITE_REGISTER = 22, | ||||
| 	MODBUS_READ_WRITE_MULTIPLE_REGISTERS = 23, | ||||
| 	MODBUS_READ_FIFO_QUEUE = 24, | ||||
| 	/* file record access */ | ||||
| 	MODBUS_READ_FILE_RECORD = 20, | ||||
| 	MODBUS_WRITE_FILE_RECORD = 21, | ||||
| 	/* diagnostics */ | ||||
| 	MODBUS_READ_EXCEPTION_STATUS = 7, | ||||
| 	MODBUS_DIAGNOSTIC = 8, /* sub codes: 00-18,20 */ | ||||
| 	MODBUS_GET_COM_EVENT_COUNTER = 11, | ||||
| 	MODBUS_GET_COM_EVENT_LOG = 12, | ||||
| 	MODBUS_REPORT_SLAVE_ID = 17, | ||||
| 	MODBUS_READ_DEVICE_IDENTIFICATION = 43, /* sub codes: 14 */ | ||||
| } modbus_function_code_t; | ||||
|  | ||||
| typedef enum { | ||||
| 	MODBUS_EXCEPTION_ILLEGAL_FUNCTION = 1, | ||||
| 	MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS = 2, | ||||
| 	MODBUS_EXCEPTION_ILLEGAL_REGISTER_QUANTITY = 2, | ||||
| 	MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE = 3, | ||||
| 	MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE = 4, | ||||
| 	MODBUS_EXCEPTION_ACKNOWLEDGE = 5, | ||||
| 	MODBUS_EXCEPTION_SLAVE_DEVICE_BUSY = 6, | ||||
| 	MODBUS_EXCEPTION_MEMORY_PARITY_ERROR = 8, | ||||
| 	MODBUS_EXCEPTION_GATEWAY_PATH_UNAVAILABLE = 10, | ||||
| 	MODBUS_EXCEPTION_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND = 11, | ||||
| 	MODBUS_EXCEPTION_ILLEGAL_DEVICE_ID_CODE = 3 | ||||
| } modbus_exception_code_t; | ||||
|  | ||||
| typedef struct { | ||||
| 	modbus_function_code_t function_code : 8; | ||||
| 	uint16_t register_address; // e.g. first register of A0: 0 | ||||
| 	uint16_t register_number;  // e.g. first register of A0: 40001 | ||||
| 	uint8_t  register_count; // number of registers to be read/written | ||||
|  | ||||
| 	modbus_exception_code_t exception; | ||||
|  | ||||
| 	uint8_t broadcast; // 1 if broadcast, 0 otherwise | ||||
|  | ||||
| 	union { | ||||
| 		uint8_t buffer8b[MODBUS_MAX_RTU_FRAME_SIZE]; | ||||
| 		uint16_t buffer16b[MODBUS_MAX_RTU_FRAME_SIZE/2]; | ||||
| 		uint16_t input_registers[MODBUS_MAX_REGISTERS]; | ||||
| 		uint16_t holding_registers[MODBUS_MAX_REGISTERS]; | ||||
| 		int16_t  input_registers_signed[MODBUS_MAX_REGISTERS]; | ||||
| 		int16_t  holding_registers_signed[MODBUS_MAX_REGISTERS]; | ||||
| 	}; | ||||
|  | ||||
| 	/* process device id */ | ||||
| 	uint8_t read_device_id_code; | ||||
| 	uint8_t object_id; | ||||
| } modbus_transaction_t; | ||||
|  | ||||
| typedef enum { | ||||
| 	MODBUS_DO_START_NUMBER = 1, // Discrete output coils | ||||
| 	MODBUS_DO_END_NUMBER = 9999, | ||||
| 	MODBUS_DI_START_NUMBER = 10001, // Discrete input contacts | ||||
| 	MODBUS_DI_END_NUMBER = 19999, | ||||
| 	MODBUS_AI_START_NUMBER = 30001, // Analog input registers | ||||
| 	MODBUS_AI_END_NUMBER = 39999, | ||||
| 	MODBUS_AO_START_NUMBER = 40001, // Analog output (holding registers) | ||||
| 	MODBUS_AO_END_NUMBER = 49999 | ||||
| } modbus_register_number_t; | ||||
|  | ||||
| typedef enum { | ||||
| 	MODBUS_CONFORMITY_BASIC = 1, | ||||
| 	MODBUS_CONFORMITY_REGULAR = 2, | ||||
| 	MODBUS_CONFORMITY_EXTENDED = 3, | ||||
| 	MODBUS_INDIVIDUAL_ACCESS = 4 /* not actually part of conformity, but I'll keep it here anyway */ | ||||
| } modbus_conformity_level_t; | ||||
|  | ||||
| /* Device ID datatypes */ | ||||
| #define MODBUS_DEVICE_ID_OBJECT_NUM 7 | ||||
| typedef struct { | ||||
| 	union { | ||||
| 		struct { | ||||
| 			/* Basic category (mandatory part) */ | ||||
| 			char *VendorName; | ||||
| 			char *ProductCode; | ||||
| 			char *MajorMinorRevision; | ||||
| 			/* Regular category (optional part) */ | ||||
| 			char *VendorUrl; | ||||
| 			char *ProductName; | ||||
| 			char *ModelName; | ||||
| 			char *UserApplicationName; | ||||
| 			/* Extended category (optional part) */ | ||||
| 			// Nothing here yet! | ||||
| 		} object_name; | ||||
| 		char *object_id[MODBUS_DEVICE_ID_OBJECT_NUM]; | ||||
| 	}; | ||||
| 	uint8_t conformity_level; | ||||
| } modbus_device_id_t; | ||||
|  | ||||
| /* | ||||
|  * Global variables | ||||
|  */ | ||||
|  | ||||
| /* device address: declared in modbus.c */ | ||||
| extern uint8_t modbus_slave_address; | ||||
|  | ||||
| /* shared modbus buffer; defined in modbus.c; may be used elsewhere in code */ | ||||
| extern uint8_t modbus_buffer[]; | ||||
|  | ||||
| /* modbus device id struct */ | ||||
| extern modbus_device_id_t *modbus_device_id; | ||||
|  | ||||
| /* | ||||
|  * Function prototypes | ||||
|  */ | ||||
|  | ||||
| /* process message: should be called in when modbus message was received (e.g. in main.c) | ||||
|  * modbus_process_msg() may call following functions: | ||||
|  *     - modbus_callback_function() if data readout is requested | ||||
|  *     - modbus_uart_transmit_function() if response is required | ||||
|  * Both functions have to be implemented by user. | ||||
|  */ | ||||
| int8_t modbus_slave_process_msg(const uint8_t *buffer, int len); | ||||
| int8_t modbus_slave_init_device_id(modbus_device_id_t *device_id); | ||||
| int8_t modbus_slave_set_address(uint8_t address); | ||||
| /* modbus callback function type - should be implemented by user (e.g. in main.c) */ | ||||
| int8_t modbus_slave_callback(modbus_transaction_t *transaction); | ||||
| /* UART transmit function type - should be implemented by user (e.g. in main.c) */ | ||||
| int8_t modbus_transmit_function(uint8_t *buffer, uint16_t data_len); | ||||
|  | ||||
| #endif /* SRC_MODBUS_H_ */ | ||||
| @@ -1,57 +0,0 @@ | ||||
| /* | ||||
|  * sht4x.h | ||||
|  * | ||||
|  *  Created on: Jun 8, 2021 | ||||
|  *      Author: user | ||||
|  */ | ||||
|  | ||||
| #ifndef INC_SHT4X_H_ | ||||
| #define INC_SHT4X_H_ | ||||
|  | ||||
| #include "stdint.h" | ||||
| #include "stm32l0xx_ll_i2c.h" | ||||
| #include "stm32l0xx_ll_utils.h" | ||||
| #include "i2c.h" | ||||
| #include "crc8.h" | ||||
|  | ||||
| /* | ||||
|  * Defines & macros | ||||
|  */ | ||||
|  | ||||
| #define SHT4X_I2C_ADDRESS 0x44 | ||||
|  | ||||
| /* | ||||
|  * Return values | ||||
|  */ | ||||
|  | ||||
| #define SHT4X_OK 0 | ||||
| #define SHT4X_ERROR -1 // generic error | ||||
| #define SHT4X_CRC8_ERROR -2 // checksum failed | ||||
|  | ||||
| /* | ||||
|  * Data types | ||||
|  */ | ||||
|  | ||||
| typedef enum { | ||||
| 	SHT4X_START_MEAS_HIGH_PRECISION = 0xFD, | ||||
| 	SHT4X_START_MEAS_MEDIUM_PRECISION = 0xF6, | ||||
| 	SHT4X_START_MEAS_LOW_PRECISION = 0xE0, | ||||
| 	SHT4X_READ_SERIAL = 0x89, | ||||
| 	SHT4X_SOFT_RESET = 0x94, | ||||
| 	SHT4X_HEATER_200_mW_1_s = 0x39, | ||||
| 	SHT4X_HEATER_200_mW_01_s = 0x32, | ||||
| 	SHT4X_HEATER_110_mW_1_s = 0x2F, | ||||
| 	SHT4X_HEATER_110_mW_01_s = 0x24, | ||||
| 	SHT4X_HEATER_20_mW_1_s = 0x1E, | ||||
| 	SHT4X_HEATER_20_mW_01_s = 0x15 | ||||
| } sht4x_cmd_t; | ||||
|  | ||||
| /* | ||||
|  * Function prototypes | ||||
|  */ | ||||
|  | ||||
| int8_t sht4x_send_cmd(sht4x_cmd_t cmd); | ||||
| int8_t sht4x_read_data(uint8_t *buffer, int len); | ||||
| int8_t sht4x_measure(int16_t *temperature, uint16_t *relative_humidity); | ||||
|  | ||||
| #endif /* INC_SHT4X_H_ */ | ||||
| @@ -44,23 +44,11 @@ int8_t config_read(config_t *config) | ||||
| { | ||||
| 	config->modbus_addr = *(uint16_t *) (CONFIG_EEPROM_ADDR_MODBUS_ADDR); | ||||
| 	config->baudrate_index = *(uint16_t *) (CONFIG_EEPROM_ADDR_BAUDRATE_INDEX); | ||||
| 	config->led_on = *(uint16_t *) (CONFIG_EEPROM_ADDR_LED_ON); | ||||
| 	config->led_brightness = *(uint16_t *) (CONFIG_EEPROM_ADDR_LED_BRIGHTNESS); | ||||
| 	config->led_smooth = *(uint16_t *) (CONFIG_EEPROM_ADDR_LED_SMOOTH); | ||||
| 	config->led_co2_alert_limit1 = *(uint16_t *) (CONFIG_EEPROM_ADDR_LED_ALERT1); | ||||
| 	config->led_co2_alert_limit2 = *(uint16_t *) (CONFIG_EEPROM_ADDR_LED_ALERT2); | ||||
| 	config->scd4x_t_offset = *(int16_t *) (CONFIG_EEPROM_ADDR_SCD4x_T_OFFSET); | ||||
|  | ||||
| 	/* Check if the EEPROM is initialized - do not check: | ||||
| 	 *  LED ON | ||||
| 	 *  LED SMOOTH | ||||
| 	 *  SCD4x T OFFSET | ||||
| 	 *  BAUDRATE INDEX | ||||
| 	 *  those can be 0 */ | ||||
| 	if ((config->modbus_addr 			== EEPROM_EMPTY_BYTE) || | ||||
| 		(config->led_co2_alert_limit1 	== EEPROM_EMPTY_BYTE) || | ||||
| 		(config->led_co2_alert_limit2 	== EEPROM_EMPTY_BYTE) || | ||||
| 		(config->led_brightness 		== EEPROM_EMPTY_BYTE)) | ||||
| 	if (config->modbus_addr == EEPROM_EMPTY_BYTE) | ||||
| 	{ | ||||
| 		return CONFIG_ERROR; | ||||
| 	} | ||||
| @@ -82,48 +70,13 @@ int8_t config_write(config_t *config) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Write BAUDRATE */ | ||||
| 	if (eeprom_program_halfword(CONFIG_EEPROM_ADDR_BAUDRATE_INDEX, config->baudrate_index)  != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Write LED ON */ | ||||
| 	if (eeprom_program_byte(CONFIG_EEPROM_ADDR_LED_ON, config->led_on)  != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Write LED BRIGHTNESS */ | ||||
| 	if (eeprom_program_halfword(CONFIG_EEPROM_ADDR_LED_BRIGHTNESS, config->led_brightness)  != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Write LED SMOOTH */ | ||||
| 	if (eeprom_program_byte(CONFIG_EEPROM_ADDR_LED_SMOOTH, config->led_smooth)  != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Write LED CO2 ALERT LIMIT 1 */ | ||||
| 	if (eeprom_program_halfword(CONFIG_EEPROM_ADDR_LED_ALERT1, config->led_co2_alert_limit1) != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Write LED CO2 ALERT LIMIT 2 */ | ||||
| 	if (eeprom_program_halfword(CONFIG_EEPROM_ADDR_LED_ALERT2, config->led_co2_alert_limit2) != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Write LED SCD4x TEMPERATURE OFFSET */ | ||||
| 	if (eeprom_program_halfword(CONFIG_EEPROM_ADDR_SCD4x_T_OFFSET, config->scd4x_t_offset) != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Lock EEPROM*/ | ||||
| 	if (eeprom_lock() != EEPROM_OK) | ||||
| 	{ | ||||
|   | ||||
| @@ -1,28 +0,0 @@ | ||||
| /* | ||||
|  * crc.c | ||||
|  * | ||||
|  *  Created on: Jun 9, 2021 | ||||
|  *      Author: user | ||||
|  */ | ||||
|  | ||||
| #include "crc8.h" | ||||
|  | ||||
| /* Stolen from Sensirion SCD4x datasheet, section 3.11 */ | ||||
| uint8_t crc8_calculate(const uint8_t *data, uint16_t count) | ||||
| { | ||||
| 	uint16_t current_byte; | ||||
| 	uint8_t crc = CRC8_INIT; | ||||
| 	uint8_t crc_bit; | ||||
| 	/* calculates 8-Bit checksum with given polynomial */ | ||||
| 	for (current_byte = 0; current_byte < count; ++current_byte) { | ||||
| 		crc ^= (data[current_byte]); | ||||
| 		for(crc_bit = 8; crc_bit > 0; --crc_bit) { | ||||
| 			if (crc & 0x80) { | ||||
| 				crc =(crc << 1) ^ CRC8_POLYNOMIAL; | ||||
| 			} else { | ||||
| 				crc = (crc << 1); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return crc; | ||||
| } | ||||
| @@ -1,202 +0,0 @@ | ||||
| /* | ||||
|  * ltr329.c | ||||
|  * | ||||
|  *  Created on: May 22, 2022 | ||||
|  *      Author: david | ||||
|  */ | ||||
| #include "ltr329.h" | ||||
| #include "main.h" /* for uart_disable_interrupts() */ | ||||
|  | ||||
| static int8_t ltr329_read_register (ltr329_cmd_t register_addr, uint8_t *register_data ) | ||||
| { | ||||
| 	uint8_t tx_buffer[1]; | ||||
| 	uint8_t rx_buffer[1]; | ||||
| 	int result; | ||||
|  | ||||
| 	// start measurement | ||||
| 	tx_buffer[0] = register_addr; | ||||
| 	/* disable interrupts to prevent modbus/i2c conflict */ | ||||
| 	uart_disable_interrupts(); | ||||
| 	result = i2c_transmit(LTR329_I2C_ADDRESS<<1, tx_buffer, 1); | ||||
| 	uart_enable_interrupts(); | ||||
| 	if (result != I2C_OK) { | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	LL_mDelay(10); /* 10 ms should be enough */ | ||||
| 	/* read out */ | ||||
| 	uart_disable_interrupts(); | ||||
| 	result = i2c_receive(LTR329_I2C_ADDRESS<<1, rx_buffer, 1); | ||||
| 	uart_enable_interrupts(); | ||||
| 	if (result != I2C_OK) { | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	*register_data = rx_buffer[0]; | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| static int8_t ltr329_write_register (ltr329_cmd_t register_addr, uint8_t register_data) | ||||
| { | ||||
| 	uint8_t tx_buffer[2]; | ||||
| 	int result; | ||||
|  | ||||
| 	// start measurement | ||||
| 	tx_buffer[0] = register_addr; | ||||
| 	tx_buffer[1] = register_data; | ||||
| 	/* disable interrupts to prevent modbus/i2c conflict */ | ||||
| 	uart_disable_interrupts(); | ||||
| 	result = i2c_transmit(LTR329_I2C_ADDRESS<<1, tx_buffer, 2); | ||||
| 	uart_enable_interrupts(); | ||||
| 	if (result != I2C_OK) { | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| int8_t ltr329_write_settings (ltr329_gain_t gain, ltr329_als_mode_t mode, ltr329_integration_time_t integ_time, ltr329_measurement_rate_t meas_rate) | ||||
| { | ||||
| 	int8_t result; | ||||
| 	/* Write Gain and ALS Mode */ | ||||
| 	result = ltr329_write_register(LTR329_ALS_CONTR, (gain | mode)); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	/* Write Integration Time and Measurement Rate */ | ||||
|  | ||||
| 	result = ltr329_write_register(LTR329_ALS_MEAS_RATE, (integ_time | meas_rate)); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| int8_t ltr329_read_settings (ltr329_gain_t *gain, ltr329_als_mode_t *mode, ltr329_integration_time_t *integ_time, ltr329_measurement_rate_t *meas_rate) | ||||
| { | ||||
| 	int8_t result; | ||||
| 	uint8_t control_register_data; | ||||
| 	uint8_t rate_register_data; | ||||
|  | ||||
| 	result = ltr329_read_register(LTR329_ALS_CONTR, &control_register_data); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	result = ltr329_read_register(LTR329_ALS_MEAS_RATE, &rate_register_data); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	uint8_t control_register_gain_mask = 0b00011100; | ||||
| 	uint8_t control_register_mode_mask = 0b00000001; | ||||
| 	uint8_t rate_register_int_time_mask = 0b00111000; | ||||
| 	uint8_t rate_register_rate_mask = 0b00000111; | ||||
|  | ||||
| 	/* Return Registers Values */ | ||||
| 	/* TODO: This might not be safe */ | ||||
| 	*gain = control_register_data & control_register_gain_mask; | ||||
| 	*mode = control_register_data & control_register_mode_mask; | ||||
| 	*integ_time = rate_register_data & rate_register_int_time_mask; | ||||
| 	*meas_rate = rate_register_data & rate_register_rate_mask; | ||||
|  | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| int8_t ltr329_sw_reset( void ) | ||||
| { | ||||
| 	int8_t result; | ||||
| 	/* Write Gain and ALS Mode */ | ||||
| 	result = ltr329_write_register(LTR329_ALS_CONTR, 0b00000010); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| int8_t ltr329_measure (uint16_t *data_ch0, uint16_t *data_ch1) | ||||
| { | ||||
| 	uint8_t ch0_l, ch0_h, ch1_l, ch1_h; | ||||
| 	int result; | ||||
| 	result = ltr329_read_register(LTR329_ALS_DATA_CH0_0, &ch0_l); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	result = ltr329_read_register(LTR329_ALS_DATA_CH0_1, &ch0_h); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	result = ltr329_read_register(LTR329_ALS_DATA_CH1_0, &ch1_l); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	result = ltr329_read_register(LTR329_ALS_DATA_CH1_1, &ch1_h); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	*data_ch0 = (ch0_h << 8) + ch0_l; | ||||
| 	*data_ch1 = (ch1_h << 8) + ch1_l; | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| int8_t ltr329_read_status_register(uint8_t *data_valid, uint8_t *new_data, ltr329_gain_t *gain) | ||||
| { | ||||
| 	int8_t result; | ||||
| 	uint8_t status_register_data; | ||||
| 	result = ltr329_read_register(LTR329_ALS_STATUS, &status_register_data); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Check data valid */ | ||||
| 	uint8_t data_invalid_mask = 0b10000000; | ||||
| 	if ((status_register_data & data_invalid_mask) == data_invalid_mask) | ||||
| 	{ | ||||
| 		*data_valid = 0; | ||||
| 	} else | ||||
| 	{ | ||||
| 		*data_valid = 1; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if there is new data */ | ||||
| 	uint8_t data_status_mask = 0b00000100; | ||||
| 	if ((status_register_data & data_status_mask) == data_status_mask) | ||||
| 	{ | ||||
| 		*new_data = 1; | ||||
| 	} else | ||||
| 	{ | ||||
| 		*new_data = 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Check Gain */ | ||||
| 	/* TODO: This might not be safe */ | ||||
| 	uint8_t gain_mask = 0b01110000; | ||||
| 	*gain = status_register_data & gain_mask; | ||||
|  | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| int8_t ltr329_read_device_info (uint8_t *manufacturer_id, uint8_t *part_id) | ||||
| { | ||||
| 	int8_t result; | ||||
| 	result = ltr329_read_register(LTR329_MANUFAC_ID, manufacturer_id); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	result = ltr329_read_register(LTR329_PART_ID, part_id); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	return LTR329_OK; | ||||
| } | ||||
| @@ -74,27 +74,26 @@ enum | ||||
| { | ||||
| 	REGISTER_NUM_T = 30010, /* deg C */ | ||||
| 	REGISTER_NUM_T_F = 30011, /* deg F */ | ||||
| 	REGISTER_NUM_RH = 30012 /* %, from SHT4x */ | ||||
|  | ||||
| 	REGISTER_NUM_RH = 30012, /* %, from SHT4x */ | ||||
| 	REGISTER_NUM_LIGHT_INTENSITY_0 = 30013, /* ticks, from LTR329 */ /*TODO: Find out what it is */ | ||||
| 	REGISTER_NUM_LIGHT_INTENSITY_1 = 30014, /* ticks, from LTR329 */ /*TODO: Find out what it is */ | ||||
| 	REGISTER_NUM_DEVICE_IDENTIFIER = 30100 | ||||
| 	/* VOC Index has initial blackout beriod, when the data is not ready. VOC index is 0 during this period */ | ||||
| } data_registers_numbers; | ||||
|  | ||||
| enum | ||||
| { | ||||
| 	REGISTER_NUM_LTR329_GAIN = 40001, | ||||
| 	REGISTER_NUM_LTR329_MEAS_RATE = 40002, | ||||
| 	REGISTER_NUM_LTR329_INTEGRATION_TIME = 40003, | ||||
| 	REGISTER_NUM_LTR329_MODE = 40004, | ||||
| 	/* TODO: Change registers to start with the modbus specific settings? */ | ||||
| 	/* TODO: Seal the gap */ | ||||
| 	REGISTER_NUM_MODBUS_ADDR = 40007, | ||||
| 	REGISTER_NUM_BAUDRATE = 40008, | ||||
| 	REGISTER_NUM_RESET_DEVICE = 40100 | ||||
| } config_registers_numbers; | ||||
|  | ||||
| enum | ||||
| { | ||||
| 	REGISTER_NUM_VENDOR_NAME = 30010, | ||||
| 	REGISTER_NUM_PRODUCT_CODE = 30011, | ||||
| 	REGISTER_NUM_REVISION = 30012, | ||||
| 	REGISTER_NUM_PRODUCT_NAME = 30013, | ||||
| 	REGISTER_NUM_SERIAL_NUMBER = 30014 | ||||
| } identification_registers_numbers; | ||||
|  | ||||
| /* Variables to store the measured data */ | ||||
| int16_t T_SHT4x, TF_SHT4x; | ||||
| uint16_t RH_SHT4x; | ||||
| @@ -106,6 +105,7 @@ config_t sensor_config; | ||||
| modbus_device_id_t device_id; | ||||
| uint8_t sensor_config_pending_write = 0; | ||||
| uint8_t baudrate_changed = 0; | ||||
| uint8_t ltr329_config_changed = 0; | ||||
| uint8_t modbus_address_changed = 0; | ||||
| uint8_t co2_valid = 0; | ||||
|  | ||||
| @@ -126,6 +126,54 @@ void USART2_TX_Buffer(uint8_t* buffer_tx, uint16_t buffer_tx_len); | ||||
| /* Private user code ---------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN 0 */ | ||||
|  | ||||
| int8_t sht4x_i2c_transmit(uint8_t address, uint8_t *buffer, int len) | ||||
| { | ||||
| 	return i2c_transmit(address, buffer, len); | ||||
| } | ||||
|  | ||||
| int8_t sht4x_i2c_receive(uint8_t address, uint8_t *buffer, int len) | ||||
| { | ||||
| 	return i2c_receive(address, buffer, len); | ||||
| } | ||||
|  | ||||
| uint8_t sensirion_crc8_calculate(const uint8_t *data, uint16_t count) | ||||
| { | ||||
| 	return crc8_calculate(data, count); | ||||
| } | ||||
|  | ||||
| int8_t sht4x_disable_interrupts(void) | ||||
| { | ||||
| 	return uart_disable_interrupts(); | ||||
| } | ||||
|  | ||||
| int8_t sht4x_enable_interrupts(void) | ||||
| { | ||||
| 	return uart_enable_interrupts(); | ||||
| } | ||||
|  | ||||
| int8_t ltr329_i2c_transmit(uint8_t address, uint8_t *buffer, int len) | ||||
| { | ||||
| 	return i2c_transmit(address, buffer, len); | ||||
| } | ||||
| int8_t ltr329_i2c_receive(uint8_t address, uint8_t *buffer, int len) | ||||
| { | ||||
| 	return i2c_receive(address, buffer, len); | ||||
| } | ||||
|  | ||||
| int8_t ltr329_disable_interrupts(void) | ||||
| { | ||||
| 	return uart_disable_interrupts(); | ||||
| } | ||||
|  | ||||
| int8_t ltr329_enable_interrupts(void) | ||||
| { | ||||
| 	return uart_enable_interrupts(); | ||||
| } | ||||
|  | ||||
| void delay_ms(int delay_ms) | ||||
| { | ||||
| 	LL_mDelay(delay_ms); | ||||
| } | ||||
| /* USER CODE END 0 */ | ||||
|  | ||||
| /** | ||||
| @@ -162,12 +210,6 @@ int main(void) | ||||
|   if (config_read_status != CONFIG_OK) | ||||
|   { | ||||
| 	  sensor_config.modbus_addr = MODBUS_DEFAULT_SLAVE_ADDRESS; | ||||
| 	  sensor_config.led_co2_alert_limit1 = CONFIG_DEFAULT_LED_ALERT1_LIMIT; | ||||
| 	  sensor_config.led_co2_alert_limit2 = CONFIG_DEFAULT_LED_ALERT2_LIMIT; | ||||
| 	  sensor_config.led_on = CONFIG_DEFAULT_LED_ON; | ||||
| 	  sensor_config.led_brightness = CONFIG_DEFAULT_LED_BRIGHTNESS; | ||||
| 	  sensor_config.led_smooth = CONFIG_DEFAULT_LED_SMOOTH; | ||||
| 	  sensor_config.scd4x_t_offset = CONFIG_DEFAULT_SCD4x_T_OFFSET; | ||||
| 	  sensor_config.baudrate_index = CONFIG_DEFAULT_BAUDRATE_INDEX; | ||||
|   } | ||||
|   /* USER CODE END SysInit */ | ||||
| @@ -231,7 +273,16 @@ int main(void) | ||||
| 	  ltr_ret = ltr329_write_settings(LTR329_GAIN_48X, LTR329_MODE_ACTIVE, LTR329_INTEGRATION_100MS, LTR329_MEAS_RATE_100MS); | ||||
| 	  /* TODO: Check register status */ | ||||
|   } while (ltr_ret != 0); | ||||
|  | ||||
|   /* LTR329 Innitialized */ | ||||
|   ltr329_gain_t ltr329_gain; | ||||
|   ltr329_als_mode_t ltr329_mode; | ||||
|   ltr329_integration_time_t ltr329_int_time; | ||||
|   ltr329_measurement_rate_t ltr329_meas_rate; | ||||
|   ltr329_read_settings(<r329_gain, <r329_mode, <r329_int_time, <r329_meas_rate); | ||||
|   sensor_config.ltr329_gain = ltr329_gain; | ||||
|   sensor_config.ltr329_mode = ltr329_mode; | ||||
|   sensor_config.ltr329_integ_time = ltr329_int_time; | ||||
|   sensor_config.ltr329_meas_rate = ltr329_meas_rate; | ||||
|  | ||||
|   static uint32_t new_baud; | ||||
|   /* Enter the main loop */ | ||||
| @@ -263,8 +314,10 @@ int main(void) | ||||
| 	  } | ||||
| 	  /* if config changed (MODBUS write), reflect changes to EEPROM */ | ||||
| 	  if (sensor_config_pending_write) { | ||||
| 		  uart_disable_interrupts(); | ||||
| 		  config_write(&sensor_config); | ||||
| 		  sensor_config_pending_write = 0; | ||||
| 		  uart_enable_interrupts(); | ||||
| 	  } | ||||
| 	  if (modbus_address_changed) | ||||
| 	  { | ||||
| @@ -284,6 +337,11 @@ int main(void) | ||||
|  | ||||
| 		  new_baud = LL_USART_GetBaudRate(USART2, SYSTICK_FREQ_HZ, LL_USART_OVERSAMPLING_16); | ||||
| 	  } | ||||
| 	  if(ltr329_config_changed) | ||||
| 	  { | ||||
| 		  ltr329_write_settings(sensor_config.ltr329_gain, sensor_config.ltr329_mode, sensor_config.ltr329_integ_time, sensor_config.ltr329_meas_rate); | ||||
| 		  ltr329_config_changed = 0; | ||||
| 	  } | ||||
| 	  /* It is time for measurement */ | ||||
| 	  if (tim21_elapsed_period == 1) | ||||
| 	  { | ||||
| @@ -640,6 +698,12 @@ int8_t modbus_slave_callback(modbus_transaction_t *transaction) | ||||
| 					case REGISTER_NUM_RH: | ||||
| 						transaction->input_registers[i] = (uint16_t)RH_SHT4x; | ||||
| 						break; | ||||
| 					case REGISTER_NUM_LIGHT_INTENSITY_0: | ||||
| 						transaction->input_registers[i] = (uint16_t)light_ch0; | ||||
| 						break; | ||||
| 					case REGISTER_NUM_LIGHT_INTENSITY_1: | ||||
| 						transaction->input_registers[i] = (uint16_t)light_ch1; | ||||
| 						break; | ||||
| 					default: | ||||
| 						return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED; | ||||
| 				} | ||||
| @@ -656,6 +720,22 @@ int8_t modbus_slave_callback(modbus_transaction_t *transaction) | ||||
| 					case REGISTER_NUM_BAUDRATE: | ||||
| 						transaction->holding_registers[i] = (uint16_t)(sensor_config.baudrate_index); | ||||
| 						break; | ||||
| 					case REGISTER_NUM_LTR329_GAIN: | ||||
| 						transaction->holding_registers[i] = (uint16_t)(sensor_config.ltr329_gain); | ||||
| 						/* TODO : IMPLEMENT */ | ||||
| 						break; | ||||
| 					case REGISTER_NUM_LTR329_INTEGRATION_TIME: | ||||
| 						transaction->holding_registers[i] = (uint16_t)(sensor_config.ltr329_integ_time); | ||||
| 						/* TODO : IMPLEMENT */ | ||||
| 						break; | ||||
| 					case REGISTER_NUM_LTR329_MEAS_RATE: | ||||
| 						transaction->holding_registers[i] = (uint16_t)(sensor_config.ltr329_meas_rate); | ||||
| 						/* TODO : IMPLEMENT */ | ||||
| 						break; | ||||
| 					case REGISTER_NUM_LTR329_MODE: | ||||
| 						transaction->holding_registers[i] = (uint16_t)(sensor_config.ltr329_mode); | ||||
| 						/* TODO : IMPLEMENT */ | ||||
| 						break; | ||||
| 					default: | ||||
| 						return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED; | ||||
| 				} | ||||
| @@ -693,6 +773,56 @@ int8_t modbus_slave_callback(modbus_transaction_t *transaction) | ||||
| 							return MODBUS_ERROR_OUT_OF_BOUNDS; | ||||
| 						} | ||||
| 						break; | ||||
| 					case REGISTER_NUM_LTR329_GAIN: | ||||
| 						if (transaction->holding_registers[i] != LTR329_GAIN_1X || | ||||
| 							transaction->holding_registers[i] != LTR329_GAIN_2X || | ||||
| 							transaction->holding_registers[i] != LTR329_GAIN_4X || | ||||
| 							transaction->holding_registers[i] != LTR329_GAIN_8X || | ||||
| 							transaction->holding_registers[i] != LTR329_GAIN_48X || | ||||
| 							transaction->holding_registers[i] != LTR329_GAIN_96X) | ||||
| 						{ | ||||
| 							return MODBUS_ERROR_OUT_OF_BOUNDS; | ||||
| 						} | ||||
| 						sensor_config.ltr329_gain = (ltr329_gain_t) (transaction->holding_registers[i]); | ||||
| 						ltr329_config_changed = 1; | ||||
| 						break; | ||||
| 					case REGISTER_NUM_LTR329_INTEGRATION_TIME: | ||||
| 						if (transaction->holding_registers[i] != LTR329_INTEGRATION_50MS  || | ||||
| 							transaction->holding_registers[i] != LTR329_INTEGRATION_100MS || | ||||
| 							transaction->holding_registers[i] != LTR329_INTEGRATION_150MS || | ||||
| 							transaction->holding_registers[i] != LTR329_INTEGRATION_200MS || | ||||
| 							transaction->holding_registers[i] != LTR329_INTEGRATION_250MS || | ||||
| 							transaction->holding_registers[i] != LTR329_INTEGRATION_300MS || | ||||
| 							transaction->holding_registers[i] != LTR329_INTEGRATION_350MS || | ||||
| 							transaction->holding_registers[i] != LTR329_INTEGRATION_400MS) | ||||
| 						{ | ||||
| 							return MODBUS_ERROR_OUT_OF_BOUNDS; | ||||
| 						} | ||||
| 						sensor_config.ltr329_integ_time = (ltr329_integration_time_t) (transaction->holding_registers[i]); | ||||
| 						ltr329_config_changed = 1; | ||||
| 						break; | ||||
| 					case REGISTER_NUM_LTR329_MEAS_RATE: | ||||
| 						if (transaction->holding_registers[i] != LTR329_MEAS_RATE_50MS   || | ||||
| 							transaction->holding_registers[i] != LTR329_MEAS_RATE_100MS  || | ||||
| 							transaction->holding_registers[i] != LTR329_MEAS_RATE_200MS  || | ||||
| 							transaction->holding_registers[i] != LTR329_MEAS_RATE_500MS  || | ||||
| 							transaction->holding_registers[i] != LTR329_MEAS_RATE_1000MS || | ||||
| 							transaction->holding_registers[i] != LTR329_MEAS_RATE_2000MS) | ||||
| 						{ | ||||
| 							return MODBUS_ERROR_OUT_OF_BOUNDS; | ||||
| 						} | ||||
| 						sensor_config.ltr329_meas_rate = (ltr329_measurement_rate_t) (transaction->holding_registers[i]); | ||||
| 						ltr329_config_changed = 1; | ||||
| 						break; | ||||
| 					case REGISTER_NUM_LTR329_MODE: | ||||
| 						if (transaction->holding_registers[i] != LTR329_MODE_STAND_BY   || | ||||
| 							transaction->holding_registers[i] != LTR329_MODE_ACTIVE) | ||||
| 						{ | ||||
| 							return MODBUS_ERROR_OUT_OF_BOUNDS; | ||||
| 						} | ||||
| 						sensor_config.ltr329_mode = (ltr329_als_mode_t) (transaction->holding_registers[i]); | ||||
| 						ltr329_config_changed = 1; | ||||
| 						break; | ||||
| 					default: | ||||
| 						return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED; | ||||
| 				} | ||||
|   | ||||
| @@ -1,431 +0,0 @@ | ||||
| /* | ||||
|  * modbus.c | ||||
|  * | ||||
|  *  Created on: Jul 18, 2021 | ||||
|  *      Author: user | ||||
|  */ | ||||
|  | ||||
| #include "modbus.h" | ||||
|  | ||||
| /* | ||||
|  * Global variables | ||||
|  */ | ||||
|  | ||||
| /* Modbus TX buffer; can be also used for RX in memory constrained systems (e.g. in main.c); | ||||
|  * NOTE if shared buffer is used for TX/RX, care must be taken to prevent writing into buffer | ||||
|  * during execution of modbus_process_message() */ | ||||
| uint8_t modbus_buffer[MODBUS_MAX_RTU_FRAME_SIZE]; | ||||
|  | ||||
| /* MODBUS device address */ | ||||
| uint8_t modbus_slave_address = MODBUS_DEFAULT_SLAVE_ADDRESS; | ||||
|  | ||||
| /* Device ID struct */ | ||||
| modbus_device_id_t *modbus_device_id = NULL; | ||||
|  | ||||
| /* | ||||
|  * CRC16 functions | ||||
|  * see https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf | ||||
|  * section 6.2.2 | ||||
|  */ | ||||
|  | ||||
| /* CRC16 (without memory mapped values) | ||||
|  * taken from https://ctlsys.com/support/how_to_compute_the_modbus_rtu_message_crc/ */ | ||||
| uint16_t modbus_CRC16(const uint8_t *buf, int len) | ||||
| { | ||||
| 	uint16_t crc = 0xFFFF; | ||||
|  | ||||
| 	for (int pos = 0; pos < len; pos++) { | ||||
| 		crc ^= (uint16_t)buf[pos];          // XOR byte into least sig. byte of crc | ||||
|         | ||||
| 		for (int i = 8; i != 0; i--) {    // Loop over each bit | ||||
| 			if ((crc & 0x0001) != 0) {      // If the LSB is set | ||||
| 				crc >>= 1;                    // Shift right and XOR 0xA001 | ||||
| 				crc ^= 0xA001; | ||||
| 			} else {                            // Else LSB is not set | ||||
| 				crc >>= 1;                    // Just shift right | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes) | ||||
| 	return crc;   | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Private functions | ||||
|  */ | ||||
|  | ||||
| static uint8_t modbus_fill_device_id_objects(uint8_t *buffer, modbus_transaction_t *transaction) | ||||
| { | ||||
| 	/* we assume buffer is 256 - MODBUS_READ_DEVICE_ID_RESPONSE_HEADER_LEN = 252 bytes long */ | ||||
| 	/* find out how many objects we copy to buffer */ | ||||
| 	int len; | ||||
| 	uint8_t object_index = transaction->object_id; | ||||
| 	uint8_t object_count; | ||||
| 	uint8_t more_follows = MODBUS_NO_MORE_FOLLOWS; | ||||
| 	uint8_t next_object_id; | ||||
| 	uint8_t last_object; | ||||
| 	const uint8_t max_len = 256 - MODBUS_READ_DEVICE_ID_RESPONSE_HEADER_LEN; | ||||
|  | ||||
| 	/* last object index */ | ||||
| 	if (transaction->read_device_id_code == MODBUS_CONFORMITY_BASIC) { | ||||
| 		last_object = MODBUS_BASIC_OBJECT_COUNT; | ||||
| 	} else if (transaction->read_device_id_code == MODBUS_CONFORMITY_REGULAR) { | ||||
| 		last_object = MODBUS_REGULAR_OBJECT_COUNT; | ||||
| 	/* extended not implemented */ | ||||
| //	} else if (transaction->read_device_id_code == MODBUS_CONFORMITY_EXTENDED){ | ||||
| //		last_object = MODBUS_EXTENDED_OBJECT_COUNT; | ||||
| 	} else { | ||||
| 		/* fallback: regular */ | ||||
| 		last_object = MODBUS_REGULAR_OBJECT_COUNT; | ||||
| 	} | ||||
| 	last_object--; // we need index | ||||
| 	/* copy as many objects as possible */ | ||||
| 	do { | ||||
| 		/* copy object */ | ||||
| 		int object_len = strlen(modbus_device_id->object_id[object_index]); | ||||
| 		if (len + object_len + 2 > max_len) { | ||||
| 			more_follows = MODBUS_MORE_FOLLOWS; | ||||
| 			next_object_id = object_index; | ||||
| 			break; | ||||
| 		} | ||||
| 		/* offset is for "more follows", "next object id", "object count" */ | ||||
| 		buffer[MODBUS_READ_DEVICE_ID_RESPONSE_OFFSET + len++] = object_index; | ||||
| 		buffer[MODBUS_READ_DEVICE_ID_RESPONSE_OFFSET + len++] = object_len; | ||||
| 		/* note that string copied to buffer is not null-terminated */ | ||||
| 		strncpy((char*)(buffer + len), (char*)modbus_device_id->object_id[object_index++], object_len); | ||||
| 		len += object_len; | ||||
| 		object_count++; | ||||
| 	} while (object_index < last_object); | ||||
| 	buffer[0] = more_follows; | ||||
| 	buffer[1] = next_object_id; | ||||
| 	buffer[2] = object_count; | ||||
| 	return MODBUS_READ_DEVICE_ID_RESPONSE_OFFSET + len; | ||||
| } | ||||
|  | ||||
| /* here we assume buffer has minimal size of MODBUS_MAX_RTU_FRAME_SIZE; | ||||
|  * this function is private, so hopefully it's going to be ok */ | ||||
| static int8_t modbus_transaction_to_buffer(uint8_t *buffer, uint8_t *msg_len, modbus_transaction_t *transaction) | ||||
| { | ||||
| 	uint16_t crc16; | ||||
| 	uint8_t byte_count; | ||||
| 	uint8_t buffer_pos = 0; | ||||
|  | ||||
| 	// TODO use relative indices (increments) instead of absolute | ||||
| 	buffer[buffer_pos++] = modbus_slave_address; | ||||
| 	buffer[buffer_pos++] = transaction->function_code; | ||||
| 	*msg_len = 5; | ||||
|  | ||||
| 	if (transaction->function_code & MODBUS_ERROR_FLAG) { | ||||
| 		/* sending error reply */ | ||||
| 		buffer[buffer_pos++] = transaction->exception; | ||||
| 	} else { | ||||
| 		switch (transaction->function_code) { | ||||
| 			case MODBUS_READ_HOLDING_REGISTERS: | ||||
| 			case MODBUS_READ_INPUT_REGISTERS: | ||||
| 				byte_count = transaction->register_count * 2; | ||||
| 				buffer[buffer_pos++] = byte_count; | ||||
| 				*msg_len = byte_count + 5; | ||||
| 				for (int i = 0; i < transaction->register_count; i++) { | ||||
| 					// TODO endianness handling | ||||
| 					/* buffer16b is alias for both holding and input register buffers */ | ||||
| 					buffer[buffer_pos++] = transaction->buffer16b[i] >> 8; | ||||
| 					buffer[buffer_pos++] = transaction->buffer16b[i] & 0xff; | ||||
| 				} | ||||
| 				break; | ||||
| 			case MODBUS_WRITE_SINGLE_REGISTER: | ||||
| 				buffer[buffer_pos++] = (uint8_t) (transaction->register_address >> 8); | ||||
| 				buffer[buffer_pos++] = (uint8_t) transaction->register_address; | ||||
| 				buffer[buffer_pos++] = (uint8_t) (transaction->holding_registers[0] >> 8); | ||||
| 				buffer[buffer_pos++] = (uint8_t) transaction->holding_registers[0]; | ||||
| 				*msg_len = 8; /* includes 2 bytes for CRC */ | ||||
| 				break; | ||||
| 			case MODBUS_WRITE_MULTIPLE_REGISTERS: | ||||
| 				buffer[buffer_pos++] = (uint8_t) (transaction->register_address >> 8); | ||||
| 				buffer[buffer_pos++] = (uint8_t) transaction->register_address; | ||||
| 				buffer[buffer_pos++] = (uint8_t) (transaction->register_count >> 8); | ||||
| 				buffer[buffer_pos++] = (uint8_t) transaction->register_count; | ||||
| 				*msg_len = 8; /* includes 2 bytes for CRC */ | ||||
| 				break; | ||||
| 			case MODBUS_READ_DEVICE_IDENTIFICATION: | ||||
| 				/* MEI type */ | ||||
| 				buffer[buffer_pos++] = MODBUS_MEI; | ||||
| 				/* read device id */ | ||||
| 				buffer[buffer_pos++] = transaction->read_device_id_code; | ||||
| 				/* conformity level */ | ||||
| 				buffer[buffer_pos++] = modbus_device_id->conformity_level; | ||||
| 				/* fill buffer with as many objects as possible  */ | ||||
| 				*msg_len = modbus_fill_device_id_objects(buffer+buffer_pos, transaction); | ||||
| 				*msg_len += 7; /* includes 2 bytes for CRC */ | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
| 	crc16 = modbus_CRC16(buffer, buffer_pos); /* last two bytes is the checksum itself */ | ||||
| 	buffer[buffer_pos++] = crc16 & 0xff; | ||||
| 	buffer[buffer_pos++] = crc16 >> 8; | ||||
| 	return MODBUS_OK; | ||||
| } | ||||
|  | ||||
| static int8_t modbus_process_device_id_request(const uint8_t *buffer, int len, modbus_transaction_t *transaction) | ||||
| { | ||||
| 	uint8_t MEI_type; | ||||
| 	uint8_t read_device_id_code; | ||||
| 	uint8_t object_id; | ||||
| 	uint8_t buffer_pos = 0; | ||||
|  | ||||
| 	if (transaction->broadcast == 1) { | ||||
| 		/* Read device ID broadcast - invalid; ignore (master will get timeout) */ | ||||
| 		return MODBUS_ERROR; | ||||
| 	} | ||||
| 	if (modbus_device_id == NULL) { | ||||
| 		/* modbus_device_id not initialized; user should use modbus_slave_init_device_id() first */ | ||||
| 		transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DEVICE_ID_CODE; | ||||
| 		return MODBUS_OK; | ||||
| 	} | ||||
| 	if (len < MODBUS_READ_DEVICE_ID_REQUEST_LEN) { | ||||
| 		/* frame too short, ignore */ | ||||
| 		return MODBUS_ERROR; | ||||
| 	} | ||||
| 	/* next byte should be MEI = 0x0E */ | ||||
| 	MEI_type = buffer[buffer_pos++]; | ||||
| 	if (MEI_type != MODBUS_MEI) { | ||||
| 		/* invalid MEI, ignore. I have no idea what MEI does, but it should always be 0x0E */ | ||||
| 		return MODBUS_ERROR; | ||||
| 	} | ||||
| 	/* next byte is read device id code */ | ||||
| 	read_device_id_code = buffer[buffer_pos++]; | ||||
| 	/* read device id code can only have values 1,2,3,4 */ | ||||
| 	if (read_device_id_code < 1 || read_device_id_code > 4) { | ||||
| 		transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DEVICE_ID_CODE; | ||||
| 		return MODBUS_OK; | ||||
| 	} | ||||
| 	transaction->read_device_id_code = read_device_id_code; | ||||
| 	/* next byte is object id */ | ||||
| 	object_id = buffer[buffer_pos++]; | ||||
| 	transaction->object_id = object_id; | ||||
| 	if (object_id > MODBUS_DEVICE_ID_OBJECT_NUM) { | ||||
| 		/* illegal object ID */ | ||||
| 		transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS; | ||||
| 		return MODBUS_OK; | ||||
| 	} | ||||
| 	/* Message processed */ | ||||
| 	return MODBUS_OK; | ||||
| } | ||||
|  | ||||
| /* returns ERROR only when no response to master is needed */ | ||||
| static int8_t modbus_process_read_write_request(const uint8_t *buffer, int len, modbus_transaction_t *transaction) | ||||
| { | ||||
| 	uint8_t byte_count; | ||||
| 	int8_t callback_result; | ||||
| 	uint8_t buffer_pos = 0; | ||||
|  | ||||
| 	/* set starting register number */ | ||||
| 	switch (transaction->function_code) { | ||||
| 	/* coils */ | ||||
| 	case MODBUS_READ_DO: | ||||
| 	case MODBUS_WRITE_SINGLE_DO: | ||||
| 	case MODBUS_WRITE_MULTIPLE_DO: | ||||
| 		transaction->register_number = MODBUS_DO_START_NUMBER; | ||||
| 		break; | ||||
| 	/* discrete inputs */ | ||||
| 	case MODBUS_READ_DI: | ||||
| 		transaction->register_number = MODBUS_DI_START_NUMBER; | ||||
| 		break; | ||||
| 	/* input registers */ | ||||
| 	case MODBUS_READ_AI: | ||||
| 		transaction->register_number = MODBUS_AI_START_NUMBER; | ||||
| 		break; | ||||
| 	/* holding registers */ | ||||
| 	case MODBUS_READ_AO: | ||||
| 	case MODBUS_WRITE_SINGLE_AO: | ||||
| 	case MODBUS_WRITE_MULTIPLE_AO: | ||||
| 	case MODBUS_READ_WRITE_MULTIPLE_REGISTERS: | ||||
| 		transaction->register_number = MODBUS_AO_START_NUMBER; | ||||
| 		break;	 | ||||
| 	} | ||||
|  | ||||
| 	#define MODBUS_FLAG_WRITE  0x01 | ||||
| 	#define MODBUS_FLAG_SINGLE 0x02 | ||||
| 	uint8_t flags = 0x00; | ||||
|  | ||||
| 	/* process message */ | ||||
| 	switch (transaction->function_code) { | ||||
| 	case MODBUS_WRITE_SINGLE_COIL: | ||||
| 	case MODBUS_WRITE_SINGLE_REGISTER: /* holding register */ | ||||
| 		flags |= MODBUS_FLAG_SINGLE; | ||||
| 	case MODBUS_WRITE_MULTIPLE_COILS: | ||||
| 	case MODBUS_WRITE_MULTIPLE_REGISTERS: | ||||
| 		flags |= MODBUS_FLAG_WRITE; | ||||
| 	case MODBUS_READ_DISCRETE_INPUTS: | ||||
| 	case MODBUS_READ_COILS: | ||||
| 	case MODBUS_READ_INPUT_REGISTERS: | ||||
| 	case MODBUS_READ_HOLDING_REGISTERS: | ||||
| 		if (len < MODBUS_MINIMAL_READWRITE_LEN) { | ||||
| 			/* buffer too short to contain everything we need */ | ||||
| 			return MODBUS_ERROR; | ||||
| 		} | ||||
| 		transaction->register_address = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1]; | ||||
| 		buffer += 2; | ||||
| 		// TODO check length! | ||||
| 		if (flags & MODBUS_FLAG_WRITE) { | ||||
| 			if (flags & MODBUS_FLAG_SINGLE) { | ||||
| 				transaction->holding_registers[0] = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1]; | ||||
| 				buffer_pos += 2; | ||||
| 			} else { | ||||
| 				/* Write multiple registers */ | ||||
| 				transaction->register_count = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1]; | ||||
| 				buffer_pos += 2; | ||||
| 				if (len < MODBUS_MINIMAL_WRITE_MULTIPLE_LEN) { | ||||
| 					return MODBUS_ERROR; | ||||
| 				} | ||||
| 				byte_count = buffer[buffer_pos++]; | ||||
| 				if (transaction->register_count > 123 || 2*transaction->register_count != byte_count) { | ||||
| 					/* Max number of register is defined by Modbus_Application_Protocol_V1_1b, section 6.12 */ | ||||
| 					transaction->exception = MODBUS_EXCEPTION_ILLEGAL_REGISTER_QUANTITY; | ||||
| 				} else { | ||||
| 					if (len < MODBUS_MINIMAL_WRITE_MULTIPLE_LEN + byte_count) { | ||||
| 						return MODBUS_ERROR; | ||||
| 					} | ||||
| 					for (uint8_t i = 0; i < transaction->register_count; i++) { | ||||
| 						transaction->holding_registers[i] = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1]; | ||||
| 						buffer_pos += 2; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			transaction->register_count = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1]; | ||||
| 			buffer_pos += 2; | ||||
| 			if ( | ||||
| 					transaction->register_count < 1 || | ||||
| 					transaction->register_count > MODBUS_MAX_REGISTERS | ||||
| 			   ) { | ||||
| 				transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE; | ||||
| 			} | ||||
| 		} | ||||
| 		// add offset to register number | ||||
| 		transaction->register_number += transaction->register_address; | ||||
| 		break; | ||||
| 	default: | ||||
| 		/* function code not known / not implemented, reply with | ||||
| 		 * ExceptionCode 1 */ | ||||
| 		transaction->exception = MODBUS_EXCEPTION_ILLEGAL_FUNCTION; | ||||
| 		break; | ||||
| 	} | ||||
| 	/* data in modbus_buffer have been processed and buffer can be re-used for TX */ | ||||
| 	/* handle reply */ | ||||
| 	if (transaction->exception != 0) { | ||||
| 		/* indicate error */ | ||||
| 		transaction->function_code |= MODBUS_ERROR_FLAG; | ||||
| 	} else { | ||||
| 		callback_result = modbus_slave_callback(transaction); | ||||
| 		/* error handling */ | ||||
| 		if (callback_result != MODBUS_OK) { | ||||
| 			transaction->function_code |= MODBUS_ERROR_FLAG; | ||||
| 			if (callback_result == MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED) { | ||||
| 				transaction->exception = MODBUS_EXCEPTION_ILLEGAL_FUNCTION; | ||||
| 			} else if (callback_result == MODBUS_ERROR_REGISTER_NOT_IMPLEMENTED) { | ||||
| 				transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return MODBUS_OK; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Public function definitions | ||||
|  */ | ||||
|  | ||||
| int8_t modbus_slave_set_address(uint8_t address) | ||||
| { | ||||
| 	if (address == 0) { | ||||
| 		/* address 0 is broadcast address */ | ||||
| 		return MODBUS_ERROR; | ||||
| 	} | ||||
| 	modbus_slave_address = address; | ||||
| 	return MODBUS_OK; | ||||
| } | ||||
|  | ||||
|  | ||||
| int8_t modbus_slave_process_msg(const uint8_t *buffer, int len) | ||||
| { | ||||
|  | ||||
|  | ||||
| 	/* | ||||
| 	 * TODO list: | ||||
| 	 * | ||||
| 	 * 1) check that errors and exceptions are handled according to Modbus_Application_Protocol_V1_1b.pdf | ||||
| 	 * 2) buffer overflow prevention: for each function code, check that buffer is long enough | ||||
| 	 */ | ||||
|  | ||||
|  | ||||
| 	/* transaction holds message context and content: | ||||
| 	 * it wraps all necessary buffers and variables */ | ||||
| 	modbus_transaction_t transaction; | ||||
| 	uint8_t buffer_pos = 0; | ||||
|  | ||||
| 	if (len < MODBUS_MINIMAL_FRAME_LEN) { | ||||
| 		/* frame too short; return error (no reply needed) */ | ||||
| 		return MODBUS_ERROR_FRAME_INVALID; | ||||
| 	} | ||||
| 	/* check CRC first */ | ||||
| 	uint16_t crc_received = (buffer[len - 1] << 8) | buffer[len - 2]; | ||||
| 	uint16_t crc_calculated = modbus_CRC16(buffer, len - 2); | ||||
| 	if (crc_received != crc_calculated) { | ||||
| 		/* CRC mismatch, return error (no reply needed) */ | ||||
| 		return MODBUS_ERROR_CRC; | ||||
| 	} | ||||
| 	/* check if address matches ours */ | ||||
| 	uint8_t address = buffer[buffer_pos++]; | ||||
| 	transaction.broadcast = (address == MODBUS_BROADCAST_ADDR); | ||||
| 	if (address != modbus_slave_address && transaction.broadcast != 1) { | ||||
| 		/* Message is not for us (no reply needed) */ | ||||
| 		return MODBUS_OK; | ||||
| 	} | ||||
| 	/* get function code */ | ||||
| 	transaction.function_code = buffer[buffer_pos++]; | ||||
| 	transaction.exception = 0; | ||||
| 	uint8_t request_processing_result; | ||||
| 	if (transaction.function_code == MODBUS_READ_DEVICE_IDENTIFICATION) { | ||||
| 		/* Read device ID request is quite complicated, therefore it has its own processing function */ | ||||
| 		request_processing_result = modbus_process_device_id_request(buffer + buffer_pos, len - buffer_pos, &transaction); | ||||
| 	} else { | ||||
| 		/* process other requests: input register read, holding register read/write */ | ||||
| 		request_processing_result = modbus_process_read_write_request(buffer + buffer_pos, len - buffer_pos, &transaction); | ||||
| 	} | ||||
| 	uint8_t msg_len; | ||||
| 	/* reply only if request was processed successfully and message was not broadcast */ | ||||
| 	if (request_processing_result == MODBUS_OK && transaction.broadcast == 0) { | ||||
| 		modbus_transaction_to_buffer(modbus_buffer, &msg_len, &transaction); | ||||
| 		/* send reply */ | ||||
| 		modbus_transmit_function(modbus_buffer, msg_len); | ||||
| 	} | ||||
| 	return MODBUS_OK; | ||||
| } | ||||
|  | ||||
| int8_t modbus_slave_init_device_id(modbus_device_id_t *device_id) | ||||
| { | ||||
| 	if (device_id == NULL) { | ||||
| 		return MODBUS_ERROR; | ||||
| 	} | ||||
| 	/* at least basic category objects have to be implemented */ | ||||
| 	if (	device_id->object_name.VendorName == NULL  || | ||||
| 			device_id->object_name.ProductCode == NULL || | ||||
| 			device_id->object_name.MajorMinorRevision == NULL | ||||
| 	) { | ||||
| 		return MODBUS_ERROR; | ||||
| 	} | ||||
| 	/* set conformity level: currently only "basic" and "regular" is implemented */ | ||||
| 	if ( 	device_id->object_id[3] != NULL && | ||||
| 			device_id->object_id[4] != NULL && | ||||
| 			device_id->object_id[5] != NULL | ||||
|  | ||||
| 	) { | ||||
| 		/* strings are present in regular category (optional) */ | ||||
| 		device_id->conformity_level = MODBUS_CONFORMITY_REGULAR; | ||||
| 	} else { | ||||
| 		device_id->conformity_level = MODBUS_CONFORMITY_BASIC; | ||||
| 	} | ||||
| 	/* we support both stream and individual access to objects */ | ||||
| 	device_id->conformity_level |= MODBUS_DEVICE_ID_INDIVIDUAL_ACCESS_FLAG; | ||||
| 	modbus_device_id = device_id; | ||||
| 	return MODBUS_OK; | ||||
| } | ||||
| @@ -1,67 +0,0 @@ | ||||
| /* | ||||
|  * sht4x.c | ||||
|  * | ||||
|  *  Created on: Jun 8, 2021 | ||||
|  *      Author: user | ||||
|  */ | ||||
|  | ||||
| #include "sht4x.h" | ||||
| #include "main.h" /* for uart_disable_interrupts() */ | ||||
|  | ||||
| int8_t sht4x_send_cmd(sht4x_cmd_t cmd) | ||||
| { | ||||
| 	return SHT4X_OK; | ||||
| } | ||||
|  | ||||
| int8_t sht4x_read_data(uint8_t *buffer, int len) | ||||
| { | ||||
| 	return SHT4X_OK; | ||||
| } | ||||
|  | ||||
| int8_t sht4x_measure(int16_t *temperature, uint16_t *relative_humidity) | ||||
| { | ||||
| 	uint8_t buffer[32]; | ||||
| 	int result; | ||||
|  | ||||
| 	/* disable interrupts to prevent modbus/i2c conflict */ | ||||
| 	// start measurement | ||||
| 	buffer[0] = SHT4X_START_MEAS_HIGH_PRECISION; | ||||
| 	uart_disable_interrupts(); | ||||
| 	result = i2c_transmit(SHT4X_I2C_ADDRESS<<1, buffer, 1); | ||||
| 	uart_enable_interrupts(); | ||||
| 	if (result != I2C_OK) { | ||||
| 		return SHT4X_ERROR; | ||||
| 	} | ||||
| 	LL_mDelay(10); /* 10 ms should be enough */ | ||||
| 	/* read out */ | ||||
| 	uart_disable_interrupts(); | ||||
| 	result = i2c_receive(SHT4X_I2C_ADDRESS<<1, buffer, 6); | ||||
| 	uart_enable_interrupts(); | ||||
| 	if (result != I2C_OK) { | ||||
| 		return SHT4X_ERROR; | ||||
| 	} | ||||
| 	/* Convert to T and RH; taken directly from pseudocode in SHT4x datasheet, page 3 */ | ||||
| 	uint32_t t_ticks = (buffer[0] << 8) + buffer[1]; | ||||
| 	uint8_t t_crc = buffer[2]; | ||||
| 	uint32_t rh_ticks = (buffer[3] << 8) + buffer[4]; | ||||
| 	uint8_t rh_crc = buffer[5]; | ||||
| 	/* check CRC-8 checksum */ | ||||
| 	uint8_t crc_correct = crc8_calculate(buffer, 2) == t_crc; | ||||
| 	crc_correct &= crc8_calculate(buffer + 3, 2) == rh_crc; | ||||
| 	if (!crc_correct) { | ||||
| 		return SHT4X_CRC8_ERROR; | ||||
| 	} | ||||
| 	/* copy data to output variables */ | ||||
| 	int t_degC = -450 + 10 * 175 * t_ticks / 65535; /* temperature * 10 */ | ||||
| 	int rh_pRH = -6 + 125 * rh_ticks / 65535; | ||||
| 	if (rh_pRH > 100) { | ||||
| 			rh_pRH = 100; | ||||
| 	} | ||||
| 	if (rh_pRH < 0) { | ||||
| 		rh_pRH = 0; | ||||
| 	} | ||||
| 	*temperature = t_degC; | ||||
| 	*relative_humidity = rh_pRH; | ||||
|  | ||||
| 	return SHT4X_OK; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 David Žaitlík
					David Žaitlík