8266sg90舵机+WPF上位机

发布时间 2023-07-23 15:36:37作者: xXinG9199

上位机可以通过串口控制舵机、蜂鸣器。

示例:
image-20230723151914883
image-20230723152036627

Esp8266部分

基于Arduino,依赖库:U8g2、Servo。

接线引脚看代码哈。

#include <Arduino.h>
#include <Servo.h>
#include <U8g2lib.h>

/***OLED 引脚 */
#define SCL 5  /* 时钟引脚*/
#define SDA 4  /* 数据引脚*/
#define RES 14 /* 复位引脚*/
#define DC 12  /* 命令控制引脚*/
#define CS 13  /* 片选引脚*/

/***蜂鸣器 引脚 */
#define BuzzerPin 15

U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/SCL, /* data=*/SDA, /* cs=*/CS, /* dc=*/DC, /* reset=*/RES);
Servo sg90;
/**
 * @brief 初始化OLED屏幕
 *
 */
void OLED_Init(void);
/**
 * @brief OLED显示SG90的角度
 *
 * @param i 角度 0~180
 */
void OLED_SG90(int i);

/**
 * @brief 串口接收
 */
void Serial_Recv(void);

/**
 * @brief 设置SG90舵机角度
 *
 * @param angle 角度 0-180
 */
void Set_SG90(int angle);

/**
 * @brief 蜂鸣器初始化
 *
 */
void Init_Buzzer(void);

/**
 * @brief 设置蜂鸣器
 *
 * @param mode 模式
 * 1 -> di(短)
 * 2 -> di~(长)
 * 3 —> di~di(短、急促的)
 */
void Set_Buzzer(int mode);

void setup()
{
  Serial.begin(115200);
  OLED_Init();

  sg90.attach(2);
  Set_SG90(90);
  OLED_SG90(90);

  Init_Buzzer();
  Set_Buzzer(2);
}

void loop()
{
  Serial_Recv();
}

char buf[4] = {0x00};
void Serial_Recv()
{
  if (Serial.available() >= 4)
  {
    Serial.readBytes(buf, 4);

    /***报文: FF __ __ FE */
    if (buf[0] == 0xFF && buf[3] == 0xFE)
    {
      if (buf[1] == 0x00)
      {
        /**舵机: FF 00 __ FE*/
        Set_SG90(buf[2]);
        OLED_SG90(buf[2]);
        Serial.write(buf, 4);
        delay(20);
      }
      else if (buf[1] == 0x01)
      {
        /** 蜂鸣器: FF 01 __ FE*/
        Set_Buzzer(buf[2]);
        Serial.write(buf, 4);
        delay(20);
      }
    }
  }
}

void Set_SG90(int angle)
{
  sg90.write(angle);
}

char oled_buf[4];
void OLED_SG90(int i)
{
  u8g2.setCursor(50, 40);
  sprintf(oled_buf, "%03d", i);
  u8g2.print(oled_buf);
  u8g2.sendBuffer();
}

void OLED_Init(void)
{
  u8g2.begin();            // 初始化 u8g2
  u8g2.disableUTF8Print(); // 关闭UTF-8模式

  u8g2.setFont(u8g2_font_unifont_t_chinese2); // use chinese2 for all the glyphs of "你好世界"
  u8g2.setFontDirection(0);
  u8g2.clearBuffer();

  u8g2.setCursor(0, 15);
  u8g2.print("SG90:");

  u8g2.setCursor(20, 40);
  u8g2.print("P:");

  u8g2.sendBuffer();
}

void Init_Buzzer(void)
{
  pinMode(BuzzerPin, OUTPUT);
}

void Set_Buzzer(int mode)
{
  switch (mode)
  {
  case 0:
    digitalWrite(BuzzerPin, HIGH);
    delay(100);
    digitalWrite(BuzzerPin, LOW);
    break;
  case 1:
    digitalWrite(BuzzerPin, HIGH);
    delay(300);
    digitalWrite(BuzzerPin, LOW);
    break;
  case 2:
    digitalWrite(BuzzerPin, HIGH);
    delay(50);
    digitalWrite(BuzzerPin, LOW);
    delay(50);
    digitalWrite(BuzzerPin, HIGH);
    delay(50);
    digitalWrite(BuzzerPin, LOW);
    break;
  default:
    break;
  }
}

上位机部分

串口数据帧可以看esp代码

MainWindow.xaml

