当前位置: 首页 > news >正文

mqtt+esp32公网控制PIn 2 led灯

PlatformIO 配置

platformio.ini

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps =knolleary/PubSubClient @ ^2.8
build_flags =-D LED_PIN=2               ; 板载LED(可改成你的实际引脚)-D WIFI_SSID="你的WiFi"-D WIFI_PASS="你的WiFi密码"-D MQTT_HOST="你的外网MQTT域名或IP"-D MQTT_PORT=1883          ; 先用1883,跑通后可换8883-D MQTT_USER="你的mqtt用户名"-D MQTT_PASS="你的mqtt密码"-D DEVICE_ID="esp32-led-01"

main.cpp

点击查看代码
#include <WiFi.h>
#include <PubSubClient.h>// ====== 编译期注入的配置(见 platformio.ini)======
#ifndef WIFI_SSID
#define WIFI_SSID "YOUR_WIFI"
#endif
#ifndef WIFI_PASS
#define WIFI_PASS "YOUR_PASS"
#endif
#ifndef MQTT_HOST
#define MQTT_HOST "broker.example.com"
#endif
#ifndef MQTT_PORT
#define MQTT_PORT 1883
#endif
#ifndef MQTT_USER
#define MQTT_USER ""
#endif
#ifndef MQTT_PASS
#define MQTT_PASS ""
#endif
#ifndef DEVICE_ID
#define DEVICE_ID "esp32-led-01"
#endif
#ifndef LED_PIN
#define LED_PIN 2
#endif// ====== 主题约定 ======
String t_base         = String("home/") + DEVICE_ID;
String t_cmd          = t_base + "/cmd";
String t_state        = t_base + "/state";
String t_online       = t_base + "/online";  // LWT
String t_ip           = t_base + "/ip";
String t_rssi         = t_base + "/rssi";WiFiClient wifiClient;            // 先用非TLS,跑通后可改 WiFiClientSecure
PubSubClient mqttClient(wifiClient);unsigned long lastReportMs = 0;
bool ledOn = false;void setLed(bool on) {ledOn = on;digitalWrite(LED_PIN, on ? HIGH : LOW);// 上报状态(retained),方便手机端看到最新状态mqttClient.publish(t_state.c_str(), on ? "ON" : "OFF", true);
}void ensureWiFi() {if (WiFi.status() == WL_CONNECTED) return;WiFi.mode(WIFI_STA);WiFi.begin(WIFI_SSID, WIFI_PASS);Serial.print("Connecting WiFi");int tries = 0;while (WiFi.status() != WL_CONNECTED) {delay(500);Serial.print(".");if (++tries > 60) { // 约30秒Serial.println("\nWiFi connect timeout, retry...");WiFi.disconnect(true);delay(1000);WiFi.begin(WIFI_SSID, WIFI_PASS);tries = 0;}}Serial.println("\nWiFi connected: " + WiFi.localIP().toString());
}void mqttCallback(char* topic, byte* payload, unsigned int length) {String msg;msg.reserve(length);for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];Serial.printf("MQTT <- [%s] %s\n", topic, msg.c_str());if (String(topic) == t_cmd) {// 兼容纯文本与JSONString s = msg;s.toUpperCase();if (s == "ON") { setLed(true); return; }if (s == "OFF") { setLed(false); return; }// 简单JSON解析(不引lib,轻量处理)if (msg.indexOf("\"on\"")  != -1 || msg.indexOf("\"ON\"")  != -1) { setLed(true);  return; }if (msg.indexOf("\"off\"") != -1 || msg.indexOf("\"OFF\"") != -1) { setLed(false); return; }}
}void ensureMQTT() {if (mqttClient.connected()) return;mqttClient.setServer(MQTT_HOST, MQTT_PORT);mqttClient.setCallback(mqttCallback);// LWT 设置:掉线时 broker 自动发 "offline"// 注意:必须 connect() 时一次性传入String clientId = String(DEVICE_ID) + "-" + String((uint32_t)ESP.getEfuseMac(), HEX);Serial.printf("Connecting MQTT as %s ...\n", clientId.c_str());if (mqttClient.connect(clientId.c_str(),MQTT_USER, MQTT_PASS,t_online.c_str(), 1, true, "offline"   // LWT: topic, qos, retained, payload)) {Serial.println("MQTT connected.");// 上线标记mqttClient.publish(t_online.c_str(), "online", true);// 基础信息(retained)mqttClient.publish(t_ip.c_str(), WiFi.localIP().toString().c_str(), true);// 订阅控制主题mqttClient.subscribe(t_cmd.c_str());// 立即上报一次状态(retained)setLed(ledOn);} else {Serial.printf("MQTT connect failed, state=%d\n", mqttClient.state());}
}void setup() {Serial.begin(115200);pinMode(LED_PIN, OUTPUT);setLed(false);ensureWiFi();ensureMQTT();
}void loop() {if (WiFi.status() != WL_CONNECTED) ensureWiFi();if (!mqttClient.connected()) ensureMQTT();mqttClient.loop();// 每10秒上报一次 RSSI,便于远程诊断unsigned long now = millis();if (now - lastReportMs > 10000 && mqttClient.connected()) {lastReportMs = now;long rssi = WiFi.RSSI();char buf[16];snprintf(buf, sizeof(buf), "%ld", rssi);mqttClient.publish(t_rssi.c_str(), buf, false);}
}

setup-mosquitto.sh

点击查看代码
#!/usr/bin/env bash
set -euo pipefail# =========================
# Mosquitto quick installer
# - Creates MQTT user/password
# - Configures 1883 and 8883 (TLS)
# - Self-signed TLS by default, or Let's Encrypt with --letsencrypt
# - Enables and starts systemd service
# =========================USER_NAME=""
USER_PASS=""
DOMAIN=""
EMAIL=""
MODE="selfsigned" # or letsencryptlog() { echo -e "\033[1;32m[+] $*\033[0m"; }
warn(){ echo -e "\033[1;33m[!] $*\033[0m"; }
err() { echo -e "\033[1;31m[-] $*\033[0m" >&2; exit 1; }usage() {cat <<'EOF'
Usage:setup-mosquitto.sh -u <user> -p <pass> -d <domain> [--selfsigned|--letsencrypt] [-m <email>]Examples:sudo ./setup-mosquitto.sh -u mqttuser -p mqttpass --selfsigned -d mqtt.example.comsudo ./setup-mosquitto.sh -u mqttuser -p mqttpass --letsencrypt -d mqtt.example.com -m you@example.com
EOFexit 1
}# ---- Parse args ----
while [[ $# -gt 0 ]]; docase "$1" in-u) USER_NAME="${2:-}"; shift 2;;-p) USER_PASS="${2:-}"; shift 2;;-d) DOMAIN="${2:-}"; shift 2;;-m) EMAIL="${2:-}"; shift 2;;--selfsigned) MODE="selfsigned"; shift;;--letsencrypt) MODE="letsencrypt"; shift;;-h|--help) usage;;*) err "Unknown arg: $1";;esac
done[[ -z "$USER_NAME" || -z "$USER_PASS" || -z "$DOMAIN" ]] && usage
if [[ "$MODE" == "letsencrypt" && -z "$EMAIL" ]]; thenerr "Let's Encrypt mode requires -m <email>"
fi# ---- OS detect ----
if command -v apt-get >/dev/null 2>&1; thenPKG_MGR="apt"
elif command -v dnf >/dev/null 2>&1; thenPKG_MGR="dnf"
elif command -v yum >/dev/null 2>&1; thenPKG_MGR="yum"
elseerr "Unsupported OS: need apt, dnf or yum."
fi
log "Detected package manager: $PKG_MGR"# ---- Install packages ----
log "Installing Mosquitto and tools..."
if [[ "$PKG_MGR" == "apt" ]]; thenapt-get update -yDEBIAN_FRONTEND=noninteractive apt-get install -y mosquitto mosquitto-clients opensslif [[ "$MODE" == "letsencrypt" ]]; thenapt-get install -y certbotfi
else# Enable EPEL on RHEL-like for mosquitto if neededif ! rpm -q mosquitto >/dev/null 2>&1; thenif [[ "$PKG_MGR" != "apt" ]]; thenif ! rpm -q epel-release >/dev/null 2>&1; thenlog "Installing EPEL repository..."if [[ "$PKG_MGR" == "dnf" ]]; thendnf install -y epel-releaseelseyum install -y epel-releasefifififiif [[ "$PKG_MGR" == "dnf" ]]; thendnf install -y mosquitto mosquitto-clients openssl[[ "$MODE" == "letsencrypt" ]] && dnf install -y certbotelseyum install -y mosquitto mosquitto-clients openssl[[ "$MODE" == "letsencrypt" ]] && yum install -y certbotfi
fisystemctl enable mosquitto >/dev/null 2>&1 || true# ---- Prepare directories ----
CONF_DIR="/etc/mosquitto"
CONF_D="${CONF_DIR}/conf.d"
CERT_DIR="${CONF_DIR}/certs"
PASS_FILE="${CONF_DIR}/passwd"
mkdir -p "$CONF_D" "$CERT_DIR"# ---- Create MQTT user/password ----
log "Creating MQTT user ..."
# mosquitto_passwd will create file if not exist; -b for batch mode
mosquitto_passwd -b -c "$PASS_FILE" "$USER_NAME" "$USER_PASS"
chown mosquitto:mosquitto "$PASS_FILE"
chmod 640 "$PASS_FILE"# ---- TLS certs ----
CERT_FILE="${CERT_DIR}/server.crt"
KEY_FILE="${CERT_DIR}/server.key"
CHAIN_FILE="" # for LEif [[ "$MODE" == "selfsigned" ]]; thenlog "Generating self-signed certificate for CN=${DOMAIN} ..."openssl req -x509 -nodes -newkey rsa:2048 \-keyout "$KEY_FILE" \-out "$CERT_FILE" \-subj "/C=XX/ST=State/L=City/O=Org/OU=IT/CN=${DOMAIN}" \-days 825CHAIN_FILE="$CERT_FILE" # not used but keep var non-empty
elselog "Obtaining Let's Encrypt certificate for ${DOMAIN} (standalone) ..."# This will bind TCP/80 temporarily; ensure no web server occupies it.systemctl stop mosquitto || truecertbot certonly --standalone --non-interactive --agree-tos \-m "$EMAIL" -d "$DOMAIN"CERT_FILE="/etc/letsencrypt/live/${DOMAIN}/fullchain.pem"KEY_FILE="/etc/letsencrypt/live/${DOMAIN}/privkey.pem"CHAIN_FILE="$CERT_FILE"
fichown -R mosquitto:mosquitto "$CERT_DIR" || true
chmod 600 "$KEY_FILE" || true# ---- Write Mosquitto config ----
SECURE_CONF="${CONF_D}/secure.conf"
log "Writing Mosquitto config to ${SECURE_CONF} ..."
cat > "$SECURE_CONF" <<EOF
# ======= Generated by setup-mosquitto.sh =======
persistence true
persistence_location /var/lib/mosquitto/
log_timestamp true
log_type error
log_type warning
log_type notice
log_type information# Global auth
allow_anonymous false
password_file ${PASS_FILE}# Plain MQTT (LAN / testing). Comment out to disable.
listener 1883
protocol mqtt# TLS listener for external access
listener 8883
protocol mqtt
certfile ${CERT_FILE}
keyfile ${KEY_FILE}# Optional: tune keepalive/limits (uncomment if needed)
# max_inflight_messages 20
# max_queued_messages 1000
# autosave_interval 1800
EOF# ---- Firewall rules ----
if command -v ufw >/dev/null 2>&1; thenif ufw status | grep -q "Status: active"; thenlog "Opening ports 1883 and 8883 in ufw ..."ufw allow 1883/tcp || trueufw allow 8883/tcp || truefi
elif command -v firewall-cmd >/dev/null 2>&1; thenif systemctl is-active --quiet firewalld; thenlog "Opening ports 1883 and 8883 in firewalld ..."firewall-cmd --add-port=1883/tcp --permanent || truefirewall-cmd --add-port=8883/tcp --permanent || truefirewall-cmd --reload || truefi
elsewarn "No ufw/firewalld detected; ensure 1883/8883 are open in your security group."
fi# ---- Restart service ----
log "Restarting Mosquitto ..."
systemctl restart mosquitto# ---- Show summary ----
echo
log "Done. Summary:"
echo "  Broker (no TLS):  mqtt://${DOMAIN}:1883"
echo "  Broker (TLS):     mqtts://${DOMAIN}:8883"
echo "  User/Pass:        ${USER_NAME} / (hidden)"
echo "  Password file:    ${PASS_FILE}"
echo "  Config file:      ${SECURE_CONF}"
if [[ "$MODE" == "selfsigned" ]]; thenecho "  TLS (self-signed): ${CERT_FILE} / ${KEY_FILE}"warn "Clients must trust the self-signed cert (or disable cert validation for testing)."
elseecho "  TLS (LE):         ${CERT_FILE} / ${KEY_FILE}"warn "Certbot will NOT auto-reload mosquitto after renewal; consider a systemd timer hook."
fiecho
log "Quick test (without TLS):"
echo "  # Publish:"
echo "  mosquitto_pub -h ${DOMAIN} -p 1883 -u ${USER_NAME} -P '<pass>' -t test -m hello"
echo "  # Subscribe:"
echo "  mosquitto_sub -h ${DOMAIN} -p 1883 -u ${USER_NAME} -P '<pass>' -t test -v"
echo
log "Quick test (with TLS):"
if [[ "$MODE" == "selfsigned" ]]; thenecho "  mosquitto_sub -h ${DOMAIN} -p 8883 --capath /etc/ssl/certs -u ${USER_NAME} -P '<pass>' -t test -v --insecure"echo "  mosquitto_pub -h ${DOMAIN} -p 8883 --capath /etc/ssl/certs -u ${USER_NAME} -P '<pass>' -t test -m hello --insecure"
elseecho "  mosquitto_sub -h ${DOMAIN} -p 8883 --capath /etc/ssl/certs -u ${USER_NAME} -P '<pass>' -t test -v"echo "  mosquitto_pub -h ${DOMAIN} -p 8883 --capath /etc/ssl/certs -u ${USER_NAME} -P '<pass>' -t test -m hello"
fi# Optional hint for LE auto-reload
if [[ "$MODE" == "letsencrypt" ]]; thenechowarn "Tip: add a certbot deploy hook to reload mosquitto after renewal:"echo '  echo -e "#!/bin/sh\nsystemctl reload mosquitto" | tee /etc/letsencrypt/renewal-hooks/deploy/mosquitto-reload.sh'echo '  chmod +x /etc/letsencrypt/renewal-hooks/deploy/mosquitto-reload.sh'
fi
chmod +x setup-mosquitto.sh
sudo ./setup-mosquitto.sh -u mqttuser -p mqttpass --selfsigned -d mqtt.example.com
# 或者用 Let’s Encrypt:
# sudo ./setup-mosquitto.sh -u mqttuser -p mqttpass --letsencrypt -d mqtt.example.com -m you@example.com

报错是:Duplicate persistence_location。
说明全局项 persistence / persistence_location 在两个文件里都写了(主文件 mosquitto.conf 和我们写的 conf.d/secure.conf),Mosquitto 不允许重复定义。
按下面修:

0x01) 去掉 secure.conf 里的全局持久化两行

sudo sed -i 's/^\s*persistence\s\+.*/# &/' /etc/mosquitto/conf.d/secure.conf
sudo sed -i 's/^\s*persistence_location\s\+.*/# &/' /etc/mosquitto/conf.d/secure.conf

0x02)(可选保险)如果主配置里开了 port 1883,避免与 listener 1883 冲突

sudo sed -i 's/^\s*port\s\+1883/# port 1883/' /etc/mosquitto/mosquitto.conf
sudo sed -i 's/^\s*listener\s\+1883/# listener 1883/' /etc/mosquitto/mosquitto.conf
sudo sed -i 's/^\s*listener\s\+8883/# listener 8883/' /etc/mosquitto/mosquitto.conf

0x03) 重启并看状态

sudo systemctl restart mosquitto
sudo systemctl status mosquitto -l --no-pager

0x04) 快速验证

