ESPS3-工控拓展板

一. 单功能测试

1.数字来输出

对于四个继电器 依次输出

// 定义需要控制的GPIO引脚
int gpioPins[] = {4, 5, 6, 7};
int pinCount = 4;

// 用于记录循环次数的变量(可选)
int loopCounter = 0;

void setup() {
  // 初始化串口通信,波特率设置为115200(常用调试波特率)
  Serial.begin(115200);
  // 等待串口连接稳定(对于某些开发板是必要的)
  while (!Serial) {
    ; // 等待串口就绪(仅对原生USB有效,一般可省略)
  }

  // 打印启动信息
  Serial.println("=== ESP32-S3 GPIO 顺序输出测试开始 ===");
  Serial.print("将控制 ");
  Serial.print(pinCount);
  Serial.println(" 个 GPIO 引脚。");

  // 将所有引脚初始化为输出模式
  for (int i = 0; i < pinCount; i++) {
    pinMode(gpioPins[i], OUTPUT);
    digitalWrite(gpioPins[i], LOW);
    // 日志:记录每个引脚的初始化状态
    Serial.print("GPIO ");
    Serial.print(gpioPins[i]);
    Serial.println(" 已初始化为输出,初始电平 LOW。");
  }
  Serial.println("初始化完成,开始循环输出。\n");
}

void loop() {
  // 增加循环计数,便于观察执行轮次
  loopCounter++;
  Serial.print("********** 第 ");
  Serial.print(loopCounter);
  Serial.println(" 轮循环开始 **********");

  // 依次控制每个引脚
  for (int i = 0; i < pinCount; i++) {
    int currentPin = gpioPins[i];

    // --- 输出高电平 ---
    digitalWrite(currentPin, HIGH);
    // 日志:记录引脚及其电平变化(带时间戳,利用 millis())
    Serial.print("[Time: ");
    Serial.print(millis());
    Serial.print(" ms] GPIO ");
    Serial.print(currentPin);
    Serial.println(" -> HIGH (3.3V)");

    // 高电平持续1秒
    delay(2000);

    // --- 输出低电平 ---
    digitalWrite(currentPin, LOW);
    // 日志:记录引脚回到低电平
    Serial.print("[Time: ");
    Serial.print(millis());
    Serial.print(" ms] GPIO ");
    Serial.print(currentPin);
    Serial.println(" -> LOW (0V)");

    // 注意:这里没有额外的延时,会立即切换到下一个引脚
    // 如果需要观察每个引脚低电平的停留时间,可在此处加一个短延时,但会影响顺序间隔
    // delay(100); // 可选:加一个微小间隔便于观察
    delay(2000);
  }

  // 一轮结束,打印换行以便区分
  Serial.println("--- 本轮循环结束,立即开始下一轮 ---\n");
  // loop() 函数会自然循环,无需额外 delay
}

2.开关量输入

当37 38 39 40四个引脚有24V 输入时,会有对应信号输出。

/*
 * ESP32-S3 数字输入调试示例
 * 引脚: GPIO 37, 38, 39, 40
 * 外部下拉电阻已安装,高电平有效(3.3V = 有输入,0V = 无输入)
 * 串口调试信息:实时打印引脚状态变化(上升沿/下降沿)
 */

// 定义输入引脚数组
const int inputPins[] = {37, 38, 39, 40};
const int pinCount = 4;

// 存储每个引脚上一次的状态(初始为 LOW)
int lastState[pinCount] = {LOW, LOW, LOW, LOW};

// 记录启动后的运行时间(用于日志时间戳)
unsigned long startTime = 0;

void setup() {
  // 初始化串口,波特率 115200
  Serial.begin(115200);
  // 等待串口稳定(对于基于USB的开发板可忽略)
  while (!Serial) {
    ; // 等待
  }

  // 打印启动信息
  Serial.println("========================================");
  Serial.println(" ESP32-S3 数字输入调试程序");
  Serial.println(" 引脚: 37, 38, 39, 40 (外部下拉电阻)");
  Serial.println(" 高电平(3.3V) = 有输入,低电平(0V) = 无输入");
  Serial.println("========================================\n");
  startTime = millis();

  // 初始化所有引脚为输入模式(不启用内部上拉/下拉)
  for (int i = 0; i < pinCount; i++) {
    pinMode(inputPins[i], INPUT);
    // 读取初始状态并保存
    lastState[i] = digitalRead(inputPins[i]);
    // 打印初始状态
    Serial.print("[初始化] GPIO ");
    Serial.print(inputPins[i]);
    Serial.print(" 状态: ");
    Serial.println(lastState[i] == HIGH ? "有输入 (HIGH)" : "无输入 (LOW)");
  }
  Serial.println("\n开始监控引脚状态变化...\n");
}

