ESP8266实战(一)--wifi时钟的制作

1.设计概述

esp8266是一款性价比极高的wifi蓝牙芯片。它不光集成了wifi蓝牙模组,同时内部集成了单片机内核,可以把它当普通的单片机使用。同时它支arduino编程,micropython编程和自带的idf库编程,使用起来非常方便。

这次设计以esp8266为主控,通过wifi读取ntp时间,来实现wifi时钟。

2.硬件设计

2.1整体设计和功能介绍

整体设计如下图:
在这里插入图片描述
硬件电路主要是由以下几个部分组成: WIFI模块(ESP8266)、USB转串口电路、供电电路、外设电路(包括OLED显示屏、MPU-6050模块和SHT31温湿度采集模块)。
主要功能:
1.显示日期
2.通过重力感应切换显示
3.显示温湿度

2.2相关硬件介绍

1.esp8266
在这里插入图片描述
其支持802.11b/g/n WIFI协议,内部的微处理器主频范围在80Mhz到160Mhz,有32Mbit的FLASH存储。具体的资料参见其数据手册。
2.mpu6050
在这里插入图片描述
MPU-6050对陀螺仪和加速度计分别用了三个16位的ADC, 将其测量的模拟量转化为可输出的数字量。陀螺仪可测范围为±250,上500,1000,±2000°/秒(dps), 加速度计可测范围为±2,±4,±8,±16g。
3.SHT31
在这里插入图片描述
它有两个型号(见下图),他们只是量程不一样,使用起来是完全一样的。它比DHT11好用。
在这里插入图片描述
4.oled
在这里插入图片描述
这是一个四线的oled,建议用1.3存的,这个例子中用了0.96寸的。时间的显示可以通用。图片的显示需要改像素。

3.电路设计

整体的电路设计如下图:
在这里插入图片描述

3.1自动下载电路

在这里插入图片描述
这里使用了G版本的CH340,所以需要晶振,但它便宜!!!C版本的不需要晶振。其实这部分电路可以省去,接外部的下载器下载也可以。

3.2 完整的esp8266应用电路

在这里插入图片描述
运行模式说明如下图:
在这里插入图片描述

3.3PCB设计

在这里插入图片描述
觉得麻烦没有敷铜。
在这里插入图片描述
在这里插入图片描述

4.程序设计

4.1程序流程图

在这里插入图片描述
采用Smart Config/AirKiss 配置网络。

4.2参考程序

这里采用的是arduino IDE开发。

//**********************************************************************
//主程序:用来显示网络时间,并显示温湿度数据
//**********************************************************************
#include "main.h"

bool autoConfig()                             //用之前的配网参数自动联网
{
  WiFi.mode(WIFI_STA);
  u8g2.setCursor(0, 12);
  u8g2.print("Connect to Ap ...");
  u8g2.sendBuffer();
  WiFi.begin();
  for (int i = 0; i < 20; i++)
  {
    int wstatus = WiFi.status();
    if (wstatus == WL_CONNECTED)
    {
      Serial.println("AutoConfig Success");
      Serial.printf("SSID: %s\r\n", WiFi.SSID().c_str());
      Serial.printf("PSW:  %s\r\n", WiFi.psk().c_str());
      Serial.print("IP:   ");
      Serial.println(WiFi.localIP());         //得到IP地址
      return true;
    }
    else
    {
      Serial.print("AutoConfig Waiting......");
      Serial.println(wstatus);
      delay(1000);
    }
  }
  Serial.println("AutoConfig Faild!" );
  return false;
}

void smartConfig()
{
  u8g2.clearBuffer();
  u8g2.setCursor(0, 12);
  u8g2.print("Waiting smartcfg....");
  u8g2.sendBuffer();
  WiFi.mode(WIFI_STA);
  Serial.println("\r\nWaiting for Smartconfig");
  delay(2000);
  WiFi.beginSmartConfig();                  //等待配网
 
  while (1)
  {
    Serial.print(".");
    delay(400);
    if (WiFi.smartConfigDone())             //配网完成
    {
      Serial.println("SmartConfig Success");
      Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
      Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
      WiFi.setAutoConnect(true);            //设置自动连接
      break;
    }
  }
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());           //得到IP地址
}

