本文介绍: 同样,我们建立起来了web服务手机连接上了我们的网络,对于手机请求,我们也可以做相应的动作,从而实现手机控制ESP32-CAM。这块板子上还有个wifi,把它利用起来,如果手机能连上这个wifi,那不就相当于可以通过wifi控制这块板子了吗?以上是一个路径请求,当有人通过浏览器访问http://192.168.1.1的时候,我们就给他返回个字符串”我们要提前对这个端口做一下监听,当别人用浏览器放问我们的时候,我们要给出响应。以上就是手机连接ESP32-CAM的WiFi打开手机浏览器,在地址栏

关闭ESP32-CAM LED

因为ESP32-CAM通电后LED灯是亮的,超级亮,太晃眼,所以第一需求是关掉它

void setup() {
  pinMode(4, OUTPUT);   // 设置GPIO4为输出模式
  digitalWrite(4, LOW); // 调低电平关闭LED
}

void loop() {

}

让ESP32-CAM LED闪烁

既然可以关掉了,那能不能让它闪一下呢?

void setup() {
  pinMode(4, OUTPUT);   // 设置GPIO4为输出模式
}

void loop() {
  digitalWrite(4, LOW);
  delay(1000);
  digitalWrite(4, HIGH);
  delay(1000);
}

串口输出”Hello, World!”

因为ESP32-CAM是连到串口上面进行调试的,那么能不能让串口输出一些字符

void setup() {
  Serial.begin(115200); // 初始化串口通信波特率为115200
}

void loop() {
  Serial.println("Hello, world!"); // 输出字符串串口,并自动换行
  delay(1000); // 延时1秒
}

串口控制LED灯开关

从串口发送一些指令,控制这块板子做动作

void setup() {
  pinMode(4, OUTPUT); // 设置GPIO 4为输出模式
  digitalWrite(4, LOW); // 关闭LED灯
  Serial.begin(115200);
}

void loop() {
  // 接收控制指令
  if (Serial.available()) {
    char command = Serial.read();
    switch (command) {
      case '1':
        digitalWrite(4, HIGH); // 开启LED灯
        Serial.println("开灯");
        break;
      case '0':
        digitalWrite(4, LOW); // 关闭LED灯
        Serial.println("关灯");
        break;
    }
  }
  delay(100);
}

手机连接ESP32-CAM WiFi

这块板子上还有个wifi,把它利用起来,如果手机能连上这个wifi,那不就相当于可以通过wifi控制这块板子了吗?

WiFi得有个名字,连接wifi的密码
wifi分配一个ip地址
让他作为网关
再配个子网掩码
WiFi网络建立完成

WiFi的AP模式:AccessPoint就是接入点的意思,就是自己共享出来允许别人连接访问接入

#include <WiFi.h&gt;

// 替换成你喜欢的网络名称密码
const char* ssid = "ESP32_Wifi";
const char* password = "12345678";
IPAddress local_IP(192, 168, 1, 1); // 设置本地IP地址
IPAddress gateway(192, 168, 1, 1);  // 设置网关
IPAddress subnet(255, 255, 255, 0);  // 设置子网掩码

void setup()
{
  pinMode(4, OUTPUT);     // 设置GPIO4为输出模式
  digitalWrite(4, LOW);   // 关闭LED灯
  Serial.begin(115200);   // 初始化串口通信波特率为115200

  // 配置静态IP地址
  if (!WiFi.config(local_IP, gateway, subnet)) {
    Serial.println("配置失败");
  }

  // 建立Wi-Fi网络
  WiFi.mode(WIFI_AP);
  boolean result = WiFi.softAP(ssid, password);

  if (result == true) {
    Serial.print("网络名 SSID: ");
    Serial.println(ssid);
    Serial.print("IP 地址: ");
    Serial.println(WiFi.softAPIP());
  } else {
    Serial.println("Wifi启动失败.");
  }
}

void loop() {
  // 其他代码
}

这样手机就可以连接上ESP32-CAM的WiFi了

ESP32-CAM建立Web服务器

