ESP32 项目笔记:基于ESP32主控和立创EDA的蓝牙控制电动滑板车 (适配Mixly图形化编程)


在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


1. 按键控制电机PWM输出

1.1 硬件设计

按键 五向开关
在这里插入图片描述

手柄遥控器(按键):
18 下拉输入 – 前按 --转动
19 下拉输入-- 后按 --方向控制Flag

直流电机控制器:
26 数字输出–电机方向控制,按1下PIN19调换转动方向,其他(按2下)方向不变
25 PWM: 模拟输出

注意:G25 和 G26 外接控制电机控制器两根线原本是用来控制电机正反转的,若两根线短接一次就调换一次电机转向;若是两根线断开,不接任何信号,电机一直转动。这就是为什么ESP32扩展板上面没有插上ESP32主控开发板,电机一直转动的原因

1.2 程序设计

在这里插入图片描述

pin26 一低一高 调换电机转动的方向
在这里插入图片描述
其实,第一个 pin26 设为 高可以不要,程序就成了以下:
在这里插入图片描述

在这里插入图片描述

1.3 线性控制

电机启动加速,停止减速的时候不能突然停下来,而是缓慢地降速或加速。

在这里插入图片描述


2. ADC测量电压

在这里插入图片描述

电阻串联组成分压电路:把29.4V电压分成11份,1K电阻占1份,10K电阻占10

在这里插入图片描述

模数转换器 (ADC),使用 ESP32 读取模拟值意味着您可以测量 0V~29.4V之间的不同电压电平。

模拟输入引脚具有 12 位分辨率。 这意味着当您读取模拟输入时,其范围可能会在 0 到 4095 之间变化。将测量的电压分配给 04095 (4095 = 2^12 -1)之间的值,其中 0 V 对应于 0,29.4V 对应于 4095。等比例放大 139.2857142857143 。

ADC是非线性的理想情况下,您会期望在使用 ESP32 ADC 引脚时具有线性行为。然而,这不会发生。

意味着您的 ESP32 无法区分 29.3 V 和 29.4 V。两种电压的值相同:4095。对于非常低的电压值也会发生同样的情况:对于 0 V 和 0.1 V,您将获得相同的值:0。使用 ESP32 ADC 引脚时需要牢记这一点。

2.1 剩余电量百分比%检测

通过单片机读取模拟信号 GPIO34, 范围是0~4095,然后 map函数映射将电压转换为0~100后显示在oled屏幕上。

display.print(map(34,0,4095,0,100));

2.2 读取ADC值计算成电压程序

/**
  * @brief  通过读取20次ADC并计算平均值后计算出电源电压
  * @param  chn:   ADC的通道      analogRead(ADC_PIN)
  * @retval volt: 电压
  */
float getBatteryVoltServer(const int chn){
  int adc_temp = 0;     // 求和
  int average_val = 0;  // 平均值
  float volt;           // 电压
  const float revise = 1.05;    // 1.052759767579195
  for (int t = 0; t < 20; t++){
      adc_temp += analogRead(chn); // GPIO 34  
      delay(5);
  }
  average_val = adc_temp/20;//求平均值
  volt = average_val*3.3*11.0*revise/1.0/4096; // Revise = 1.03 ,2^12(采样率)=4096  
  return volt; 

}

*2.3 ESP32 ADC /DAC

网友:esp32的adc不行,硬件问题,这是向来的坑?

float adc;
adc = analogRead(ADC_PIN) * 4.2 / 4095;           //  ADC 原始结果计算电压公式:Vout = Dout * Vmax / Dmax
adc = analogReadMilliVolts(ADC_PIN) * 11 / 1000.0;  // 10k + 1k 电阻串联分4.2V电压  1V = 1000mV

ESP32的ADC 和DAC 使用要点总结:

  • 1.要用 dacWrite() 而非 analogWrite() 函数,前者 输出模拟信号,后者输出 PWM 信号;
  • 2.要使用analogReadMilliVolts() 直接读取电压,这个函数会结合衰减系数和特征曲线进行计算。
  • 3.要把esp32 库升级到2.0.5版本以上,否则 analogReadMilliVolts() 读数是错误的。
  • 4.使用电阻分压,使待测电压降低到2500mV以内。
  • 5.根据 ADC原始结果计算电压,可以使用公式:Vout = Dout * Vmax / Dmax
    - Vout :电压
    - Dout :ADC 原始数字读数。
    - Vmax :最大可测量输入模拟电压,请参阅 ADC 衰减
    - Dmax:输出 ADC 原始数字读取结果的最大值,在 Single Read 模式下为 4095,在 Continuous Read 模式下为 4095。

参考资料:


3. 蜂鸣器

3.1 硬件设计

  • 蜂鸣器 C360615

在这里插入图片描述


4. OLED屏幕显示

4.1 硬件设计

  • GPIO 21 (SDA)
  • GPIO 22 (SCL)

4.2 程序设计

在这里插入图片描述
有个问题没有解决,在单独用USB数据线给开发板和扩展板连接的时候,OLED屏幕可以正常显示,但是连接在29.4V 和 电机控制器上的时候,OLED屏幕没有显示画面。后来发现是之前版本的esp32的扩展板有问题,跟换扩展板后问题解决。


5. 扩展板设计

在这里插入图片描述

问题:

1、 扩展板上的开关再打开的一瞬间,为什么会触发电机转动?

  • 原因:参考问题4

2、开发板单独连接USB供电,oled屏幕可以正常显示,但是连接在电机控制器上面就不能正常显示?

  • 原因:原扩展的问题

3、板载引出复位按钮,开发板下载程序后,需要按动RST复位按钮启动程序。

  • 原因:ESP32-DevKitC开发板设置问题

