安卓APP控制ESP32

您所在的位置:网站首页 天干地支时钟安卓app 安卓APP控制ESP32

安卓APP控制ESP32

2024-07-09 22:21| 来源: 网络整理| 查看: 265

目录

初衷

技术栈

安卓端

ESP32端

初衷

        本人所学专业为电力电子专业,想做关于负载的连续变化实验,变化频率一定,首先想到的是电子负载,但是实验室中现有的电子负载无法应用与所研究的小功率变换器,于是想通过单片机来控制继电器进行负载切换。

        最简单的方法是PC通过串口连接单片机,单片机控制继电器吸合,进行负载的断开与连接。

        但是这一点都不好玩,太枯燥,不符合创客精神,是时候尝试各种开发的结合!

        控制必须远程,远程必须联网,联网显示QR,APP扫码连接。(还有另外一种方式SmartConfig,跟AirKiss技术类似,通过广播的方式,将WiFi信息间接传递给ESP32单片机)

       对于单片机的选择,具备联网功能的ESP32 WROOM,价格美丽,资源足够。

        安卓APP开发有多种方式,最简单的是使用web相关技术开发,uniapp,webapp等,对于页面质量的提升极其明显,但是感觉不够接近底层,对于底层逻辑的开发体验不如原生,因此使用Android Studio从而通过java进行app开发。

        成果展示:

        手机APP:

技术栈

        1.C控制单片机配置网络(手机作为服务器,ESP32为客户端),通过二维码显示设备ip信息(设备:ESP32 WROOM,TFT屏幕,5V继电器)

        2.安卓原生程序开发,扫码连接设备,从而控制单片机,同时显示单片机状态。(Android Studio)

安卓端

    为实现扫码功能,使用了华为的统一扫码服务HMS scan kiticon-default.png?t=N7T8https://developer.huawei.com/consumer/cn/hms/huawei-scankit

     (挺好用的,简单配置一下即可,添加相关依赖)

(1)首先是Manifest配置,添加相机权限,网络权限等

 (2)添加gradle依赖包

//build.gradle plugins { id 'com.android.application' } android { namespace 'com.example.esp32_iot' compileSdk 33 defaultConfig { applicationId "com.example.esp32_iot" minSdk 29 targetSdk 33 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'com.google.android.material:material:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' implementation "com.huawei.hms:scanplus:1.1.3.301" }

(3)界面设计

 (4)逻辑实现

//MainActicity.java package com.example.esp32_iot; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.StrictMode; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import com.huawei.hms.hmsscankit.ScanUtil; import com.huawei.hms.ml.scan.HmsScan; import com.huawei.hms.ml.scan.HmsScanAnalyzerOptions; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private TextView tv; private EditText IPet,port; private Handler myhandler; private Socket socket; private String str = ""; boolean running = false; private Button conn,disconn,on,off; private StartThread st; private ReceiveThread rt; public static final int CAMERA_REQ_CODE = 111; public static final int DECODE = 1; private static final int REQUEST_CODE_SCAN_ONE = 0X01; String[] permissions = new String[]{Manifest.permission.READ_PHONE_STATE}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = findViewById(R.id.tv); IPet = findViewById(R.id.IPet); port = findViewById(R.id.port); conn = (Button) findViewById(R.id.conn); disconn = (Button) findViewById(R.id.disconn); on = (Button) findViewById(R.id.on); off = findViewById(R.id.off); setButtonOnStartState(true);//设置按钮状态为可点击-也就是可以开始连接 conn.setOnClickListener(this); disconn.setOnClickListener(this); on.setOnClickListener(this); off.setOnClickListener(this); myhandler = new MyHandler(); } public void loadScanKitBtnClick(View view) { requestPermission(CAMERA_REQ_CODE, DECODE); } //编辑请求权限 private void requestPermission(int requestCode, int mode) { ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE}, requestCode); } //权限申请返回 @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (permissions == null || grantResults == null) { return; } if (grantResults.length < 2 || grantResults[0] != PackageManager.PERMISSION_GRANTED || grantResults[1] != PackageManager.PERMISSION_GRANTED) { return; } if (requestCode == CAMERA_REQ_CODE) { ScanUtil.startScan(this, REQUEST_CODE_SCAN_ONE, new HmsScanAnalyzerOptions.Creator().create()); } } //Activity回调 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode != RESULT_OK || data == null) { return; } if (requestCode == REQUEST_CODE_SCAN_ONE) { HmsScan obj = data.getParcelableExtra(ScanUtil.RESULT); if (obj != null) { Toast.makeText(this,obj.originalValue,Toast.LENGTH_SHORT).show(); IPet.setText(obj.originalValue); } } } @Override public void onClick(View v) { switch (v.getId()){ case R.id.conn: //此时按下开始连接按键新建线程 st = new StartThread(); st.start(); setButtonOnStartState(false); break; case R.id.on: //发送请求数据 new Thread(new Runnable() { @Override public void run() { try { OutputStream os = null; os = socket.getOutputStream();//得到socket的输出流 //输出EditText里面的数据,数据最后加上换行符才可以让服务器端的readline()停止阻塞 os.write(("123\n").getBytes("utf-8")); }catch (IOException e){ e.printStackTrace(); } } }).start(); break; case R.id.off: new Thread(new Runnable() { @Override public void run() { try { OutputStream os = null; os = socket.getOutputStream();//得到socket的输出流 //输出EditText里面的数据,数据最后加上换行符才可以让服务器端的readline()停止阻塞 os.write(("456\n").getBytes("utf-8")); }catch (IOException e){ e.printStackTrace(); } } }).start(); break; case R.id.disconn: new Thread(new Runnable() { @Override public void run() { try { OutputStream os = null; os = socket.getOutputStream();//得到socket的输出流 //输出EditText里面的数据,数据最后加上换行符才可以让服务器端的readline()停止阻塞 os.write(("789\n").getBytes("utf-8")); }catch (IOException e){ e.printStackTrace(); } } }).start(); running = false; setButtonOnStartState(true); try { socket.close(); }catch (NullPointerException e){ e.printStackTrace(); disPlayToast("断开连接"); }catch (IOException e){ e.printStackTrace(); } break; } } private class StartThread extends Thread{ @Override public void run(){ try { int portnum = Integer.parseInt(port.getText().toString()); socket = new Socket(IPet.getText().toString(),portnum); System.out.println(IPet.getText()); rt = new ReceiveThread(socket); rt.start(); running = true; System.out.println(socket.isConnected()); if(socket.isConnected()){//成功连接,获取socket对象,发送成功消息 Message msg0 = myhandler.obtainMessage(); msg0.what = 0; myhandler.sendMessage(msg0); tv.setText("已连接ESP32"); } }catch (IOException e){ e.printStackTrace(); } } } private class ReceiveThread extends Thread{ private InputStream is; public ReceiveThread(Socket socket) throws IOException{ is = socket.getInputStream(); } @Override public void run(){ while (running){ InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); try { //读取服务器端发过来的数据,阻塞一直到收到结束符\n\r System.out.println(str = br.readLine()); }catch (NullPointerException e){ running = false; Message msg2 = myhandler.obtainMessage(); msg2.what = 2; myhandler.sendMessage(msg2); e.printStackTrace(); break; }catch (IOException e){ e.printStackTrace(); } Message msg = myhandler.obtainMessage(); msg.what = 1; msg.obj = str; myhandler.sendMessage(msg); try{ sleep(400); }catch (InterruptedException e){ e.printStackTrace(); } } Message msg2 = myhandler.obtainMessage(); msg2.what = 2; myhandler.sendMessage(msg2); } } //设置按钮状态 private void setButtonOnStartState(boolean flag) { conn.setEnabled(flag); disconn.setEnabled(!flag); on.setEnabled(!flag); off.setEnabled(!flag); } private void disPlayToast(String s){ Toast.makeText(this,s,Toast.LENGTH_SHORT).show(); } class MyHandler extends Handler{//在主线程中处理Handler传送回来的额message @Override public void handleMessage(Message msg){ switch (msg.what){ case 1: String str = (String) msg.obj; System.out.println(msg.obj); tv.setText(str); break; case 0: disPlayToast("连接成功"); break; case 2: disPlayToast("服务器已断开"); tv.setText(null); setButtonOnStartState(true); break; } } } }

