使用ESP32+舵机+萤石摄像头制作一个远程逗猫棒

准备的工具:
1.Arduino IDE :https://www.arduino.cc(下载安装)
2.一台云主机(包含域名)
3.一个萤石云摄像头(一定要萤石云的)
4.ESP32开发板
5.S3003舵机
6.杜邦线若干
7.逗猫棒
8.Arduino舵机库:https://github.com/arduino-libraries/Servo
9.一个能用的DDNS

预览地址:http://pan.myzhazha.xyz:11452

首先,下载安装Arduino IDE并安装
打开Arduino IDE选择文件>首选项>附加开发板管理器网址 右侧的小图标
填入https://dl.espressif.com/dl/package_esp32_index.json

添加ESP32开发板地址

然后选择工具>开发板>开发板管理器


开发板管理器

等它下载完索引之后在搜索框输入ESP32然后安装


搜索ESP32

把下载好的Arduino舵机库文件夹复制到电脑C:\Users\用户名\Documents\Arduino\libraries\文件夹下,然后重新打开Arduino IDE

舵机库

打开Arduino IDE之后把预设的代码删除,把以下代码复制到Arduino IDE里、
注意把wifi名称和密码改为你自己的

#include <WiFi.h>

const int servoPin = 13;  /* 设置控制舵机信号引脚为D13 */

const char* ssid = "WIFI-SSID"; /* 你的wifi名称,不能为中文 */
const char* password = "password"; /*你的wifi密码 */

int dutyCycle = 0;
//int position1 = 0;

/* Setting PWM properties */
const int PWMFreq = 50;
const int PWMChannel = 0;
const int PWMResolution = 8;
const int MAX_DUTY_CYCLE = (int)(pow(2, PWMResolution) - 1);

WiFiServer espServer(80); /* Instance of WiFiServer with port number 80 */
/* 80 is the Port Number for HTTP Web Server */

/* A String to capture the incoming HTTP GET Request */
String request;