4、电源 ->扩展板->电机控制,扩展板上面设置电源总开关,这样既控制了扩展板的电源,也控制了电机控制器的电源。不然的话,电机控制器老是跟电源连接,在下载程序或者扩展板上没有插上开发板的时候,接上总电源 电机控制器会控制电机一直转动。

  • 原因: G25 和 G26 外接控制电机控制器两根线原本是用来控制电机正反转的,若两根线短接一次就调换一次电机转向;若是两根线断开,不接任何信号,电机一直转动。这就是为什么ESP32扩展板上面没有插上ESP32主控开发板(或者扩展板拨动开关的一瞬间)电机一直转动(或瞬间转动)的原因
  • 解决方法:可以利用扩展板上面的电源总开关,单刀双掷开关。开关拨动一侧,即控制扩展板、开发板或者电机控制器的电源;开关拨动到另一侧,电路连接电池的GNDG25管脚的电机PWM控制线连接,始终输出0,这样就禁止电机上电转动。参考:CD4053典型应用电路之模拟开关CD4053与单片机的连接。

5.1 LM2956 Buck电路原理

电路原理:由于LM2596属于开关型MOS管,通过一开一关控制输出电压,输出方波,要得到平滑的直流,需要输出端配合电容和电感组成LC低通滤波器。电感防止开关电路时电流的突变,且不消耗能量;电容防止电路中电压的突变。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

问题:

1、ESP32开发板不用29V转5V电源可以正常工作,使用LM2596 to 5V烧坏了两块开发板 ,开关电源输入电源滤波电容可能引起的问题 LM2596通断电之后损坏的原因分析

在这里插入图片描述

解决: 使用吸收上电尖峰性能更好的低ESR的铝电解电容或者钽电容来解决电压突变的问题,且合理的布局


5.1.1 元器件选型

参考:【B站@唐老师讲电赛】电源大师12—BUCK降压电路 BOOST升压电路,电感电容参数计算,前馈电容Cff计算

电容

电容和电感组成LC低通滤波器,电感防止电路中电流的突变,电容防止电路中电压的突变。

在这里插入图片描述

  • 220uf 25V 立创编号C12450

在这里插入图片描述

  • 0.1uF/100nF ±10% 50V 0805封装 ,并联Cin两端的钽电容 编号C1711

在这里插入图片描述

电感

电感防止电路中的电流突变。根据计算选择对应电感值、额定电流以及一体成型的电感。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 33uH ±20% 3A 107mΩ YSPI1050-330M 立创编号C497900 (推荐使用这种一体成型的电感)

在这里插入图片描述

二极管

同右侧的电感、电容组成一条完整的回路,避免电子堆积在电感的一侧烧坏电路。

以下是 1N5822 40V 3A 525mV@3A 肖特基二极管 ,立创编号C915962
在这里插入图片描述

SS34 表贴 二极管
在这里插入图片描述

5.1.2 LM2596 PCB Layout 设计

BUCK 开关电源PCB layout 宝典

在这里插入图片描述


5.1.3 滑动开关

  • SS12D11 G5 2档3脚
  • 立创商品编号 C319012 (直插,立式,不带固定柱)
  • 立创商品编号 C319010 (直插,卧式,带固定柱版)
  • 立创商品编号 C319015 (直插,卧式,不带固定柱版)
    在这里插入图片描述

RJ-45 以太网连接器

以太网连接器 俗称’水晶头’,专业术语为RJ-45连接器,属于双绞线以太网接口类型。RJ-45插头只能沿固定方向插入,设有一个塑料弹片与RJ-45插槽卡住以防止脱落。

  • RJ45 10P/8C 立创商品编号: C386758

在这里插入图片描述

注意:RJ-45只是用作导线连接器使用,选择引脚和凹槽内金属触点直通的,不是带网络变压器的连接座。


6. 遥控手柄

小技巧:通过专利检索网站可以快速查找市面上的遥控手柄的设计作参考。例如:【佰腾网】 一种新型电动滑板车遥控器及电动滑板车
在这里插入图片描述

在这里插入图片描述

  • 拨盘电位器
    在这里插入图片描述

在这里插入图片描述

  • 无人机/PS2摇杆

在这里插入图片描述

在这里插入图片描述

6.1 五向按键版

在这里插入图片描述

COMMON是公共端,可以根据需要接VCC或者GND,配置上拉或者下拉输入。CENTER是按键按下,其他A B C D分别对应上下左右方向按键。

在这里插入图片描述
在这里插入图片描述

6.2 摇杆版

  • 多功能开关 / 微型指拨摇杆开关 带中心按键 THB001P / 插件 立创商品编号:C2685355

在这里插入图片描述

在这里插入图片描述
有线RJ-45, 0.96寸oled ,无震动马达,摇杆版

在这里插入图片描述

在这里插入图片描述
有线RJ-45, 0.91寸oled ,震动马达,摇杆版

在这里插入图片描述

在这里插入图片描述


项目成果

硬件设计

立创EDA ESP32扩展板

第一版:

在这里插入图片描述

第一版烧坏了两块ESP32 WROOM 32 开发板,是因为DC BUCK降压电路没有设计好,PCB layout和电容的选型问题。

第二版:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

第三版:

在这里插入图片描述

修改后
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

立创EDA ESP32遥控手柄

第一版:

在这里插入图片描述

第二版:

在这里插入图片描述

第三版:

在这里插入图片描述

第四版:

为了更好设计外壳,去掉了四个耳朵,为了顶面放置电池,将带哪个组放到了底面。

在这里插入图片描述

在这里插入图片描述

集成ESP32开发板

在这里插入图片描述

在这里插入图片描述

备注:这一版存在蓝牙了连接通信距离短的问题


软件设计

五向按键版的遥控手柄

Mixly 程序

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Mixly 2.0

米思齐2.0版本 软件包比较大,有6G多。 利用blockly
在这里插入图片描述

Mixly 米思齐 图形化封装插件源代码路径:
在这里插入图片描述