void loop() {
  // 遍历所有引脚
  for (int i = 0; i < pinCount; i++) {
    int currentPin = inputPins[i];
    int currentState = digitalRead(currentPin);   // 读取当前电平
    
    // 检测到状态变化(上升沿或下降沿)
    if (currentState != lastState[i]) {
      // 更新时间戳
      unsigned long elapsed = millis() - startTime;
      
      // 打印变化信息
      Serial.print("[");
      Serial.print(elapsed);
      Serial.print(" ms] GPIO ");
      Serial.print(currentPin);
      Serial.print(" 变化 -> ");
      
      if (currentState == HIGH) {
        Serial.println("有输入 (上升沿)");
      } else {
        Serial.println("无输入 (下降沿)");
      }
      
      // 更新该引脚的最后状态
      lastState[i] = currentState;
    }
  }
  
  // 可选的循环周期:为了避免过度占用串口,可增加一个小延时
  // 但检测变化是即时的,不加延时能最快捕捉。若需要减轻CPU负载,可加 delay(10)
  delay(10); // (可选)
}

3.模拟量 输出 4-20mA

/*
 * ESP32-S3 PWM 输出校准版
 * 引脚: GPIO1, 14位分辨率, 1000Hz
 * 百分比 0~100% 对应 0~20mA (可校准)
 * 最大有效PWM值限定为 15047 (根据实测校准)
 */

#define PWM_PIN         1
#define PWM_FREQ        1000
#define PWM_RESOLUTION  14
#define PWM_MAX_HARD     ((1 << PWM_RESOLUTION) - 1)  // 16383
// 根据您的实测,20mA 对应的 PWM 值(请根据实际电流调整)
#define PWM_MAX_EFFECTIVE 15036   // 100% 占空比对应的 PWM 值

const int PWM_MAX = PWM_MAX_EFFECTIVE;  // 程序内部使用的最大值

void setDutyPercent(int percent);
void setDutyRaw(int duty);
void printHelp();

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);

  analogWriteResolution(PWM_PIN, PWM_RESOLUTION);
  analogWriteFrequency(PWM_PIN, PWM_FREQ);
  pinMode(PWM_PIN, OUTPUT);

  Serial.println("\n========================================");
  Serial.println("   ESP32-S3 PWM 输出 (校准版)");
  Serial.println("========================================");
  Serial.printf("  GPIO 引脚        : %d\n", PWM_PIN);
  Serial.printf("  硬件最大PWM值    : %d\n", PWM_MAX_HARD);
  Serial.printf("  有效最大PWM值    : %d (对应 20mA)\n", PWM_MAX);
  Serial.println("  占空比 0~100% 线性对应 0~20mA");
  Serial.println("========================================\n");
  Serial.println("命令格式:");
  Serial.println("  • 输入 0~100       → 设置百分比");
  Serial.println("  • 输入 v 数值      → 直接设置PWM值 (0~PWM_MAX)");
  Serial.println("  • 输入 auto        → 自动扫描");
  Serial.println("  • 输入 help / ?    → 显示帮助");
  Serial.println();

  setDutyPercent(0);
}

void loop() {
  if (Serial.available() > 0) {
    String input = Serial.readStringUntil('\n');
    input.trim();
    if (input.length() == 0) return;

    // 调试打印
    Serial.print("🔍 原始输入: '");
    Serial.print(input);
    Serial.println("'");

    // 处理 auto(自动扫描)
    if (input.equalsIgnoreCase("auto")) {
      Serial.println("\n--- 开始自动循环扫描 (5%步进, 每步保持1秒) ---");
      for (int p = 0; p <= 100; p += 5) {
        setDutyPercent(p);
        delay(1000);
      }
      for (int p = 100; p >= 0; p -= 5) {
        setDutyPercent(p);
        delay(1000);
      }
      Serial.println("--- 自动循环结束 ---\n");
      printHelp();
      return;
    }

    // 帮助
    if (input.equalsIgnoreCase("help") || input.equals("?")) {
      printHelp();
      return;
    }

    // 直接数值命令 "v 数值"
    if (input.startsWith("v ") || input.startsWith("V ")) {
      String numStr = input.substring(2);
      numStr.trim();
      int raw = numStr.toInt();
      if (raw >= 0 && raw <= PWM_MAX) {
        setDutyRaw(raw);
      } else {
        Serial.printf("❌ 无效数值,请输入 0 ~ %d 之间的整数\n", PWM_MAX);
      }
      return;
    }

    // 百分比输入
    int percent = input.toInt();
    if (percent >= 0 && percent <= 100) {
      setDutyPercent(percent);
    } else {
      Serial.println("❌ 无效输入!请使用:0~100(百分比)或 v 数值(PWM值)");
      printHelp();
    }
  }
  delay(50);
}

