// CANBus.java
package com.appinventor.ai_canbus;
import com.google.appinventor.components.annotations.*;
import com.google.appinventor.components.common.*;
import com.google.appinventor.components.runtime.*;
import android.util.Log;
@DesignerComponent(
version = 1,
description = "CAN Bus Interface Extension (SocketCAN)",
category = ComponentCategory.EXTENSION,
nonVisible = true,
iconName = "images/extension.png"
)
@SimpleObject(external = true)
public class CANBus extends AndroidNonvisibleComponent {
private static final String TAG = "CANBus";
// CAN参数
private int baudRate = 500000;
private String devicePath = "/dev/vcan0";
// SocketCAN文件描述符
private int socketFd = -1;
public CANBus(ComponentContainer container) {
super(container.$form());
}
@SimpleProperty
public void BaudRate(int rate) {
this.baudRate = rate;
}
@SimpleProperty
public int BaudRate() {
return this.baudRate;
}
@SimpleProperty
public void DevicePath(String path) {
this.devicePath = path;
}
@SimpleProperty
public String DevicePath() {
return this.devicePath;
}
@SimpleFunction(description = "Open CAN interface")
public void Open() {
new Thread(() -> {
try {
socketFd = openSocketCAN(devicePath, baudRate);
if (socketFd >= 0) {
form.runOnUiThread(() -> DeviceOpenedSuccess());
startReceiveThread();
} else {
form.runOnUiThread(() -> DeviceOpenedFailure("Failed to open CAN device"));
}
} catch (Exception e) {
Log.e(TAG, "Open error", e);
form.runOnUiThread(() -> DeviceOpenedFailure(e.getMessage()));
}
}).start();
}
private int openSocketCAN(String device, int baudRate) {
// 创建socketcan套接字
int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (sock < 0) return -1;
// 设置波特率
struct can_bittiming bt = new struct_can_bittiming();
bt.bitrate = baudRate;
bt.sample_point = 875;
setupBittiming(sock, bt);
// 绑定接口
struct sockaddr_can addr = new struct_sockaddr_can();
addr.can_ifindex = getInterfaceIndex(device);
if (bind(sock, addr, sizeof(struct_sockaddr_can)) < 0) {
close(sock);
return -1;
}
return sock;
}
@SimpleFunction(description = "Send CAN frame")
public void Send(final int canId, final List<byte> data) {
new Thread(() -> {
try {
if (socketFd < 0) {
form.runOnUiThread(() -> SendFailure("CAN not opened"));
return;
}
byte[] frame = buildCANFrame(canId, false, data);
int result = write(socketFd, frame, frame.length);
if (result > 0) {
form.runOnUiThread(() -> SendSuccess(canId));
} else {
form.runOnUiThread(() -> SendFailure("Write failed"));
}
} catch (Exception e) {
form.runOnUiThread(() -> SendFailure(e.getMessage()));
}
}).start();
}
private byte[] buildCANFrame(int canId, boolean extended, List<byte> data) {
// CAN frame structure for SocketCAN
// struct can_frame {
// can_id_t can_id; // 32-bit
// __u8 dlc; // data length code
// __u8 data[8]; // data
// }
ByteBuffer buffer = ByteBuffer.allocate(16);
buffer.order(ByteOrder.LITTLE_ENDIAN);
int id = canId;
if (extended) id |= CAN_EFF_FLAG;
id |= CAN_RTR_FLAG; // Data frame (not RTR)
buffer.putInt(id);
buffer.put((byte)data.size());
for (int i = 0; i < 8; i++) {
if (i < data.size()) {
buffer.put(data.get(i));
} else {
buffer.put((byte)0);
}
}
return buffer.array();
}
private void startReceiveThread() {
new Thread(() -> {
byte[] buffer = new byte[16];
while (socketFd >= 0) {
try {
int nbytes = read(socketFd, buffer, buffer.length);
if (nbytes > 0) {
ByteBuffer bb = ByteBuffer.wrap(buffer);
bb.order(ByteOrder.LITTLE_ENDIAN);
int canId = bb.getInt() & 0x1FFFFFFF;
boolean extended = (buffer[0] & 0x80) != 0;
int dlc = bb.get() & 0x0F;
List<Byte> data = new ArrayList<>();
for (int i = 0; i < dlc; i++) {
data.add(bb.get());
}
final int fCanId = canId;
final List<Byte> fData = data;
form.runOnUiThread(() -> FrameReceived(fCanId, fData));
}
} catch (Exception e) {
Log.e(TAG, "Receive error", e);
break;
}
}
}).start();
}
@SimpleFunction(description = "Send OBD-II request")
public void SendOBDRequest(final int mode, final int pid) {
// 构建OBD请求帧
// 0x7DF是OBD广播地址
List<Byte> data = new ArrayList<>();
data.add((byte)0x02); // 长度
data.add((byte)mode); // 模式
data.add((byte)pid); // PID
data.add((byte)0x00); // 填充
data.add((byte)0x00);
data.add((byte)0x00);
data.add((byte)0x00);
data.add((byte)0x00);
Send(0x7DF, data);
}
@SimpleFunction(description = "Close CAN interface")
public void Close() {
try {
if (socketFd >= 0) {
close(socketFd);
socketFd = -1;
}
} catch (Exception e) {
Log.e(TAG, "Close error", e);
}
}
// Events
@SimpleEvent
public void DeviceOpenedSuccess() {}
@SimpleEvent
public void DeviceOpenedFailure(String error) {}
@SimpleEvent
public void FrameReceived(int canId, List<Byte> data) {}
@SimpleEvent
public void SendSuccess(int canId) {}
@SimpleEvent
public void SendFailure(String error) {}
@SimpleEvent
public void BusError(String error) {}
}