舵機和LED使用了同一個GPIO,但他們確實可以正常使用。
原因是引腳只是給出一定脈寬的訊號,而舵機和LED分別對這個訊號做出響應。
20ms的脈衝週期,各自脈寬的表現
-
LED在20ms時熄滅,隨著脈寬減小,越來越亮。
-
舵機在0.5ms至2.5ms之間工作,其餘部分都不工作
因此只要設定LED亮度時避開舵機工作區間即可。
用digitalWrite
時LED只能被設定為最亮或熄滅,避開了舵機工作區間。
若使用analogWrite
控制LED亮度,同理只要避開舵機區間即可。
程式碼見下:(110行有測試)
#include <ESP8266WiFi.h>
#include <Servo.h>
#include <PubSubClient.h>
//WiFi config
const char* ssid = "Tenda_D7BEA8";
const char* pwd = "********";
//mqtt config
const char* mqtt_broker = "192.168.0.103";
const int mqtt_port = 1883;
const char* topic = "/lock";
WiFiClient espclient;
PubSubClient client(espclient); //必須傳一個client作為引數
//servo config
#define servo_pin 2 //GPIO2--D4 //和燈共用一個pin,但是控制舵機的脈寬只有一小部分。
#define servo_ON 60 //開門的角度
#define servo_OFF 150 //回位的角度
Servo servo;
//LED config
#define led_OFF 199 //200即佔空比100%時不亮,佔空比越低越亮
#define led_ON 100
void setup() {
analogWriteFreq(50);
analogWriteRange(200); //0.1ms
Serial.begin(115200);
Serial.println();
//LED
analogWrite(LED_BUILTIN, led_ON); //注意LED_BUILTIN也是GPIO2
//servo
servo.attach(servo_pin, 500, 2500);
servo.write(servo_OFF);
delay(100);
//wifi
connect_wifi();
//mqtt
client.setServer(mqtt_broker, mqtt_port);
client.setCallback(callback);
connect_mqtt();
client.subscribe(topic);
}
void loop() {
client.loop();
}
void connect_wifi() {
analogWrite(LED_BUILTIN, led_ON);
Serial.printf("connecting to %s......", ssid);
WiFi.begin(ssid, pwd);
while (!WiFi.isConnected()) {
delay(500);
}
Serial.println("connected");
analogWrite(LED_BUILTIN, led_OFF);
}
void reconnect_wifi() {
Serial.printf("reconnecting to %s......", ssid);
analogWrite(LED_BUILTIN, led_ON);
WiFi.disconnect(); //清楚連線資訊,防止路由器重啟後通道變化
WiFi.begin(ssid, pwd);
while (!WiFi.isConnected()) {
delay(500);
}
Serial.println("connected");
analogWrite(LED_BUILTIN, led_OFF);
delay(20);
}
void connect_mqtt() {
analogWrite(LED_BUILTIN, led_ON);
while (!client.connected()) {
String client_id = "esp8266-client-" + String(WiFi.macAddress());
Serial.printf("client %s is connecting to broker %s ... ", client_id.c_str(), mqtt_broker);
if (client.connect(client_id.c_str())) {
Serial.println("connected");
delay(20);
} else {
Serial.printf("failed with state %d\n", client.state());
delay(2000);
}
}
analogWrite(LED_BUILTIN, led_OFF);
delay(20);
}
void callback(char *topic, uint8_t *payload, unsigned int length) {
analogWrite(LED_BUILTIN, led_ON);
String msg = "";
for (unsigned int i = 0; i < length; i++) {
msg += (char)payload[i];
}
Serial.printf("receive message from topic %s: %s\n", topic, msg.c_str());
act_on_msg(msg);
Serial.println("--------------------------");
// delay(1000);
// analogWrite(LED_BUILTIN, 5); //這裡本只想控制燈但是這個脈寬在舵機控制範圍內,所以它被驅動
analogWrite(LED_BUILTIN, led_OFF);
}
void act_on_msg(String msg) {
if (msg.equals("open_door")) {
servo.write(servo_ON);
delay(2000);
}
servo.write(servo_OFF);
delay(200);
}