D:\mixly\Mixly2.0-win32-x64\resources\app\board\arduino_esp32\libraries\ThirdParty

Mixly 米思齐 图形化依赖C代码头文件:
在这里插入图片描述
D:\mixly\Mixly2.0-win32-x64\arduino-cli\libraries\skateboard

资料下载

ESP32 for Arduino IDE

参考资料:

有线—五向按键版本
// V.04.22
#include <Wire.h>                // 使用I2C库  ESP32 GPIO22(SCL) GPIO21(SDA)
#include <Adafruit_GFX.h>        //Adafruit 库写入显示器
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128      // 使用的是 128×64 OLED 显示屏
#define SCREEN_HEIGHT 64 
#define LEDC_CHANNEL_0     0     // PWM通道 0 
#define LEDC_TIMER_13_BIT  13    // 占空比分辨率 13位   注意:使用1至2^13-1的值来控制亮度
#define LEDC_BASE_FREQ     5000  // PWM 信号频率 5000 Hz 
//#define PWM_PIN            25    // PWM输出 GPIO 25

const int ADC_PIN = 34;           // ADC GPIO 34 (Analog ADC1_CH6) 
const int PWM_PIN = 25;           // PWM GPIO 5
const int BUZ_PIN =  27;          // BUZZER GPIO 27
const int BUT_D_PIN = 15;         // BUTTON DOWN GPIO 15 (五向按键按下)

int limitflag  = 5000; 
int brightness = limitflag;    // PWM值变量
int fadeAmount = 5;    // PWM累增(减)值

int adcValue = 0;       // 存放读取的ADC数值

int buzState = LOW;                       // 蜂鸣器标识位
unsigned long previousMillis1 = 0;         // 记录蜂鸣器上次时间
const long interval = 50;               // 间隔时间 1000

boolean oledState = false;                 // 标识位 Flag
unsigned long previousMillis2 = 0;         // 记录oled上次时间

//  I2C 通信协议
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);// (-1) 参数表示您的 OLED 显示器没有 RESET 引脚

// PWM 输出控制函数
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {
  uint32_t duty = (8191 / valueMax) * min(value, valueMax);  //  8191 = 2 ^ 13 - 1
  ledcWrite(channel, duty);
}
// 外部中断服务函数
void IRAM_ATTR detectsMovement() {
  Serial.println("BUTTON DOWN !!!");
  oledState = true;     // 标识位 Flag
  previousMillis2 =  millis(); 
}
void setup() {
  
  Serial.begin(115200);
  pinMode(BUZ_PIN, OUTPUT);          //蜂鸣器设置为输出模式
  pinMode(BUT_D_PIN, INPUT_PULLDOWN);  //按钮设置为外部中断源 下拉输入模式 ,按键的电路设计也要是下拉,不然触发不了中断 
  // 中断引脚、中断服务函数、中断触发方式为FALLING
  attachInterrupt(digitalPinToInterrupt(BUT_D_PIN), detectsMovement, FALLING);
  //设置定时器并将定时器连接PWM引脚 
  ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
  ledcAttachPin(PWM_PIN, LEDC_CHANNEL_0);
  // 
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  delay(2000);
  display.clearDisplay();// 清除显示
  display.setTextSize(1);// 设置文本大小
  display.setTextColor(WHITE);// 设置文本颜色
  display.setCursor(0, 0);//设置显示坐标
  display.println(F("Naiva'S TEST"));// 
  display.setCursor(0, 20);//设置显示坐标
  display.println(F("ESP32 for Arduino IDE"));//
  display.display(); // 屏幕上实际显示文本
}

void loop() {
  unsigned long currentMillis = millis(); // 单片机内部定时器记录 当前时间
  
  // oled 屏幕  外部中断按键控制显示 和 间隔0.1S刷新屏幕
  if((oledState || (currentMillis - previousMillis2 >= 500)) && !(adcValue<=2800)){
    previousMillis2 = currentMillis;
    
    Serial.println("按键:刷新屏幕显示PWM + ADC ");
    display.clearDisplay();// 清除显示
    display.setTextSize(1);// 设置文本大小
    display.setTextColor(WHITE);// 设置文本颜色
    display.setCursor(0, 20);//设置显示坐标
    display.print(F("PWM: "));
    display.println(brightness);
    display.setCursor(0, 35);//设置显示坐标
    display.print(F("ADC: "));
    display.println(adcValue); 
    display.display(); // 屏幕上实际显示文本
    
    oledState = false;// 
   }
  // ADC 
    adcValue = analogRead(ADC_PIN);
    Serial.print("adcValue:");Serial.println(adcValue);
    
  // PWM 输出
    // ledcAnalogWrite(LEDC_CHANNEL_0, brightness);  //在LEDC通道0上设置PWM亮度
    ledcWrite(LEDC_CHANNEL_0,brightness);
    Serial.print("brightness:");Serial.println(brightness);
    brightness = brightness + fadeAmount;      // 改变亮度
    if (brightness <= limitflag || brightness >= 8000)  // 加/减PWM范围5000~ 8000
    {fadeAmount = -fadeAmount;}

  // 低电压报警 ADC 
  if(adcValue<=2800){ 
    if (currentMillis - previousMillis1 >= interval) {
    previousMillis1 = currentMillis; // 当前时间 变为 上次时间(previousMillis1) 以便下次使用
    
    Serial.println("屏幕显示:低电压报警!!!");
    display.clearDisplay();// 清除显示
    display.setTextSize(1);// 设置文本大小
    display.setTextColor(WHITE);// 设置文本颜色
    display.setCursor(0, 20);//设置显示坐标
    display.println(F("LOW WARNING!!!"));
    display.setCursor(0, 35);//设置显示坐标
    display.print(F("ADC: "));
    display.println(adcValue); 
    display.display(); // 屏幕上实际显示文本
    
     if(buzState == LOW) {buzState = HIGH;} else {buzState = LOW;}
     digitalWrite(BUZ_PIN, buzState);
    }
    }// 关闭蜂鸣器器
  else {digitalWrite(BUZ_PIN, LOW);}
    
    
  delay(100);
}