<Window x:Class="WpfApp1.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:WpfApp1"
  mc:Ignorable="d"
  Title="MainWindow"
  Height="250"
  Width="800">
  <StackPanel>
    <StackPanel HorizontalAlignment="Left"
      Orientation="Horizontal"
      Height="30"
      Margin="50 50 0 0">
      <Label Content="舵机:" />
      <Slider x:Name="slider"
        HorizontalAlignment="Left"
        Margin="10 0 0 0 "
        Minimum="0"
        Maximum="180"
        PreviewMouseUp="slider_MouseUp"
        Width="604"
        Value="{Binding Pos}"
        Height="23">
      </Slider>

      <Label x:Name="label"
        Content="{Binding Pos, StringFormat={}{0}d}"
        HorizontalAlignment="Left"
        Height="23"
        Width="40"
        Margin="10 0 0 0 "
        VerticalAlignment="Top" />

    </StackPanel>

    <StackPanel Orientation="Horizontal"
      HorizontalAlignment="Left"
      Margin="50 20 0 0">
      <Label Content="蜂鸣器:" />



      <Button  Height="25"
        Width="70"
        Margin="10 0 0 0 "
        Content="Mode 1"
        Click="btn_buzz_mode1"/>

      <Button  Height="25"
        Width="70"
        Margin="10 0 0 0 "
        Content="Mode 2"
        Click="btn_buzz_mode2"/>

      <Button  Height="25"
        Width="70"
        Margin="10 0 0 0 "
        Content="Mode 3"
        Click="btn_buzz_mode3" />       

      <Button  Height="25"
        Width="70"
        Margin="10 0 0 0 "
        Content="Mode 4"
        Click="btn_buzz_mode4" />

    </StackPanel>

  </StackPanel>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.IO.Ports;
using System.Threading;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        private SerialPort serialPort;
        private MainViewModel m;
        public MainWindow()
        {
            InitializeComponent();
            m = new MainViewModel();
            this.DataContext = m;

            // 初始化串口设置
            serialPort = new SerialPort("COM8", 115200); // 替换为实际的串口号和波特率

            serialPort.DataReceived += SerialPort_DataReceived;  // 接收回调
            serialPort.Open();
            if (serialPort.IsOpen == false)
            {
                Debug.WriteLine("串口打开失败,请检查是否有占用!");
            }
        }

        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            // 处理接收到的数据
            byte[] buffer = new byte[serialPort.BytesToRead];
            serialPort.Read(buffer, 0, buffer.Length);

            string receivedData = BitConverter.ToString(buffer).Replace("-", " ");
            Dispatcher.Invoke(() =>
            {
                //ReceivedDataTextBox.Text +=  + Environment.NewLine;
                Debug.WriteLine(receivedData);
            });
        }



        private void slider_MouseUp(object sender, MouseButtonEventArgs e)
        {

            byte[] bytes = new byte[4];
            bytes[0] = 0xFF;
            bytes[1] = 0x00;
            bytes[2] = BitConverter.GetBytes(m.Pos)[0];
            bytes[3] = 0xFE;
            serialPort.Write(bytes, 0, bytes.Length);
            Thread.Sleep(30);
        }

        private void btn_buzz_mode1(object sender, RoutedEventArgs e)
        {
            byte[] bytes = new byte[4];
            bytes[0] = 0xFF;
            bytes[1] = 0x01;
            bytes[2] = 0x00;
            bytes[3] = 0xFE;
            serialPort.Write(bytes, 0, bytes.Length);
            Thread.Sleep(30);
        }
        private void btn_buzz_mode2(object sender, RoutedEventArgs e)
        {
            byte[] bytes = new byte[4];
            bytes[0] = 0xFF;
            bytes[1] = 0x01;
            bytes[2] = 0x01;
            bytes[3] = 0xFE;
            serialPort.Write(bytes, 0, bytes.Length);
            Thread.Sleep(30);
        }

        private void btn_buzz_mode3(object sender, RoutedEventArgs e)
        {
            byte[] bytes = new byte[4];
            bytes[0] = 0xFF;
            bytes[1] = 0x01;
            bytes[2] = 0x02;
            bytes[3] = 0xFE;
            serialPort.Write(bytes, 0, bytes.Length);
            Thread.Sleep(30);
        }

        private void btn_buzz_mode4(object sender, RoutedEventArgs e)
        {
            byte[] bytes = new byte[4];
            bytes[0] = 0xFF;
            bytes[1] = 0x01;
            bytes[2] = 0x03;
            bytes[3] = 0xFE;
            serialPort.Write(bytes, 0, bytes.Length);
            Thread.Sleep(30);
        }
    }
}

MainViewModel.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpfApp1
{
    internal class MainViewModel : INotifyPropertyChanged
    {
        private int pos;

        public int Pos
        {
            get => pos;
            set
            {

                this.pos = value;
                RaisePropertyChanged("Pos");
            }
        }

        public event PropertyChangedEventHandler? PropertyChanged;
        private void RaisePropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}