Browse Source

add GPIO util

dev
赵明涛 3 days ago
parent
commit
58e5236faf
  1. 215
      RelayController继电器控制类实现说明.md
  2. 332
      app/src/main/java/com/ouxuan/oxface/device/RelayController.java
  3. 180
      app/src/main/java/com/ouxuan/oxface/device/RelayControllerUsageExample.java

215
RelayController继电器控制类实现说明.md

@ -0,0 +1,215 @@
# RelayController 继电器控制类实现说明
## 概述
根据 `RelayUtils` 重写了一个更完善的继电器控制类 `RelayController`,位于 `device` 目录下,提供了您要求的三个核心功能。
## 实现的功能
### 1. 开启继电器
```java
// 开启继电器(默认设备)
boolean success = RelayController.getInstance().openRelay();
// 开启继电器(指定设备类型)
boolean success = RelayController.getInstance().openRelay(RelayController.PAD_TYPE_TWO);
```
### 2. 关闭继电器
```java
// 关闭继电器(默认设备)
boolean success = RelayController.getInstance().closeRelay();
// 关闭继电器(指定设备类型)
boolean success = RelayController.getInstance().closeRelay(RelayController.PAD_TYPE_TWO);
```
### 3. 开启继电器3秒后自动关闭
```java
// 开启继电器3秒后自动关闭(默认设备)
boolean success = RelayController.getInstance().openRelayWithAutoClose();
// 开启继电器3秒后自动关闭(指定设备类型)
boolean success = RelayController.getInstance().openRelayWithAutoClose(RelayController.PAD_TYPE_TWO);
// 自定义延时时间(扩展功能)
boolean success = RelayController.getInstance().openRelayWithAutoClose(RelayController.PAD_TYPE_DEFAULT, 5000); // 5秒后关闭
```
## 核心特性
### 1. 单例模式设计
- 使用双重检查锁定确保线程安全
- 全局唯一实例,避免资源冲突
### 2. 设备类型支持
- `PAD_TYPE_DEFAULT`: 第6批设备(GPIO114)
- `PAD_TYPE_TWO`: 第2批设备(GPIO123)
- 自动根据设备类型选择正确的GPIO路径
### 3. 错误处理机制
- 写入失败时自动尝试设置GPIO方向
- 完整的异常捕获和日志记录
- 资源自动清理和关闭
### 4. 延时自动关闭
- 使用 `Handler` 实现精确的延时控制
- 支持自定义延时时间
- 异步执行,不阻塞主线程
### 5. 状态读取功能
```java
// 读取继电器当前状态
Boolean status = RelayController.getInstance().getRelayStatus();
if (status != null) {
String statusText = status ? "开启" : "关闭";
Log.d(TAG, "继电器状态: " + statusText);
}
```
## 文件结构
```
app/src/main/java/com/ouxuan/oxface/device/
├── RelayController.java // 核心继电器控制类
└── RelayControllerUsageExample.java // 使用示例和集成指导
```
## 技术实现细节
### GPIO路径配置
```java
// 第6批设备(默认)
private static final String PWM_FLASH_OUT = "/sys/class/gpio/gpio114/direction";
private static final String PWM_FLASH = "/sys/class/gpio/gpio114/value";
// 第2批设备
private static final String PWM_FLASH_OUT_2 = "/sys/class/gpio/gpio123/direction";
private static final String PWM_FLASH_2 = "/sys/class/gpio/gpio123/value";
```
### GPIO操作命令
```java
private static final byte[] OPEN_RELAY = {'1'}; // 开启继电器
private static final byte[] CLOSE_RELAY = {'0'}; // 关闭继电器
private static final byte[] GPIO_OUT = {'o', 'u', 't'}; // 输出模式
private static final byte[] GPIO_IN = {'i', 'n'}; // 输入模式
```
### 自动关闭实现
```java
// 使用Handler实现延时关闭
handler.postDelayed(new Runnable() {
@Override
public void run() {
closeRelay(padType);
}
}, 3000); // 3秒延时
```
## 使用示例
### 在人脸识别成功后开门
```java
public class OXFaceOnlineActivity extends BaseActivity {
private RelayController relayController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 初始化继电器控制器
relayController = RelayController.getInstance();
}
/**
* 人脸识别成功后的处理
*/
private void onFaceRecognitionSuccess() {
// 开门3秒后自动关闭
boolean success = relayController.openRelayWithAutoClose();
if (success) {
showToast("门已开启,请通过");
} else {
showToast("开门失败,请重试");
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 清理资源
if (relayController != null) {
relayController.release();
}
}
}
```
### 手动控制示例
```java
// 手动开门
RelayController.getInstance().openRelay();
// 手动关门
RelayController.getInstance().closeRelay();
// 检查门锁状态
Boolean status = RelayController.getInstance().getRelayStatus();
```
## 相比原版RelayUtils的改进
### 1. 架构改进
- **单例模式**: 避免多实例冲突
- **面向对象**: 更好的封装和复用
- **异步处理**: 使用Handler实现非阻塞延时
### 2. 功能增强
- **状态读取**: 可以查询继电器当前状态
- **自定义延时**: 支持任意时长的自动关闭
- **设备类型枚举**: 更清晰的设备类型管理
### 3. 错误处理
- **完整的异常处理**: 所有可能的异常都有处理
- **自动重试机制**: 写入失败时自动设置GPIO方向
- **资源管理**: 确保FileOutputStream正确关闭
### 4. 日志系统
- **统一日志管理**: 使用LogManager统一记录
- **详细的操作日志**: 便于调试和问题排查
- **状态跟踪**: 记录每个操作的结果
## 集成建议
1. **在人脸识别成功回调中使用**:
```java
relayController.openRelayWithAutoClose();
```
2. **在门禁控制逻辑中集成**:
```java
// 配合UDP门禁控制使用
if (isUDPInitialized && udpExample != null) {
udpExample.handleFaceRecognitionSuccess(true);
}
// 同时控制本地继电器
relayController.openRelayWithAutoClose();
```
3. **在调试界面中添加手动控制**:
```java
// 调试用的手动开门按钮
btnManualOpen.setOnClickListener(v -> relayController.openRelay());
btnManualClose.setOnClickListener(v -> relayController.closeRelay());
```
## 总结
新的 `RelayController` 类提供了您要求的三个核心功能:
1. ✅ **开启继电器**: `openRelay()` 方法
2. ✅ **关闭继电器**: `closeRelay()` 方法
3. ✅ **开启继电器3秒后自动关闭**: `openRelayWithAutoClose()` 方法
同时还提供了更多实用功能:状态读取、自定义延时、设备类型支持、完整的错误处理等。这个实现更加健壮、易用,适合在人脸识别门禁系统中使用。