功能描述:

  • 按键输入控制加速,减速,刹车及电机正反转方向: 线性加速–按住五向按键的前进按键不放进行线性累加100,达到线性加速效果;线性减速–松开五向按键的前进按键,线性每次在原来PWM速度上累减100;刹车–按一下五向按键的后退按键实现紧急刹车;正反转控制–按下五向按键的中间按键ENTER,实现电机方向调换。
  • 电机PWM控制:初始化PWM通道设置及线性控制电机缓慢加速与减速。
  • ADC测量电压:ADC 显示剩余电量百分比%
  • 蜂鸣器低电压报警: 当读取到的ADC<=2500(limitLowADC 低电压阈值)时,进入防误判检测 checkState,确认低电压无误后,通过内部定时计数器实现蜂鸣器间隔 50ms 连续警报声。
  • 0.96OLED屏幕显示millis定时计数器实现实时刷新显示oled 屏幕,还可以通过按下五向按键的中间按键(ENTER),触发外部中断从而刷新屏幕。

在这里插入图片描述

// V.04.24
#include <Wire.h>                // 使用I2C库  ESP32 GPIO22(SCL) GPIO21(SDA)
#include <Adafruit_GFX.h>        //Adafruit 库写入显示器
#include <Adafruit_SSD1306.h>
// 初始化PWM通道设置
#define SCREEN_WIDTH 128         // 使用的是 128×64 OLED 显示屏
#define SCREEN_HEIGHT 64 
#define LEDC_CHANNEL_0     0     // PWM通道 0 
#define LEDC_TIMER_13_BIT  13    // 占空比分辨率 13位   注意:使用1至2^13-1的值来控制亮度
#define LEDC_BASE_FREQ     5000  // PWM 信号频率 5000 Hz 
//#define PWM_PIN            25    // PWM输出 GPIO 25

// 定义GPIO引脚
const int ADC_PIN = 34;           // ADC GPIO 34 (Analog ADC1_CH6) 
const int PWM_PIN = 25;           // PWM GPIO 25 
const int DIR_PIN = 26;           // DIRECTION GPIO  26 
const int BUZ_PIN =  27;          // BUZZER GPIO 27
const int BUT_D_PIN = 15;         // BUTTON DOWN GPIO 15    (五向按键-下)
const int BUT_F_PIN = 18;         // BUTTON FORWARD GPIO 18 (五向按键-前)
const int BUT_B_PIN = 19;         // BUTTON BACK GPIO 19    (五向按键按-后)


// 定义PWM相关变量
int limitfDownPWM  = 3000;          // PWM (下限)值
int limitfUpPWM    = 8000;          // PWM (上限)值
int targetPWM;                      // PWM 目标值 
int pwmValue = 3000;       // PWM值变量
int fadeAmount = 100;               // PWM累增(减)量

// 定义ADC相关变量
int adcValue = 0;          // 存放读取的ADC数值
int limitLowADC = 2500;    // 低电压变量

// 定义蜂鸣器相关变量
int buzState = LOW;                       // 蜂鸣器标识位
unsigned long previousMillis1 = 0;        // 记录蜂鸣器 上次时间
const long intervalBUZ = 50;              // 蜂鸣器鸣叫 间隔时间 50ms

// 定义OLED屏幕相关变量
boolean oledState = false;                 // 标识位 Flag
unsigned long previousMillis2 = 0;         // 记录OLED 上次时间 刷新时间
const long intervalOLED = 200;             // 屏幕刷新 间隔时间 200ms

//  I2C 通信协议
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);// (-1) 参数表示您的 OLED 显示器没有 RESET 引脚

// PWM 输出控制函数
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {
  uint32_t duty = (8191 / valueMax) * min(value, valueMax);  //  8191 = 2 ^ 13 - 1
  ledcWrite(channel, duty);
}
// 外部中断服务函数 -- 刷新屏幕和控制电机方向
void IRAM_ATTR detectsMovement() {
  Serial.println("BUTTON DOWN !!!");
  digitalWrite(DIR_PIN,LOW);						 // 电机 GPIO26 方向控制输出 0
  oledState = true;     							 // OLED 标识位 
  previousMillis2 =  millis(); 
}
void setup() {
  Serial.begin(115200);
  pinMode(BUZ_PIN, OUTPUT);             //GPIO27 蜂鸣器 设置为输出模式
  pinMode(DIR_PIN, OUTPUT);             //GPIO26 电机方向 设置为输出模式
  // 注意:GPIO一定要设置为内部下拉输入模式,否则读取到的数值是0和1来回跳动,导致加减速度时 PWM停滞在某一两个数值之间来回跳动
  pinMode(BUT_F_PIN, INPUT_PULLDOWN);            //GPIO18 五向按键按-前 设置为下拉输入模式
  pinMode(BUT_B_PIN, INPUT_PULLDOWN);            //GPIO19 五向按键按-后 设置为下拉输入模式
  pinMode(BUT_D_PIN, INPUT_PULLDOWN);   //GPIO19 五向按键按-下 设置为外部中断源 下拉输入模式 ,按键的电路设计也要是下拉,不然触发不了中断 
  
  // 中断引脚、中断服务函数、中断触发方式为FALLING
  attachInterrupt(digitalPinToInterrupt(BUT_D_PIN), detectsMovement, FALLING);
  
  //设置定时器并将定时器连接PWM引脚 
  ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
  ledcAttachPin(PWM_PIN, LEDC_CHANNEL_0);
  // 
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  display.clearDisplay();// 清除显示
  display.setTextSize(1);// 设置文本大小
  display.setTextColor(WHITE);// 设置文本颜色
  display.setCursor(0, 0);//设置显示坐标
  display.println(F("Naiva'S TEST"));// 
  display.setCursor(0, 20);//设置显示坐标
  display.println(F("ESP32 for Arduino IDE"));//
  display.display(); // 屏幕上实际显示文本
  delay(3000);
}