ESP32已经共享在网络上了,
IP地址是:192.168.1.1
http协议访问通常是80端口
我们要提前对这个端口做一下监听,当别人访问我们的时候,我们要给出响应
http://192.168.1.1/
访问地址就是这个格式
所以这就是web服务这就算建立起了一个web服务器
服务器就是干活的
负责监听端口,并且做出响应,这就叫做通讯
对方发送请求服务器给他反馈

#include <WiFi.h&gt;
#include <ESPAsyncWebServer.h>

const char* ssid = "ESP32";
const char* password = "12345678";
IPAddress local_IP(192, 168, 1, 1); // 设置本地IP地址
IPAddress gateway(192, 168, 1, 1);  // 设置网关
IPAddress subnet(255, 255, 255, 0);  // 设置子网掩码

AsyncWebServer server(80);  // 创建服务器对象指定监听端口80

void setup()
{
  pinMode(4, OUTPUT);     // 设置GPIO4为输出模式
  digitalWrite(4, LOW);   // 关闭LED灯
  Serial.begin(115200);   // 初始化串口通信,波特率为115200

  // 配置静态IP地址
  if (!WiFi.config(local_IP, gateway, subnet)) {
    Serial.println("STA Failed to configure");
  }

  // 建立Wi-Fi网络
  WiFi.mode(WIFI_AP);
  boolean result = WiFi.softAP(ssid, password);

  if (result == true) {
    Serial.print("网络名 SSID: ");
    Serial.println(ssid);
    Serial.print("IP地址: ");
    Serial.println(WiFi.softAPIP());
  } else {
    Serial.println("Wifi启动失败.");
  }
  
  // 响应网站路径请求
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", "Hello, World!");
  }); 

  server.begin();  // 启动服务器
}

void loop() {
  // 其他代码
}

以上是一个根路径请求,当有人通过浏览器访问http://192.168.1.1的时候,我们就给他返回一个字符串”Hello, World!

手机浏览访问ESP32-CAM Web服务器控制LED开关

同样,我们建立起来了web服务,手机连接上了我们的网络,对于手机的请求,我们也可以做相应的动作,从而实现手机控制ESP32-CAM

#include <WiFi.h>
#include <ESPAsyncWebServer.h>

const char* ssid = "ESP32_Wifi";
const char* password = "12345678";
IPAddress local_IP(192, 168, 1, 1); // 设置本地IP地址
IPAddress gateway(192, 168, 1, 1);  // 设置网关
IPAddress subnet(255, 255, 255, 0);  // 设置子网掩码

AsyncWebServer server(80);  // 创建服务器对象指定监听端口80

void setup()
{
  pinMode(4, OUTPUT);     // 设置GPIO4为输出模式
  digitalWrite(4, LOW);   // 关闭LED
  Serial.begin(115200);   // 初始化串口通信,波特率为115200

  // 配置静态IP地址
  if (!WiFi.config(local_IP, gateway, subnet)) {
    Serial.println("配置失败");
  }

  // 建立Wi-Fi网络
  WiFi.mode(WIFI_AP);
  boolean result = WiFi.softAP(ssid, password);

  if (result == true) {
    Serial.print("网络名 SSID: ");
    Serial.println(ssid);
    Serial.print("IP地址: ");
    Serial.println(WiFi.softAPIP());
  } else {
    Serial.println("WiFi启动失败.");
  }
  
  // 响应网站路径请求
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", "Hello, World!");
  });  
  
  // 响应打开LED的请求
  server.on("/led/on", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(4, HIGH);
    request->send(200);
  });
  
  // 响应关闭LED的请求
  server.on("/led/off", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(4, LOW);
    request->send(200);
  });  
  
  server.begin();  // 启动服务器
}

void loop() {
  // 其他代码
}

以上就是,手机连接ESP32-CAM的WiFi,打开手机浏览器,在地址栏
输入 http://192.168.1.1/ 的时候,web服务器就返回”Hello, World!”字符串
输入 http://192.168.1.1/led/on 的时候,将LED打开
输入 http://192.168.1.1/led/off 的时候,将LED关闭
从而实现手机控制

然而,真正的需求是要用

安卓app控制ESP32-CAM上面的LED灯开关

通讯协议

1.ESP32-CAM通电以后建立了WiFi网络,并且启动了web服务,开始监听网络请求

