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>
|
||||
|
||||
/ {
|
||||
model = "STMicroelectronics STM32F103RB-NUCLEO board";
|
||||
compatible = "st,stm32f103rb-nucleo";
|
||||
model = "Iten engineering Valve Node";
|
||||
compatible = "iten,valve-node", "st,stm32f103rb";
|
||||
|
||||
chosen {
|
||||
zephyr,console = &usart2;
|
||||
zephyr,shell-uart = &usart2;
|
||||
zephyr,console = &usart1;
|
||||
zephyr,shell-uart = &usart1;
|
||||
zephyr,sram = &sram0;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,canbus = &can1;
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
compatible = "gpio-leds";
|
||||
|
||||
green_led_2: led_2 {
|
||||
gpios = <&gpioa 5 GPIO_ACTIVE_HIGH>;
|
||||
gpios = <&gpiob 2 GPIO_ACTIVE_HIGH>;
|
||||
label = "User LD2";
|
||||
};
|
||||
};
|
||||
|
|
@ -81,8 +81,7 @@
|
|||
};
|
||||
|
||||
&clk_hse {
|
||||
hse-bypass;
|
||||
clock-frequency = <DT_FREQ_M(8)>; /* STLink 8MHz clock */
|
||||
clock-frequency = <DT_FREQ_M(8)>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
|
|
@ -104,15 +103,26 @@
|
|||
&usart1 {
|
||||
pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>;
|
||||
pinctrl-names = "default";
|
||||
current-speed = <115200>;
|
||||
status = "okay";
|
||||
current-speed = <115200>;
|
||||
};
|
||||
|
||||
&usart2 {
|
||||
pinctrl-0 = <&usart2_tx_pa2 &usart2_rx_pa3>;
|
||||
current-speed = <9600>;
|
||||
pinctrl-names = "default";
|
||||
current-speed = <115200>;
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
CONFIG_SERIAL=y
|
||||
# enable console
|
||||
CONFIG_CONSOLE=y
|
||||
CONFIG_UART_CONSOLE=y
|
||||
|
||||
# enable GPIO
|
||||
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