Android部分和云平台数据流转
Android部分需要一定的基础,至少你得有Android Studio 这个软件吧。
如有需要可以联系我,简单的修改和Android软件可以帮代做。
可自行下载Android,源码可全部给出。
创建工程+配置工程
首先创建一个空的工程。
主要注意两个地方,一个是保存路径,一个是最后那个选择,另外两个似乎是另一种语言。使用java语言编写就选这个选项。
然后需要配置AndroidManifest.xml这个文件
主要是配置所需的权限,app需要访问网络的权限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
现在的版本基本上要求动态申请权限,所以需要在Activity中进行申请。
private List<String> mPermissionlist;
private static final int REQUEST_EXTERNAL_STORAGE = 10;
private static final String[] PERMISSIONS = {
"android.permission.ACCESS_NETWORK_STATE",
"android.permission.INTERNET",
};
private void initPermission(){
mPermissionlist.clear();
//逐个判断你要的权限是否已经通过
for (String permission : PERMISSIONS) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
mPermissionlist.add(permission);//添加还未授予的权限
}
}
//申请权限
if(mPermissionlist.size()>0){//有权限没有通过,需要申请
ActivityCompat.requestPermissions(this,PERMISSIONS,REQUEST_EXTERNAL_STORAGE);
}
}
最后,mqtt协议相关库导入。Android连接到阿里云有官方的sdk可以进行下载然后使用,也可以直接通过mqtt协议进行连接,这里使用库连接,就不再去下载SDK了。
在模块中的build.gradle中找到对应位置
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
UI界面
主要是两个fragment,两个简单页面,一个两个按钮控制,一个调试用。
主界面fragment_home.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical"
>
<Button
android:id="@+id/light_on"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:text="开灯"
android:textSize="50sp"
android:background="@drawable/on_button_shape_circle"
android:gravity="center"/>
<Button
android:id="@+id/light_off"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerHorizontal="true"
android:gravity="center"
android:textSize="50sp"
android:background="@drawable/off_button_shape_circle"
android:text="关灯" />
</LinearLayout>
</RelativeLayout>
两个圆形按钮的实现
drawable中 on_button_shape_circle.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<corners android:radius="0dp"/><!-- 设置圆角弧度 -->
<solid android:color="#3ddc84" /> <!-- 设置背景颜色 -->
<stroke
android:width="0dp"
android:color="#fff" /><!-- 设置边框 -->
</shape>
调试界面fragment_debug.xml
java实现功能
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Fragment fragment1;
private Fragment fragment2;
private boolean isFragment1Displayed = true;
public static Handler handler;
private List<String> mPermissionlist;
private static final int REQUEST_EXTERNAL_STORAGE = 10;
private static final String[] PERMISSIONS = {
"android.permission.ACCESS_NETWORK_STATE",
"android.permission.INTERNET",
};
private void initPermission(){
mPermissionlist.clear();
//逐个判断你要的权限是否已经通过
for (String permission : PERMISSIONS) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
mPermissionlist.add(permission);//添加还未授予的权限
}
}
//申请权限
if(mPermissionlist.size()>0){//有权限没有通过,需要申请
ActivityCompat.requestPermissions(this,PERMISSIONS,REQUEST_EXTERNAL_STORAGE);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 权限申请和设备验证连接到阿里云平台
// 动态申请权限
mPermissionlist = new ArrayList<>();
//6.0才用动态权限 现在都大于6.0了
initPermission();
System.out.println("申请完成");
// Initialize fragments
fragment1 = new FragmentHome();
fragment2 = new FragmentDebug();
// Load Fragment1 by default
loadFragment(fragment1);
System.out.println("加载完成");
// Find the button and set a long click listener
Button switchButton = findViewById(R.id.switchButton);
switchButton.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (isFragment1Displayed) {
loadFragment(fragment2);
} else {
loadFragment(fragment1);
}
isFragment1Displayed = !isFragment1Displayed;
return true; // Return true to indicate the long click was handled
}
});
handler = new Handler(Looper.getMainLooper()) {//用于处理
@SuppressLint("SetTextI18n")
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1: //开机校验更新回传
break;
case 2: // 反馈回传
break;
case 3: //MQTT 收到消息回传
System.out.println(msg.obj.toString()); // 显示MQTT数据
break;
case 200: //连接成功
switchButton.setEnabled(true);
System.out.println("连接成功");
break;
case 404: //连接失败
Toast.makeText(MainActivity.this, "连接失败", Toast.LENGTH_SHORT).show();
switchButton.setEnabled(false);
System.out.println("连接失败");
break;
default:
break;
}
}
};
//设备注册,连接到阿里云平台
MqttHelper mqttHelper = new MqttHelper();
mqttHelper.MqttInit();
//初始化就是配置了对应的参数,在类中
//然后需要设置回调函数
}
private void loadFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragmentContainer, fragment);
transaction.addToBackStack(null); // 将当前事务加入到返回栈,支持返回时回到之前的 Fragment
transaction.commit();
}
}
主要实现的就是一个界面的长按切换fragment和权限申请,还有一个mqtthelper的对象创建,具体的连接操作在helper类中实现。
public class MqttHelper {
public static MqttClient mqttClient;
private MqttConnectOptions options;
public static final String servo_topic = "";
public final String person_detected_topic = "";
public MqttHelper() {
}
// MQTT连接函数
private void MqttConnect() {
new Thread(new Runnable() {
@Override
public void run() {
try {
if (!mqttClient.isConnected()) //如果还未连接
{
mqttClient.connect(options);
//连接成功设置订阅
mqttClient.subscribe(servo_topic,1);
mqttClient.subscribe(person_detected_topic,1);
MainActivity.handler.obtainMessage(200).sendToTarget();
}
} catch (Exception e) {
e.printStackTrace();
e.getMessage();
MainActivity.handler.obtainMessage(404).sendToTarget();
}
}
}).start();
}
// MQTT重新连接函数
private void startReconnect() {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
if (!mqttClient.isConnected()) {
// Log.d("MQTT", "Connection lost, attempting to reconnect...");
MqttConnect();
}
}
}, 1000, 10 * 1000, TimeUnit.MILLISECONDS);
}
public void MqttInit() {
try {
String clientId = "";
// url 端口号为 1883
String mqttHostUrl = "tcp://iot-06z00by4rtywg1d.mqtt.iothub.aliyuncs.com";
String username = "";
String passwd = "";
System.out.println("准备创建");
mqttClient = new MqttClient(mqttHostUrl, clientId, new MemoryPersistence());
System.out.println("创建成功");
// 设置连接选项
options = new MqttConnectOptions();
// mqttConnectOptions.setCleanSession(false);
options.setUserName(username); // 用户名:productKey + deviceName
options.setPassword(passwd.toCharArray()); // 密码:deviceSecret
options.setConnectionTimeout(10);
// 设置会话心跳时间 单位为秒 服务器30秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
options.setKeepAliveInterval(30);
mqttClient.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable cause) {
Log.d("MQTT", "Connection lost, attempting to reconnect...");
// 这里可以添加重连逻辑
// startReconnect();
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
// 处理接收到的消息
System.out.println("messageArrived----------topic: "+ topic);
System.out.println("message: "+ message);
System.out.println(" Message: " + new String(message.getPayload()));
//处理逻辑
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
// 处理消息发送完成后的回调
System.out.println("delivery success");
}
});
// 连接到 MQTT 服务器
mqttClient.connect(options);
System.out.println("mqtt连接成功");
//连接成功设置订阅
mqttClient.subscribe(servo_topic,1);
mqttClient.subscribe(person_detected_topic,1);
// MainActivity.handler.obtainMessage(200, obj).sendToTarget(); 同样可以带参数传递
MainActivity.handler.obtainMessage(200).sendToTarget();
// // 发布消息
// String publishTopic = "/sys/" + productKey + "/" + deviceName + "/thing/event/property/post";
// MqttMessage message = new MqttMessage("Test message".getBytes());
// message.setQos(1); // 设置消息 QoS(质量服务)
// mqttClient.publish(publishTopic, message);
// Log.d("MQTT", "Message sent: Test message");
} catch (Exception e) {
e.printStackTrace();
MainActivity.handler.obtainMessage(404).sendToTarget();
}
}
// 断开 MQTT 连接
public void disconnect() {
try {
if (mqttClient != null && mqttClient.isConnected()) {
mqttClient.disconnect();
Log.d("MQTT", "Disconnected from broker.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
这里需要注意的就一个地方mqttHostUrl = “tcp://iot-06z00by4rtywg1d.mqtt.iothub.aliyuncs.com”;
这里需要加一个前缀,不然无法连接成功。tcp://
需要加前缀“tcp://”
String mqttHostUrl = "tcp://iot-06z00by4rtywg1d.mqtt.iothub.aliyuncs.com";
MqttHelper中实现了初始化函数,MqttInit配置设备Mqtt的连接参数,创建连接对象,然后进行初次连接,如果连接失败会尝试重连,连接成功后会进行订阅和回调函数的设置,回调函数用来处理到达的信息。
这里暂时没有进行处理,目前还没有什么需要展示的信息,可以配置,有人经过时消息转发到手机从而实现提醒功能。
另外就是调试fragment_debug中的逻辑就是对按键和手动输入值进行处理,fragment_home也是类似操作,而且更简单就不列出来了。
public class FragmentDebug extends Fragment {
public FragmentDebug() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
// 加载 Fragment 的布局
View view = inflater.inflate(R.layout.fragment_debug, container, false);
// 获取控件引用
EditText inputVal = view.findViewById(R.id.inputVal);
Button homing = view.findViewById(R.id.homing);
Button setButton = view.findViewById(R.id.setButton);
// 设置按钮的点击事件
homing.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 发布消息 开灯操作
MqttMessage message = new MqttMessage("90".getBytes());
message.setQos(1); // 设置消息 QoS(质量服务)
try {
MqttHelper.mqttClient.publish(MqttHelper.servo_topic, message);
} catch (MqttException e) {
Toast.makeText(getActivity(), "归位失败", Toast.LENGTH_SHORT).show();
System.out.println("归位失败");
throw new RuntimeException(e);
}
}
});
// 设置按钮的点击事件
setButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 发布消息 开灯操作
String setVal = inputVal.getText().toString().trim();
MqttMessage message = new MqttMessage(setVal.getBytes());
message.setQos(1); // 设置消息 QoS(质量服务)
try {
MqttHelper.mqttClient.publish(MqttHelper.servo_topic, message);
} catch (MqttException e) {
Toast.makeText(getActivity(), "设置 "+setVal+" 失败", Toast.LENGTH_SHORT).show();
System.out.println("设置 "+setVal+" 失败");
throw new RuntimeException(e);
}
}
});
return view;
}
}
主要就是两个按键的操作,都是发送一个舵机角度值给云平台,一个是将输入的舵机角度值,一个是置为90,90°舵机处于平的状态,这样不影响手动开关灯。fragment_home类似。
云平台数据流转
无论是esp32还是android都只是和云平台的交互,想要实现esp32和android的通讯就需要借助云平台的帮助,将一方的消息转发给另一方,比如将Android的控制消息转发给esp32从而实现控制。
阿里云平台中的消息转发–》云产品流转就可以实现这样的功能。
首先需要创建数据源和数据目的,数据源就是来自Android设备的控制信号,数据目的自然就是ESP32对应topic,这样转发到ESP32就能进行接收和解析。
创建数据目的后需要为其添加Topic。需要选择产品和设备,以及topic
根据自己创建的产品和设备进行选择输入,注意这里要选android 设备,另外这里的主题选用的是我们自定义的topic,也可以选择其他topic。
数据目的,发布到另一个topic,就是esp32的那个topic,产品选对。
然后就需要创建解析器,用来转发数据。
首先需要关联数据源和数据目的
最后解析器脚本只需要改对应主题就行,就是ESP32设备对应的主题。
// 如果默认脚本自动保存过,继续绑定数据目的,默认脚本不会自动更新
// 此时清空脚本并保存之后,重新进入草稿页即可重新生成包含最新数据目的的默认脚本
// 设备上报数据内容,json格式
var data = payload('json');
// 流转到另一个Topic
writeIotTopic(1005, "/***/${DeviceName}/user/servo_control", data);
编辑好后可以进行简单的调试,可以正确的接收消息即可。
最后进行发布,然后启动解析器即可。
实物演示
配置好代码启动esp32,将app下载到手机并打开后,云平台显示在线即可实现通讯。
效果演示
light_control
以上就是全部内容了,有什么问题欢迎评论区留言,有需要帮助的地方也欢迎留言或私信。
若觉得有帮助,欢迎点赞关注收藏💕。