来抄作业啦!
最近想做个物联网台灯
第一步【买台灯】、第一步当然是买一个台灯啦,自己建模那成本太高了,不说模型成本,就一个柔光罩和国标护眼A级的灯珠都找不到,这里挑一个可以拆的台灯,方便后面加入物联网模块。
第二步【扔原装电池和电路板】,接下来就是拆开台灯,把里面电池断掉扔掉,换上一个航模1s动力电池,容量大点,因为原装的电池是真的弱!!标3600毫安可能连1000毫安都不到!台灯内部的电路板也不要了。
第三步【买配件】,买需要的配件。①esp8266一个,②触摸开关一个(设置为自锁,高低电平都行,我这里设置为低电平),③电容一堆,④电阻一堆,⑤三极管一堆(我买了俩,80V,1.5A,基极5V)。
“触摸开关”
“电容”
“三极管,npn,80V,5V,1.5A”
第四步【写arduino程序】,给esp8266写程序,esp8266其实是一个带有WiFi功能的NodeMCU,下面是代码,写完我们来分析程序然后连线。
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
#include <Arduino.h>
#include <WiFiUdp.h>
#include <Ticker.h>
//const int pin=11;//白灯
//const int piny=10;//黄灯
#define pin D5
#define piny D6
unsigned int dengzhuangtai=0;//灯开关状态
unsigned int dengjiajianzhuangtai=1;//灯加减状态
unsigned int dengczhuangtai=1;//灯加减状态
unsigned int sensorValue=255;//总数值
unsigned int sensorValuexx=255;//颜色总数值
double ValueW=255;
double ValueY=255;
//const int dig = 8;//开关控制器
//const int digc = 7;//开关色温控制器
#define dig D8
#define digc D7
Ticker timer; // 定时器
const uint16_t firstWordAddress = 0; // "hello" 的存储地址
const uint16_t secondWordAddress = 200; // "god" 的存储地址
const char* initialSSID = "wwwxxxqqq000001"; // ESP8266 初始的SSID
const char* initialPassword = "12345678"; // 初始密码
String targetSSID = "0"; // 要连接的目标网络SSID
String targetPassword = "12345678"; // 目标网络密码
String ld = "0";
String sw = "0";
String onoff = "0";
int zhuangtai=1; //单片机状态,0表示跳过配对状态(默认),1表示配对状态
//调试结束改为0
WiFiUDP udp;
unsigned int localUdpPort = 4210; // 本地UDP端口
char incomingPacket[255]; // 用于接收数据的缓冲区
char replyPacket[] = "Hi there! Got the message :-)"; // 回复的消息
ESP8266WebServer server(80); // 创建服务器在端口 80
void setup() {
if(zhuangtai=1){
EEPROM.begin(512); // 初始化 EEPROM,指定大小
Serial.begin(115200);
// 设置为热点模式
WiFi.softAP(initialSSID, initialPassword);
Serial.println("Setup as AP mode");
Serial.print("AP IP address: ");
Serial.println(WiFi.softAPIP());
// 服务器连接wifi(获取wifi的ssid和password)
server.on("/", HTTP_POST, []() { // 改为处理 POST 请求
if (server.hasArg("plain") == false) { // 检查是否有数据
server.send(400, "text/plain", "Bad Request: No data received");
return;
}
String body = server.arg("plain"); // 获取数据
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, body); // 反序列化 JSON
if (error) { // 如果反序列化失败
server.send(400, "text/plain", "Bad Request: Invalid JSON");
return;
}
// 打印接收到的 JSON 数据
// 检查 JSON 数据中的 "a" 键
String ssid = doc["ssid"];
String pwd = doc["pwd"];
Serial.println("Received JSON data: ssid=" + ssid);
// 存储 "ssid"
targetSSID = ssid;
storeWordInEEPROM(targetSSID,firstWordAddress);
// 存储 "pwd"
targetPassword = pwd;
storeWordInEEPROM(targetPassword,secondWordAddress);
Serial.println("Received JSON data:");
serializeJsonPretty(doc, Serial);
server.send(200, "text/plain", "OK");
switchToTargetWiFi();
udp.begin(localUdpPort); // 开启udp端口
Serial.printf("Now listening at IP %s, UDP port %d\n", WiFi.localIP().toString().c_str(), localUdpPort);
});
// 服务器控制指令路径
server.on("/a", HTTP_POST, []() { // 改为处理 POST 请求
if (server.hasArg("plain") == false) { // 检查是否有数据
server.send(400, "text/plain", "Bad Request: No data received");
return;
}
String body = server.arg("plain"); // 获取数据
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, body); // 反序列化 JSON
if (error) { // 如果反序列化失败
server.send(400, "text/plain", "Bad Request: Invalid JSON");
return;
}
// 检查亮度,色温,开关指令
String a1 = doc["ld"];
String a2 = doc["sw"];
String a3 = doc["onoff"];
ld=a1;
sw=a2;
onoff=a3;
// 数据转换
sensorValue=map(ld.toInt(),0,100,0,255);
sensorValuexx=map(sw.toInt(),0,100,0,510);
if(sensorValuexx>255){
ValueW=255;
ValueY=510-sensorValuexx;
}
if(sensorValuexx<=255){
ValueW=sensorValuexx;
ValueY=255;
}
// 亮度调节
analogWrite(pin,sensorValue*ValueW/255);
analogWrite(piny,sensorValue*ValueY/255);
// 开关
if(dengzhuangtai!=onoff.toInt()){
dengzhuangtai=onoff.toInt();
digitalWrite(pin,onoff.toInt()); //只有第一层if的开关电平为高,关灯
digitalWrite(piny,onoff.toInt());
}
Serial.println("ld:"+ld);
Serial.println("sw:"+sw);
Serial.println("onoff:"+onoff);
Serial.println("Received JSON data:");
serializeJsonPretty(doc, Serial);
server.send(200, "text/plain", "OK");
});
server.begin();
}else{
// 读取并打印 "ssid"
targetSSID = readWordFromEEPROM(firstWordAddress);
Serial.println("Read word 1: " + targetSSID);
// 读取并打印 "pwd"
targetPassword = readWordFromEEPROM(secondWordAddress);
Serial.println("Read word 2: " + targetPassword);
}
// 定时器调用udp广播发送
timer.attach(2, repeatTask);
}
void loop() {
// http服务器永不覆灭,web服务器一直在运行监听手机的请求(连接路由器请求和控制指令)
server.handleClient();
//放在循环里面的这部分是监听触控开关
// Serial.println(dengczhuangtai);
unsigned int digValue2;
unsigned int digValuec2;
unsigned int digValue;
digValue=digitalRead(dig); //第一层if进入时开关按钮电平
unsigned int digValuec;
digValuec=digitalRead(digc); //第一层if进入时色温按钮电平
if(digValue==1||digValuec==1){
delay(500);
digValue2=digitalRead(dig); //第二层if进入时开关按钮电平
digValuec2=digitalRead(digc); //第二层if进入时色温按钮电平
if((digValue2==0)&&(dengzhuangtai==0)&&(digValue==1)){
dengzhuangtai=1;
digitalWrite(pin,0); //只有第一层if的开关电平为高,关灯
digitalWrite(piny,0);
}
else if((digValue2==0)&&(dengzhuangtai==1)&&(digValue==1)){
dengzhuangtai=0;
analogWrite(pin,sensorValue*ValueW/255); //只有第一层if的开关电平为高,开灯
analogWrite(piny,sensorValue*ValueY/255);
}
else if((digValue2==1)&&(dengjiajianzhuangtai==0)&&(digValue==1)){
dengjiajianzhuangtai=1; //一二层开关都是高电平,亮度调节
while(1){
if(sensorValue<255){
sensorValue++;
}
analogWrite(pin,sensorValue*ValueW/255);
analogWrite(piny,sensorValue*ValueY/255);
digValue2=digitalRead(dig); //更新第二层if的等开关电平状态
if(digValue2==0){
break; //如果停止按开关,灯亮度停止变化(增加亮度)
}
delay(10);
}
}
else if((digValue2==1)&&(dengjiajianzhuangtai==1)&&(digValue==1)){
dengjiajianzhuangtai=0;
while(1){
if(sensorValue>0){
sensorValue--;
}
analogWrite(pin,sensorValue*ValueW/255);
analogWrite(piny,sensorValue*ValueY/255);
digValue2=digitalRead(dig);
if(digValue2==0){
break; //如果停止按开关,灯亮度停止变化(降低亮度)
}
delay(10);
}
}
else if((digValuec2==1)&&(dengczhuangtai==1)&&(digValuec==1)){
dengczhuangtai=0;
while(1){
if(ValueW<255){
ValueW++;
}
if(ValueY>0){
ValueY--;
}
analogWrite(pin,sensorValue*ValueW/255);
analogWrite(piny,sensorValue*ValueY/255);
digValuec2=digitalRead(digc);
if(digValuec2==0){
break;
}
delay(10);
}
}
else if((digValuec2==1)&&(dengczhuangtai==0)&&(digValuec==1)){
dengczhuangtai=1;
while(1){
if(ValueW>0){
ValueW--;
}
if(ValueY<255){
ValueY++;
}
analogWrite(pin,sensorValue*ValueW/255);
analogWrite(piny,sensorValue*ValueY/255);
digValuec2=digitalRead(digc);
if(digValuec2==0){
break;
}
delay(10);
}
}
}
}
// 把工作模式从热点模式切换到WiFi连接模式(连接路由器)
void switchToTargetWiFi() {
WiFi.softAPdisconnect(true); // 关闭热点
WiFi.begin(targetSSID, targetPassword); // 连接到新的Wi-Fi网络
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("Connected to target Wi-Fi");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void storeWordInEEPROM(String word, uint16_t address) {
int wordLength = word.length();
// 先存储字符串长度
EEPROM.write(address, wordLength);
// 再存储字符串内容
for (int i = 0; i < wordLength; i++) {
EEPROM.write(address + 1 + i, word[i]);
}
EEPROM.commit(); // 提交更改,确保数据被真正写入 EEPROM
}
String readWordFromEEPROM(uint16_t address) {
int length = EEPROM.read(address);
String word = "";
for (int i = 0; i < length; i++) {
word += char(EEPROM.read(address + 1 + i));
}
return word;
}
// 定时器发送udp广播
void repeatTask() {
// 广播自己的ip地址
String broadcastMessage = "wwwxxxqqq=" + WiFi.localIP().toString();
udp.beginPacketMulticast(IPAddress(255, 255, 255, 255), localUdpPort, WiFi.localIP());
udp.write(broadcastMessage.c_str());
udp.endPacket();
}
第五步【写app控制端】,根据arduino的程序写手机控制端
项目结构,用了kotlin
链接:https://pan.baidu.com/s/150YhGc9wtXR0O5m21v-YLA?pwd=1111
提取码:1111
上面链接是项目的工程,所有代码都有注释
手机app所有的代码。
第六步【根据程序来接线】,外部电路整体思路:pwm控制三极管基极,pwm占空比越小,三极管导通时间越长,灯也就越亮。但是大家都听说过pwm调光的危害,那就是频闪伤眼睛。接下来就轮到电容和电阻起作用了,RC滤波器,先计算一下截止频率,选出C,C越大越好,然后串联上电阻,测试(如果有示波器就能看到滤波后的效果了),总之就是消除频闪。所以整个arduino只需要控制pwm占空比就好了。
接下来分析和手机互联,arduino esp8266先是热点模式(AP模式),手机连接上eps8266的热点,软件提示用户输入自己家路由器的名称和密码,完成后发送ssid和password给esp8266,esp8266接收到WiFi账号和密码,存储在ROM中,切换到WiFi模式,根据密码链接指定的路由器,然后就开始每隔二秒向局域网广播自己的状态(IP地址,开机状态,亮度,色温),如果手机打开了app,app就会识别这些状态并显示在软件上,且手机可以向esp8266发起http请求来传递指令
物联网小灯
需要改进的地方:(如果看懂了上面的内容,改进起来也是非常容易的)
①小灯虽然有记忆WiFi密码的功能,但是为了调试,本人较懒,没有加是否有可用WiFi的判断。
②小灯只能在局域网内控制,本人是有云服务器的,但是懒得搭建,如果结合云端,可以做到真正的物联网(只要连山网就可控制,不再局限于局域网)
③小灯没有添加开机判断,使得调节亮度/色温也可以开灯
④小灯没有给手机app反馈当前色温和亮度等状态,只是单纯手机app控制小灯(不过很好解决,使用局域网广播或者手机端发起http请求),我比较推荐手机端发起http请求,这样占用局域网带宽小,也容易解决,就是单片机加一个服务器请求路径,手机端加一个okhttp请求。