// 百分比 → PWM值 (线性映射,0%→0, 100%→PWM_MAX)
void setDutyPercent(int percent) {
  percent = constrain(percent, 0, 100);
  int duty = (long)percent * PWM_MAX / 100;
  setDutyRaw(duty);
}

// 直接设置PWM值 (限制不超过PWM_MAX)
void setDutyRaw(int duty) {
  duty = constrain(duty, 0, PWM_MAX);
  analogWrite(PWM_PIN, duty);
  unsigned long now = millis();
  float percent = (float)duty * 100.0 / PWM_MAX;
  float current = (float)duty * 20.0 / PWM_MAX;   // 按0~20mA线性计算
  Serial.printf("[%lu ms] PWM值: %5d  (占空比: %6.2f%%)  → 电流: %5.2f mA\n",
                now, duty, percent, current);
}

void printHelp() {
  Serial.println("----------------------------------------");
  Serial.println("命令说明:");
  Serial.println("  • 输入 0~100           → 设置百分比");
  Serial.printf("  • 输入 v 数值 (0~%d) → 直接设置PWM值\n", PWM_MAX);   // 修正点:使用 printf
  Serial.println("  • 输入 auto             → 自动扫描");
  Serial.println("  • 输入 help 或 ?       → 显示帮助");
  Serial.println("----------------------------------------\n");
}

4.模拟量输入

/*
 * ESP32-S3 综合测试程序:PWM输出 (0-20mA) + ADS1115采集 (0-20mA)
 * ============================================================
 * PWM输出:  GPIO1, 14位, 1000Hz, 校准值15036对应20mA
 * ADS1115:  I2C SDA=GPIO9, SCL=GPIO10, 地址0x48 (ADDR接GND)
 *           采样电阻150Ω, 0-3.0V 对应 0-20mA
 * 串口命令: 波特率115200
 *   - 输入 0~100          : 设置PWM占空比百分比
 *   - 输入 v 数值         : 直接设置PWM计数值 (0~15036)
 *   - 输入 auto           : 自动扫描0→100→0 (步进5%, 间隔1s)
 *   - 输入 read           : 立即读取一次ADS1115并打印
 *   - 输入 help / ?       : 显示帮助
 *   - 输入(空)          : 持续自动打印采集值 (默认)
 */

#include <Wire.h>
#include <Adafruit_ADS1X15.h>

// ===================== PWM 输出配置 =====================
#define PWM_PIN         1
#define PWM_FREQ        1000
#define PWM_RESOLUTION  14
#define PWM_MAX_HARD    ((1 << PWM_RESOLUTION) - 1)  // 16383
#define PWM_MAX_EFFECTIVE 15036   // 根据校准,此值对应20mA

// ===================== ADS1115 配置 =====================
#define I2C_SDA         9
#define I2C_SCL         10
#define ADS1115_ADDR    0x48      // ADDR接GND
#define ADS1115_GAIN    GAIN_ONE  // ±4.096V
#define ADS_CHANNEL     0         // 使用A0通道

// ===================== 电压/电流映射 (150Ω) =====================
const float VOLTAGE_MIN = 0.0;    // 0mA 对应电压
const float VOLTAGE_MAX = 3.0;    // 20mA 对应电压 (20mA * 150Ω)
const float CURRENT_MIN = 0.0;
const float CURRENT_MAX = 20.0;

// ===================== 全局对象 =====================
Adafruit_ADS1115 ads;
int currentPercent = 0;           // 当前输出百分比

// ===================== 函数声明 =====================
void setDutyPercent(int percent);
void setDutyRaw(int duty);
void readAndPrintADC();
float mapFloat(float x, float in_min, float in_max, float out_min, float out_max);
void printHelp();

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);

  // -------- 初始化PWM --------
  analogWriteResolution(PWM_PIN, PWM_RESOLUTION);
  analogWriteFrequency(PWM_PIN, PWM_FREQ);
  pinMode(PWM_PIN, OUTPUT);
  setDutyPercent(0);   // 初始输出0mA

  // -------- 初始化I2C和ADS1115 --------
  Wire.begin(I2C_SDA, I2C_SCL);
  if (!ads.begin(ADS1115_ADDR, &Wire)) {
    Serial.println("❌ 未检测到ADS1115!请检查接线。");
    while (1) delay(1000);
  }
  ads.setGain(ADS1115_GAIN);

  // -------- 打印启动信息 --------
  Serial.println("\n========================================");
  Serial.println("   ESP32-S3 综合测试 (输出+采集)");
  Serial.println("========================================");
  Serial.printf(" PWM输出: GPIO%d, %dHz, %d位, 20mA对应PWM=%d\n", 
                PWM_PIN, PWM_FREQ, PWM_RESOLUTION, PWM_MAX_EFFECTIVE);
  Serial.printf(" ADS1115: I2C GPIO%d(SDA) & GPIO%d(SCL), 地址0x%02X\n", 
                I2C_SDA, I2C_SCL, ADS1115_ADDR);
  Serial.printf(" 采样电阻: 150Ω, 满量程 %.1fV\n", VOLTAGE_MAX);
  Serial.println(" 映射: 0V→0mA, 3.0V→20mA");
  Serial.println("========================================\n");
  printHelp();
  Serial.println("\n开始运行,每秒自动采集并显示...\n");
}

void loop() {
  // -------- 处理串口命令 --------
  if (Serial.available() > 0) {
    String input = Serial.readStringUntil('\n');
    input.trim();
    if (input.length() == 0) return;

    // 调试打印原始输入(可选)
    // Serial.print("🔍 原始输入: '"); Serial.print(input); Serial.println("'");

    // -------- 命令: auto (自动扫描) --------
    if (input.equalsIgnoreCase("auto")) {
      Serial.println("\n--- 开始自动扫描 (5%步进, 间隔1s) ---");
      for (int p = 0; p <= 100; p += 5) {
        setDutyPercent(p);
        delay(1000);
        readAndPrintADC();   // 每步采集一次
      }
      for (int p = 100; p >= 0; p -= 5) {
        setDutyPercent(p);
        delay(1000);
        readAndPrintADC();
      }
      Serial.println("--- 扫描结束 ---\n");
      printHelp();
      return;
    }

    // -------- 命令: read (立即读取) --------
    if (input.equalsIgnoreCase("read")) {
      readAndPrintADC();
      return;
    }

    // -------- 命令: help / ? --------
    if (input.equalsIgnoreCase("help") || input.equals("?")) {
      printHelp();
      return;
    }

    // -------- 命令: v 数值 (直接PWM值) --------
    if (input.startsWith("v ") || input.startsWith("V ")) {
      String numStr = input.substring(2);
      numStr.trim();
      int raw = numStr.toInt();
      if (raw >= 0 && raw <= PWM_MAX_EFFECTIVE) {
        setDutyRaw(raw);
        Serial.printf("✅ 设置PWM值: %d (约 %.2f%%)\n", raw, (float)raw*100/PWM_MAX_EFFECTIVE);
        readAndPrintADC();   // 设置后立即采集显示
      } else {
        Serial.printf("❌ 无效数值,请输入 0 ~ %d\n", PWM_MAX_EFFECTIVE);
      }
      return;
    }

    // -------- 命令: 百分比 (0~100) --------
    int percent = input.toInt();
    if (percent >= 0 && percent <= 100) {
      setDutyPercent(percent);
      Serial.printf("✅ 设置占空比: %d%%\n", percent);
      readAndPrintADC();   // 设置后立即采集显示
    } else {
      Serial.println("❌ 无效命令,输入 0~100 或 'help' 查看帮助。");
    }
  }

  // -------- 循环主体:每秒自动采集一次并显示 --------
  static unsigned long lastPrint = 0;
  if (millis() - lastPrint >= 1000) {
    lastPrint = millis();
    readAndPrintADC();   // 每秒打印一次当前采集值
  }
}

// ===================== PWM 设置函数 =====================
void setDutyPercent(int percent) {
  percent = constrain(percent, 0, 100);
  currentPercent = percent;
  int duty = (long)percent * PWM_MAX_EFFECTIVE / 100;
  setDutyRaw(duty);
}

void setDutyRaw(int duty) {
  duty = constrain(duty, 0, PWM_MAX_EFFECTIVE);
  analogWrite(PWM_PIN, duty);
  // 可选的调试输出:
  // Serial.printf("PWM set to %d\n", duty);
}

// ===================== ADS1115 读取与打印 =====================
void readAndPrintADC() {
  int16_t adcValue = ads.readADC_SingleEnded(ADS_CHANNEL);
  float voltage = ads.computeVolts(adcValue);
  float current = mapFloat(voltage, VOLTAGE_MIN, VOLTAGE_MAX, CURRENT_MIN, CURRENT_MAX);
  
  unsigned long now = millis();
  Serial.printf("[%lu ms] ADC=%5d  |  %.3f V  |  %5.2f mA  (输出设定: %d%%)\n",
                now, adcValue, voltage, current, currentPercent);
}

// ===================== 工具函数 =====================
float mapFloat(float x, float in_min, float in_max, float out_min, float out_max) {
  if (x < in_min) x = in_min;
  if (x > in_max) x = in_max;
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

void printHelp() {
  Serial.println("----------------------------------------");
  Serial.println("串口命令说明:");
  Serial.println("  输入 0~100        : 设置PWM占空比百分比");
  // 修正下面这一行:使用 printf 而不是 println
  Serial.printf("  输入 v 数值       : 直接设置PWM值 (0~%d)\n", PWM_MAX_EFFECTIVE);
  Serial.println("  输入 auto         : 自动扫描0→100→0 (步进5%, 间隔1s)");
  Serial.println("  输入 read         : 立即读取一次ADS1115并显示");
  Serial.println("  输入 help 或 ?   : 显示此帮助");
  Serial.println("  (无命令时)       : 每秒自动采集并显示");
  Serial.println("----------------------------------------");
}

5.RS485调试

/*
 * ESP32-S3 RS485 通信调试代码
 * 
 * 硬件连接:
 * - RO (接收)  -> GPIO11
 * - DI (发送)  -> GPIO12
 * - RE# & DE   -> GPIO15 (短接)
 * 
 * 功能: 程序启动后,会通过RS485每秒发送一条消息,并监听回应。
 *       所有通过RS485接收到的数据都会在串口监视器中打印出来。
 */

#include <HardwareSerial.h>

// 定义引脚
#define RS485_RX_PIN  11   // RO 引脚
#define RS485_TX_PIN  12   // DI 引脚
#define RS485_EN_PIN  15   // RE 和 DE 使能引脚 (短接)

// 创建硬件串口实例,使用 UART1
HardwareSerial rs485Serial(1);

void setup() {
  // 初始化调试串口 (连接电脑的USB)
  Serial.begin(115200);
  while (!Serial) {
    ; // 等待串口连接
  }
  Serial.println("ESP32-S3 RS485 调试程序启动");

  // 初始化使能引脚为输出模式
  pinMode(RS485_EN_PIN, OUTPUT);
  
  // 默认设置为接收模式 (RE=DE=LOW)
  digitalWrite(RS485_EN_PIN, LOW);

  // 初始化 RS485 串口
  // 参数: 波特率, 数据格式, RX引脚, TX引脚
  rs485Serial.begin(115200, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
  
  Serial.println("RS485 串口初始化完成,等待数据...");
}

void loop() {
  // ----- 1. 发送数据 (切换到发送模式) -----
  // 将RE和DE引脚拉高,进入发送模式
  digitalWrite(RS485_EN_PIN, HIGH);
  // 短暂延迟,确保模式切换稳定
  delay(1);

  // 发送一条测试消息
  rs485Serial.println("Hello from ESP32-S3!");
  Serial.println("RS485 已发送: Hello from ESP32-S3!");

  // 等待数据发送完成
  rs485Serial.flush();
  // 发送完毕后,立即切换回接收模式
  digitalWrite(RS485_EN_PIN, LOW);
  // 短暂延迟,确保模式切换稳定
  delay(1);

  // ----- 2. 接收数据 (处于接收模式) -----
  // 检查是否有数据从RS485总线传来
  if (rs485Serial.available()) {
    String receivedMessage = "";
    // 读取所有可用的数据
    while (rs485Serial.available()) {
      char c = rs485Serial.read();
      receivedMessage += c;
    }
    // 在串口监视器中打印接收到的数据
    Serial.print("RS485 接收到: ");
    Serial.println(receivedMessage);
  }

  // 等待1秒后进行下一轮循环
  delay(500);
}
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