void digitalClockDisplay()                //打印时间
{
  int years, months, days, hours, minutes, seconds, weekdays;
  years = year();
  months = month();
  days = day();
  hours = hour();
  minutes = minute();
  seconds = second();
  weekdays = weekday();
  if (seconds == 0){                     //一分钟打印一次时间
    Serial.printf("%d/%d/%d %d:%d:%d Weekday:%d\n", years, months, days, hours, minutes, seconds, weekdays);
  }
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_unifont_t_chinese2);
  u8g2.setCursor(0, 14);

  int temp1, humi1;
  temp1 = rtemp * 10; 
  humi1 = rhumi; 
  u8g2.print("H:");
  if (humi1 < 10){
    u8g2.print(" "); 
    u8g2.print(humi1); 
  }else
    u8g2.print(humi1); 
  u8g2.print("%");

  u8g2.setCursor(64, 14);
  u8g2.print("T:");
  if (temp1 < 100){
    u8g2.print(" "); 
    u8g2.print(temp1 / 10); 
  }else
    u8g2.print(temp1 / 10); 
  u8g2.print(".");
  u8g2.print(temp1 % 10); 
  u8g2.drawXBM(112, 0, 16, 16, du);
  
  String currentTime = "";
  if (hours < 10)
    currentTime += 0;
  currentTime += hours;
  currentTime += ":";
  if (minutes < 10)
    currentTime += 0;
  currentTime += minutes;
  currentTime += ":";
  if (seconds < 10)
    currentTime += 0;
  currentTime += seconds;
  String currentDay = "";
  currentDay += years;
  currentDay += "/";
  if (months < 10)
    currentDay += 0;
  currentDay += months;
  currentDay += "/";
  if (days < 10)
    currentDay += 0;
  currentDay += days;

  u8g2.setFont(u8g2_font_logisoso24_tr);
  u8g2.setCursor(0, 44);
  u8g2.print(currentTime);
  u8g2.setCursor(0, 61);
  u8g2.setFont(u8g2_font_unifont_t_chinese2);
  u8g2.print(currentDay);
  u8g2.drawXBM(80, 48, 16, 16, xing);
  u8g2.setCursor(95, 62);
  u8g2.print("期");   //必须是u8g2_font_unifont_t_chinese2里有的字
  if (weekdays == 1)
    u8g2.print("日");
  else if (weekdays == 2)
    u8g2.print("一");
  else if (weekdays == 3)
    u8g2.print("二");
  else if (weekdays == 4)
    u8g2.print("三");
  else if (weekdays == 5)
    u8g2.print("四");
  else if (weekdays == 6)
    u8g2.print("五");
  else if (weekdays == 7)
    u8g2.drawXBM(111, 49, 16, 16, liu);
  u8g2.sendBuffer();
}
void displayimage()
{
  u8g2.clearBuffer();
  u8g2.drawXBM(0, 0, 128, 64, bmp2);
  u8g2.sendBuffer();
}
void printDigits(int digits)                  //打印时间数据
{
  Serial.print(":");
  if (digits < 10)                            //打印两位数字
    Serial.print('0');
  Serial.print(digits);
}
time_t getNtpTime()                           //获取NTP时间
{
  bool Time_Recv_Flag = false;
  IPAddress ntpServerIP;                      //NTP服务器的IP地址
  
  while (Udp.parsePacket() > 0) ;             //之前的数据没有处理的话一直等待 discard any previously received packets
  WiFi.hostByName(ntpServerName, ntpServerIP);//从网站名获取IP地址
  if (PowerOn_Flag == 0)                      //第一次才打印
  {
    Serial.println("Transmit NTP Request");
    Serial.print(ntpServerName);
    Serial.print(": ");
    Serial.println(ntpServerIP);
  }

  while (Time_Recv_Flag == false)               //如果是上电第一次获取数据的话,要一直等待,直到获取到数据,不是第一次的话,没获取到数据,直接返回
  {
    sendNTPpacket(ntpServerIP);                 //发送数据包
    uint32_t beginWait = millis();
    while (millis() - beginWait < 1500) 
    {
      int size = Udp.parsePacket();             //接收数据
      if (size >= NTP_PACKET_SIZE) 
      {
        Serial.println("Receive NTP Response");
        Udp.read(packetBuffer, NTP_PACKET_SIZE);  //从缓冲区读取数据
        
        unsigned long secsSince1900;
        secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
        secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
        secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
        secsSince1900 |= (unsigned long)packetBuffer[43];
        if (PowerOn_Flag == 0)                    //第一次收到数据
        {
          u8g2.setCursor(0, 36);
          u8g2.print("Ntp data get ok");
          u8g2.sendBuffer();
        }
        Time_Recv_Flag = true;
        return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
      }
    }
    if (PowerOn_Flag == 1)                        //不是第一次
    {
      return 0;
    }
  }
  Serial.println("No NTP Response :-(");
  return 0;                                         //没获取到数据的话返回0
}
  

void sendNTPpacket(IPAddress &address)              //发送数据包到NTP服务器
{
  memset(packetBuffer, 0, NTP_PACKET_SIZE);         //缓冲区清零

  packetBuffer[0] = 0b11100011;                     // LI, Version, Mode   填充缓冲区数据
  packetBuffer[1] = 0;                              // Stratum, or type of clock
  packetBuffer[2] = 6;                              // Polling Interval
  packetBuffer[3] = 0xEC;                           // Peer Clock Precision
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;

  Udp.beginPacket(address, 123);                    //NTP服务器端口123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);         //发送udp数据
  Udp.endPacket();                                  //发送结束
  
  if (PowerOn_Flag == 0)
  {
    Serial.println("Send Ntp data...");
  }
}

