本帖最后由 gaoyang9992006 于 2022-9-18 13:11 编辑
#申请原创# @21小跑堂
硬件:ESP8266模组,OLED显示屏,这里我选择的是SSD1306驱动芯片的0.91寸128*32分辨率的
开发软件:ESP8266Flasher(用于烧录NodeMCU固件);ESPlorer(用于上传Lua脚本)
第一步:获取适合本项目的NodeMCU
登陆以下网址,在线构建适合本项目的固件
https://nodemcu-build.com/
本项目要用到的模块有:I2C 、timer、SJSON、SNTP、HTTP、U8G2
另外根据需要选择U8G2的相关库
可以有以下字体库选择,非常的nice,如果没啥想法,第一个不点进去就行了,用默认的那两个字符库。
第二步:烧录固件,这个打开软件安装提示操作就行了
第三步:编写代码
该软件要实现以下功能:连接WIFI网络;驱动OLED,并设置使用的字符库;获取网络时间同步到本地;获取天气信息写入到全局变量;显示到OLED
-- 连接WiFi
function connectWiFi()
station_cfg={}
station_cfg.ssid = "XGY-NJY"
station_cfg.pwd = "njy123456"
station_cfg.save = true
print(wifi.sta.getip())
wifi.setmode(wifi.STATION)
wifi.sta.config(station_cfg)
print(wifi.sta.getip())
end
只需要修改成你的WIFI名字和密码即可
-- OLED初始化
function init_oled()
i2c.setup(id, sda, scl, i2c.SLOW)
disp = u8g2.ssd1306_i2c_128x32_univision(id, sla)
-- 设置字体
disp:setFont(u8g2.font_unifont_t_symbols)
--disp:setFont(u8g2.font_6x10_tf)
disp:setFontRefHeightExtendedText()
--disp:setDrawColor(1)
disp:setFontPosTop()
disp:setFontDirection(0)
-- 画边框
--disp:drawFrame(0, 0, 128, 32)
end
如果想要画个边框,可以去掉倒数第二行的注释“--”
接下来获取网络时间,先设置一下网络NTP服务器地址
--NTP服务器列表
NTP_SERVERS = {
"ntp.aliyun.com",
"time.asia.apple.com",
"cn.ntp.org.cn",
"time.windows.com",
"cn.pool.ntp.org"
}
--获取网络时间
function getNetTime()
sntp.sync(NTP_SERVERS,
function(sec, usec, server, info)
print('sync', sec, usec, server)
getWeather()
end,
function()
print('failed!')
end
)
end
获取天气信息,可以根据你的需要找一个支持json数据返回的天气服务网站,接口,我这里用的心知天气
--获取天气
function getWeather()
http.get("http://api.seniverse.com/v3/weather/now.json?key=这里是你的私钥&location=nanjing&language=en&unit=c", nil,
function(code, data)
if (code < 0) then
print("HTTP request failed")
else
-- print(code, data)
r = sjson.decode(data)
print("City: \t"..r.results[1]["location"]["name"])
print("TianQiCode: \t"..r.results[1]["now"]["code"])
print("\nTianQi: \t"..r.results[1]["now"]["text"])
print("Tem: \t"..r.results[1]["now"]["temperature"])
--推送到OLED显示
--disp:clearBuffer()
--disp:drawStr(0, 0, r.results[1]["location"]["name"]..":"..r.results[1]["now"]["text"].."-"..r.results[1]["now"]["temperature"])
--disp:sendBuffer()
--该函数仅用于获取天气信息存储在全局变量里,方便显示函数调用
city = r.results[1]["location"]["name"]
weather_now_code = tonumber(r.results[1]["now"]["code"])
now_temperature = r.results[1]["now"]["temperature"]
end
end)
end
其他的部分应该会Lua的都知道怎么写了,关键函数已提供。
效果非常的奈斯。
我设置的是第一行每隔五秒切换一次。
最后给大家看看我的完整源码
-- 管脚定义
sda = 5 -- GPIO14
scl = 6 -- GPIO12
sla = 0x3c -- oled的地址,一般为0x3c
id = 0 -- iic总线 和 oled初始化,定义i2c编号id为0
i = 0 --一个计数器
--获取的信息
city =""
weather_now_code = 1
now_temperature=25
year=2022
month=09
day=07
yday=1
wday=1
hour=1
minute=1
second=1
--字符宽和高
width = 16
height = 16
bits = string.char(
--sting.char()方法
--下边就是字模
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
)
--显示到oled上
function print_sheshidu(x,y)
--是否显示背景色
disp:setDrawColor(1)
disp:setBitmapMode(0)
-----以位图的方式显示
disp:drawXBM(x,y,width,height,bits)
end
--NTP服务器列表
NTP_SERVERS = {
"ntp.aliyun.com",
"time.asia.apple.com",
"cn.ntp.org.cn",
"time.windows.com",
"cn.pool.ntp.org"
}
-- OLED初始化
function init_oled()
i2c.setup(id, sda, scl, i2c.SLOW)
disp = u8g2.ssd1306_i2c_128x32_univision(id, sla)
-- 设置字体
disp:setFont(u8g2.font_unifont_t_symbols)
--disp:setFont(u8g2.font_6x10_tf)
disp:setFontRefHeightExtendedText()
--disp:setDrawColor(1)
disp:setFontPosTop()
disp:setFontDirection(0)
-- 画边框
--disp:drawFrame(0, 0, 128, 32)
end
-- 显示函数
function oled_show_msg()
-- 设置显示内容
disp:drawStr(0, 0, " --Hello OLED-- ")
disp:drawStr(0, 16, "1234567890ABCDEF")
-- 将内容发送到oled
disp:sendBuffer()
end
-- 连接WiFi
function connectWiFi()
station_cfg={}
station_cfg.ssid = "XGY-NJY"
station_cfg.pwd = "njy123456"
station_cfg.save = true
print(wifi.sta.getip())
wifi.setmode(wifi.STATION)
wifi.sta.config(station_cfg)
print(wifi.sta.getip())
end
--获取网络时间
function getNetTime()
sntp.sync(NTP_SERVERS,
function(sec, usec, server, info)
print('sync', sec, usec, server)
getWeather()
end,
function()
print('failed!')
end
)
end
function blinkTime()
--获取当前时间写到全局变量,方便调用
tm = rtctime.epoch2cal(rtctime.get()+(8*3600))
year= tm["year"]
month= tm["mon"]
day = tm["day"]
yday= tm["yday"]
wday= tm["wday"]
hour= tm["hour"]
minute= tm["min"]
second= tm["sec"]
end
function disTIME()
tm = rtctime.epoch2cal(rtctime.get()+(8*3600))
--打印转换后的年月日时分秒,以及今年的第几天,本周的第几天,本周第几天是从周日算第一天开始的,值的范围是1~7,周一是2
print(string.format("%04d/%02d/%02d %02d:%02d:%02d %03d %d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"],tm["yday"],tm["wday"]))
disp:clearBuffer() --每次显示新内容前,清理一下缓冲区,保证不受上次显示内容影响
--推送给I2C显示年月日,今天是今年的第几天,今天是本周的第几天
disp:drawStr(0, 0, string.format("%04d-%02d-%02d %03d %d", tm["year"], tm["mon"], tm["day"], tm["yday"], tm["wday"]))
--第二行显示时分秒
disp:drawStr(0, 16, string.format(" %02d:%02d:%02d ", tm["hour"], tm["min"], tm["sec"]))
disp:sendBuffer()
end
--获取天气
function getWeather()
http.get("http://api.seniverse.com/v3/weather/now.json?key=换成你的私钥&location=nanjing&language=en&unit=c", nil,
function(code, data)
if (code < 0) then
print("HTTP request failed")
else
-- print(code, data)
r = sjson.decode(data)
print("City: \t"..r.results[1]["location"]["name"])
print("TianQiCode: \t"..r.results[1]["now"]["code"])
print("\nTianQi: \t"..r.results[1]["now"]["text"])
print("Tem: \t"..r.results[1]["now"]["temperature"])
--该函数仅用于获取天气信息存储在全局变量里,方便显示函数调用
city = r.results[1]["location"]["name"]
weather_now_code = tonumber(r.results[1]["now"]["code"])
now_temperature = r.results[1]["now"]["temperature"]
end
end)
end
function display_OLED091_A()
disp:clearBuffer()
disp:drawStr(0, 0, string.format("%04d-%02d-%02d %03d %d", year,month,day,yday,wday))
disp:drawStr(0, 16,string.format(" %02d:%02d:%02d ", hour,minute,second))
disp:sendBuffer()
end
function display_OLED091_B()
disp:clearBuffer()
--显示城市名
disp:drawStr(0, 0, city.." ")
--根据天气代码选择显示不同的图标表示天气信息
--在显示位置上先计算前面的内容占用了多少宽度
if(weather_now_code<=3) then
disp:drawGlyph((string.len(city)+1)*8,0,0x2600)
elseif (weather_now_code<=8) then
disp:drawGlyph((string.len(city)+1)*8,0,0x2601)
elseif (weather_now_code<=19) then
disp:drawGlyph((string.len(city)+1)*8,0,0x2602)
elseif (weather_now_code<=25) then
disp:drawGlyph((string.len(city)+1)*8,0,0x2603)
else
disp:drawGlyph((string.len(city)+1)*8,0,0x2604)
end
--跟着显示温度
disp:drawStr((string.len(city)+2)*8, 0, " "..now_temperature)
--绘制℃
print_sheshidu((string.len(city)+3+string.len(now_temperature))*8,0)
--在第二行显示时分秒
disp:drawStr(0, 16,string.format(" %02d:%02d:%02d ", hour,minute,second))
disp:sendBuffer()
end
-- 主函数
function main()
init_oled()
--开机显示一些简单的测试信息
oled_show_msg()
disp:clearBuffer()
connectWiFi()
getNetTime()
--每1秒时间从本地取时间显示一次
mytimer = tmr.create()
mytimer:register(1000, tmr.ALARM_AUTO, function()
blinkTime()
i=i+1
--print("This:", i)
print("This".."-|"..string.format("%02d",i).."|-"..string.format("%04d/%02d/%02d %02d:%02d:%02d %03d %d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"],tm["yday"],tm["wday"]))
if (i>=60) then
i=0
print("a minute")
elseif(i%10>=5) then
display_OLED091_A()
else
--disTIME()
display_OLED091_B()
end
end )
mytimer:start()
--定时每分钟从网络获取时间一次,每分钟获取一次天气信息
NetTimer = tmr.create()
NetTimer:register(60000, tmr.ALARM_AUTO,function()
getNetTime()
getWeather()
print("get NET TIME")
end )
NetTimer:start()
end
-- 运行程序
main()
第四步:
将代码保存成init.lua
通过串口上传到ESP8266模块即可。
好了,你学会了吗?这应该是当前网上最简单好学的一个物联网天气时钟教程了。
做了一块连接板,本来还想做外壳呢,想想算了,哈哈,后面可能还会玩其他的功能
https://www.bilibili.com/video/bv1GP4y1o7ey
|
得益于健康优质的开发生态,ESP8266可以很好的作为入门物联网的首选。通过简单的配置即可获取天气数据,作者不仅成功拉取了云端数据,还较好的显示出来,并动态刷新,实现效果较为良好。