思路介绍完之后,附上giteeESP32 IOT APPicon-default.png?t=N7T8https://gitee.com/cheplus/esp32-iotapp

ESP32端

通过C语言配置,platformio

使用的屏幕是TFT 240*240(ST7789驱动)

配置页如下(.pio\libdeps\upesy_wroom\TFT_eSPI\User_Setups\Setup24_ST7789.h)

#define TFT_MISO -1 #define TFT_MOSI 23 #define TFT_SCLK 18 #define TFT_CS -1 // Not connected #define TFT_DC 2 #define TFT_RST 15 // Connect reset to ensure display initialises #include #include #include #include // Hardware-specific library #include TFT_eSPI tft = TFT_eSPI(); // Invoke custom library const char *AP_SSID = "Redmi K30S Ultra"; const char *AP_Password = "qwerty123"; WiFiServer esp32_server(8080); QRcode_eSPI qrcode(&tft); void setup(void) { tft.init(); pinMode(2, OUTPUT); pinMode(13, OUTPUT); tft.fillScreen(0xffa500); Serial.begin(115200); delay(500); Serial.println("111111111111"); WiFi.begin(AP_SSID, AP_Password); // 设置AP模式热点的名称和密码 while (WiFi.status() != WL_CONNECTED) { delay(5000); Serial.println("正在连接"); tft.println("正在连接"); } Serial.println("连接成功"); tft.setCursor(0, 0, 4); // Set "cursor" at top left corner of display (0,0) and select font 4 tft.setTextColor(TFT_BLUE); tft.println("Connected"); tft.println(WiFi.localIP()); qrcode.init(); // create qrcode //delay(50000); qrcode.create(WiFi.localIP().toString().c_str()); esp32_server.begin(); } void loop() { WiFiClient client = esp32_server.available(); String msgs = ""; if (client) { if(client.connected()){ tft.fillScreen(0x42a5f5); tft.setTextColor(TFT_WHITE); tft.println("client Connected"); } while (client.connected()) { // while (client.available() > 0) // { // char c = client.read(); // Serial.println(c); // msgs += c; // } if (client.available()) //如果有数据可读取 { String line = client.readStringUntil('\n'); //读取数据到换行符 Serial.print("读取到数据:"); Serial.println(line); msgs = line + "\n"; client.write(msgs.c_str()); //将收到的数据回发 //tft.println(line.c_str()); if(line=="123"){ digitalWrite(2, HIGH); digitalWrite(13, HIGH); }else if(line=="456"){digitalWrite(2, LOW);digitalWrite(13, LOW);} } //client.print(msgs); // 将socket客户端接收的数据发送回去 msgs = ""; delay(10); } client.stop(); qrcode.create(WiFi.localIP().toString().c_str()); Serial.println("Client disconnected"); } }

这个程序就相对简单,附上gitee

ESP32 IOT TFTicon-default.png?t=N7T8https://gitee.com/cheplus/esp32-iottft实物图



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3