赞
踩
esp8266是一款性价比极高的wifi蓝牙芯片。它不光集成了wifi蓝牙模组,同时内部集成了单片机内核,可以把它当普通的单片机使用。同时它支arduino编程,micropython编程和自带的idf库编程,使用起来非常方便。
这次设计以esp8266为主控,通过wifi读取ntp时间,来实现wifi时钟。
整体设计如下图:
硬件电路主要是由以下几个部分组成: WIFI模块(ESP8266)、USB转串口电路、供电电路、外设电路(包括OLED显示屏、MPU-6050模块和SHT31温湿度采集模块)。
主要功能:
1.显示日期
2.通过重力感应切换显示
3.显示温湿度
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寸的。时间的显示可以通用。图片的显示需要改像素。
整体的电路设计如下图:
这里使用了G版本的CH340,所以需要晶振,但它便宜!!!C版本的不需要晶振。其实这部分电路可以省去,接外部的下载器下载也可以。
运行模式说明如下图:
觉得麻烦没有敷铜。
采用Smart Config/AirKiss 配置网络。
这里采用的是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
这个里的led灯买错了,本来是一个RGD彩灯,结果买成了三阶亮度的白灯。上摇可以调节灯的显示状态。
左摇和右摇可以切换图片显示。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。