Added modbus lib and test
This commit is contained in:
parent
8d5139c621
commit
57f7060c0e
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"nrf-connect.applications": [
|
||||||
|
"${workspaceFolder}\\modbus_test"
|
||||||
|
],
|
||||||
|
"files.associations": {
|
||||||
|
"log.h": "c",
|
||||||
|
"modbus.h": "c",
|
||||||
|
"array": "c",
|
||||||
|
"string": "c",
|
||||||
|
"string_view": "c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,12 +10,12 @@
|
||||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
model = "STMicroelectronics STM32F103RB-NUCLEO board";
|
model = "Iten engineering Valve Node";
|
||||||
compatible = "st,stm32f103rb-nucleo";
|
compatible = "iten,valve-node", "st,stm32f103rb";
|
||||||
|
|
||||||
chosen {
|
chosen {
|
||||||
zephyr,console = &usart2;
|
zephyr,console = &usart1;
|
||||||
zephyr,shell-uart = &usart2;
|
zephyr,shell-uart = &usart1;
|
||||||
zephyr,sram = &sram0;
|
zephyr,sram = &sram0;
|
||||||
zephyr,flash = &flash0;
|
zephyr,flash = &flash0;
|
||||||
zephyr,canbus = &can1;
|
zephyr,canbus = &can1;
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
compatible = "gpio-leds";
|
compatible = "gpio-leds";
|
||||||
|
|
||||||
green_led_2: led_2 {
|
green_led_2: led_2 {
|
||||||
gpios = <&gpioa 5 GPIO_ACTIVE_HIGH>;
|
gpios = <&gpiob 2 GPIO_ACTIVE_HIGH>;
|
||||||
label = "User LD2";
|
label = "User LD2";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -81,8 +81,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
&clk_hse {
|
&clk_hse {
|
||||||
hse-bypass;
|
clock-frequency = <DT_FREQ_M(8)>;
|
||||||
clock-frequency = <DT_FREQ_M(8)>; /* STLink 8MHz clock */
|
|
||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -104,15 +103,26 @@
|
||||||
&usart1 {
|
&usart1 {
|
||||||
pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>;
|
pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>;
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
current-speed = <115200>;
|
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
current-speed = <115200>;
|
||||||
};
|
};
|
||||||
|
|
||||||
&usart2 {
|
&usart2 {
|
||||||
pinctrl-0 = <&usart2_tx_pa2 &usart2_rx_pa3>;
|
pinctrl-0 = <&usart2_tx_pa2 &usart2_rx_pa3>;
|
||||||
|
current-speed = <9600>;
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
current-speed = <115200>;
|
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
modbus0 {
|
||||||
|
compatible = "zephyr,modbus-serial";
|
||||||
|
status = "okay";
|
||||||
|
//de-gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&usart3 {
|
||||||
|
pinctrl-0 = <&usart3_tx_pb10 &usart3_rx_pb11>;
|
||||||
|
current-speed = <115200>;
|
||||||
|
pinctrl-names = "default";
|
||||||
};
|
};
|
||||||
|
|
||||||
&i2c1 {
|
&i2c1 {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
CONFIG_SERIAL=y
|
CONFIG_SERIAL=y
|
||||||
# enable console
|
# enable console
|
||||||
CONFIG_CONSOLE=y
|
CONFIG_CONSOLE=y
|
||||||
CONFIG_UART_CONSOLE=y
|
|
||||||
|
|
||||||
# enable GPIO
|
# enable GPIO
|
||||||
CONFIG_GPIO=y
|
CONFIG_GPIO=y
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
#include "modbus.h"
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/sys/util.h>
|
||||||
|
#include <zephyr/modbus/modbus.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
|
||||||
|
// LOG_MODULE_REGISTER(mbc, CONFIG_LOG_DEFAULT_LEVEL);
|
||||||
|
LOG_MODULE_REGISTER(mbc, 4); // Set log level to 4 (Debug)
|
||||||
|
|
||||||
|
#define MODBUS_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(zephyr_modbus_serial)
|
||||||
|
|
||||||
|
static int client_iface;
|
||||||
|
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
int level; // Water level value
|
||||||
|
int minimum; // Minimum value
|
||||||
|
int maximum; // Maximum value
|
||||||
|
int factor; // Factor for unit conversion
|
||||||
|
} measurement;
|
||||||
|
|
||||||
|
int mb_init_client(void)
|
||||||
|
{
|
||||||
|
const char iface_name[] = {DEVICE_DT_NAME(MODBUS_NODE)};
|
||||||
|
client_iface = modbus_iface_get_by_name(iface_name);
|
||||||
|
LOG_DBG("Modbus client interface: %d", client_iface);
|
||||||
|
if (client_iface < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to get Modbus interface by name: %s", iface_name);
|
||||||
|
return client_iface;
|
||||||
|
}
|
||||||
|
return modbus_init_client(client_iface, client_param);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mb_read_holding_registers(int node, uint16_t reg_addr, uint16_t *data, size_t len)
|
||||||
|
{
|
||||||
|
return modbus_read_holding_regs(client_iface, node, reg_addr, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mb_read()
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
int16_t data[5] = {0};
|
||||||
|
rc = mb_read_holding_registers(1, 0x0002, data, sizeof(data) / sizeof(data[0]));
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to read holding registers: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
LOG_HEXDUMP_DBG(data, sizeof(data), "Holding Registers Data");
|
||||||
|
|
||||||
|
int unit, decimals;
|
||||||
|
unit = data[0];
|
||||||
|
decimals = data[1];
|
||||||
|
int factor;
|
||||||
|
switch (unit)
|
||||||
|
{
|
||||||
|
case 1: // cm
|
||||||
|
factor = 10;
|
||||||
|
break;
|
||||||
|
case 2: // mm
|
||||||
|
factor = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERR("Unknown unit: %d", unit);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
switch(decimals)
|
||||||
|
{
|
||||||
|
case 0: // no decimals
|
||||||
|
factor /= 1;
|
||||||
|
break;
|
||||||
|
case 1: // one decimal
|
||||||
|
factor /= 10;
|
||||||
|
break;
|
||||||
|
case 2: // two decimals
|
||||||
|
factor /= 100;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERR("Unknown decimals: %d", decimals);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
measurement.factor = factor;
|
||||||
|
measurement.level = data[2] * factor;
|
||||||
|
measurement.minimum = data[3] * factor;
|
||||||
|
measurement.maximum = data[4] * factor;
|
||||||
|
LOG_DBG("Water level: %dmm, Minimum: %dmm, Maximum: %dmm",
|
||||||
|
measurement.level, measurement.minimum, measurement.maximum);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mb_read_water_level(double *mb_read_water_level)
|
||||||
|
{
|
||||||
|
int rc = mb_read();
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to read water level: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
*mb_read_water_level = (double)measurement.level / 1000.0; // Convert to meters
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mb_read_water_level_mm(int *mb_read_water_level)
|
||||||
|
{
|
||||||
|
int rc = mb_read();
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to read water level: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
*mb_read_water_level = measurement.level;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mb_read_minimum_mm(int *mb_read_minimum)
|
||||||
|
{
|
||||||
|
int rc = mb_read();
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to read water level: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
*mb_read_minimum = measurement.minimum;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mb_read_maximum_mm(int *mb_read_maximum)
|
||||||
|
{
|
||||||
|
int rc = mb_read();
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to read water level: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
*mb_read_maximum = measurement.maximum;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mb_write_minimum_mm(int minimum)
|
||||||
|
{
|
||||||
|
int rc = mb_read();
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to read water level: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
modbus_write_holding_reg(client_iface, 1, 0x0005, minimum / measurement.factor);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mb_write_maximum_mm(int maximum)
|
||||||
|
{
|
||||||
|
int rc = mb_read();
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to read water level: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
modbus_write_holding_reg(client_iface, 1, 0x0006, maximum / measurement.factor);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef __waterlevel_h
|
||||||
|
#define __waterlevel_h
|
||||||
|
|
||||||
|
#include <zephyr/modbus/modbus.h>
|
||||||
|
|
||||||
|
const static struct modbus_iface_param client_param = {
|
||||||
|
.mode = MODBUS_MODE_RTU,
|
||||||
|
.rx_timeout = 50000,
|
||||||
|
.serial = {
|
||||||
|
.baud = 9600,
|
||||||
|
.parity = UART_CFG_PARITY_NONE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
int mb_init_client(void);
|
||||||
|
int mb_read_holding_registers(int node, uint16_t reg_addr, uint16_t *data, size_t len);
|
||||||
|
int mb_read_water_level(double *mb_read_water_level);
|
||||||
|
int mb_read_water_level_mm(int *mb_read_water_level_mm);
|
||||||
|
int mb_read_minimum_mm(int *mb_read_minimum);
|
||||||
|
int mb_read_maximum_mm(int *mb_read_maximum);
|
||||||
|
int mb_write_minimum_mm(int minimum);
|
||||||
|
int mb_write_maximum_mm(int maximum);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
build
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Win32",
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/**"
|
||||||
|
],
|
||||||
|
"defines": [
|
||||||
|
"_DEBUG",
|
||||||
|
"UNICODE",
|
||||||
|
"_UNICODE"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
|
|
||||||
|
list(APPEND BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||||
|
|
||||||
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
project(hello_world)
|
||||||
|
|
||||||
|
target_sources(app PRIVATE src/main.c)
|
||||||
|
target_sources(app PRIVATE ../lib/modbus.c)
|
||||||
|
target_include_directories(app PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../lib
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
&usart1 {
|
||||||
|
status = "okay";
|
||||||
|
current-speed = <9600>;
|
||||||
|
modbus0 {
|
||||||
|
compatible = "zephyr,modbus-serial";
|
||||||
|
status = "okay";
|
||||||
|
// de-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; /* D9 */
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
CONFIG_LOG=y
|
||||||
|
CONFIG_LOG_DEFAULT_LEVEL=3
|
||||||
|
CONFIG_CBPRINTF_FP_SUPPORT=y
|
||||||
|
|
||||||
|
CONFIG_UART_CONSOLE=y # Console on USART1
|
||||||
|
#CONFIG_RTT_CONSOLE=y
|
||||||
|
#CONFIG_USE_SEGGER_RTT=y
|
||||||
|
|
||||||
|
CONFIG_UART_INTERRUPT_DRIVEN=y
|
||||||
|
CONFIG_UART_LINE_CTRL=n
|
||||||
|
|
||||||
|
CONFIG_MODBUS=y
|
||||||
|
CONFIG_MODBUS_ROLE_CLIENT=y
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
/* Testing MODBUS functionality
|
||||||
|
This code initializes a Modbus client, sets minimum and maximum water levels,
|
||||||
|
reads the current water level, and logs the results.
|
||||||
|
You can set the zero point and the 2m point for water level in lines 32 and 38.
|
||||||
|
*/
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/drivers/gpio.h>
|
||||||
|
|
||||||
|
#include "modbus.h"
|
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL);
|
||||||
|
|
||||||
|
/* 1000 msec = 1 sec */
|
||||||
|
#define SLEEP_TIME_MS 1000
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = mb_init_client();
|
||||||
|
if (rc != 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to initialize Modbus client: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
LOG_INF("Modbus client initialized successfully");
|
||||||
|
|
||||||
|
double water_level = 0.0;
|
||||||
|
int water_level_mm, water_level_min_mm, water_level_max_mm = 0;
|
||||||
|
|
||||||
|
rc = mb_write_minimum_mm(42); // Set the zero point for water level
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to write minimum water level: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
rc = mb_write_maximum_mm(2000); // Set the 2m point for water level
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to write maximum water level: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
rc = mb_read_minimum_mm(&water_level_min_mm);
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to read minimum water level: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
rc = mb_read_maximum_mm(&water_level_max_mm);
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to read maximum water level: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
LOG_INF("Water zero point is set to: %dmm", water_level_min_mm);
|
||||||
|
LOG_INF("Water 2m point is set to: %dmm", water_level_max_mm);
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{ /* Read water level */
|
||||||
|
rc = mb_read_water_level(&water_level);
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to read water level: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
rc = mb_read_water_level_mm(&water_level_mm);
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
LOG_ERR("Failed to read water level: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
LOG_INF("Water level: %.3fm", water_level);
|
||||||
|
LOG_INF("Water level: %dmm", water_level_mm);
|
||||||
|
LOG_INF("Modbus test completed successfully");
|
||||||
|
return 0;
|
||||||
|
k_sleep(K_SECONDS(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue