first commit

This commit is contained in:
编码猿
2024-09-27 01:22:07 +08:00
commit 1360daaab0
931 changed files with 131068 additions and 0 deletions

View File

@@ -0,0 +1 @@
d55db5fb-33b8-4a23-bda7-c40e5abf7980

View File

@@ -0,0 +1,37 @@
#include "src/led/led.h"
#include "src/config/config.h"
#include "src/wifi/wifi.h"
#include "src/motion/motion.h"
#include "src/web/web.h"
#include "src/socket/socket.h"
#include "src/buzzer/buzzer.h"
Web web;
Socket socket;
ConfigWifi wifi;
Motion motion;
Buzzer buzzer;
void setup()
{
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
// 初始化wifiap模式
wifi.init();
// 初始化电机驱动的各个引脚 和 舵机,和运动方法封装
motion.init();
// 蜂鸣器
buzzer.init();
// 初始化 websocket 服务
socket.init();
// 初始化 httpServer 服务主要加载Web的控制面板
web.init();
}
void loop()
{
socket.Server->loop();
web.WebController->handleClient();
}

View File

@@ -0,0 +1 @@
fc2363c0-9720-43c0-bced-f43fcaabb0c7

View File

@@ -0,0 +1 @@
7682cea9-47ad-46fa-8ada-afd10dfa0655

View File

@@ -0,0 +1,161 @@
body,
ul,
li,
h1,
h2,
h3,
h4,
h5,
h6,
p {
margin: 0;
padding: 0;
list-style: none;
}
body,
html {
width: 100%;
height: 100%;
}
body {
position: relative;
background: url("http://192.168.4.4:81/stream");
background-size: cover;
background-position: center;
}
.remote_rod {
position: absolute;
padding: 0 30px;
width: 100%;
bottom: 40px;
display: flex;
justify-content: space-between;
box-sizing: border-box;
}
.rod {
width: 128px;
height: 128px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.vertical {
background: url("../img/vertical.png");
}
.level {
background: url("../img/level.png");
}
.level img {
position: absolute;
font-size: 32px;
display: inline-block;
text-align: center;
border-radius: 100%;
}
.level .action img {
width: 38px;
height: 38px;
}
.icon-zidongdadeng {
width: 43px !important;
height: 43px !important;
top: -70px;
right: 0;
}
.icon-jinggao_o {
left: -24px;
top: -44px;
}
.icon-xiaolaba {
left: -68px;
top: 43px;
}
.icon-hand {
left: -68px;
top: 43px;
}
.level .active {
color: #f8b84ef2;
}
.top_action {
width: 100%;
position: absolute;
display: flex;
justify-content: space-between;
margin-top: 15px;
padding: 0 15px;
box-sizing: border-box;
}
.back {
width: 40px;
height: 40px;
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
background: #c5c5d5;
margin-right: 20px;
}
.right_action .back:last-child {
margin-right: 0 !important;
}
.top_action .obstacle,
.icon-hand {
width: 20px;
height: 20px;
}
.speed {
width: 100px;
border: 1px solid #000;
padding: 5px 15px;
border-radius: 20px;
display: flex;
align-items: center;
}
.speed img {
margin-right: 15px;
}
.right_action {
display: flex;
align-items: center;
}
.lamp {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 200px;
box-sizing: border-box;
position: absolute;
bottom: 10px;
width: 100%;
}
.lamp img {
padding: 0px 12px;
background: #c5c4d5;
color: #fff;
border-radius: 30px;
height: 35px;
}
.open {
animation: openLamp 0.8s infinite alternate;
}
@keyframes openLamp {
from {
background: #a2a1a1;
}
to {
background: #f8bd5d;
}
}

View File

@@ -0,0 +1 @@
a388ed8d-0d80-4fac-8a45-35c99b4a5c34

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 B

View File

@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>智能小车控制面板</title>
<link rel="stylesheet" href="./css/index.css">
</head>
<body>
<div class="remote_rod">
<div class="vertical rod">
<img id="stick1" src="./img/joystick-speed.png" />
</div>
<div class="level rod">
<img id="stick2" src="./img/joystick-blue.png" />
<div class="action">
<img class="icon-zidongdadeng" src="./img/headlight.png" alt="">
<img class="icon-jinggao_o" src="./img/warning.png" alt="">
<img class="icon-xiaolaba" src="./img/horn.png" alt="">
</div>
</div>
</div>
<div class="top_action">
<div></div>
<div class="speed">
<img src="./img/stop.png" alt=""> 0km/h
</div>
<div class="right_action">
<div class="back hand_wrap">
<img class="icon-hand" src="./img/hand.png" alt="">
</div>
<div class="back obstacle_wrap">
<img class="obstacle" src="./img/obstacle.png" alt="">
</div>
</div>
</div>
<!-- <div class="lamp">
<img class="open" src="./img/leftlight.png" alt="">
<img src="./img/rightlight.png" alt="">
</div> -->
<audio class="actionAudio" src="./img/action.mp3" controls hidden></audio>
</body>
<script src="./js/utils.js"></script>
<script src="./js/WebSocket.js"></script>
<script src="./js/Joystick.js"></script>
<script src="./js/index.js"></script>
</html>

View File

@@ -0,0 +1 @@
525da242-f4be-4a40-9f41-1589a0071a96

View File

@@ -0,0 +1,134 @@
class Joystick {
// 元素id最大移动距离
constructor(stickID, direction, callback) {
this.id = stickID;
this.maxDistance = 64;
this.direction = direction;
this.callback = callback;
this.time = null
this.stick = document.getElementById(stickID);
// 开始拖动的位置,用于计算偏移量
this.dragStart = null;
// 如果存在多个操纵手柄,则跟踪触摸标识符
this.touchId = null;
this.active = false;
this.EventListener();
}
handleDown(event) {
this.active = true;
this.stick.style.transition = '0s';
// 阻止默认事件
event.preventDefault();
// 记录初始点击的坐标
if (event.changedTouches) {
this.dragStart = { x: event.changedTouches[0].clientX, y: event.changedTouches[0].clientY };
} else {
this.dragStart = { x: event.clientX, y: event.clientY };
}
// console.log("event: ", event);
// console.log("event.changedTouches: ", event.changedTouches);
// 记录点击的id
if (event.changedTouches) {
this.touchId = event.changedTouches[0].identifier;
}
}
handleMove(event) {
if (!this.active) return;
if (event.changedTouches && this.touchId != event.changedTouches[0].identifier) return;
// 处理多点触控
if (event.changedTouches) {
for (let i = 0; i < event.changedTouches.length; i++) {
if (this.touchId == event.changedTouches[i].identifier) {
event.clientX = event.changedTouches[i].clientX;
event.clientY = event.changedTouches[i].clientY;
}
}
}
// 计算 x 轴距离
const xDiff = event.clientX - this.dragStart.x;
// 计算 y 轴距离
const yDiff = event.clientY - this.dragStart.y;
// 计算 角度
const angle = Math.atan2(yDiff, xDiff);
// 计算 距离
const distance = Math.min(this.maxDistance, Math.hypot(xDiff, yDiff));
// 计算 xy 的新位置
const xPosition = distance * Math.cos(angle);
const yPosition = distance * Math.sin(angle);
if (this.time && (Date.now() - this.time) < 30) return
this.time = Date.now()
if (this.direction == "x") {
// console.log("xPosition: ", Math.sign(xPosition));
// 左边 0 ~ -64 -1 3
if (xPosition < 0) {
if (xPosition < -63) {
console.log("左边: ", xPosition);
this.callback(3)
}
// 右边 1 4
} else if (xPosition > 0){
if (xPosition > 63) {
console.log("右边: ", xPosition);
this.callback(4)
}
}
// 将图片移动到新位置
this.stick.style.transform = `translate3d(${xPosition}px,0px, 0px)`;
// this.callback(Math.sign(xPosition) == -1 ? 3 : 4)
} else {
console.log("yPosition: ", yPosition);
this.stick.style.transform = `translate3d(0px,${yPosition}px, 0px)`;
this.callback(Math.sign(yPosition) == -1 ? 1 : 2, Math.round(255 * Math.round(Math.abs(yPosition)) / 64))
}
}
handleUp(event) {
if (!this.active) return;
if (event.changedTouches && this.touchId != event.changedTouches[0].identifier) return;
this.active = false;
// console.log("3 handleUp ==== ", this.active);
this.stick.style.transition = '.2s';
this.stick.style.transform = `translate3d(0px, 0px, 0px)`;
this.touchId = null;
this.callback(0, 0)
}
EventListener() {
// 鼠标 & 手指 按下
this.stick.addEventListener('mousedown', this.handleDown.bind(this));
this.stick.addEventListener('touchstart', this.handleDown.bind(this));
// 鼠标 & 手指 移动
document.addEventListener('mousemove', this.handleMove.bind(this), { passive: false });
document.addEventListener('touchmove', this.handleMove.bind(this), { passive: false });
// 鼠标 & 手指 松开
document.addEventListener('mouseup', this.handleUp.bind(this));
document.addEventListener('touchend', this.handleUp.bind(this));
}
}

View File

@@ -0,0 +1,38 @@
class WS {
constructor() {
this.ws = null;
this.connect();
this.onopen();
this.onclose();
this.onerror();
}
connect() {
this.ws = new WebSocket("ws://192.168.4.1:81");
console.log("this.ws: ", this.ws);
}
onopen() {
this.ws.onopen = () => {
console.log("WebSocket open");
}
}
onmessage(callback) {
this.ws.onmessage = (e) => {
callback(e.data)
}
}
onclose() {
this.ws.onclose = (e) => {
console.log("close");
}
}
onerror() {
this.ws.onerror = (e) => {
console.log("onerror");
}
}
}

View File

@@ -0,0 +1,157 @@
/**
* wx.send("操作类型|操作状态|其他数值")
* 操作类型y 代表速度摇杆
* x 代表方向摇杆
* o 代表其他操作
*/
class IndexPage {
constructor() {
// 自动壁障的开关
this.obstacleStatus =
localStorage.getItem("obstacleStatus") == null
? false
: JSON.parse(localStorage.getItem("obstacleStatus"))
// 前大灯的开关
this.fronLamp =
localStorage.getItem("fronLamp") == null
? false
: JSON.parse(localStorage.getItem("fronLamp"))
// 双闪的开关
this.doubleStatus =
localStorage.getItem("doubleStatus") == null
? false
: JSON.parse(localStorage.getItem("doubleStatus"))
// 手势跟随的开关
this.handStatus =
localStorage.getItem("handStatus") == null
? false
: JSON.parse(localStorage.getItem("handStatus"))
this.doubleIntervalId = null;
this.socket = new WS();
this.Joystick();
this.bindActionButton();
}
Joystick() {
// 前进后退摇杆
// status-1 前进 1 后退0 停止
// speed前进后退的速度 0 ~ 255 之间
var s1 = new Joystick("stick1", "y", (status, speed) => {
this.socket.ws.send(`y|${status}|${speed}`)
});
// 左右转向摇杆
// status3左转 4右转 0 转向恢复原样(直行)
var s2 = new Joystick("stick2", "x", (status) => {
this.socket.ws.send(`x|${status}`)
});
this.socket.onmessage((res) => {
console.log("res = ",res);
alert("web收到请求为")
alert(JSON.stringify(res))
res = res.split("|");
switch (res[0]) {
case "server":
if (res[1] == "y") {
$("#stick1").style = "";
$("#stick2").style = "";
s1.active = false;
s1.dragStart = { x: 0, y: 0 };
s2.active = false;
s2.dragStart = { x: 0, y: 0 };
}
break;
case "client":
break;
default:
break;
}
})
}
bindActionButton() {
if (this.obstacleStatus) {
$(".obstacle_wrap").classList.add("open")
}
if (this.fronLamp) {
$(".icon-zidongdadeng").classList.add("open")
}
if (this.doubleStatus) {
$(".icon-jinggao_o").classList.add("open")
}
if (this.handStatus) {
$(".hand_wrap").classList.add("open")
}
// 喇叭开关
$(".icon-xiaolaba").onclick = () => {
this.play();
this.socket.ws.send(`b|buzzer|1`)
}
// 手势跟随
$(".hand_wrap").onclick = () => {
this.play();
this.handStatus = !this.handStatus
$(".hand_wrap").classList.toggle("open")
localStorage.setItem("handStatus", this.handStatus)
this.socket.ws.send(`b|openHand|${this.handStatus ? '1' : '0'}`)
}
// 前面大灯
$(".icon-zidongdadeng").onclick = () => {
this.play();
this.fronLamp = !this.fronLamp
$(".icon-zidongdadeng").classList.toggle("open")
localStorage.setItem("fronLamp", this.fronLamp)
this.socket.ws.send(`b|fronLamp|${this.fronLamp ? '1' : '0'}`)
}
// 双闪
$(".icon-jinggao_o").onclick = () => {
this.play();
this.doubleStatus = !this.doubleStatus
$(".icon-jinggao_o").classList.toggle("open")
localStorage.setItem("doubleStatus", this.doubleStatus)
if (this.doubleStatus) {
this.doubleIntervalId = setInterval(() => this.socket.ws.send(`b|doubleFlash|1`), 1000)
} else {
clearInterval(this.doubleIntervalId)
this.socket.ws.send(`b|doubleFlash|0`)
}
}
$(".obstacle_wrap").onclick = () => {
this.play();
// 1 true, 0 false
this.obstacleStatus = !this.obstacleStatus
$(".obstacle_wrap").classList.toggle("open")
localStorage.setItem("obstacleStatus", this.obstacleStatus)
this.socket.ws.send(`b|obstacle|${this.obstacleStatus ? '1' : '0'}`)
}
}
play() {
$(".actionAudio").play();
}
}
new IndexPage();

View File

@@ -0,0 +1,20 @@
class Utils {
$(className) {
return document.querySelector(className);
}
debounce(fn, delay = 1000) {
let timer = null //借助闭包
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(fn, delay) // 简化写法
}
}
}
var { $, debounce } = new Utils();

View File

@@ -0,0 +1,133 @@
class Joystick {
// 元素id最大移动距离
constructor(stickID, direction, callback) {
this.id = stickID;
this.maxDistance = 64;
this.direction = direction;
this.callback = callback;
this.time = null
this.stick = document.getElementById(stickID);
// 开始拖动的位置,用于计算偏移量
this.dragStart = null;
// 如果存在多个操纵手柄,则跟踪触摸标识符
this.touchId = null;
this.active = false;
this.EventListener();
}
handleDown(event) {
this.active = true;
this.stick.style.transition = '0s';
// 阻止默认事件
event.preventDefault();
// 记录初始点击的坐标
if (event.changedTouches) {
this.dragStart = { x: event.changedTouches[0].clientX, y: event.changedTouches[0].clientY };
} else {
this.dragStart = { x: event.clientX, y: event.clientY };
}
// console.log("event: ", event);
// console.log("event.changedTouches: ", event.changedTouches);
// 记录点击的id
if (event.changedTouches) {
this.touchId = event.changedTouches[0].identifier;
}
}
handleMove(event) {
if (!this.active) return;
// 处理多点触控
if (event.changedTouches) {
for (let i = 0; i < event.changedTouches.length; i++) {
if (this.touchId == event.changedTouches[i].identifier) {
event.clientX = event.changedTouches[i].clientX;
event.clientY = event.changedTouches[i].clientY;
}
}
}
// 计算 x 轴距离
const xDiff = event.clientX - this.dragStart.x;
// 计算 y 轴距离
const yDiff = event.clientY - this.dragStart.y;
// 计算 角度
const angle = Math.atan2(yDiff, xDiff);
// 计算 距离
const distance = Math.min(this.maxDistance, Math.hypot(xDiff, yDiff));
// 计算 xy 的新位置
const xPosition = distance * Math.cos(angle);
const yPosition = distance * Math.sin(angle);
if (this.time && (Date.now() - this.time) < 160) return
this.time = Date.now()
if (this.direction == "x") {
// console.log("xPosition: ", Math.sign(xPosition));
// 左边 0 ~ -64 -1 3
if (xPosition < 0) {
if (xPosition < -63) {
console.log("左边: ", xPosition);
this.callback(3)
}
// 右边 1 4
} else if (xPosition > 0){
if (xPosition > 63) {
console.log("右边: ", xPosition);
this.callback(4)
}
}
// 将图片移动到新位置
this.stick.style.transform = `translate3d(${xPosition}px,0px, 0px)`;
// this.callback(Math.sign(xPosition) == -1 ? 3 : 4)
} else {
console.log("yPosition: ", yPosition);
this.stick.style.transform = `translate3d(0px,${yPosition}px, 0px)`;
this.callback(Math.sign(yPosition) == -1 ? 1 : 2, Math.round(255 * Math.round(Math.abs(yPosition)) / 64))
}
}
handleUp(event) {
if (!this.active) return;
if (event.changedTouches && this.touchId != event.changedTouches[0].identifier) return;
this.active = false;
// console.log("3 handleUp ==== ", this.active);
this.stick.style.transition = '.2s';
this.stick.style.transform = `translate3d(0px, 0px, 0px)`;
this.touchId = null;
this.callback(0, 0)
}
EventListener() {
// 鼠标 & 手指 按下
this.stick.addEventListener('mousedown', this.handleDown.bind(this));
this.stick.addEventListener('touchstart', this.handleDown.bind(this));
// 鼠标 & 手指 移动
document.addEventListener('mousemove', this.handleMove.bind(this), { passive: false });
document.addEventListener('touchmove', this.handleMove.bind(this), { passive: false });
// 鼠标 & 手指 松开
document.addEventListener('mouseup', this.handleUp.bind(this));
document.addEventListener('touchend', this.handleUp.bind(this));
}
}

View File

@@ -0,0 +1 @@
e6d74282-79e8-4c4b-82d4-91695dc62e77

View File

@@ -0,0 +1 @@
60f5f62f-30da-4cf5-b7c7-ee2d02be432a

View File

@@ -0,0 +1,24 @@
#include "buzzer.h"
#include "../config/config.h"
void Buzzer::init()
{
Serial.println("Buzzer init....");
pinMode(BuzzerPin, OUTPUT);
digitalWrite(BuzzerPin, LOW);
}
void Buzzer::on()
{
digitalWrite(BuzzerPin, HIGH);
}
void Buzzer::off()
{
digitalWrite(BuzzerPin, LOW);
}
void Buzzer::warning()
{
on();
delay(200);
off();
}

View File

@@ -0,0 +1,14 @@
#ifndef _BUZZER_H__
#define _BUZZER_H__
#include <Arduino.h>
class Buzzer
{
public:
void init();
void on();
void off();
void warning();
};
#endif

View File

@@ -0,0 +1 @@
7c54a6e6-291e-460a-b441-b17b9557640e

View File

@@ -0,0 +1,52 @@
#ifndef _CONFIG_H__
#define _CONFIG_H__
#include <Arduino.h>
// 自己家中的 2.4G wifi名称
#define WifiName "bmy"
// 自己家中的 2.4G wifi密码
#define WifiPwd "lb714500"
// WiFiManager 自动生成的wifi名和密码用于小车的自动配网
#define AutoWiFiManagerName "AutoWiFiManager"
#define AutoWiFiManagerPwd "123456789"
#define WebSocketsPort 81
#define WebServerPort 80
// 蜂鸣器
#define BuzzerPin 1 // TX 引脚
// 双闪灯(转向灯)
#define BottomLeftPin 10 // SD3 引脚
#define BottomRightPin 3 // RX 引脚
// 小车的五种 状态码
#define STOP 0 // 停止
#define FORWARD 1 // 前进
#define BACKWARD 2 // 后退
#define TURNLEFT 3 // 左转
#define TURNRIGHT 4 // 右转
#define STRAIGHT 5 // 直行
// 电机驱动接线说明
// D5, D6, D8, D2 是 PWM引脚可以用来控制减速电机的速度 0 ~ 255
// D0, D1, D3, D4, D7 是普通引脚,可以通过高低电压来控制车轮转向
#define leftMotor1 16 // D0 红线
#define leftMotor2 5 // D1 褐线
#define leftPWM 4 // D2 橙线
#define rightMotor1 0 // D3 黑线
#define rightMotor2 2 // D4 白线
#define rightPWM 14 // D5 灰线
// 转向舵机
#define servoPin 13 // D7 白线
class Config
{
};
#endif