无 TLS
mosquitto_sub -h <你的域名或IP> -p 1883 -u <user> -P '<pass>' -t test -v &
mosquitto_pub  -h <你的域名或IP> -p 1883 -u <user> -P '<pass>' -t test -m hello

电脑命令行测试(局域网)

订阅状态

mosquitto_sub -h www.taotao01.fun -p 1883 -u mqttuser -P mqttpass -t home/esp32-led-01/state -v

控制 LED

# 开灯
mosquitto_pub -h www.taotao01.fun -p 1883 -u mqttuser -P mqttpass -t home/esp32-led-01/cmd -m ON
# 关灯
mosquitto_pub -h www.taotao01.fun -p 1883 -u mqttuser -P mqttpass -t home/esp32-led-01/cmd -m OFF

立刻停用 & 取消开机自启

sudo systemctl stop mosquitto
sudo systemctl disable mosquitto
sudo systemctl status mosquitto --no-pager关闭防火墙放行(按你系统其一执行)UFW:sudo ufw delete allow 1883/tcp || true
sudo ufw delete allow 8883/tcp || truefirewalld:sudo firewall-cmd --remove-port=1883/tcp --permanent || true
sudo firewall-cmd --remove-port=8883/tcp --permanent || true
sudo firewall-cmd --reload || true停用配置(不删,方便以后恢复)sudo mv /etc/mosquitto/conf.d/secure.conf /etc/mosquitto/conf.d/secure.conf.disabled
# 可选:清理账号文件
# sudo rm -f /etc/mosquitto/passwd检查端口是否已关闭sudo ss -ltnp | grep -E ':(1883|8883)' || echo "1883/8883 已关闭"以后想再用sudo mv /etc/mosquitto/conf.d/secure.conf.disabled /etc/mosquitto/conf.d/secure.conf
sudo systemctl enable mosquitto
sudo systemctl start mosquitto想彻底卸载(可选)sudo apt-get purge -y mosquitto mosquitto-clients
sudo rm -rf /etc/mosquitto /var/lib/mosquitto /var/log/mosquitto
# 如果之前用了 Let's Encrypt,还可:
# sudo certbot delete --cert-name www.taotao01.fun
http://www.aitangshan.cn/news/756.html

