Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add modbus.Master #49

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
663 changes: 658 additions & 5 deletions components/comm/include/maix_modbus.hpp

Large diffs are not rendered by default.

474 changes: 467 additions & 7 deletions components/comm/src/maix_modbus.cpp

Large diffs are not rendered by default.

32 changes: 16 additions & 16 deletions examples/maix_modbus/main/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,27 @@ using namespace maix::comm;

int _main(int argc, char* argv[])
{
auto Slave = modbus::Slave(
modbus::Mode::RTU, // mode
"/dev/ttyS0", // serial device
0x00, 10, // coils
0x00, 10, // discrete input
0x00, 10, // input registers
0x00, 10, // holding registers
115200, 1, // serial 115200-8N1, slave id: 1
0, // tcp port
true); // debug ON

// auto Slave = modbus::Slave(
// modbus::Mode::TCP, // mode
// "", // ip, keep empty
// modbus::Mode::RTU, // mode
// "/dev/ttyS0", // serial device
// 0x00, 10, // coils
// 0x00, 10, // discrete input
// 0x00, 10, // input registers
// 0x00, 10, // holding registers
// 0, 1, // serial, ignore
// 5020, // tcp port
// false); // debug OFF
// 115200, 1, // serial 115200-8N1, slave id: 1
// 0, // tcp port
// true); // debug ON

auto Slave = modbus::Slave(
modbus::Mode::TCP, // mode
"", // ip, keep empty
0x00, 10, // coils
0x00, 10, // discrete input
0x00, 10, // input registers
0x00, 10, // holding registers
0, 1, // serial, ignore
5020, // tcp port
false); // debug OFF

std::vector<uint16_t> data{0x22, 0x33, 0x44};
Slave.input_registers(data, 3);
Expand Down
7 changes: 7 additions & 0 deletions examples/maix_modbus_loopback/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
build
dist
.config.mk
.flash.conf.json
data
/CMakeLists.txt
__pycache__
9 changes: 9 additions & 0 deletions examples/maix_modbus_loopback/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
maix_modbus_loopback Project based on MaixCDK
====





This is a project based on MaixCDK, build method please visit [MaixCDK](https://github.com/sipeed/MaixCDK)

10 changes: 10 additions & 0 deletions examples/maix_modbus_loopback/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
id: maix_modbus_loopback
name: maix_modbus_loopback
name[zh]:
version: 1.0.0
#icon: assets/hello.png
author:
desc:
desc[zh]:
files:
# assets: assets
73 changes: 73 additions & 0 deletions examples/maix_modbus_loopback/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
############### Add include ###################
list(APPEND ADD_INCLUDE "include"
)
list(APPEND ADD_PRIVATE_INCLUDE "")
###############################################

############ Add source files #################
# list(APPEND ADD_SRCS "src/main.c"
# "src/test.c"
# )
append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS
# list(REMOVE_ITEM COMPONENT_SRCS "src/test2.c")
# FILE(GLOB_RECURSE EXTRA_SRC "src/*.c")
# FILE(GLOB EXTRA_SRC "src/*.c")
# list(APPEND ADD_SRCS ${EXTRA_SRC})
# aux_source_directory(src ADD_SRCS) # collect all source file in src dir, will set var ADD_SRCS
# append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS
# list(REMOVE_ITEM COMPONENT_SRCS "src/test.c")
# set(ADD_ASM_SRCS "src/asm.S")
# list(APPEND ADD_SRCS ${ADD_ASM_SRCS})
# SET_PROPERTY(SOURCE ${ADD_ASM_SRCS} PROPERTY LANGUAGE C) # set .S ASM file as C language
# SET_SOURCE_FILES_PROPERTIES(${ADD_ASM_SRCS} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp -D BBBBB")
###############################################

###### Add required/dependent components ######
list(APPEND ADD_REQUIREMENTS basic comm peripheral)
###############################################

###### Add link search path for requirements/libs ######
# list(APPEND ADD_LINK_SEARCH_PATH "${CONFIG_TOOLCHAIN_PATH}/lib")
# list(APPEND ADD_REQUIREMENTS pthread m) # add system libs, pthread and math lib for example here
# set (OpenCV_DIR opencv/lib/cmake/opencv4)
# find_package(OpenCV REQUIRED)
###############################################

############ Add static libs ##################
# list(APPEND ADD_STATIC_LIB "lib/libtest.a")
###############################################

#### Add compile option for this component ####
#### Just for this component, won't affect other
#### modules, including component that depend
#### on this component
# list(APPEND ADD_DEFINITIONS_PRIVATE -DAAAAA=1)

#### Add compile option for this component
#### and components depend on this component
# list(APPEND ADD_DEFINITIONS -DAAAAA222=1
# -DAAAAA333=1)
###############################################

############ Add static libs ##################
#### Update parent's variables like CMAKE_C_LINK_FLAGS
# set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group libmaix/libtest.a -ltest2 -Wl,--end-group" PARENT_SCOPE)
###############################################

######### Add files need to download #########
# list(APPEND ADD_FILE_DOWNLOADS "{
# 'url': 'https://*****/abcde.tar.xz',
# 'urls': [], # backup urls, if url failed, will try urls
# 'sites': [], # download site, user can manually download file and put it into dl_path
# 'sha256sum': '',
# 'filename': 'abcde.tar.xz',
# 'path': 'toolchains/xxxxx',
# }"
# )
#
# then extracted file in ${DL_EXTRACTED_PATH}/toolchains/xxxxx,
# you can directly use then, for example use it in add_custom_command
##############################################

# register component, DYNAMIC or SHARED flags will make component compiled to dynamic(shared) lib
register_component()
Empty file.
3 changes: 3 additions & 0 deletions examples/maix_modbus_loopback/main/include/main.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once


222 changes: 222 additions & 0 deletions examples/maix_modbus_loopback/main/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@

#include "maix_basic.hpp"
#include "main.h"
#include "maix_modbus.hpp"
#include "maix_pinmap.hpp"
#include <iomanip> // std::setw and std::setfill
#include <sstream> // std::stringstream
#include <thread> // std::thread

using namespace maix;
using namespace maix::comm;

/** MODE: RTU/TCP
* NOTE: RTU UART0(Slave) <--> UART1(Master)
*/
constexpr modbus::Mode MODE = modbus::Mode::RTU;
// constexpr modbus::Mode MODE = modbus::Mode::TCP;

/* slave cfg */
constexpr uint8_t REGISTERS_START_ADDRESS = 0x00;
constexpr uint32_t REGISTERS_NUMBER = 10;

/* rtu cfg */
constexpr uint32_t RTU_SLAVE_ID = 1;
constexpr int RTU_BAUDRATE = 115200;

/* tcp cfg */
constexpr int TCP_PORT = 5020;

int master_rtu_thread()
{
if(peripheral::pinmap::set_pin_function("A19", "UART1_TX") != err::Err::ERR_NONE) {
log::error("init uart1 failed!");
return -1;
}
if (peripheral::pinmap::set_pin_function("A18", "UART1_RX") != err::Err::ERR_NONE) {
log::error("init uart failed!");
return -1;
}

// modbus::set_master_debug(true);

modbus::MasterRTU master("/dev/ttyS1", RTU_BAUDRATE);

while (!app::need_exit()) {
// log::info("master thread running...");

std::vector<uint16_t> rt = master.read_holding_registers(RTU_SLAVE_ID, REGISTERS_NUMBER, REGISTERS_START_ADDRESS, 2000);
if (rt.empty())
continue;

std::stringstream ss;
ss << "Master read: ";
for (const auto& value : rt) {
ss << "0x" << std::hex << std::setw(4) << std::setfill('0') << value << " ";
}
log::info(ss.str().c_str());
time::sleep(1);
}

return 0;
}

int slave_rtu_thread()
{
modbus::Registers cfg;
cfg.coils.start_address = REGISTERS_START_ADDRESS;
cfg.coils.size = REGISTERS_NUMBER;
cfg.discrete_inputs.start_address = REGISTERS_START_ADDRESS;
cfg.discrete_inputs.size = REGISTERS_NUMBER;
cfg.holding_registers.start_address = REGISTERS_START_ADDRESS;
cfg.holding_registers.size = REGISTERS_NUMBER;
cfg.input_registers.start_address = REGISTERS_START_ADDRESS;
cfg.input_registers.size = REGISTERS_NUMBER;

modbus::Slave slave(
modbus::Mode::RTU,
"/dev/ttyS0",
cfg,
RTU_BAUDRATE,
RTU_SLAVE_ID
);

while (!app::need_exit()) {
// log::info("slave thread running...");

auto rt = slave.receive(2000);
if (rt != err::Err::ERR_NONE)
continue;

modbus::RequestType type = slave.request_type();
if (type == modbus::RequestType::READ_HOLDING_REGISTERS) {
std::stringstream ss;
ss << "Slave update: ";
for (int i = 0; i < slave->nb_registers; ++i) {
slave->tab_registers[i]++;
ss << "0x" << std::hex << std::setw(4) << std::setfill('0') << slave->tab_registers[i] << " ";
}
log::info(ss.str().c_str());
}

slave.reply();
}

return 0;
}

void rtu_loopback()
{
std::thread slave_th(slave_rtu_thread);
time::sleep_ms(500);
std::thread master_th(master_rtu_thread);

slave_th.join();
master_th.join();
}

int slave_tcp_thread()
{
modbus::Registers cfg;
cfg.coils.start_address = REGISTERS_START_ADDRESS;
cfg.coils.size = REGISTERS_NUMBER;
cfg.discrete_inputs.start_address = REGISTERS_START_ADDRESS;
cfg.discrete_inputs.size = REGISTERS_NUMBER;
cfg.holding_registers.start_address = REGISTERS_START_ADDRESS;
cfg.holding_registers.size = REGISTERS_NUMBER;
cfg.input_registers.start_address = REGISTERS_START_ADDRESS;
cfg.input_registers.size = REGISTERS_NUMBER;

modbus::Slave slave(
modbus::Mode::TCP,
"",
cfg,
0,
0,
TCP_PORT
);

while (!app::need_exit()) {
// log::info("slave thread running...");

auto rt = slave.receive(2000);
if (rt != err::Err::ERR_NONE)
continue;

modbus::RequestType type = slave.request_type();
if (type == modbus::RequestType::READ_HOLDING_REGISTERS) {
std::stringstream ss;
ss << "Slave update: ";
for (int i = 0; i < slave->nb_registers; ++i) {
slave->tab_registers[i]++;
ss << "0x" << std::hex << std::setw(4) << std::setfill('0') << slave->tab_registers[i] << " ";
}
log::info(ss.str().c_str());
}

slave.reply();
}

return 0;
}

int master_tcp_thread()
{
// modbus::set_master_debug(true);

modbus::MasterTCP master(TCP_PORT);

while (!app::need_exit()) {
// log::info("master thread running...");

std::vector<uint16_t> rt = master.read_holding_registers("127.0.0.1", REGISTERS_NUMBER, REGISTERS_START_ADDRESS, 2000);
if (rt.empty())
continue;

std::stringstream ss;
ss << "Master read: ";
for (const auto& value : rt) {
ss << "0x" << std::hex << std::setw(4) << std::setfill('0') << value << " ";
}
log::info(ss.str().c_str());
time::sleep(1);
}

return 0;
}


void tcp_loopback()
{
std::thread slave_th(slave_tcp_thread);
time::sleep_ms(500);
std::thread master_th(master_tcp_thread);

slave_th.join();
master_th.join();
}

int _main(int argc, char* argv[])
{

if constexpr (MODE == modbus::Mode::RTU) {
rtu_loopback();
} else {
tcp_loopback();
}

return 0;
}

int main(int argc, char* argv[])
{
// Catch signal and process
sys::register_default_signal_handle();

// Use CATCH_EXCEPTION_RUN_RETURN to catch exception,
// if we don't catch exception, when program throw exception, the objects will not be destructed.
// So we catch exception here to let resources be released(call objects' destructor) before exit.
CATCH_EXCEPTION_RUN_RETURN(_main, -1, argc, argv);
}