电压读取
This commit is contained in:
parent
f6312ffbce
commit
a4d926713f
|
|
@ -68,6 +68,9 @@ add_library(edge_proxy_lib STATIC
|
|||
|
||||
#SQL
|
||||
src/dataStorage/data_storage.cc
|
||||
|
||||
#tts
|
||||
src/tts/piper_tts_interface.cc
|
||||
)
|
||||
|
||||
target_include_directories(edge_proxy_lib PUBLIC
|
||||
|
|
@ -82,7 +85,7 @@ target_link_libraries(edge_proxy_lib PRIVATE
|
|||
SQLite::SQLite3
|
||||
pthread
|
||||
nlohmann_json
|
||||
rknn_api
|
||||
# rknn_api
|
||||
rknnrt
|
||||
)
|
||||
|
||||
|
|
@ -103,15 +106,15 @@ target_link_libraries(edge_proxy PRIVATE
|
|||
# =================#================================================
|
||||
# 测试目标
|
||||
# =================================================================
|
||||
# add_executable(test
|
||||
# src/test.cc
|
||||
# src/piper_tts_interface.cc
|
||||
add_executable(test
|
||||
src/test.cc
|
||||
src/systemMonitor/iio_sensor.cc
|
||||
|
||||
# )
|
||||
)
|
||||
|
||||
# target_link_libraries(test PRIVATE
|
||||
# edge_proxy_lib
|
||||
# )
|
||||
target_link_libraries(test PRIVATE
|
||||
edge_proxy_lib
|
||||
)
|
||||
|
||||
|
||||
# 1. 定义新的可执行文件 (我们将把代码放在 src/streamer/ 目录下)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ services:
|
|||
- /dev/rga:/dev/rga
|
||||
- /dev/dri:/dev/dri
|
||||
|
||||
- source: /sys/bus/iio/devices/iio:device0
|
||||
target: /sys/bus/iio/devices/iio:device0
|
||||
group_add:
|
||||
- "20"
|
||||
- "44"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
#include "iio_sensor.h" // 必须包含自己的头文件
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
// --- PIMPL 模式的具体实现 ---
|
||||
// 这里我们只有一个空的实现,因为我们不再在类内部保存文件流
|
||||
// 但保留这个结构可以让我们在不改变.h文件的情况下未来扩展功能
|
||||
struct IioSensor::Impl {
|
||||
// 当前实现为空
|
||||
// std::ifstream voltageFile; // 这种方式已被抛弃
|
||||
};
|
||||
|
||||
// --- 构造函数 ---
|
||||
// 现在只负责构建路径和检查文件是否存在,不打开文件
|
||||
IioSensor::IioSensor(const std::string& devicePath, const std::string& channelName)
|
||||
: devicePath_(devicePath), channelName_(channelName) {
|
||||
|
||||
// 使用 std::filesystem 安全地拼接路径
|
||||
std::filesystem::path fullFilePath = std::filesystem::path(devicePath_) / channelName_;
|
||||
fullFilePath_ = fullFilePath.string();
|
||||
|
||||
// 检查文件是否存在,这是一个非常好的防御性编程实践
|
||||
if (!std::filesystem::exists(fullFilePath_)) {
|
||||
throw IioSensorError("IIO Channel Error: File not found at " + fullFilePath_ + "\n"
|
||||
"Please check the path (" + devicePath_ + ") and ensure the sensor is connected and configured.");
|
||||
}
|
||||
// 初始化 PIMPL,即使为空,也需要创建
|
||||
pImpl_ = std::make_unique<Impl>();
|
||||
}
|
||||
|
||||
// --- 析构函数 ---
|
||||
// std::unique_ptr 会自动为我们释放 Impl 对象的内存,无需手动删除
|
||||
IioSensor::~IioSensor() = default;
|
||||
|
||||
|
||||
// --- 公共接口实现 ---
|
||||
|
||||
int64_t IioSensor::readRawValue() {
|
||||
// --- 1. 每次读取都创建一个新的文件流对象 ---
|
||||
// 使用RAII管理这个临时文件流,函数结束时它会自动关闭
|
||||
std::ifstream voltageFile(fullFilePath_, std::ios::in);
|
||||
|
||||
// --- 2. 检查文件是否成功打开 ---
|
||||
if (!voltageFile.is_open()) {
|
||||
throw IioSensorError("IIO Channel Error: Failed to open file " + fullFilePath_ + "\n"
|
||||
"Check permissions (the process might need to be in the 'iio' group).");
|
||||
}
|
||||
|
||||
// --- 3. 读取文件内容 ---
|
||||
std::string voltageValueStr;
|
||||
// std::getline 从文件流中读取一行
|
||||
if (!std::getline(voltageFile, voltageValueStr)) {
|
||||
// 如果读取操作失败(例如文件为空,但根据IIO模型,这里更可能是其他问题)
|
||||
throw IioSensorError("IIO Channel Error: Failed to read data from file " + fullFilePath_);
|
||||
}
|
||||
|
||||
// --- 4. 字符串到数字的转换 ---
|
||||
try {
|
||||
// std::stoll 是 C++11 引入的,安全地将字符串转换为 long long
|
||||
// 对于ADC值,long long (int64_t) 通常更合适
|
||||
int64_t rawValue = std::stoll(voltageValueStr);
|
||||
return rawValue;
|
||||
|
||||
} catch (const std::invalid_argument& e) {
|
||||
// 如果字符串不是一个有效的数字
|
||||
throw IioSensorError("IIO Channel Error: Invalid data format in file " + fullFilePath_ +
|
||||
". Content: '" + voltageValueStr + "'");
|
||||
} catch (const std::out_of_range& e) {
|
||||
// 如果数字超出 long long 的表示范围
|
||||
throw IioSensorError("IIO Channel Error: Number out of range in file " + fullFilePath_);
|
||||
}
|
||||
}
|
||||
|
||||
double IioSensor::readVoltage(double referenceVoltage, int64_t adcResolution, double gain) {
|
||||
// --- 1. 调用 readRawValue() 来复用错误处理和读取逻辑 ---
|
||||
// 这符合DRY (Don't Repeat Yourself) 原则
|
||||
int64_t rawValue = readRawValue();
|
||||
|
||||
// --- 2. 进行运行时参数检查 ---
|
||||
// static_assert 不适用于运行时参数,我们使用 if 语句
|
||||
if (adcResolution <= 0) {
|
||||
throw std::invalid_argument("ADC Resolution must be a positive number.");
|
||||
}
|
||||
if (referenceVoltage <= 0) {
|
||||
throw std::invalid_argument("Reference Voltage must be a positive number.");
|
||||
}
|
||||
if (gain == 0.0) {
|
||||
throw std::invalid_argument("Gain cannot be zero.");
|
||||
}
|
||||
|
||||
// --- 3. 计算电压并返回 ---
|
||||
// 注意:这里的转换是基于原始值的,具体物理含义需要根据你的数据手册确定
|
||||
// (rawValue / Resolution) * ReferenceVoltage * Gain
|
||||
return static_cast<double>(rawValue) / static_cast<double>(adcResolution) * referenceVoltage * gain;
|
||||
}
|
||||
|
||||
// int main() {
|
||||
// // --- 配置参数 ---
|
||||
// // !!! 修改这里以匹配你的设备和通道 !!!
|
||||
// const std::string iio_device_path = "/sys/bus/iio/devices/iio:device0";
|
||||
// const std::string iio_channel_name = "in_voltage2_raw"; // 例如,连接到 IN2 通道
|
||||
|
||||
// const double adc_reference_voltage = 1.8; // ADC参考电压
|
||||
// const int64_t adc_resolution = 4096; // ADC分辨率 (e.g., 2^12 for 12-bit)
|
||||
// const double signal_gain = 11.0; // 信号通路的增益
|
||||
// std::cout << "--- Starting IIO Sensor Reader (Version 2) ---" << std::endl;
|
||||
// std::cout << "Reading from device: " << iio_device_path << std::endl;
|
||||
// std::cout << "Reading channel: " << iio_channel_name << std::endl;
|
||||
// std::cout << "-----------------------------------------------" << std::endl;
|
||||
|
||||
// // 注意:传感器对象在 try 块外创建,如果构造失败(找不到文件),会立即被捕获
|
||||
// IioSensor sensor(iio_device_path, iio_channel_name);
|
||||
// while (true) {
|
||||
// try {
|
||||
// // 封装后的调用方式非常简洁!
|
||||
// // 每次调用 readVoltage,内部都会重新打开文件读取一次
|
||||
// double actual_voltage = sensor.readVoltage(adc_reference_voltage, adc_resolution, signal_gain);
|
||||
// // 输出结果
|
||||
// std::cout << "Actual Voltage: " << actual_voltage << " V" << std::endl;
|
||||
// } catch (const IioSensorError& e) {
|
||||
// // 如果单次读取失败(例如文件内容格式错误、无权限等),打印错误但继续循环
|
||||
// std::cerr << "[!] Read Error: " << e.what() << std::endl;
|
||||
// }
|
||||
|
||||
// // 每秒钟读取一次
|
||||
// std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
// }
|
||||
// return 0;
|
||||
// }
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
#ifndef IIO_SENSOR_H
|
||||
#define IIO_SENSOR_H
|
||||
|
||||
#include <string>
|
||||
#include <memory> // For std::unique_ptr
|
||||
#include <stdexcept>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
// 自定义异常类,专门用于处理IIO相关的错误
|
||||
// 继承自 std::runtime_error,这样我们就可以用 catch(std::exception&) 来捕获它
|
||||
class IioSensorError : public std::runtime_error {
|
||||
public:
|
||||
// 使用父类的构造函数来初始化错误信息
|
||||
explicit IioSensorError(const std::string& message) : std::runtime_error(message) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 一个用于从 Linux Industrial I/O (IIO) 子系统读取原始ADC值的类。
|
||||
*
|
||||
* 这个类封装了与IIO设备文件交互的细节,包括路径构建、文件读取、错误处理
|
||||
* 和数据类型转换。在这种实现中,每次读取操作都会重新打开和关闭设备文件,
|
||||
* 这对于 IIO “一次性快照”式的文件模型是最健壮和最简单的方式。
|
||||
*/
|
||||
class IioSensor {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数,初始化IIO传感器读取器。
|
||||
*
|
||||
* 它会验证设备路径和通道是否存在,但**不会**在此时打开文件。
|
||||
* 文件将在每次 readRawValue() 或 readVoltage() 调用时临时打开。
|
||||
*
|
||||
* @param devicePath IIO设备的绝对路径 (e.g., "/sys/bus/iio/devices/iio:device0").
|
||||
* @param channelName 要读取的通道名 (e.g., "in_voltage2_raw").
|
||||
* @throws IioSensorError 如果设备路径或通道路径不存在。
|
||||
*/
|
||||
IioSensor(const std::string& devicePath, const std::string& channelName);
|
||||
|
||||
/**
|
||||
* @brief 析构函数,当前为空,因为资源由RAII在每次读取时管理。
|
||||
*/
|
||||
~IioSensor();
|
||||
|
||||
/**
|
||||
* @brief 从IIO通道读取一次原始ADC值。
|
||||
*
|
||||
* 每次调用此函数都会打开、读取并关闭文件。
|
||||
*
|
||||
* @return int64_t 读取到的原始整数值。
|
||||
* @throws IioSensorError 如果文件不存在、无法打开、读取失败或值无效。
|
||||
*/
|
||||
int64_t readRawValue();
|
||||
|
||||
/**
|
||||
* @brief 从IIO通道读取一次原始ADC值并转换为电压。
|
||||
*
|
||||
* 内部会调用 `readRawValue()` 来获取原始数据。
|
||||
*
|
||||
* @param referenceVoltage ADC的参考电压,单位为伏特 (e.g., 3.3, 1.8).
|
||||
* @param adcResolution ADC的分辨率 (e.g., 4096 for 12-bit, 65536 for 16-bit).
|
||||
* @param gain (可选) 信号通路的总增益 (e.g., 11 for 11x gain).
|
||||
* @return double 计算出的电压值。
|
||||
* @throws IioSensorError 如果在读取值的过程中发生任何错误,或参数无效。
|
||||
*/
|
||||
double readVoltage(double referenceVoltage, int64_t adcResolution, double gain = 1.0);
|
||||
|
||||
private:
|
||||
// --- 私有成员变量 ---
|
||||
std::string devicePath_; // IIO设备路径
|
||||
std::string channelName_; // IIO通道名称
|
||||
std::string fullFilePath_; // 完整的IIO通道文件路径
|
||||
|
||||
// PIMPL 结构体,用来隐藏实现细节
|
||||
// 虽然我们不再在构造函数里打开文件,但保留它可以将来轻松扩展
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl_;
|
||||
};
|
||||
|
||||
#endif // IIO_SENSOR_H
|
||||
61
src/test.cc
61
src/test.cc
|
|
@ -1,16 +1,53 @@
|
|||
#include "piper_tts_interface.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
// #include "piper_tts_interface.h"
|
||||
// #include <string>
|
||||
// #include <iostream>
|
||||
|
||||
// int main() {
|
||||
// PiperTTSInterface tts_processor;
|
||||
// // 示例 1: 只提供文本和输出文件名
|
||||
// std::string text1 = "你好,世界。温度传感器出现故障。只需输入文本即可。";
|
||||
// std::string file1 = "hello_world.wav";
|
||||
// if (tts_processor.text_to_speech(text1, file1)) {
|
||||
// std::cout << "Audio for '" << text1 << "' generated successfully!" << std::endl;
|
||||
// } else {
|
||||
// std::cerr << "Failed to generate audio for '" << text1 << "'." << std::endl;
|
||||
// }
|
||||
// return 0;
|
||||
// }
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include "systemMonitor/iio_sensor.h" // 包含我们刚刚创建的头文件
|
||||
int main() {
|
||||
PiperTTSInterface tts_processor;
|
||||
// 示例 1: 只提供文本和输出文件名
|
||||
std::string text1 = "你好,世界。温度传感器出现故障。只需输入文本即可。";
|
||||
std::string file1 = "hello_world.wav";
|
||||
if (tts_processor.text_to_speech(text1, file1)) {
|
||||
std::cout << "Audio for '" << text1 << "' generated successfully!" << std::endl;
|
||||
} else {
|
||||
std::cerr << "Failed to generate audio for '" << text1 << "'." << std::endl;
|
||||
// --- 配置参数 ---
|
||||
// !!! 修改这里以匹配你的设备和通道 !!!
|
||||
const std::string iio_device_path = "/sys/bus/iio/devices/iio:device0";
|
||||
const std::string iio_channel_name = "in_voltage2_raw"; // 例如,连接到 IN2 通道
|
||||
|
||||
const double adc_reference_voltage = 1.8; // ADC参考电压
|
||||
const int64_t adc_resolution = 4096; // ADC分辨率 (e.g., 2^12 for 12-bit)
|
||||
const double signal_gain = 11.0; // 信号通路的增益
|
||||
std::cout << "--- Starting IIO Sensor Reader (Version 2) ---" << std::endl;
|
||||
std::cout << "Reading from device: " << iio_device_path << std::endl;
|
||||
std::cout << "Reading channel: " << iio_channel_name << std::endl;
|
||||
std::cout << "-----------------------------------------------" << std::endl;
|
||||
|
||||
// 注意:传感器对象在 try 块外创建,如果构造失败(找不到文件),会立即被捕获
|
||||
IioSensor sensor(iio_device_path, iio_channel_name);
|
||||
while (true) {
|
||||
try {
|
||||
// 封装后的调用方式非常简洁!
|
||||
// 每次调用 readVoltage,内部都会重新打开文件读取一次
|
||||
double actual_voltage = sensor.readVoltage(adc_reference_voltage, adc_resolution, signal_gain);
|
||||
// 输出结果
|
||||
std::cout << "Actual Voltage: " << actual_voltage << " V" << std::endl;
|
||||
} catch (const IioSensorError& e) {
|
||||
// 如果单次读取失败(例如文件内容格式错误、无权限等),打印错误但继续循环
|
||||
std::cerr << "[!] Read Error: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
// 每秒钟读取一次
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue