This commit is contained in:
dianjixz
2026-01-27 18:28:38 +08:00
commit 0ce1c2ba3c
16 changed files with 4230 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "SDK"]
path = SDK
url = https://github.com/m5stack/M5Stack_Linux_Libs.git
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 M5Stack
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+369
View File
@@ -0,0 +1,369 @@
# EC Proxy - Embedded Controller Communication System
<p align="center"><img src="https://static-cdn.m5stack.com/resource/public/assets/m5logo2022.svg" alt="M5Stack" width="300" height="300"></p>
<p align="center">
EC Proxy is a high-performance embedded controller communication system that provides hardware abstraction and control for embedded devices. It bridges Modbus RTU communication with modern ZeroMQ-based RPC/PUB-SUB architecture, enabling intuitive hardware control through simple command-line tools or programmatic interfaces.
</p>
## Table of Contents
* [Features](#features)
* [Architecture](#architecture)
* [Components](#components)
* [System Requirements](#system-requirements)
* [Compilation](#compilation)
* [Installation](#installation)
* [Usage](#usage)
* [Hardware Support](#hardware-support)
* [Documentation](#documentation)
* [Contributing](#contributing)
* [License](#license)
## Features
* **Modbus RTU Bridge**: Seamless communication with embedded controllers via serial interface
* **ZeroMQ RPC Service**: Modern, high-performance remote procedure call interface
* **Event Broadcasting**: Real-time PUB/SUB event system for hardware state changes
* **Comprehensive Hardware Control**: Manage power, USB ports, PCIe slots, RGB LEDs, LCD displays, fans, and sensors
* **Network Interface Management**: Automatic IP address synchronization for multiple network interfaces
* **Configuration Persistence**: Flash storage for critical configuration data
* **Multi-threaded Architecture**: Efficient concurrent handling of multiple hardware subsystems
* **Simple CLI Tool**: User-friendly command-line interface for all hardware operations
* **Cross-platform**: Built for embedded Linux devices (ARM64 architecture)
* **Open Source**: Licensed under MIT License
## Architecture
The EC Proxy system consists of two main components that work together to provide hardware abstraction:
```
┌─────────────────┐ Modbus RTU ┌──────────────────┐ ZMQ RPC/PUB ┌─────────────┐
│ Hardware EC │ ◄─────────────────► │ EC Proxy │ ◄──────────────────► │ Client │
│ (STM32/MCU) │ /dev/ttyS3 │ Server │ IPC/TCP Sockets │ (CLI/App) │
└─────────────────┘ 115200-921600bps └──────────────────┘ └─────────────┘
```
### Communication Flow
1. **Hardware Layer**: Embedded controller (EC) exposes Modbus RTU interface over serial port
2. **Proxy Layer**: EC Proxy server translates Modbus commands to ZMQ RPC calls
3. **Client Layer**: CLI tools or applications interact via simple RPC interface
4. **Event Layer**: Hardware events (button presses, status changes) broadcast via PUB/SUB
## Components
### 1. EC Proxy Server (`ec_proxy`)
The proxy server daemon that manages all hardware communication:
- Modbus RTU serial communication
- ZeroMQ RPC service (`ipc:///tmp/rpc.ec_prox`)
- Event publisher (`ipc:///tmp/llm/ec_prox.event.socket`)
- Network interface monitoring
- Automatic fan control
- Button event handling
### 2. EC CLI Tool (`cli`)
Command-line interface for hardware control:
```bash
# Control hardware peripherals
ec_cli device --fan -d 80 # Set fan to 80% speed
ec_cli device --rgb -d 5 # Set RGB LED mode
ec_cli device --board # Get power consumption info
ec_cli device --poweroff -d 1 # Trigger system poweroff
# Execute custom RPC calls
ec_cli exec -l # List all available functions
ec_cli exec -f <function> -d <data> # Call custom function
# Monitor hardware events
ec_cli echo --button # Subscribe to button events
```
See [detailed documentation](./projects/ec_proxy/README.md) for complete command reference.
## System Requirements
### Hardware
- ARM64-based embedded Linux device (AX630C, AX650N, or compatible)
- Serial port for Modbus communication (e.g., `/dev/ttyS3`)
- Embedded controller with Modbus RTU support
### Software
- **Operating System**: Ubuntu 20.04+ (ARM64) or compatible Linux distribution
- **Cross-compilation Toolchain**: aarch64-none-linux-gnu-gcc 10.3+
- **Build Tools**: SCons, Python 3.8+
- **Libraries**: ZeroMQ, libmodbus, fmt, nlohmann-json
## Compilation
### 1. Install Cross-compilation Toolchain
```bash
# Download and install ARM64 cross-compilation toolchain
wget https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/linaro/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
sudo tar Jxvf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz -C /opt
# Add to PATH (add to ~/.bashrc for persistence)
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin:$PATH
```
### 2. Install Build Dependencies
```bash
# Install required packages
sudo apt update
sudo apt install -y python3 python3-pip libffi-dev git
# Install Python build tools
pip3 install parse scons requests kconfiglib
```
### 3. Clone and Build
```bash
# Clone repository
git clone https://github.com/m5stack/Ai_Pyramid_ec_proxy.git
cd Ai_Pyramid_ec_proxy
# Initialize submodules (SDK components)
git submodule update --init --recursive
# Navigate to EC Proxy project
cd projects/ec_proxy
# Clean previous builds (optional)
scons distclean
# Compile (use -j flag for parallel compilation)
scons -j$(nproc)
```
### 4. Build Output
After successful compilation, binaries will be located in:
```
projects/ec_proxy/build/
├── main_ec_proxy/
│ └── ec_proxy # Proxy server executable
└── main_ec_cli/
└── ec_cli # CLI tool executable
```
## Installation
### Deploy to Target Device
```bash
# Copy binaries to target device
scp build/main_ec_proxy/ec_proxy root@<target-ip>:/usr/local/bin/
scp build/main_ec_cli/ec_cli root@<target-ip>:/usr/local/bin/
# On target device, make executable
chmod +x /usr/local/bin/ec_proxy
chmod +x /usr/local/bin/cli
```
### Create Systemd Service (Optional)
Create `/etc/systemd/system/ec-proxy.service`:
```ini
[Unit]
Description=EC Proxy Service
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/ec_proxy
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
```
Enable and start the service:
```bash
sudo systemctl daemon-reload
sudo systemctl enable ec-proxy
sudo systemctl start ec-proxy
```
## Usage
### Start EC Proxy Server
```bash
# Run directly
./ec_proxy
# Or use systemd service
sudo systemctl start ec-proxy
```
### Using the CLI Tool
```bash
# Get hardware status
ec_cli device --board # Power consumption info
ec_cli device --version # EC firmware version
ec_cli device --fanspeed # Current fan speed
# Control peripherals
ec_cli device --fan -d 60 # Set fan PWM to 60%
ec_cli device --rgb -d 1 # Set RGB mode to 1
ec_cli device --lcd_brightness -d 200 # Set LCD brightness
# Network configuration
ec_cli device --ip_eth0 -d "192.168.1.100"
ec_cli device --flash_value # Save to flash memory
# Monitor events
ec_cli echo --button # Watch for button presses
```
## Hardware Support
EC Proxy supports control of the following hardware peripherals:
### Power Management
- Board power control
- External power switch
- USB PD power info
- Power consumption monitoring
- Scheduled power on/off
### Peripherals
- 3× USB downstream ports (with high-power mode)
- 2× PCIe slots
- GL3510 USB hub
- Grove I2C/UART interfaces
### Display & LEDs
- RGB LED array (up to 64 LEDs)
- LCD display with brightness control
- Multiple display modes
### Sensors & Control
- Fan PWM control with RPM monitoring
- Temperature-based auto fan control
- CPU voltage (VDD) adjustment
- Button input detection
### Network
- Ethernet interface management (eth0, eth1)
- WLAN interface support
- Automatic IP address synchronization
See [complete hardware documentation](./projects/ec_proxy/README.md#supported-hardware-peripherals) for detailed register mappings.
## Documentation
- **[EC Proxy Full Documentation](./projects/ec_proxy/README.md)** - Complete guide with all commands and features
- **[SDK Documentation](./SDK/README.md)** - Build system and component information
- **[API Reference](./projects/ec_proxy/README.md#supported-hardware-peripherals)** - Modbus register mapping
## Configuration
### Environment Variables
```bash
# RPC socket path (default: ipc:///tmp/rpc.ec_prox)
export AX650C_EC_PROXY_RPC_SOCKET="ipc:///tmp/rpc.ec_prox"
# PUB socket path (default: ipc:///tmp/llm/ec_prox.event.socket)
export AX650C_EC_PROXY_PUB_SOCKET="ipc:///tmp/llm/ec_prox.event.socket"
# Initial RGB LED mode (default: 1)
export AX650_EC_RGB_MODE=1
# Initial LCD display mode (default: 2)
export AX650_EC_LCD_MODE=2
# Auto power-off timer in seconds (default: 30000)
export AX650_EC_POWER_OFF_TIME=30000
```
### Modbus Configuration
- **Serial Port**: `/dev/ttyS3` (configurable in code)
- **Baud Rate**: 921600 bps (auto-negotiable)
- **Data Format**: 8N1 (8 data bits, no parity, 1 stop bit)
- **Slave Address**: 1
## Troubleshooting
### Common Issues
**1. Compilation Errors**
```bash
# Ensure toolchain is in PATH
which aarch64-none-linux-gnu-gcc
# Update submodules
git submodule update --init --recursive
# Clean and rebuild
scons distclean && scons -j$(nproc)
```
**2. Connection Issues**
```bash
# Check if proxy is running
systemctl status ec-proxy
# Verify serial port
ls -l /dev/ttyS3
# Check socket files
ls -l /tmp/rpc.ec_prox
```
**3. Permission Issues**
```bash
# Add user to dialout group for serial access
sudo usermod -aG dialout $USER
# Set proper permissions
sudo chmod 666 /dev/ttyS3
```
## Contributing
Contributions are welcome! Please:
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
### Reporting Issues
Please report bugs and feature requests via [GitHub Issues](https://github.com/m5stack/Ai_Pyramid_ec_proxy/issues).
## License
This project is licensed under the MIT License - see the LICENSE file for details.
## Acknowledgments
- M5Stack team for hardware platform support
- AXERA for AI acceleration platform
- Open source community for excellent libraries (ZeroMQ, libmodbus, fmt, nlohmann-json)
## Support
- **Documentation**: [M5Stack Docs](https://docs.m5stack.com)
- **Forum**: [M5Stack Community](https://community.m5stack.com)
- **Issues**: [GitHub Issues](https://github.com/m5stack/Ai_Pyramid_ec_proxy/issues)
---
**Note**: This project provides hardware abstraction for embedded controllers. It can be integrated with AI frameworks for advanced applications like voice-controlled hardware or automated system management.
Submodule
+1
Submodule SDK added at afbf2d2382
+11
View File
@@ -0,0 +1,11 @@
from pathlib import Path
import os
import shutil
os.environ['SDK_PATH'] = os.path.normpath(str(Path(os.getcwd())/'..'/'..'/'SDK'))
os.environ['EXT_COMPONENTS_PATH'] = os.path.normpath(str(Path(os.getcwd())/'..'/'..'/'ext_components'))
with open(str(Path(os.getcwd())/'..'/'..'/'SDK'/'tools'/'scons'/'project.py')) as f:
exec(f.read())
+13
View File
@@ -0,0 +1,13 @@
# unix
# CONFIG_TOOLCHAIN_PATH="/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin"
# win
# CONFIG_TOOLCHAIN_PATH="..\\gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf\\bin"
# CONFIG_TOOLCHAIN_PREFIX="arm-linux-gnueabihf-"
# CONFIG_TOOLCHAIN_PATH="/opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin"
CONFIG_TOOLCHAIN_PREFIX="aarch64-linux-gnu-"
CONFIG_CPU_ARCH="armv8"
CONFIG_UTILITIES_ENABLED=y
CONFIG_UTILITIES_BASE64_ENABLED=y
CONFIG_MODBUS_ENABLED=y
CONFIG_UTILITIES_FMT_ENABLED=y
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: MPL-2.0 */
/* This file is deprecated, and all its functionality provided by zmq.h */
/* Note that -Wpedantic compilation requires GCC to avoid using its custom
extensions such as #warning, hence the trick below. Also, pragmas for
warnings or other messages are not standard, not portable, and not all
compilers even have an equivalent concept.
So in the worst case, this include file is treated as silently empty. */
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) \
|| defined(_MSC_VER)
#if defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-Wcpp"
#pragma GCC diagnostic ignored "-Werror"
#pragma GCC diagnostic ignored "-Wall"
#endif
#pragma message( \
"Warning: zmq_utils.h is deprecated. All its functionality is provided by zmq.h.")
#if defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic pop
#endif
#endif
File diff suppressed because it is too large Load Diff
+32
View File
@@ -0,0 +1,32 @@
#ifndef _TBL_YIELD_H_
#define _TBL_YIELD_H_
#define CORO_CTX(name) \
static void * __coro_##name = NULL
#define CORO_BEGIN(name) \
CORO_CTX(name); \
if (__coro_##name == NULL) { \
__coro_##name = &&__coro_start_##name; \
} \
goto *__coro_##name; \
__coro_start_##name:
#define CORO_END(name) \
__coro_##name = NULL
#define CORO_CONCAT(a, b, c) a##b##c
#define CORO_YIELD_IMPL(name, codelin) \
do { \
__coro_##name = &&CORO_CONCAT(__coro_resume_, name, codelin); \
return; \
CORO_CONCAT(__coro_resume_, name, codelin):; \
} while (0)
#define CORO_YIELD(name) CORO_YIELD_IMPL(name, __LINE__)
#endif
+40
View File
@@ -0,0 +1,40 @@
import os
Import('env')
with open(env['PROJECT_TOOL_S']) as f:
exec(f.read())
SRCS = []
INCLUDE = [ADir('../include')]
PRIVATE_INCLUDE = []
REQUIREMENTS = ['pthread', 'utilities', 'modbus_component', 'zmq']
STATIC_LIB = []
DYNAMIC_LIB = []
DEFINITIONS = []
DEFINITIONS_PRIVATE = []
LDFLAGS = []
LINK_SEARCH_PATH = []
STATIC_FILES = []
LINK_SEARCH_PATH += [ADir('../static')]
LDFLAGS+=['-Wl,-rpath=/opt/m5stack/lib', '-Wl,-rpath=/usr/local/m5stack/lib', '-Wl,-rpath=/usr/local/m5stack/lib/gcc-10.3', '-Wl,-rpath=/opt/lib', '-Wl,-rpath=/opt/usr/lib', '-Wl,-rpath=./']
SRCS = [AFile('src/ec_cli_main.cpp')]
env['COMPONENTS'].append({'target':'ec_cli-1.0',
'SRCS':SRCS,
'INCLUDE':INCLUDE,
'PRIVATE_INCLUDE':PRIVATE_INCLUDE,
'REQUIREMENTS':REQUIREMENTS,
'STATIC_LIB':STATIC_LIB,
'DYNAMIC_LIB':DYNAMIC_LIB,
'DEFINITIONS':DEFINITIONS,
'DEFINITIONS_PRIVATE':DEFINITIONS_PRIVATE,
'LDFLAGS':LDFLAGS,
'LINK_SEARCH_PATH':LINK_SEARCH_PATH,
'STATIC_FILES':STATIC_FILES,
'REGISTER':'project'
})
@@ -0,0 +1,383 @@
#include <iostream>
#include <modbus.h>
#include "pzmq.hpp"
#include "cmdline.hpp"
#include <fmt/core.h>
#include <fmt/format.h>
#include <cstdlib>
#include <list>
#include <vector>
#include "json.hpp"
int condition = 1;
const char *rpc_socket_path;
const char *pub_socket_path;
void info_fun(cmdline::parser &a, std::string set_fun, std::string get_fun)
{
if (a.exist("data") || a.exist("DataRaw")) {
if (set_fun.length() == 0) return;
std::string DataRaw = a.get<std::string>("DataRaw");
if (DataRaw.empty()) {
DataRaw = fmt::format("{{ \"data\": {} }}", a.get<std::string>("data"));
}
StackFlows::pzmq Context(rpc_socket_path);
Context.call_rpc_action(set_fun, DataRaw,
[set_fun](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
if (data->size() > 0) {
std::cout << data->string() << std::endl;
} else {
std::cout << "call " << set_fun << " faile!" << std::endl;
}
});
} else {
if (get_fun.length() == 0) {
std::cout << "please set data: -d or -D" << std::endl;
return;
}
StackFlows::pzmq Context(rpc_socket_path);
Context.call_rpc_action(get_fun, "{}",
[get_fun](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
if (data->size() > 0) {
std::cout << data->string() << std::endl;
} else {
std::cout << "call " << get_fun << " faile! " << std::endl;
}
});
}
}
void info_set_fun(cmdline::parser &a, std::string set_fun, std::string default_data)
{
if (set_fun.length() == 0) return;
std::string DataRaw = default_data;
StackFlows::pzmq Context(rpc_socket_path);
Context.call_rpc_action(set_fun, DataRaw,
[set_fun](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
if (data->size() > 0) {
std::cout << data->string() << std::endl;
} else {
std::cout << "call " << set_fun << " faile!" << std::endl;
}
});
}
void lcd_putc_fun(cmdline::parser &a)
{
if (a.exist("data")) {
std::string DataRaw = a.get<std::string>("data");
StackFlows::pzmq Context(rpc_socket_path);
for (auto i = 0; i < DataRaw.length(); i++) {
Context.call_rpc_action("lcd_putc", fmt::format("{{ \"data\": {} }}", (int)DataRaw[i]),
[](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
if (data->size() > 0) {
} else {
std::cout << "call lcd_putc faile!" << std::endl;
}
});
}
}
return;
}
void get_info_fun(cmdline::parser &a, std::string fun)
{
StackFlows::pzmq Context(rpc_socket_path);
Context.call_rpc_action(fun, "{\"data\":\"None\"}",
[fun](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
if (data->size() > 0) {
std::cout << data->string() << std::endl;
} else {
std::cout << "call " << fun << " faile!" << std::endl;
}
});
}
void set_info_fun(cmdline::parser &a, std::string fun)
{
if (a.exist("data") || a.exist("DataRaw")) {
std::string DataRaw = a.get<std::string>("DataRaw");
if (DataRaw.empty()) {
DataRaw = fmt::format("{{ \"data\": {} }}", a.get<std::string>("data"));
}
StackFlows::pzmq Context(rpc_socket_path);
Context.call_rpc_action(fun, DataRaw,
[fun](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
if (data->size() > 0) {
std::cout << data->string() << std::endl;
} else {
std::cout << "call " << fun << "faile!" << std::endl;
}
});
}
}
void fan_speed_fun(cmdline::parser &a)
{
StackFlows::pzmq Context(rpc_socket_path);
Context.call_rpc_action("fan_get_speed", "{}",
[](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
if (data->size() > 0) {
std::cout << data->string() << std::endl;
} else {
std::cout << "call fan_get_speed faile!" << std::endl;
}
});
}
void fan_pwm_fun(cmdline::parser &a)
{
if (a.exist("data") || a.exist("DataRaw")) {
std::string DataRaw = a.get<std::string>("DataRaw");
if (DataRaw.empty()) {
DataRaw = fmt::format("{{ \"data\": {} }}", a.get<std::string>("data"));
}
StackFlows::pzmq Context(rpc_socket_path);
Context.call_rpc_action("fan_set_pwm", DataRaw,
[](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
if (data->size() > 0) {
std::cout << data->string() << std::endl;
} else {
std::cout << "call fan_set_pwm faile!" << std::endl;
}
});
} else {
StackFlows::pzmq Context(rpc_socket_path);
Context.call_rpc_action("fan_get_pwm", "{}",
[](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
if (data->size() > 0) {
std::cout << data->string() << std::endl;
} else {
std::cout << "call fan_get_pwm faile!" << std::endl;
}
});
}
}
void rgb_fun(cmdline::parser &a)
{
if (a.exist("data") || a.exist("DataRaw")) {
std::string DataRaw = a.get<std::string>("DataRaw");
if (DataRaw.empty()) {
DataRaw = fmt::format("{{ \"data\": {} }}", a.get<std::string>("data"));
}
StackFlows::pzmq Context(rpc_socket_path);
Context.call_rpc_action("rgb_set_mode", DataRaw,
[](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
if (data->size() > 0) {
std::cout << data->string() << std::endl;
} else {
std::cout << "call rgb_set_mode faile!" << std::endl;
}
});
} else {
StackFlows::pzmq Context(rpc_socket_path);
Context.call_rpc_action("rgb_get_mode", "{}",
[](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
if (data->size() > 0) {
std::cout << data->string() << std::endl;
} else {
std::cout << "call rgb_get_mode faile!" << std::endl;
}
});
}
}
void exec_fun(cmdline::parser &a)
{
if (a.exist("list")) {
StackFlows::pzmq Context(rpc_socket_path);
std::cout << "list" << std::endl;
Context.call_rpc_action("list_action", "None",
[](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
if (data->size() > 0) {
try {
nlohmann::json _data = nlohmann::json::parse(data->string());
std::cout << _data.dump(4) << std::endl;
} catch (const nlohmann::json::parse_error &e) {
std::cerr << "JSON parsing failed for list_action response. Error: " << e.what()
<< ". Invalid JSON data received: " << data->string() << std::endl;
}
} else {
std::cout << "call list_action faile!" << std::endl;
}
});
return;
}
const std::string fun = a.get<std::string>("fun");
const std::string data = a.get<std::string>("data");
if (fun.length() == 0) {
std::cout << "fun is empty! cli exec -f <fun> -d <data>" << std::endl;
return;
}
StackFlows::pzmq Context(rpc_socket_path);
Context.call_rpc_action(fun, data, [](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
if (data->size() > 0) {
std::cout << "" << data->string() << std::endl;
} else {
std::cout << "call ax650_ec_prox faile!" << std::endl;
}
});
}
void echo_fun(cmdline::parser &a)
{
if (a.exist("button")) {
StackFlows::pzmq Context(pub_socket_path, ZMQ_SUB,
[](StackFlows::pzmq *_pzmq, const std::shared_ptr<StackFlows::pzmq_data> &data) {
std::cout << data->string() << std::endl;
});
while (condition) {
usleep(100 * 1000);
}
exit(0);
}
}
void signalHandler(int signum)
{
condition = 0;
std::exit(0); // 这会调用全局对象的析构函数
}
struct call_fun {
std::string fun;
char flage;
std::string dec;
std::function<void(cmdline::parser &a)> fun_call;
};
int main(int argc, char *argv[])
{
if (argc < 2) {
error_print_exit:
std::cout << R"help(
git [--version] [--help]
<> [<>]
cli
ec cli help device
device 使
ec git help exec
exec
prox git help echo
echo prox
'cli help -a' 'cli help -g'
'cli help <>' 'cli help <>'
)help" << std::endl;
return 0;
}
signal(SIGTERM, signalHandler);
signal(SIGINT, signalHandler);
rpc_socket_path = getenv("AX650C_EC_PROXY_RPC_SOCKET");
if (rpc_socket_path == NULL) {
rpc_socket_path = "ipc:///tmp/rpc.ec_prox";
}
pub_socket_path = getenv("AX650C_EC_PROXY_PUB_SOCKET");
if (pub_socket_path == NULL) {
pub_socket_path = "ipc:///tmp/llm/ec_prox.event.socket";
}
if (std::string(argv[1]) == "device") {
cmdline::parser a;
// clang-format off
std::list<struct call_fun> cmd_list = {
{"rgb", 'r', "Get or set RGB LED display mode", std::bind(&info_fun, std::placeholders::_1, "rgb_set_mode", "rgb_get_mode")},
{"poweron_time", 0, "Get or set the scheduled power-on time", std::bind(&info_fun, std::placeholders::_1, "poweron_set_time", "poweron_get_time")},
{"poweroff_time", 0, "Get or set the scheduled power-off time", std::bind(&info_fun, std::placeholders::_1, "poweroff_set_time", "poweroff_get_time")},
{"rgb_size", 0, "Get or set the number of RGB LEDs in the array", std::bind(&info_fun, std::placeholders::_1, "rgb_set_size", "rgb_get_size")},
{"rgb_get_color", 0, "Get the color value of a specific RGB LED", std::bind(&info_fun, std::placeholders::_1, "rgb_get_color", "")},
{"rgb_set_color", 0, "Set color for a specific RGB LED, example: cli --rgb_set_color -d '{\"rgb_index\":0,\"rgb_color\":255}'", std::bind(&info_fun, std::placeholders::_1, "rgb_set_color", "")},
{"fan", 'f', "Get or set the fan PWM duty cycle", std::bind(&info_fun, std::placeholders::_1, "fan_set_pwm", "fan_get_pwm")},
{"fanspeed", 'F', "Get the current fan rotation speed in RPM", std::bind(&info_fun, std::placeholders::_1, "", "fan_get_speed")},
{"pd_power_info", 0, "Get the USB PD power delivery information", std::bind(&info_fun, std::placeholders::_1, "", "pd_power_info")},
{"board", 'B', "Get the board power consumption information", std::bind(&info_fun, std::placeholders::_1, "", "board_get_power_info")},
{"ip_eth0", 0, "Get or set the IP address for Ethernet interface eth0", std::bind(&info_fun, std::placeholders::_1, "eth0_ip_set", "eth0_ip_get")},
{"ip_eth1", 0, "Get or set the IP address for Ethernet interface eth1", std::bind(&info_fun, std::placeholders::_1, "eth1_ip_set", "eth1_ip_get")},
{"ip_wlan", 0, "Get or set the IP address for wireless LAN interface", std::bind(&info_fun, std::placeholders::_1, "wlan_ip_set", "wlan_ip_get")},
{"lcd", 'l', "Get or set the LCD display mode", std::bind(&info_fun, std::placeholders::_1, "lcd_set_mode", "lcd_get_mode")},
{"lcd_ram", 0, "Set the LCD RAM buffer data directly", std::bind(&info_fun, std::placeholders::_1, "lcd_set_ram", "")},
{"vddcpu", 'c', "Get or set the CPU core voltage (VDD)", std::bind(&info_fun, std::placeholders::_1, "vddcpu_set", "vddcpu_get")},
{"modbus_speed", 0, "Get or set the Modbus communication baud rate", std::bind(&info_fun, std::placeholders::_1, "modbus_set_speed", "modbus_get_speed")},
{"ext_power", 'p', "Control the external power supply output", std::bind(&info_fun, std::placeholders::_1, "ext_power", "")},
{"board_power", 'b', "Control the main board power switch", std::bind(&info_fun, std::placeholders::_1, "board_power", "")},
{"pcie0", 0, "Set the PCIe slot 0 switch on/off state", std::bind(&info_fun, std::placeholders::_1, "pcie0_set_switch", "")},
{"pcie1", 0, "Set the PCIe slot 1 switch on/off state", std::bind(&info_fun, std::placeholders::_1, "pcie1_set_switch", "")},
{"gl3510_reset", 0, "Reset the GL3510 USB hub controller", std::bind(&info_fun, std::placeholders::_1, "gl3510_reset", "")},
{"usbds1_big", 0, "Set the high-power mode for USB downstream port 1", std::bind(&info_fun, std::placeholders::_1, "usbds1_set_big_power", "")},
{"usbds2_big", 0, "Set the high-power mode for USB downstream port 2", std::bind(&info_fun, std::placeholders::_1, "usbds2_set_big_power", "")},
{"usbds1", 0, "Set the switch on/off state for USB downstream port 1", std::bind(&info_fun, std::placeholders::_1, "usbds1_set_switch", "")},
{"usbds2", 0, "Set the switch on/off state for USB downstream port 2", std::bind(&info_fun, std::placeholders::_1, "usbds2_set_switch", "")},
{"usbds3", 0, "Set the switch on/off state for USB downstream port 3", std::bind(&info_fun, std::placeholders::_1, "usbds3_set_switch", "")},
{"hdmi_loop_en", 0, "Switch HDMI OUT port input source between IN and AX8850", std::bind(&info_fun, std::placeholders::_1, "usbds3_set_switch", "")},
{"grove_uart", 0, "Set the switch on/off state for Grove UART interface", std::bind(&info_fun, std::placeholders::_1, "grove_uart_set_switch", "")},
{"grove_iic", 0, "Set the switch on/off state for Grove I2C interface", std::bind(&info_fun, std::placeholders::_1, "grove_iic_set_switch", "")},
{"flash_switch", 0, "Save switch configuration to flash memory (stores coil registers 4-14)", std::bind(&info_set_fun, std::placeholders::_1, "flash_save_switch", "{\"data\":1}")},
{"flash_value", 0, "Save value configuration to flash memory (stores holding registers 1, 2, 11, 12, 14, 16-21)", std::bind(&info_set_fun, std::placeholders::_1, "flash_save_value", "{\"data\":1}")},
{"poweroff", 0, "Trigger the system power-off sequence", std::bind(&info_fun, std::placeholders::_1, "poweroff", "")},
{"lcd_brightness", 0, "Get or set the LCD backlight brightness level", std::bind(&info_fun, std::placeholders::_1, "lcd_set_brightness", "lcd_get_brightness")},
{"lcd_putc", 'P', "Output a character or string to the LCD display", lcd_putc_fun},
{"i2c_set_reg", 0, "Write a value to an I2C device register", std::bind(&info_fun, std::placeholders::_1, "i2c_set_reg", "i2c_set_reg")},
{"i2c_get_reg", 0, "Read a value from an I2C device register", std::bind(&info_fun, std::placeholders::_1, "i2c_get_reg", "i2c_get_reg")},
{"ec_button_head_event", 0, "Get or set the event handler for EC head button press", std::bind(&info_fun, std::placeholders::_1, "ec_button_set_head_event", "ec_button_get_head_event")},
{"soc_button_head_event", 0, "Get or set the event handler for SoC head button press", std::bind(&info_fun, std::placeholders::_1, "soc_button_set_head_event", "soc_button_get_head_event")},
{"ec_button_lcd_event", 0, "Get or set the event handler for EC LCD button press", std::bind(&info_fun, std::placeholders::_1, "ec_button_set_lcd_event", "ec_button_get_lcd_event")},
{"fun_auto", 0, "Get or set the automatic control mode for the proxy service", std::bind(&info_fun, std::placeholders::_1, "fun_set_auto", "fun_get_auto")},
{"ec_modbus_set_bit", 0, "Set a specific bit in the EC Modbus coil register", std::bind(&info_fun, std::placeholders::_1, "ec_modbus_set_bit", "ec_modbus_set_bit")},
{"ec_modbus_get_bit", 0, "Get a specific bit value from the EC Modbus coil register", std::bind(&info_fun, std::placeholders::_1, "ec_modbus_get_bit", "ec_modbus_get_bit")},
{"ec_modbus_input_bits", 0, "Read the EC Modbus discrete input bits status", std::bind(&info_fun, std::placeholders::_1, "ec_modbus_get_input_bits", "ec_modbus_get_input_bits")},
{"ec_modbus_input_registers", 0, "Read the EC Modbus input registers values", std::bind(&info_fun, std::placeholders::_1, "ec_modbus_get_input_registers", "ec_modbus_get_input_registers")},
{"ec_modbus_set_hold_registers", 0, "Write values to the EC Modbus holding registers", std::bind(&info_fun, std::placeholders::_1, "ec_modbus_set_hold_registers", "ec_modbus_set_hold_registers")},
{"ec_modbus_get_hold_registers", 0, "Read values from the EC Modbus holding registers", std::bind(&info_fun, std::placeholders::_1, "ec_modbus_get_hold_registers", "ec_modbus_get_hold_registers")},
{"pcie0_exists", 0, "Check if a PCIe device is present in slot 0", std::bind(&info_fun, std::placeholders::_1, "", "pcie0_exists")},
{"pcie1_exists", 0, "Check if a PCIe device is present in slot 1", std::bind(&info_fun, std::placeholders::_1, "", "pcie1_exists")},
{"V3_3_good", 0, "Check if the 3.3V power rail is within normal operating range", std::bind(&info_fun, std::placeholders::_1, "", "V3_3_good")},
{"V1_8_good", 0, "Check if the 1.8V power rail is within normal operating range", std::bind(&info_fun, std::placeholders::_1, "", "V1_8_good")},
{"head_button", 0, "Get the current state of the head button (pressed/released)", std::bind(&info_fun, std::placeholders::_1, "", "head_button")},
{"lcd_button", 0, "Get the current state of the LCD button (pressed/released)", std::bind(&info_fun, std::placeholders::_1, "", "lcd_button")},
{"version", 0, "Get the EC firmware version information", std::bind(&info_fun, std::placeholders::_1, "", "version")},
};
// clang-format on
for (auto &cmd : cmd_list) {
a.add(cmd.fun, cmd.flage, cmd.dec);
}
a.add<std::string>("data", 'd', "call param", false);
a.add<std::string>("DataRaw", 'D', "call param raw", false);
a.parse_check(argc, argv);
int not_find_index = 1;
for (auto &cmd : cmd_list) {
if (a.exist(cmd.fun)) {
cmd.fun_call(a);
not_find_index = 0;
}
}
if (not_find_index) {
std::cout << a.usage() << std::endl;
}
} else if (std::string(argv[1]) == "exec") {
cmdline::parser a;
a.add("list", 'l', "list call function");
a.add<std::string>("fun", 'f', "call ax650c_ec_proxy function", false);
a.add<std::string>("data", 'd', "call ax650c_ec_proxy function input data", false);
a.parse_check(argc, argv);
exec_fun(a);
} else if (std::string(argv[1]) == "echo") {
cmdline::parser a;
a.add("button", 'b', "button event");
a.parse_check(argc, argv);
echo_fun(a);
std::cout << a.usage() << std::endl;
return 0;
} else {
goto error_print_exit;
}
return 0;
}
@@ -0,0 +1,40 @@
import os
Import('env')
with open(env['PROJECT_TOOL_S']) as f:
exec(f.read())
SRCS = []
INCLUDE = [ADir('../include')]
PRIVATE_INCLUDE = []
REQUIREMENTS = ['pthread', 'utilities', 'modbus_component', 'zmq']
STATIC_LIB = []
DYNAMIC_LIB = []
DEFINITIONS = []
DEFINITIONS_PRIVATE = []
LDFLAGS = []
LINK_SEARCH_PATH = []
STATIC_FILES = []
LINK_SEARCH_PATH += [ADir('../static')]
LDFLAGS+=['-Wl,-rpath=/opt/m5stack/lib', '-Wl,-rpath=/usr/local/m5stack/lib', '-Wl,-rpath=/usr/local/m5stack/lib/gcc-10.3', '-Wl,-rpath=/opt/lib', '-Wl,-rpath=/opt/usr/lib', '-Wl,-rpath=./']
SRCS = [AFile('src/ec_proxy_main.cpp')]
env['COMPONENTS'].append({'target':'ec_proxy-1.0',
'SRCS':SRCS,
'INCLUDE':INCLUDE,
'PRIVATE_INCLUDE':PRIVATE_INCLUDE,
'REQUIREMENTS':REQUIREMENTS,
'STATIC_LIB':STATIC_LIB,
'DYNAMIC_LIB':DYNAMIC_LIB,
'DEFINITIONS':DEFINITIONS,
'DEFINITIONS_PRIVATE':DEFINITIONS_PRIVATE,
'LDFLAGS':LDFLAGS,
'LINK_SEARCH_PATH':LINK_SEARCH_PATH,
'STATIC_FILES':STATIC_FILES,
'REGISTER':'project'
})
File diff suppressed because it is too large Load Diff
Binary file not shown.