void setup()
{  
  Serial.begin(115200);
  ledcSetup(PWMChannel, PWMFreq, PWMResolution);
  /* Attach the LED PWM Channel to the GPIO Pin */
  ledcAttachPin(servoPin, PWMChannel);
  ledcWrite(PWMChannel, dutyCycle);

  Serial.print("\n");
  Serial.print("Connecting to: ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA); /* Configure ESP32 in STA Mode */
  WiFi.begin(ssid, password); /* Connect to Wi-Fi based on the above SSID and Password */
  while(WiFi.status() != WL_CONNECTED)
  {
    Serial.print("*");
    delay(100);
  }
  Serial.print("\n");
  Serial.print("Connected to Wi-Fi: ");
  Serial.println(WiFi.SSID());
  delay(100);
  /* The next four lines of Code are used for assigning Static IP to ESP32 */
  /* Do this only if you know what you are doing */
  /* You have to check for free IP Addresses from your Router and */
  /* assign it to ESP32 */
  /* If you are comfortable with this step, */
  /* please un-comment the next four lines and make necessary changes */
  /* If not, leave it as it is and proceed */
  //IPAddress ip(192,168,1,6);   
  //IPAddress gateway(192,168,1,1);   
  //IPAddress subnet(255,255,255,0);   
  //WiFi.config(ip, gateway, subnet);
  delay(2000);
  Serial.print("\n");
  Serial.println("Starting ESP32 Web Server for Servo Control...");
  espServer.begin(); /* Start the HTTP web Server */
  Serial.println("ESP32 Servo Web Server Started");
  Serial.print("\n");
  Serial.print("The URL of ESP32 Servo Web Server is: ");
  Serial.print("http://");
  Serial.println(WiFi.localIP());
  Serial.print("\n");
  Serial.println("Use the above URL in your Browser to access ESP32 Servo Web Server\n");
}
void loop()
{
  WiFiClient client = espServer.available(); /* Check if a client is available */
  if(!client)
  {
    return;
  }

  Serial.println("New Client!!!");
  boolean currentLineIsBlank = true;
  while (client.connected())
  {
    if (client.available())
    {
      char c = client.read();
      request += c;
      Serial.write(c);
        /* If you've gotten to the end of the line (received a newline */
        /* character) and the line is blank, the http request has ended, */
        /* so you can send a reply */
      if (c == '\n' && currentLineIsBlank)
      {
        client.println("HTTP/1.1 200 OK");
        client.println("Content-type:text/html");
        client.println("Connection: close");
        client.println();

        client.println("<!DOCTYPE html>");
        client.println("<html>");
        
        client.println("<head><meta charset='utf8'\name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
        client.println("<link rel=\"icon\" href=\"data:,\">");

        /* CSS Styling for Text and Slider */
        
        client.println("<style>body { font-family: \"Courier New\"; margin-left:auto; margin-right:auto; text-align:center;}");
        
        client.println(".slidecontainer { width: 100%;}");
        client.println(".slider { -webkit-appearance: none;");
        client.println("width: 30%; height: 20px; background: #d3d3d3;");
        client.println("outline: none; opacity: 0.7; -webkit-transition: .2s; transition: opacity .2s;}");
        client.println(".slider:hover { opacity: 1; }");

        client.println(".slider::-webkit-slider-thumb { -webkit-appearance: none;");
        client.println("appearance: none; width: 15px; height: 28px;");
        client.println("border-radius: 30%; background: #4CAF50; cursor: pointer;}");
        client.println(".slider::-moz-range-thumb { width: 25px; height: 25px; background: #4CAF50; cursor: pointer;}</style>");
        
        client.println("<script src=\"https://code.jquery.com/jquery-3.6.0.min.js\"></script>");
        /*Actual Web Page */
        client.println("</head><body><h2>逗猫棒🐱</h2>");
        client.println("<p>在线逗猫棒</p>");
        
        client.println("<input type=\"range\" min=\"0\" max=\"90\" class=\"slider\" id=\"servoRange\" onchange=\"servo(this.value)\"/>");
        client.println("<p>角度: <span id=\"servoPos\"></span></p>");
        client.println("<script>");
        client.println("var slider = document.getElementById(\"servoRange\");");
        client.println("var output = document.getElementById(\"servoPos\");");
        client.println("output.innerHTML = slider.value;");
        client.println("slider.oninput = function(){output.innerHTML = this.value;}");
        client.println("$.ajaxSetup({timeout:1000}); function servo(angle) { ");
        client.println("$.get(\"/servovalue=\" + angle); {Connection: close};}</script>");
                
        client.println("</body></html>");   
        
        /* The request will be in the form of 
         * GET /servovalue=143 /HTTP/1.1*/
        if(request.indexOf("GET /servovalue=") != -1)
        {
          int position1 = request.indexOf('='); /* Find out the position of '=' in the request string */
          String angleStr = request.substring(position1+1); /* Next 2/3 characters inform the desired angle */
          int angleValue = angleStr.toInt();
          dutyCycle = map(angleValue, 0, 180, 5, 32);
          ledcWrite(PWMChannel, dutyCycle); 
        }
        client.println();
        break;
      }

        if(c == '\n')
        {
          currentLineIsBlank = true;
        }
        else if(c != '\r')
        {
          currentLineIsBlank = false;
        }
        //client.print("\n");
    }
  }
 
  delay(1);
  request = "";
  //client.flush();
  client.stop();
  Serial.println("Client disconnected");
  Serial.print("\n");
}

然后选择文件-保存,随便保存到一个位置
保存后选择
工具>开发板>ESP32 Arduino>ESP32 Dev Module
此时把ESP32模组插上电脑并装好串口驱动,然后在工具>端口选择你的ESP32模组所在的端口号
然后点击图中的图标进行固件编译上传


上传

在下方的窗口等待进度完成,完成后断开USB连接并插到充电头上(舵机启动时可能会造成电脑USB损坏)


上传完成

此时把你的舵机的VCC端连接到ESP32模组的VIN引脚,GND连接到GND引脚,舵机信号引脚连接到D13引脚

强烈建议舵机使用独立供电!否则容易烧毁ESP32模组的电源电路

登录你的路由器后台,查看连接的设备,找到名为espressif的设备,看一下IP地址是多少,然后在浏览器里输入该IP地址并访问,就进入到了舵机的控制页面

舵机控制

此时点击滑动条查看舵机是否转动,转动说明正常,不转动检查线路连接是否错误

然后把该ESP32模组的页面使用DDNS和端口映射,映射到公网,注意端口不要太小(最好10000以上)

新建一个文本文件,把以下内容复制进去

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <link rel="icon" href="data:,">
<style>
body { font-family: "Courier New"; margin-left:auto; margin-right:auto; text-align:center;}
.slidecontainer { width: 720px;}
.slider { -webkit-appearance: none;
width: 61%; height: 20px; background: #d3d3d3;
outline: none; opacity: 0.7; -webkit-transition: .2s; transition: opacity .2s;}
.slider:hover { opacity: 1; }
.slider::-webkit-slider-thumb { -webkit-appearance: none;
appearance: none; width: 15px; height: 28px;
border-radius: 30%; background: #4CAF50; cursor: pointer;}
.slider::-moz-range-thumb { width: 25px; height: 25px; background: #4CAF50; cursor: pointer;}

</style>
<script src="./js/jquery-3.6.0.min.js"></script>
<title>逗猫棒</title>
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?0f723813e10da06579f73257eab9cb6a";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>

</head>
<body>  
    <h2>逗猫棒🐱</h2>
    <tr>
      <td width="720" align="center" valign="middle"><p>
               
<iframe
  src="https://open.ys7.com/ezopen/h5/iframe?url=ezopen://open.ys7.com/D07329191/1.live&autoplay=1&audio=1&accessToken=at.3fv362axceoqng5u2dwh6ook9s0ab0o2-7hz4j5eple-1gktksc-liaffjlcm&templete=0"
  width="720"
  height="405"
  id="ysopen"
  allowfullscreen
>
</iframe>
    <p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</p>
    <span style=""><strong>实时画面(延时1s</strong></span>)</p>
         
<input type="range" min="0" max="90" class="slider" id="servoRange" onchange="servo(this.value)">

<p>角度: <span id="servoPos">78</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;开放时间:周一~周六 上午9:00~12:00&nbsp; 下午 14:00~18:00&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;更多逗猫玩法正在开发中,敬请期待...&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</p>
<p>在线逗猫棒by:小渣渣</p>
<script>
var slider = document.getElementById("servoRange");
var output = document.getElementById("servoPos");
    output.innerHTML = slider.value;
    slider.oninput = function(){output.innerHTML = this.value;}
    $.ajaxSetup({timeout:1000}); function servo(angle) { 
    $.get("http://pan.myzhazha.xyz:23641/servovalue=" + angle); {Connection: close};}

</script>  
      </td>
    </tr>
  
</body>
</html>

将准备好的萤石摄像头绑定手机并在摄像头设置里关闭视频加密
关闭后访问萤石云开放平台注册成为个人开发者
萤石云开放平台地址:https://open.ys7.com/
注册成共后进入左侧的我的账号>应用信息>创建应用
应用名称随便写
行业宠物关爱
勾选:我已购买软件,无需自行开发
软件名称随便写

应用信息

然后进入我的资源>设备列表,此时应该能看到绑定的摄像头,点击右侧的监控地址
在打开的页面选择:前往轻应用视频学习了解


前往轻应用视频学习了解

在打开的页面选择下方的选择设备


选择设备

按照要求填入信息
建议播放类型改为预览,清晰度流畅,播放器模板选择极简版
提交后右侧会出现代码示例,把iframe标签里的代码复制并替换到刚才的index.html文件里的iframe标签里
注意:此处的AccessToken的值每7天萤石云开放平台会自动更改一次,所以每到7天请自行更改,或者自己写脚本进行自动更改,萤石云开放平台有详细的接口调用文档

iframe标签

!替换](https://upload-images.jianshu.io/upload_images/4596711-a12e822a50a5a0f4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/720)

注意此处要把width的值改为720, height的值改为405

然后把index.html文件的此处的地址改为你的ESP32模组的地址DDNS映射之后的域名+端口


域名+端口

下载该js文件
链接:https://pan.baidu.com/s/1uRNTodbiLCislj-t7YVk0A
提取码:2333

在你的WEB服务器里创建一个文件夹专门放置该网页,把上面网盘里的文件下下载并解压,在网页文件夹里创建js文件夹,把jquery-3.6.0.min.js文件上传进去,再把index.html文件上传到与js文件夹同一目录
上传完成后使用浏览器打开你的WEB服务器地址查看能否正常访问,摄像头画面是否正常加载,舵机控制是否正常
(最好使用手机+数据流量来验证)

所有功能都正常就把逗猫棒绑在舵机上面并把舵机和摄像头安装在合适的位置
我是使用3D打印打印了一个舵机支架绑在了桌子腿上

模型
支架

3D模型下载链接
链接:https://pan.baidu.com/s/1XZFJNT7_v9iZkD0l1SiTQA
提取码:2333

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容