void loop() {
  unsigned long currentMillis = millis(); // 定时计数器=当前时间, millis()的返回值为类型unsigned long
  
// 定时计数器实现 -- 实时刷新oled 屏幕  外部中断按键控制刷新 和 实时刷新屏幕
  if((oledState || currentMillis - previousMillis2 >= intervalOLED) && !(adcValue <= limitLowADC)){
    previousMillis2 = currentMillis;
    
    // Serial.println("按键:刷新屏幕显示PWM and ADC ");
    display.clearDisplay();// 清除显示
    display.setTextSize(1);// 设置文本大小
    display.setTextColor(WHITE);// 设置文本颜色
    display.setCursor(0, 20);//设置显示坐标
    display.print(F("PWM: "));
    display.println(pwmValue);
    display.setCursor(0, 35);//设置显示坐标
    display.print(F("ADC: ")); display.print(map(adcValue,0,4095,0,100)); display.println(F("%"));// 显示剩余电量百分比%
    display.display(); // 屏幕上实际显示文本
    
    oledState = false; // 用于外部中断标签 
   }
// ADC 
    adcValue = analogRead(ADC_PIN);
    // Serial.print("adcValue:");Serial.println(adcValue);
    
// PWM 输出
	//GPIO18 BUT_F_PIN 油门
	if(digitalRead(BUT_F_PIN)==1){
		digitalWrite(DIR_PIN,HIGH);						 // GPIO26 方向控制 输出1 
		targetPWM = limitfUpPWM;                         // 线性加速 targetPWM= 8000  按下五向按键按-前
	}else{targetPWM = limitfDownPWM;}                    // 线性减速 targetPWM= 3000  松开五向按键按-前
    Serial.print("digitalRead(BUT_F_PIN):");Serial.println(digitalRead(BUT_F_PIN)); // 串口打印

	//GPIO19 BUT_B_PIN 刹车 按下五向按键按-后
	if(digitalRead(BUT_B_PIN)==1){
		pwmValue = 3000;                               // 紧急刹车 pwmValue=3000 
	}
	
	digitalWrite(DIR_PIN,HIGH);                          // GPIO26 方向控制 输出1	
	
	// pwmValue = pwmValue + fadeAmount;      // 改变亮度
	// if (pwmValue <= limitfDownPWM || pwmValue >= limitfUpPWM)  // 加/减PWM范围5000~ 8000
	// {fadeAmount = -fadeAmount;}
	
	if (pwmValue < targetPWM )  // 加/减速度,PWM范围是3000~ 8000
	{pwmValue = pwmValue + fadeAmount;}else {pwmValue = pwmValue - fadeAmount;}
    Serial.print("targetPWM:");Serial.println(targetPWM); // 串口打印
	
    // ledcAnalogWrite(LEDC_CHANNEL_0, pwmValue);         //在LEDC通道0上设置PWM亮度
    ledcWrite(LEDC_CHANNEL_0,pwmValue);                   // 设置PWM
    Serial.print("pwmValue:");Serial.println(pwmValue); // 串口打印PWM值

// 定时计数器实现 --低电压报警 ADC<=2500
  if(adcValue <= limitLowADC){ 
	static int count = 0;              // 静态变量 count, 累计记录ADC低电压状态 防止误判关闭总开关时的低电压触发蜂鸣器报警
    boolean checkState = false; 
	count ++;
	if(count >= 20){checkState = true;}else{checkState = false;}
    Serial.print("count:");Serial.println(count); // 串口打印

    if (adcValue <= limitLowADC && currentMillis - previousMillis1 >= intervalBUZ && checkState == true) {
    previousMillis1 = currentMillis; // 当前时间 变为 上次时间(previousMillis1) 以便下次使用
    
    // Serial.println("屏幕:低电压报警!!!");
    display.clearDisplay();// 清除显示
    display.setTextSize(1);// 设置文本大小
    display.setTextColor(WHITE);// 设置文本颜色
    display.setCursor(0, 20);//设置显示坐标
    display.println(F("LOW WARNING!!!"));
    display.setCursor(0, 35);//设置显示坐标
    display.print(F("ADC: ")); display.print(map(adcValue,0,4095,0,100)); display.println(F("%"));// 4095 = 2^12bit ADC 显示剩余电量百分比%
    display.display(); // 屏幕上实际显示文本
    
     if(buzState == LOW) {buzState = HIGH;} else {buzState = LOW;}
     digitalWrite(BUZ_PIN, buzState);
    }
    }// 关闭蜂鸣器器
  else {digitalWrite(BUZ_PIN, LOW);}

  delay(100);
}

// V.05.06
#include <Wire.h>                // 使用I2C库  ESP32 GPIO22(SCL) GPIO21(SDA)
#include <Adafruit_GFX.h>        //Adafruit 库写入显示器
#include <Adafruit_SSD1306.h>
// 初始化PWM通道设置
#define SCREEN_WIDTH 128         // 使用的是 128×64 OLED 显示屏
#define SCREEN_HEIGHT 64 
#define LEDC_CHANNEL_0     0     // PWM通道 0 
#define LEDC_TIMER_13_BIT  13    // 占空比分辨率 13位   注意:使用1至2^13-1的值来控制亮度
#define LEDC_BASE_FREQ     5000  // PWM 信号频率 5000 Hz 
//#define PWM_PIN            25    // PWM输出 GPIO 25