相关文章:

  • 题解:P4350 [CERC2015] Export Estimate
  • Nouveau——第三方开源NVIDIA驱动
  • (自适应手机端)政府机构网站模板 组织协会网站源码下载
  • OpenCV入门(18):图像边缘检测
  • GNOME桌面自动隐藏顶栏
  • 文件已经删除但空间未释放排查记录
  • 用通俗的语言讲讲音频格式中的位深
  • (自适应手机端)家私家纺网站模板 床上用品网站源码下载
  • PKC7150 高频交直流电流探头在智能工厂电力监测项目中的应用方案
  • 夏夜星空 - Karry
  • (自适应手机端)中英文双语网站模板 电子元件科研芯片网站模板
  • (PC+WAP)实验室化学仪器设备网站模板
  • 英伟达被约谈?国产替代迎来新机遇
  • 大型企业专属!项目管理软件排行榜TOP8,集成能力才是关键!
  • 5.多分支语句的简单运用
  • [Java/并发编程] 深度解析:Java 并行流(parallelStream) [JDK8-]
  • 实用指南:vue3对比vue2的性能优化和提升 :Vue 3 vs Vue 2
  • 最大流模板大全
  • cut命令
  • 重组蛋白表达系统|原核大肠杆菌|酵母|昆虫杆状病毒|哺乳动物表达系统
  • sort命令
  • Rocky10 编译安装 Asp.net Core_9 Nginx_1.28.0 Mariadb_11.8.3 Redis_8.2.0 (实测 笔记)
  • 8.13
  • STM32 Study Note
  • seq命令
  • UWA发布 | Unity手游性能年度蓝皮书
  • WPF优秀项目推荐:Stylet 一个非常轻量但强大的 ViewModel-First MVVM 框架
  • GNOME安装扩展配置工具及常用扩展
  • AtCoder Beginner Contest 410 (A - F)
  • 反向代理,重定向,forward