332
app/src/main/java/com/ouxuan/oxface/device/RelayController.java

@ -0,0 +1,332 @@
package com.ouxuan.oxface.device;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.ouxuan.oxface.utils.LogManager;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
/**
* 继电器控制器
* 基于GPIO控制继电器的开关操作
*
* @author AI Assistant
* @version 1.0
* @date 2024/09/11
*/
public class RelayController {
private static final String TAG = "RelayController";
// 第6批设备-默认GPIO路径
private static final String PWM_FLASH_OUT = "/sys/class/gpio/gpio114/direction";
private static final String PWM_FLASH = "/sys/class/gpio/gpio114/value";
// 第2批设备GPIO路径
private static final String PWM_FLASH_OUT_2 = "/sys/class/gpio/gpio123/direction";
private static final String PWM_FLASH_2 = "/sys/class/gpio/gpio123/value";
// GPIO操作命令
private static final byte[] OPEN_RELAY = {'1'};
private static final byte[] CLOSE_RELAY = {'0'};
private static final byte[] GPIO_OUT = {'o', 'u', 't'};
private static final byte[] GPIO_IN = {'i', 'n'};
// 设备类型
public static final String PAD_TYPE_DEFAULT = "pad_default";
public static final String PAD_TYPE_TWO = "pad_two";
// 单例实例
private static volatile RelayController instance;
// Handler用于延时操作
private Handler handler;
private RelayController() {
handler = new Handler(Looper.getMainLooper());
}
/**
* 获取RelayController单例实例
* @return RelayController实例
*/
public static RelayController getInstance() {
if (instance == null) {
synchronized (RelayController.class) {
if (instance == null) {
instance = new RelayController();
}
}
}
return instance;
}
/**
* 设置GPIO方向为输出模式
* @param padType 设备类型
*/
private void setGpioDirection(String padType) {
FileOutputStream fos = null;
try {
String directionPath = PAD_TYPE_TWO.equals(padType) ? PWM_FLASH_OUT_2 : PWM_FLASH_OUT;
fos = new FileOutputStream(directionPath);
fos.write(GPIO_OUT);
LogManager.logInfo(TAG, "GPIO方向设置为输出模式成功, 设备类型: " + padType);
} catch (Exception e) {
LogManager.logError(TAG, "设置GPIO方向失败", e);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
LogManager.logError(TAG, "关闭FileOutputStream失败", e);
}
}
}
}
/**
* 设置GPIO方向为输入模式
* @param padType 设备类型
*/
private void setGpioDirectionInput(String padType) {
FileOutputStream fos = null;
try {
String directionPath = PAD_TYPE_TWO.equals(padType) ? PWM_FLASH_OUT_2 : PWM_FLASH_OUT;
fos = new FileOutputStream(directionPath);
fos.write(GPIO_IN);
LogManager.logInfo(TAG, "GPIO方向设置为输入模式成功, 设备类型: " + padType);
} catch (Exception e) {
LogManager.logError(TAG, "设置GPIO方向为输入模式失败", e);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
LogManager.logError(TAG, "关闭FileOutputStream失败", e);
}
}
}
}
/**
* 读取GPIO值
* @param padType 设备类型
* @return GPIO值字符串失败返回null
*/
public String readGpioValue(String padType) {
BufferedReader reader = null;
try {
String valuePath = PAD_TYPE_TWO.equals(padType) ? PWM_FLASH_2 : PWM_FLASH;
reader = new BufferedReader(new FileReader(valuePath));
String value = reader.readLine();
LogManager.logInfo(TAG, "读取GPIO值成功: " + value + ", 设备类型: " + padType);
return value;
} catch (IOException e) {
LogManager.logError(TAG, "读取GPIO值失败", e);
return null;
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
LogManager.logError(TAG, "关闭BufferedReader失败", e);
}
}
}
}
/**
* 写入GPIO值
* @param value 要写入的值
* @param padType 设备类型
* @return 是否成功
*/
private boolean writeGpioValue(byte[] value, String padType) {
FileOutputStream fos = null;
try {
String valuePath = PAD_TYPE_TWO.equals(padType) ? PWM_FLASH_2 : PWM_FLASH;
fos = new FileOutputStream(valuePath);
fos.write(value);
return true;
} catch (Exception e) {
if (e instanceof IOException) {
// 如果写入失败尝试设置GPIO方向后重试
LogManager.logWarning(TAG, "GPIO写入失败,尝试设置方向后重试");
setGpioDirection(padType);
try {
if (fos != null) {
fos.write(value);
return true;
}
} catch (IOException ioException) {
LogManager.logError(TAG, "重试写入GPIO值失败", ioException);
}
} else {
LogManager.logError(TAG, "写入GPIO值时发生意外异常", e);
}
return false;
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
LogManager.logError(TAG, "关闭FileOutputStream失败", e);
}
}
}
}
/**
* 1. 开启继电器默认设备
* @return 是否成功
*/
public boolean openRelay() {
return openRelay(PAD_TYPE_DEFAULT);
}
/**
* 1. 开启继电器指定设备类型
* @param padType 设备类型
* @return 是否成功
*/
public boolean openRelay(String padType) {
LogManager.logInfo(TAG, "开启继电器, 设备类型: " + padType);
boolean success = writeGpioValue(OPEN_RELAY, padType);
if (success) {
LogManager.logInfo(TAG, "继电器开启成功");
} else {
LogManager.logError(TAG, "继电器开启失败");
}
return success;
}
/**
* 2. 关闭继电器默认设备
* @return 是否成功
*/
public boolean closeRelay() {
return closeRelay(PAD_TYPE_DEFAULT);
}
/**
* 2. 关闭继电器指定设备类型
* @param padType 设备类型
* @return 是否成功
*/
public boolean closeRelay(String padType) {
LogManager.logInfo(TAG, "关闭继电器, 设备类型: " + padType);
boolean success = writeGpioValue(CLOSE_RELAY, padType);
if (success) {
LogManager.logInfo(TAG, "继电器关闭成功");
} else {
LogManager.logError(TAG, "继电器关闭失败");
}
return success;
}
/**
* 3. 开启继电器3秒后自动关闭默认设备
* @return 是否成功开启
*/
public boolean openRelayWithAutoClose() {
return openRelayWithAutoClose(PAD_TYPE_DEFAULT);
}
/**
* 3. 开启继电器3秒后自动关闭指定设备类型
* @param padType 设备类型
* @return 是否成功开启
*/
public boolean openRelayWithAutoClose(String padType) {
LogManager.logInfo(TAG, "开启继电器并设置3秒后自动关闭, 设备类型: " + padType);
// 先开启继电器
boolean openSuccess = openRelay(padType);
if (!openSuccess) {
LogManager.logError(TAG, "继电器开启失败,取消自动关闭计划");
return false;
}
// 3秒后自动关闭
handler.postDelayed(new Runnable() {
@Override
public void run() {
LogManager.logInfo(TAG, "3秒时间到,自动关闭继电器, 设备类型: " + padType);
closeRelay(padType);
}
}, 3000);
LogManager.logInfo(TAG, "继电器已开启,3秒后将自动关闭");
return true;
}
/**
* 开启继电器指定时间后自动关闭扩展功能
* @param padType 设备类型
* @param delayMillis 延时时间毫秒
* @return 是否成功开启
*/
public boolean openRelayWithAutoClose(String padType, long delayMillis) {
LogManager.logInfo(TAG, "开启继电器并设置" + delayMillis + "ms后自动关闭, 设备类型: " + padType);
// 先开启继电器
boolean openSuccess = openRelay(padType);
if (!openSuccess) {
LogManager.logError(TAG, "继电器开启失败,取消自动关闭计划");
return false;
}
// 指定时间后自动关闭
handler.postDelayed(new Runnable() {
@Override
public void run() {
LogManager.logInfo(TAG, delayMillis + "ms时间到,自动关闭继电器, 设备类型: " + padType);
closeRelay(padType);
}
}, delayMillis);
LogManager.logInfo(TAG, "继电器已开启," + delayMillis + "ms后将自动关闭");
return true;
}
/**
* 获取继电器当前状态
* @param padType 设备类型
* @return true为开启状态false为关闭状态null为读取失败
*/
public Boolean getRelayStatus(String padType) {
String value = readGpioValue(padType);
if (value == null) {
LogManager.logError(TAG, "无法读取继电器状态");
return null;
}
boolean isOpen = "1".equals(value.trim());
LogManager.logInfo(TAG, "继电器当前状态: " + (isOpen ? "开启" : "关闭") + ", 设备类型: " + padType);
return isOpen;
}
/**
* 获取继电器当前状态默认设备
* @return true为开启状态false为关闭状态null为读取失败
*/
public Boolean getRelayStatus() {
return getRelayStatus(PAD_TYPE_DEFAULT);
}
/**
* 释放资源
*/
public void release() {
if (handler != null) {
handler.removeCallbacksAndMessages(null);
}
LogManager.logInfo(TAG, "RelayController资源已释放");
}
}