// 定义GPIO引脚
const int ADC_PIN = 34;           // ADC GPIO 34 (Analog ADC1_CH6) 
const int PWM_PIN = 25;           // PWM GPIO 25 
const int DIR_PIN = 26;           // DIRECTION GPIO  26 
const int BUZ_PIN =  27;          // BUZZER GPIO 27
const int BUT_D_PIN = 15;         // BUTTON DOWN GPIO 15    (五向按键-下)
const int BUT_F_PIN = 18;         // BUTTON FORWARD GPIO 18 (五向按键-前)
const int BUT_B_PIN = 19;         // BUTTON BACK GPIO 19    (五向按键按-后)


// 定义PWM相关变量
int limitfDownPWM  = 3000;          // PWM (下限)值
int limitfUpPWM    = 8000;          // PWM (上限)值
int targetPWM;                      // PWM 目标值 
int pwmValue = 3000;       // PWM值变量
int fadeAmount = 100;               // PWM累增(减)量

// 定义ADC相关变量
int adcValue = 0;          // 存放读取的ADC数值0~4095 -- 0V~29.4V
int limitLowADC = 1390;    // 低电压报警阈值变量 1390 -- 10V
boolean checkState = false; 

// 定义蜂鸣器相关变量
int buzState = LOW;                       // 蜂鸣器标识位
unsigned long previousMillis1 = 0;        // 记录蜂鸣器 上次时间
const long intervalBUZ = 50;              // 蜂鸣器鸣叫 间隔时间 50ms

// 定义OLED屏幕相关变量
boolean oledState = false;                 // 标识位 Flag
unsigned long previousMillis2 = 0;         // 记录OLED 上次时间 刷新时间
const long intervalOLED = 200;             // 屏幕刷新 间隔时间 200ms

//  I2C 通信协议
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);// (-1) 参数表示您的 OLED 显示器没有 RESET 引脚

// PWM 输出控制函数
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {
  uint32_t duty = (8191 / valueMax) * min(value, valueMax);  //  8191 = 2 ^ 13 - 1
  ledcWrite(channel, duty);
}
// 外部中断服务函数 -- 刷新屏幕和控制电机方向
void IRAM_ATTR detectsMovement() {
  Serial.println("BUTTON DOWN !!!");
  digitalWrite(DIR_PIN,LOW);             // 电机 GPIO26 方向控制输出 0
  oledState = true;                    // OLED 标识位 
  previousMillis2 =  millis(); 
}
void setup() {
  Serial.begin(115200);
  pinMode(BUZ_PIN, OUTPUT);             //GPIO27 蜂鸣器 设置为输出模式
  pinMode(DIR_PIN, OUTPUT);             //GPIO26 电机方向 设置为输出模式
  // 注意:GPIO一定要设置为内部下拉输入模式,否则读取到的数值是0和1来回跳动,导致加减速度时 PWM停滞在某一两个数值之间来回跳动
  pinMode(BUT_F_PIN, INPUT_PULLDOWN);            //GPIO18 五向按键按-前 设置为下拉输入模式
  pinMode(BUT_B_PIN, INPUT_PULLDOWN);            //GPIO19 五向按键按-后 设置为下拉输入模式
  pinMode(BUT_D_PIN, INPUT_PULLDOWN);   //GPIO19 五向按键按-下 设置为外部中断源 下拉输入模式 ,按键的电路设计也要是下拉,不然触发不了中断 
  
  // 中断引脚、中断服务函数、中断触发方式为FALLING
  attachInterrupt(digitalPinToInterrupt(BUT_D_PIN), detectsMovement, FALLING);
  
  //设置定时器并将定时器连接PWM引脚 
  ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
  ledcAttachPin(PWM_PIN, LEDC_CHANNEL_0);
  // 
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  display.clearDisplay();// 清除显示
  display.setTextSize(1);// 设置文本大小
  display.setTextColor(WHITE);// 设置文本颜色
  display.setCursor(0, 0);//设置显示坐标
  display.println(F("Naiva'S TEST"));// 
  display.setCursor(0, 20);//设置显示坐标
  display.println(F("ESP32 for Arduino IDE"));//
  display.display(); // 屏幕上实际显示文本
  delay(3000);
}