View File

@@ -0,0 +1 @@
24e58c56-59f7-4ab6-979c-95196881b5f2

View File

@@ -0,0 +1,53 @@
#include "LED.h"
#include "../config/config.h"
void LED::init()
{
Serial.println("LED init....");
pinMode(BottomLeftPin, OUTPUT);
pinMode(BottomRightPin, OUTPUT);
analogWrite(BottomLeftPin, 0);
analogWrite(BottomRightPin, 0);
}
// 后边两个灯的双闪
void LED::open()
{
analogWrite(BottomLeftPin, 255);
analogWrite(BottomRightPin, 255);
delay(300);
analogWrite(BottomLeftPin, 0);
analogWrite(BottomRightPin, 0);
delay(300);
}
void LED::close()
{
analogWrite(BottomLeftPin, 0);
analogWrite(BottomRightPin, 0);
}
// 开左转灯
void LED::TurnLeft()
{
analogWrite(BottomLeftPin, 255);
}
// 关左转灯
void LED::TurnLeftClose()
{
analogWrite(BottomLeftPin, 0);
}
// 开右转灯
void LED::TurnRight()
{
analogWrite(BottomRightPin, 255);
}
// 关右转灯
void LED::TurnRightClose()
{
analogWrite(BottomRightPin, 0);
}

