物联网台灯,手机APP控制台灯亮度色温

来抄作业啦!

最近想做个物联网台灯

第一步【买台灯】、第一步当然是买一个台灯啦,自己建模那成本太高了,不说模型成本,就一个柔光罩和国标护眼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请求。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值