void loop() {
  unsigned long currentMillis = millis(); // 定时计数器=当前时间, millis()的返回值为类型unsigned long
  
// 定时计数器实现 -- 实时刷新oled 屏幕  外部中断按键控制刷新 和 实时刷新屏幕
  if((oledState || currentMillis - previousMillis2 >= intervalOLED) && !(adcValue <= limitLowADC)){
    previousMillis2 = currentMillis;
    
    // Serial.println("按键:刷新屏幕显示PWM and ADC ");
    display.clearDisplay();// 清除显示
    display.setTextSize(1);// 设置文本大小
    display.setTextColor(WHITE);// 设置文本颜色
    display.setCursor(0, 15);//设置显示坐标
    display.print(F("PWM: "));
    display.println(pwmValue);
    display.setCursor(0, 30);//设置显示坐标
  display.print(F("ADC: "));  display.println(adcValue);    // 4095 = 2^12bit 
    display.setCursor(0, 45);//设置显示坐标
    display.print(F("ADC: ")); display.print(map(adcValue,0,4095,0,100)); display.println(F("%"));// 显示剩余电量百分比%
    display.display(); // 屏幕上实际显示文本
    
    oledState = false; // 用于外部中断标签 
   }
// ADC 
    adcValue = analogRead(ADC_PIN);
    // Serial.print("adcValue:");Serial.println(adcValue);
    
// PWM 输出
  //GPIO18 BUT_F_PIN 油门
  if(digitalRead(BUT_F_PIN)==1){
    digitalWrite(DIR_PIN,HIGH);            // GPIO26 方向控制 输出1 
    targetPWM = limitfUpPWM;                         // 线性加速 targetPWM= 8000  按下五向按键按-前
  }else{targetPWM = limitfDownPWM;}                    // 线性减速 targetPWM= 3000  松开五向按键按-前
    Serial.print("digitalRead(BUT_F_PIN):");Serial.println(digitalRead(BUT_F_PIN)); // 串口打印

  //GPIO19 BUT_B_PIN 刹车 按下五向按键按-后
  if(digitalRead(BUT_B_PIN)==1){
    pwmValue = 3000;                               // 紧急刹车 pwmValue=3000 
  }
  
  digitalWrite(DIR_PIN,HIGH);                          // GPIO26 方向控制 输出1 
  
  // pwmValue = pwmValue + fadeAmount;      // 改变亮度
  // if (pwmValue <= limitfDownPWM || pwmValue >= limitfUpPWM)  // 加/减PWM范围5000~ 8000
  // {fadeAmount = -fadeAmount;}
  
  if (pwmValue < targetPWM )  // 加/减速度,PWM范围是3000~ 8000
  {pwmValue = pwmValue + fadeAmount;}else {pwmValue = pwmValue - fadeAmount;}
    Serial.print("targetPWM:");Serial.println(targetPWM); // 串口打印
  
    // ledcAnalogWrite(LEDC_CHANNEL_0, pwmValue);         //在LEDC通道0上设置PWM亮度
    ledcWrite(LEDC_CHANNEL_0,pwmValue);                   // 设置PWM
    Serial.print("pwmValue:");Serial.println(pwmValue); // 串口打印PWM值

// 定时计数器实现 --低电压报警 
  static int count = 0;              // 静态变量 count, 累计记录ADC低电压状态 防止误判关闭总开关时的低电压触发蜂鸣器报警
  if(adcValue <= limitLowADC){ 
  count ++;
  if(count >= 20 && checkState == false){checkState = true;}
    Serial.print("count:");Serial.println(count); // 串口打印

    if(adcValue <= limitLowADC && currentMillis - previousMillis1 >= intervalBUZ && checkState == true) {
    previousMillis1 = currentMillis; // 当前时间 变为 上次时间(previousMillis1) 以便下次使用
    
    // Serial.println("屏幕:低电压报警!!!");
    display.clearDisplay();// 清除显示
    display.setTextSize(1);// 设置文本大小
    display.setTextColor(WHITE);// 设置文本颜色
    display.setCursor(0, 15);//设置显示坐标
    display.println(F("LOW WARNING!!!"));
    display.setCursor(0, 30);//设置显示坐标
  display.print(F("ADC: "));  display.println(adcValue);    // 4095 = 2^12bit 
    display.setCursor(0, 45);//设置显示坐标
    display.print(F("ADC: ")); display.print(map(adcValue,0,4095,0,100)); display.println(F("%"));// 4095 = 2^12bit ADC 显示剩余电量百分比%
    display.display(); // 屏幕上实际显示文本
    
     if(buzState == LOW) {buzState = HIGH;} else {buzState = LOW;}
     digitalWrite(BUZ_PIN, buzState);
   
   checkState = false;
    }
    }// 关闭蜂鸣器器
  else {digitalWrite(BUZ_PIN, LOW);count = 0;}

  delay(100);
}

有线—摇杆开关版本

在这里插入图片描述

在这里插入图片描述

问题: 摇杆开关版本,去除了中断换向控制,不知道为什么,用中断按下摇杆中间按钮的时候,esp32 老重启

// V.05.07
#include <Wire.h>                // 使用I2C库  ESP32 GPIO22(SCL) GPIO21(SDA)
#include <Adafruit_GFX.h>        //Adafruit 库写入显示器
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128         // 使用的是 128×64 OLED 显示屏
#define SCREEN_HEIGHT 64 
// 初始化PWM通道设置
#define LEDC_CHANNEL_0     0     // PWM通道 0 
#define LEDC_TIMER_8_BIT   8      // 占空比分辨率 8位   2^8 = 256
#define LEDC_BASE_FREQ     5000  // PWM 信号频率 5000 Hz 

// 定义GPIO引脚
const int ADC_PIN = 34;           // ADC GPIO 34 (Analog ADC1_CH6) 电源
const int PWM_PIN = 25;           // PWM GPIO 25 
const int DIR_PIN = 26;           // Direction GPIO  26 
const int BUZ_PIN =  27;          // Buzzer GPIO 27
const int BUT_D_PIN = 18;         // Button down GPIO 18  (按键-下)
const int ADC_RS_Y_PIN = 35;         // Rocker switch forward&back GPIO 35 (ADC1_CH7)  (摇杆-Y-前后)
const int ADC_RS_X_PIN = 15;         // Rocker switch L&R GPIO 15 (ADC2_CH3)            (摇杆-X-左右)

// 定义电源ADC相关变量 GPIO34
int adcValue = 0;          // 存放读取的ADC数值0~4095 -- 0V~29.4V
int limitLowADC = 1390;    // 低电压报警阈值变量 1390 -- 10V
boolean checkState = false;

// 定义摇杆Y轴ADC相关变量 GPIO35
int rsyadcValue = 0;          // 存放读取的ADC数值0~4095 


// 定义PWM相关变量
int dutyCycle = 0;               // PWM 占空比


// 定义DIR方向相关变量
int dirState = HIGH; 
boolean buttonState = false;                 

// 定义蜂鸣器相关变量
int buzState = LOW;                       // 蜂鸣器标识位
unsigned long previousMillis1 = 0;        // 记录蜂鸣器 上次时间
const long intervalBUZ = 50;              // 蜂鸣器鸣叫 间隔时间 50ms

// 定义OLED屏幕相关变量
boolean oledState = false;                 // 标识位 Flag
unsigned long previousMillis2 = 0;         // 记录OLED 上次时间 刷新时间
const long intervalOLED = 200;             // 屏幕刷新 间隔时间 200ms
//  I2C 通信协议
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);// (-1) 参数表示您的 OLED 显示器没有 RESET 引脚