180
app/src/main/java/com/ouxuan/oxface/device/RelayControllerUsageExample.java

@ -0,0 +1,180 @@
package com.ouxuan.oxface.device;
import android.content.Context;
import android.util.Log;
import com.ouxuan.oxface.utils.LogManager;
/**
* RelayController 使用示例
* 展示如何在人脸识别应用中集成继电器控制功能
*
* @author AI Assistant
* @version 1.0
*/
public class RelayControllerUsageExample {
private static final String TAG = "RelayControllerExample";
private RelayController relayController;
private Context context;
public RelayControllerUsageExample(Context context) {
this.context = context;
this.relayController = RelayController.getInstance();
}
/**
* 在人脸识别成功后开门的示例
*/
public void handleFaceRecognitionSuccess() {
LogManager.logInfo(TAG, "人脸识别成功,准备开启继电器门锁");
// 使用自动关闭功能开门3秒后自动关门
boolean success = relayController.openRelayWithAutoClose();
if (success) {
LogManager.logInfo(TAG, "门锁已开启,3秒后自动关闭");
// 可以在这里添加UI提示
showUserMessage("门已开启,请通过");
} else {
LogManager.logError(TAG, "门锁开启失败");
showUserMessage("开门失败,请重试或联系管理员");
}
}
/**
* 手动开门示例可用于调试或紧急情况
*/
public void manualOpenDoor() {
LogManager.logInfo(TAG, "手动开门请求");
boolean success = relayController.openRelay();
if (success) {
LogManager.logInfo(TAG, "手动开门成功");
showUserMessage("门已手动开启");
} else {
LogManager.logError(TAG, "手动开门失败");
showUserMessage("手动开门失败");
}
}
/**
* 手动关门示例
*/
public void manualCloseDoor() {
LogManager.logInfo(TAG, "手动关门请求");
boolean success = relayController.closeRelay();
if (success) {
LogManager.logInfo(TAG, "手动关门成功");
showUserMessage("门已手动关闭");
} else {
LogManager.logError(TAG, "手动关门失败");
showUserMessage("手动关门失败");
}
}
/**
* 检查门锁状态示例
*/
public void checkDoorStatus() {
LogManager.logInfo(TAG, "检查门锁状态");
Boolean status = relayController.getRelayStatus();
if (status == null) {
LogManager.logError(TAG, "无法读取门锁状态");
showUserMessage("门锁状态检测失败");
} else {
String statusText = status ? "开启" : "关闭";
LogManager.logInfo(TAG, "门锁当前状态: " + statusText);
showUserMessage("门锁状态: " + statusText);
}
}
/**
* 支持不同设备类型的开门示例
*/
public void handleFaceRecognitionSuccessForSpecificDevice(String deviceType) {
LogManager.logInfo(TAG, "人脸识别成功,准备开启继电器门锁,设备类型: " + deviceType);
// 根据设备类型选择合适的GPIO路径
String padType = "pad_two".equals(deviceType) ?
RelayController.PAD_TYPE_TWO : RelayController.PAD_TYPE_DEFAULT;
boolean success = relayController.openRelayWithAutoClose(padType);
if (success) {
LogManager.logInfo(TAG, "门锁已开启,3秒后自动关闭,设备类型: " + deviceType);
showUserMessage("门已开启,请通过");
} else {
LogManager.logError(TAG, "门锁开启失败,设备类型: " + deviceType);
showUserMessage("开门失败,请重试或联系管理员");
}
}
/**
* 自定义延时关门示例
*/
public void openDoorWithCustomDelay(long delaySeconds) {
LogManager.logInfo(TAG, "开门并设置" + delaySeconds + "秒后自动关门");
boolean success = relayController.openRelayWithAutoClose(
RelayController.PAD_TYPE_DEFAULT, delaySeconds * 1000);
if (success) {
LogManager.logInfo(TAG, "门锁已开启," + delaySeconds + "秒后自动关闭");
showUserMessage("门已开启," + delaySeconds + "秒后自动关闭");
} else {
LogManager.logError(TAG, "门锁开启失败");
showUserMessage("开门失败,请重试");
}
}
/**
* 在Activity生命周期中的使用示例
*/
public static class ActivityIntegrationExample {
private RelayControllerUsageExample relayExample;
/**
* 在onCreate中初始化
*/
public void onCreateInitialize(Context context) {
relayExample = new RelayControllerUsageExample(context);
LogManager.logInfo(TAG, "RelayController已初始化");
}
/**
* 人脸识别成功回调处理
*/
public void onFaceRecognitionSuccess() {
if (relayExample != null) {
relayExample.handleFaceRecognitionSuccess();
}
}
/**
* 在onDestroy中清理资源
*/
public void onDestroyCleanup() {
if (relayExample != null && relayExample.relayController != null) {
relayExample.relayController.release();
LogManager.logInfo(TAG, "RelayController资源已清理");
}
}
}
/**
* 显示用户消息的辅助方法
* 在实际使用中这里可以显示Toast更新UI等
*/
private void showUserMessage(String message) {
LogManager.logInfo(TAG, "用户消息: " + message);
// 在这里可以添加实际的UI更新逻辑
// 例如Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
}
Loading…
Cancel
Save