View File

@@ -0,0 +1,19 @@
#ifndef _LED_H__
#define _LED_H__
#include <Arduino.h>
class LED
{
public:
void init();
void open();
void close();
void TurnLeft();
void TurnLeftClose();
void TurnRight();
void TurnRightClose();
};
#endif

View File

@@ -0,0 +1 @@
02f5af40-b6df-49d1-9324-aafad03ffbf8

View File

@@ -0,0 +1,124 @@
#include "motion.h"
#include "../config/config.h"
#include "../led/led.h"
#include <Servo.h>
Servo servo;
LED led;
// 初始化电机驱动各个引脚
void Motion::init() {
led.init();
pinMode(leftMotor1, OUTPUT);
pinMode(leftMotor2, OUTPUT);
pinMode(leftPWM, OUTPUT);
pinMode(rightMotor1, OUTPUT);
pinMode(rightMotor2, OUTPUT);
pinMode(rightPWM, OUTPUT);
servo.attach(servoPin);
servo.write(98);
}
// 设置小车速度
void Motion::speed(int LevelSpeed) {
// 设置运动的速度
analogWrite(leftPWM, LevelSpeed);
analogWrite(rightPWM, LevelSpeed);
}
// 运动函数
void Motion::run(int cmd)
{
switch (cmd)
{
case FORWARD:
digitalWrite(leftMotor1, LOW);
digitalWrite(leftMotor2, HIGH);
digitalWrite(rightMotor1, LOW);
digitalWrite(rightMotor2, HIGH);
led.TurnLeftClose();
led.TurnRightClose();
break;
case BACKWARD:
digitalWrite(leftMotor1, HIGH);
digitalWrite(leftMotor2, LOW);
digitalWrite(rightMotor1, HIGH);
digitalWrite(rightMotor2, LOW);
led.TurnRight();
led.TurnLeft();
break;
case TURNLEFT:
led.TurnLeft();
led.TurnRightClose();
servo.write(40);
break;
case TURNRIGHT:
led.TurnRight();
led.TurnLeftClose();
servo.write(140);
break;
case STRAIGHT:
led.TurnLeftClose();
led.TurnRightClose();
servo.write(100);
break;
default:
led.TurnLeftClose();
led.TurnRightClose();
digitalWrite(leftMotor1, LOW);
digitalWrite(leftMotor2, LOW);
digitalWrite(rightMotor1, LOW);
digitalWrite(rightMotor2, LOW);
}
}
// 控制舵机转向一定角度
void Motion::turn(int nums) {
servo.write(nums);
}
// 开启双闪
void Motion::open() {
led.open();
}
// 开启双闪
void Motion::close() {
led.close();
}
// 后退并前进
void Motion::left() {
// backOff();
speed(100);
delay(100);
run(1);
}
// 后退并前进
void Motion::right() {
// backOff();
speed(100);
delay(100);
run(1);
}
// 前进
void Motion::straight() {
// 设置电机速度
speed(100);
// 直行
servo.write(90);
delay(100);
run(1);
}
// 后退
void Motion::backOff() {
speed(180);
run(2);
delay(1000);
run(0);
delay(100);
}

View File

@@ -0,0 +1,20 @@
#ifndef _MOTION_H__
#define _MOTION_H__
#include <Arduino.h>
class Motion
{
public:
void init();
void run(int cmd);
void speed(int LevelSpeed);
void turn(int nums);
void open();
void close();
void left();
void right();
void straight();
void backOff();
};
#endif

View File

@@ -0,0 +1 @@
f498de60-f87e-4eaf-a7a0-3b7a83c2547a

View File