此时ESP32-CAM共享出来一个网络
网络名:ESP32_WiFi
密码:12345678
IP地址:192.168.1.1
网关:192.168.1.1
子网掩码:255.255.255.0

2.当我们用手机去连接这个WiFi的时候,WiFi会自动分配给手机一个IP地址

验证发现
WiFi建立后Esp32-CAM的IP是192.168.4.1
连上他的手机的IP被分配成为192.168.4.2

服务端就是上面那段代码没变

3.安卓端(重点)

我用的是Java写的,没用kotlin
网上找了很多段,也问了问ChatGPT,但是都没有给出正确答案
安卓比较复杂遇到很多坑。
点击ON按钮后给ESP32-CAM发送HTTP_GET请求,ESP32-CAM接收到请求后把灯打开
点击OFF按钮后给ESP32-CAM发送HTTP_GET请求,ESP32-CAM接收到请求后把灯关闭

先说:app/build.gradle文件
里面minSdktargetSdk这个两是重点,因为有些时候一些语句写了由于版本问题,他是不支持的。

plugins {
    id 'com.android.application'
}

android {
    compileSdk 31

    defaultConfig {
        applicationId "com.example.led"
        minSdk 23
        targetSdk 31
        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.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'com.amitshekhar.android:android-networking:1.0.2'
    implementation 'com.squareup.okhttp3:okhttp:4.10.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

}

再说:app/src/main/AndroidManifest.xml文件
这三句是开启网络访问权限,这是大家已经共识的了,不开这个手机app没有连接wifi的权限

  <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
  <uses-permission android:name="android.permission.INTERNET"/>

其实我开了以后也不行,弄了好久,结果找到了这一句
就写在application
因为安卓9.0以后升级了,直接明文发送请求不让发送了,加上以下这句就行了
限制明文请求

android:usesCleartextTraffic="true"

困扰了我好久,555

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.led">
  <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
  <uses-permission android:name="android.permission.INTERNET"/>

  <application
      android:usesCleartextTraffic="true"
      android:allowBackup="true"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:roundIcon="@mipmap/ic_launcher_round"
      android:supportsRtl="true"
      android:theme="@style/Theme.LED">
    <activity
        android:name=".MainActivity"
        android:exported="true">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>

        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>
  </application>

</manifest>

界面app/src/main/res/layout/activity_main.xml文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
>
  <LinearLayout
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent">
    <Button
      android:id="@+id/onLED"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:text="ON"
      tools:ignore="HardcodedText"/>
    <Button
      android:id="@+id/offLED"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:text="OFF"
      tools:ignore="HardcodedText"/>
    <TextView
      android:id="@+id/response_text"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
    />
  </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

逻辑app/src/main/java/com/example/led/MainActivity.java

package com.example.led;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.util.Objects;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  TextView responseText;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button button_on = findViewById(R.id.onLED);
    Button button_off = findViewById(R.id.offLED);
    responseText = findViewById(R.id.response_text);
    button_on.setOnClickListener(this);
    button_off.setOnClickListener(this);
  }

  @Override
  public void onClick(View v) {
    if (v.getId() == R.id.onLED) {
      onLED();
    }
    if (v.getId() == R.id.offLED) {
      offLED();
    }
  }

  private void onLED() {
    new Thread(() -> {
      try {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
          .url("http://192.168.4.1/led/on")
          .build();
        Response response = client.newCall(request).execute();
        String responseData = Objects.requireNonNull(response.body()).string();
        showResponse(responseData);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }).start();
  }

  private void offLED() {
    new Thread(() -> {
      try {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
          .url("http://192.168.4.1/led/off")
          .build();
        Response response = client.newCall(request).execute();
        String responseData = Objects.requireNonNull(response.body()).string();
        showResponse(responseData);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }).start();
  }

  private void showResponse(final String response) {
    runOnUiThread(() -> {
      responseText.setText(response);     //responseText最初由TextView responseText;定义代表TextView对象
    });
  }
}

基本上算是给怼估上了
后面再优化优化

原文地址:https://blog.csdn.net/weixin_45853406/article/details/130593052

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_48512.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注