void imuupdate()
{
   imu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); //读取六轴原始数值
   ax = ax - axo;
   ay = ay - ayo;
   az = az - azo;
 //判断姿态,当左摇或者右摇时,改变状态变量
  if (millis() - last_update_time > interval)
  {
    if (ay > 3000 && flag)
    {
      i--;
      if(i<0)
      {
        i= 5;
        }
      flag = 0;
    }
    else if (ay < -3000 && flag)
    {
      i++;
      flag = 0;
    }
    else
    {
      flag = 1;
    }

    if (az > 5000)
    {
      j++;
      if(j >4)
      {
        j = 0;
        }
    }
    if(i >5)
    {
       i = 0;
    }

    last_update_time = millis();
  }
}

void setup() 
{

  Serial.begin(115200);             //初始化串口
  Serial.println();                 //打印回车换行

  u8g2.begin();
  u8g2.enableUTF8Print(); 
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_wqy12_t_gb2312a);
  ESP.wdtEnable(8000);              //使能软件看门狗的触发间隔MS
  ESP.wdtFeed();                    //喂狗

  if (!autoConfig())                //如果自动联网失败的话,就启动smartconfig
  {
    Serial.println("Start smartconfig");
    smartConfig();
  }
  u8g2.setCursor(0, 24);
  u8g2.print("Connted to AP ok");
  u8g2.setCursor(0, 64);
  u8g2.print("IP: " + WiFi.localIP().toString());//显示本机的IP
  u8g2.sendBuffer();
  delay(1000);                      //延时1S
 
  Serial.println("Starting UDP");               //连接时间服务器
  Udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(Udp.localPort());
  Serial.println("waiting for sync");
  
  setSyncProvider(getNtpTime);
  setSyncInterval(300);
  
  pinMode(LEDB, OUTPUT);   
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT); 
  digitalWrite(LEDB, LOW); 
  digitalWrite(LEDG, LOW); 
  digitalWrite(LEDR, HIGH); 
  
  Serial.println("SHT31 test");
  if (!sht31.begin(0x44)) 
  { // Set to 0x45 for alternate i2c addr
   Serial.println("Couldn't find SHT31");
   while (1) delay(1);
  }
  
  Wire.begin();
  Wire.setClock(400000);
  imu.initialize();                 //初始化
  Serial.println(imu.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
  unsigned short times = 200;             //采样次数
  for(int i=0;i<times;i++)
  {
        imu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); //读取六轴原始数值
        axo += ax; ayo += ay; azo += az;      //采样和
        gxo += gx; gyo += gy; gzo += gz;
    
   }
   axo /= times; ayo /= times; azo /= times; //计算加速度计偏移
   gxo /= times; gyo /= times; gzo /= times; //计算陀螺仪偏移
}

void loop() 
{
  now1 = millis();
  if ((now1 - LastTime1 >= 20000) || (LastTime1 == 0)){ //定时读取数据
    LastTime1 = now1;
    rtemp = sht31.readTemperature(); 
    rhumi = sht31.readHumidity(); 
    Serial.printf("湿度:%.3f, 温度:%.3f\r\n" ,rhumi,rtemp);
  }
  imuupdate();
  if(i == 0)
  {
  if (timeStatus() != timeNotSet)               //已经获取到数据的话
  {
    if (now() != prevDisplay)                   //如果本次数据和上次不一样的话,刷新
    {
      prevDisplay = now();
      digitalClockDisplay();
    }
  }
  }
  if(i == 1)
  {
   u8g2.clearBuffer();
   u8g2.drawXBM(0, 0, 128, 64, bmp1);
   u8g2.sendBuffer();
  }
  if(i == 2)
  {
   u8g2.clearBuffer();
   u8g2.drawXBM(0, 0, 128, 64, bmp2);
   u8g2.sendBuffer();
  }
  if(i == 3)
  {
   u8g2.clearBuffer();
   u8g2.drawXBM(0, 0, 128, 64, bmp3);
   u8g2.sendBuffer();
  }
  if(i == 4)
  {
   u8g2.clearBuffer();
   u8g2.drawXBM(0, 0, 128, 64, bmp4);
   u8g2.sendBuffer();
  }
  if(j == 0)
  {
    digitalWrite(LEDB, LOW); 
    digitalWrite(LEDG, LOW); 
    digitalWrite(LEDR, LOW); 
  }
   if(j == 1)
  {
    digitalWrite(LEDB, HIGH); 
    digitalWrite(LEDG, LOW); 
    digitalWrite(LEDR, LOW); 
  }
   if(j == 2)
  {
    digitalWrite(LEDB, HIGH); 
    digitalWrite(LEDG, HIGH); 
    digitalWrite(LEDR, LOW); 
  }
   if(j == 3)
  {
    digitalWrite(LEDB, HIGH); 
    digitalWrite(LEDG, HIGH); 
    digitalWrite(LEDR, HIGH); 
  }
}
//头文件
#ifndef __MAIN_H__
#define __MAIN_H__

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <TimeLib.h>
#include <U8g2lib.h>
#include "Wire.h"
#include "Adafruit_SHT31.h"
#include "MPU6050.h"