@@ -0,0 +1,173 @@
#include "socket.h"
#include <ESP8266WiFi.h>
#include <WebSocketsServer.h>
#include "../config/config.h"
#include "../motion/motion.h"
#include "../buzzer/buzzer.h"
Motion motionSocket;
Buzzer buzzerSocket;
Socket::Socket() {
Server = new WebSocketsServer(WebSocketsPort);
}
void Socket::init () {
// 启动WebSocket服务器
Server->begin();
// 指定事件处理函数
Server->onEvent([&](uint8_t num, WStype_t type, uint8_t *payload, size_t length) {
switch (type)
{
case WStype_CONNECTED:
Serial.println("New connection!");
break;
case WStype_DISCONNECTED:
Serial.println("Close the connection.");
break;
case WStype_TEXT:
String data = (char *)payload;
Serial.print("data ====== ");
Serial.println(data);
// digitalWrite(LED_BUILTIN, HIGH);
String type = getValue(data, '|', 0);
// 如果是红色摇杆
if (type == "y")
{
// 获取前进或是后退的方向
String direction = getValue(data, '|', 1);
Serial.println("direction ====== ");
Serial.print(direction);
// 获取前进或是后退的速度
String speed = getValue(data, '|', 2);
// 设置运动的速度
motionSocket.speed(speed.toInt());
// 设置前进的方向
motionSocket.run(direction.toInt());
// 如果是蓝色摇杆
}
else if (type == "x")
{
// 获取摇杆的左右方向
String level = getValue(data, '|', 1);
// 为0 表示松开摇杆,车轮恢复直行
if (!level.compareTo("0"))
{
motionSocket.run(5);
}
else
{
// 转向
motionSocket.run(level.toInt());
}
}
// 如果web前端的按钮操作
else if (type == "b")
{
String bType = getValue(data, '|', 1);
// 打开蜂鸣器
if (bType == "buzzer") {
buzzerSocket.warning();
// 开始自动避障
} else if (bType == "obstacle") {
String va = getValue(data, '|', 2);
SendClient(String("client|Automatic|")+String(va), IPAddress(192, 168, 4, 2));
} else if (bType == "fronLamp") {
String status = getValue(data, '|', 2);
SendClient(String("client|fronLamp|")+String(status), IPAddress(192, 168, 4, 2));
} else if (bType == "doubleFlash") {
String flash = getValue(data, '|', 2);
flash == "1" ? motionSocket.open() : motionSocket.close();
SendClient(String("client|doubleFlash|")+String(flash), IPAddress(192, 168, 4, 2));
} else if (bType == "openHand") {
String hand = getValue(data, '|', 2);
SendClient(String("client|AutoHand|")+String(hand), IPAddress(192, 168, 4, 2));
}
// 如果是各种传感器
} else if (type == "sensor") {
// 获取是哪一类传感器
String sensorType = getValue(data, '|', 1);
// 超声波传感器
if (sensorType == "ultrasonic") {
String actionType = getValue(data, '|', 2);
if (actionType == "stop") {
digitalWrite(LED_BUILTIN, HIGH);
motionSocket.run(0);
// 通知前端,自动停车后复位现在用户的拖动状态
// SendClient(String("server|y|reset"), IPAddress(192, 168, 4, 3));
} else {
digitalWrite(LED_BUILTIN, LOW);
}
// 控制舵机转动
} else if (sensorType == "servo") {
String pos = getValue(data, '|', 2);
motionSocket.turn(pos.toInt());
// 转弯并前进
} else if (sensorType == "motor") {
String turnDirection = getValue(data, '|', 2);
if (turnDirection == "left") {
motionSocket.left();
} else if (turnDirection == "right") {
motionSocket.right();
} else if (turnDirection == "straight") {
motionSocket.straight();
} else if (turnDirection == "back") {
motionSocket.backOff();
}
} else {
digitalWrite(LED_BUILTIN, LOW);
}
}
break;
}
});
}
String Socket::getValue(String data, char separator, int index) {
int found = 0;
int strIndex[] = {0, -1};
int maxIndex = data.length() - 1;
for (int i = 0; i <= maxIndex && found <= index; i++)
{
if (data.charAt(i) == separator || i == maxIndex)
{
found++;
strIndex[0] = strIndex[1] + 1;
strIndex[1] = (i == maxIndex) ? i + 1 : i;
}
}
return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}
//通过IP地址获取客户端编号
int8_t Socket::GetIp(IPAddress ip_needed) {
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
IPAddress ip = Server->remoteIP(i);
if(ip == ip_needed) {
// ip found
return i;
}
}
return -1;
}
// 向指定ip的设备发送消息
void Socket::SendClient(String data, IPAddress ip) {
Server->sendTXT(GetIp(ip), data);
}