void setup() { 
  // Serial.begin(115200);
  pinMode(BUZ_PIN, OUTPUT);             //GPIO27 蜂鸣器 设置为输出模式
  pinMode(DIR_PIN, OUTPUT);             //GPIO26 电机方向 设置为输出模式
  pinMode(BUT_D_PIN, INPUT_PULLDOWN);   //GPIO18 按键按-下 设置为外部中断源 下拉输入模式 ,按键的电路设计也要是下拉,不然触发不了中断 
  digitalWrite(DIR_PIN,dirState);       //GPIO26 电机默认方向 -- HIGH
  
  // 设置定时器并将定时器连接PWM GPIO25号引脚 
  ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_8_BIT);
  ledcAttachPin(PWM_PIN, LEDC_CHANNEL_0);
  ledcWrite(LEDC_CHANNEL_0, dutyCycle); // 初始化PWM输出值设为0 禁止电机转动
  
  // OLED 屏幕初始化
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  display.clearDisplay();// 清除显示
  display.setTextSize(1);// 设置文本大小
  display.setTextColor(WHITE);// 设置文本颜色
  display.setCursor(0, 0);//设置显示坐标
  display.println(F("Naiva'S TEST"));// 
  display.setCursor(0, 20);//设置显示坐标
  display.println(F("ESP32 for Arduino IDE"));//
  display.display(); // 屏幕上实际显示文本
  delay(3000);
}



void loop() 
{  
  unsigned long currentMillis = millis(); // 定时计数器=当前时间, millis()的返回值为类型unsigned long

// ADC 电源
    adcValue = analogRead(ADC_PIN);
  //Serial.print("adcValue:");Serial.println(adcValue);
  
// ADC 摇杆-Y-前后
    rsyadcValue = analogRead(ADC_RS_Y_PIN);
  //Serial.print("rsyadcValue:");Serial.println(rsyadcValue);
      
// PWM 控制电机速度
    dutyCycle = (map(analogRead(ADC_RS_Y_PIN),2047,4095,0,255));
  //Serial.print("dutyCycle = ");Serial.println(dutyCycle);
  if(dutyCycle <= 0 || dutyCycle > 255){dutyCycle = 0;}
  ledcWrite(LEDC_CHANNEL_0,dutyCycle);
  
// Dir 控制电机方向
    if(digitalRead(BUT_D_PIN) == HIGH && buttonState == false)
  {
    dirState = !digitalRead(DIR_PIN);     //GPIO26电机方向控制
    digitalWrite(DIR_PIN,dirState);     // GPIO26 方向控制 输出HIGH/LOW
    buttonState = true;
  }
  else if (digitalRead(BUT_D_PIN) == LOW && buttonState == true)
  {
    buttonState = false;
  }
  
  // Serial.print("BUT_D_PIN18 = ");Serial.println(digitalRead(BUT_D_PIN));
  // Serial.print("DIR_PIN26 = ");Serial.println(digitalRead(DIR_PIN));
  // Serial.print("DIR_PINdirState = ");Serial.println(dirState);

//  屏幕显示
  if(currentMillis - previousMillis2 >= intervalOLED && !(adcValue <= limitLowADC))
  {
    previousMillis2 = currentMillis;
    display.clearDisplay();// 清除显示
    display.setTextSize(1);// 设置文本大小
    display.setTextColor(WHITE);// 设置文本颜色
    display.setCursor(0, 5);//设置显示坐标
    display.print(F("PWM: "));
    display.println(dutyCycle);
    display.setCursor(0, 20);//设置显示坐标
    display.print(F("ADC: "));  display.print(adcValue); display.print(" ");display.print(map(adcValue,0,4095,0,100)); display.println(F("%"));// 显示剩余电量百分比%       
    display.setCursor(0, 35);//设置显示坐标
    display.print(F("rsyadcValue: "));  display.println(rsyadcValue);    
    // display.print(F("rsyadcValue: ")); display.print(map(adcValue,0,4095,0,100)); display.println(F("%"));// 显示剩余电量百分比%
    display.display(); // 屏幕上实际显示文本
  }


// 定时计数器 -- 低电压报警 
  static int count = 0;              // 静态变量 count, 累计记录ADC低电压状态 防止误判关闭总开关时的低电压触发蜂鸣器报警
  if(adcValue <= limitLowADC){ 
    count ++;
    if(count >= 20 && checkState == false){checkState = true;}
    // Serial.print("count:");Serial.println(count); // 串口打印
      if(adcValue <= limitLowADC && currentMillis - previousMillis1 >= intervalBUZ && checkState == true) {
        previousMillis1 = currentMillis; // 当前时间 变为 上次时间(previousMillis1) 以便下次使用
        
        display.clearDisplay();// 清除显示
        display.setTextSize(1);// 设置文本大小
        display.setTextColor(WHITE);// 设置文本颜色
        display.setCursor(0, 5);//设置显示坐标
        display.print(F("PWM: "));
        display.println(dutyCycle);
        display.setCursor(0, 20);//设置显示坐标
        display.print(F("ADC: "));  display.print(adcValue); display.print(" ");display.print(map(adcValue,0,4095,0,100)); display.println(F("% warning"));// 显示剩余电量百分比%       
        display.setCursor(0, 35);//设置显示坐标
        display.print(F("rsyadcValue: "));  display.println(rsyadcValue);    
        display.display(); // 屏幕上实际显示文本
        
        if(buzState == LOW) {buzState = HIGH;} else {buzState = LOW;}
        digitalWrite(BUZ_PIN, buzState);
       
        checkState = false;
      }
    }// 关闭蜂鸣器器
  else {digitalWrite(BUZ_PIN, LOW);count = 0;}


  delay(100); 
}


总结:

至此,2022年8月第一版结束。

感觉自己无论是画PCB电路板时元器件选型,PCB 布局还是单片机C语言程序设计时数据类型的使用(byte,u8节省内存;结构体,指针也没有用)都不很专业。有待继续学习!

第一版资料总汇


分割线1


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Naiva

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值