MPU6050 imu;    //定义对象
int16_t ax, ay, az, gx, gy, gz;             //加速度计陀螺仪原始数据
long axo = 0, ayo = 0, azo = 0;             //加速度计偏移量
long gxo = 0, gyo = 0, gzo = 0;             //陀螺仪偏移量
int flag = 0;                               //imu移动标志变量
long  last_update_time;
int interval = 400;

#define SCL  14                     //GPIO14
#define SDA  12                     //GPIO12
#define LEDB 16  
#define LEDR 13
#define LEDG 2  

U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, SCL, SDA, U8X8_PIN_NONE);   //scl,sda,rst

float rtemp;
float rhumi;

Adafruit_SHT31 sht31 = Adafruit_SHT31();

//---------------------NTP相关参数---------------------
static const char ntpServerName[] = "cn.ntp.org.cn";      //NTP服务器
  
const int timeZone = 8;               //时区,东八区为北京时间
  
WiFiUDP Udp;
unsigned int localPort = 8888;        //连接时间服务器的本地端口号

time_t prevDisplay = 0;               //上一次获取到的时间
const int NTP_PACKET_SIZE = 48;       //NTP发送数据包长度
byte packetBuffer[NTP_PACKET_SIZE];   //NTP数据包缓冲区


//---------------------Time 相关参数---------------------
int Led_Flag = HIGH;                  //默认当前灭灯
bool Led_State = false;               //灯状态
unsigned long LastTime1 = 0;
unsigned long now1;
int i = 0;  //按键标志变量, 控制切换图片
int j = 1;  //控制切换灯
typedef struct
{
  uint8_t Month;                      //RTC Date Month (in BCD format).0x01-0x12
  uint8_t Date;                       //RTC Date.1-31
  uint16_t Year;                      //RTC Date Year.2000-2099    
  uint8_t Hour;
  uint8_t Minute;
  uint8_t Second;
  uint8_t Week;
}RTC_DateTypeDef;                     //时间结构体

uint8_t PowerOn_Flag = 0;             //上电标志位
RTC_DateTypeDef Time;                 //本次读到的时间
RTC_DateTypeDef Last_Time;            //上次读到的时间

boolean isNTPConnected = false;

const unsigned char xing[] U8X8_PROGMEM = {
  0x00, 0x00, 0xF8, 0x0F, 0x08, 0x08, 0xF8, 0x0F, 0x08, 0x08, 0xF8, 0x0F, 0x80, 0x00, 0x88, 0x00,
  0xF8, 0x1F, 0x84, 0x00, 0x82, 0x00, 0xF8, 0x0F, 0x80, 0x00, 0x80, 0x00, 0xFE, 0x3F, 0x00, 0x00
};  /*星*/
const unsigned char liu[] U8X8_PROGMEM = { 
  0x40, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00,
  0x20, 0x02, 0x20, 0x04, 0x10, 0x08, 0x10, 0x10, 0x08, 0x10, 0x04, 0x20, 0x02, 0x20, 0x00, 0x00
};  /*六*/
const unsigned char du[] U8X8_PROGMEM = { 
0x06,0x00,0x89,0x2F,0x69,0x30,0x36,0x20,0x10,0x20,0x18,0x00,0x18,0x00,0x18,0x00,
0x18,0x00,0x18,0x00,0x18,0x00,0x10,0x00,0x30,0x20,0x60,0x10,0x80,0x0F,0x00,0x00,//℃
}; 
//自定义图片 128 X 64 
//添加自己的图片数组
static const unsigned char bmp1[] U8X8_PROGMEM  = {};
static const unsigned char bmp2[] U8X8_PROGMEM  = {};
static const unsigned char bmp3[] U8X8_PROGMEM  ={};
static const unsigned char bmp4[] U8X8_PROGMEM  ={};

time_t getNtpTime();
void digitalClockDisplay();
void printDigits(int digits);
void sendNTPpacket(IPAddress &address);
void displayimage();
void imuupdate();

#endif

5.效果展示

在这里插入图片描述
这个里的led灯买错了,本来是一个RGD彩灯,结果买成了三阶亮度的白灯。上摇可以调节灯的显示状态。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
左摇和右摇可以切换图片显示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值