View File

@@ -0,0 +1,20 @@
#ifndef _SOCKET_H__
#define _SOCKET_H__
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WebSocketsServer.h>
class Socket
{
public:
Socket();
void init();
WebSocketsServer *Server;
int8_t GetIp(IPAddress ip_needed);
void SendClient(String data, IPAddress ip);
private:
String getValue(String data, char separator, int index);
};
#endif

View File

@@ -0,0 +1 @@
c72523c9-04ee-40de-9873-84ae5ef8571b

View File

@@ -0,0 +1,85 @@
#include "web.h"
#include "../config/config.h"
#include <ESP8266WebServer.h>
#include <FS.h>
Web::Web()
{
WebController = new ESP8266WebServer(WebServerPort);
}
void Web::init()
{
// 启动闪存文件系统
if (SPIFFS.begin()) {
Serial.println("SPIFFS Started.");
} else {
Serial.println("SPIFFS Failed to Start.");
}
WebController->onNotFound(
std::bind(&Web::handleUserRequest, this)
);
WebController->begin();
Serial.println("HTTP server started");
}
void Web::handleUserRequest()
{
String reqResource = WebController->uri();
Serial.print("reqResource: ");
Serial.println(reqResource);
bool fileReadOK = handleFileRead(reqResource);
if (!fileReadOK)
{
WebController->send(404, "text/plain", "404 Not Found");
}
}
bool Web::handleFileRead(String resource)
{
if (resource.endsWith("/"))
{ // 如果访问地址以"/"为结尾
resource = "/index.html"; // 则将访问地址修改为/index.html便于SPIFFS访问
}
String contentType = getContentType(resource); // 获取文件类型
if (SPIFFS.exists(resource))
{ // 如果访问的文件可以在SPIFFS中找到
Serial.println("have file.....");
File file = SPIFFS.open(resource, "r"); // 则尝试打开该文件
WebController->streamFile(file, contentType); // 并且将该文件返回给浏览器
file.close(); // 并且关闭文件
return true; // 返回true
}
return false; // 如果文件未找到则返回false
}
String Web::getContentType(String filename)
{
if (filename.endsWith(".htm"))
return "text/html";
else if (filename.endsWith(".html"))
return "text/html";
else if (filename.endsWith(".css"))
return "text/css";
else if (filename.endsWith(".js"))
return "application/javascript";
else if (filename.endsWith(".png"))
return "image/png";
else if (filename.endsWith(".gif"))
return "image/gif";
else if (filename.endsWith(".jpg"))
return "image/jpeg";
else if (filename.endsWith(".ico"))
return "image/x-icon";
else if (filename.endsWith(".xml"))
return "text/xml";
else if (filename.endsWith(".pdf"))
return "application/x-pdf";
else if (filename.endsWith(".zip"))
return "application/x-zip";
else if (filename.endsWith(".gz"))
return "application/x-gzip";
return "text/plain";
}

View File

@@ -0,0 +1,19 @@
#ifndef _WEB_H__
#define _WEB_H__
#include <Arduino.h>
#include <ESP8266WebServer.h>
class Web
{
public:
Web();
void init();
ESP8266WebServer *WebController;
private:
void handleUserRequest();
bool handleFileRead(String resource);
String getContentType(String filename);
};
#endif

View File

@@ -0,0 +1 @@
302e0f8f-350d-4802-9546-03c85d2d5457

View File

@@ -0,0 +1,21 @@
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include "wifi.h"
#include "../config/config.h"
// 清除之前的wifi配置
void ConfigWifi::init() {
// 设置WiFi运行模式为无线终端模式
WiFi.mode(WIFI_AP);
Serial.println(WiFi.softAP(AutoWiFiManagerName, AutoWiFiManagerPwd) ? "Ready" : "Failed!");
Connect();
}
void ConfigWifi::Connect() {
Serial.print("Wifi Name: ");
Serial.println(AutoWiFiManagerName);
Serial.print("IP address:");
Serial.println(WiFi.softAPIP());
}

View File

@@ -0,0 +1,17 @@
#ifndef _WIFI_H__
#define _WIFI_H__
#include <Arduino.h>
#include <WiFiManager.h>
class ConfigWifi
{
private:
WiFiManager wifiManager;
public:
void init();
void Connect();
};
#endif