返回列表 发新帖我要提问本帖赏金: 50.00元(功能说明)

ESP8266采用NodeMCU固件在Lua脚本加持下实现互联网天气时钟功能

[复制链接]
1923|5
 楼主| gaoyang9992006 发表于 2022-9-9 11:21 | 显示全部楼层 |阅读模式
本帖最后由 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
  1.     -- 连接WiFi
  2.     function connectWiFi()
  3.         station_cfg={}
  4.         station_cfg.ssid = "XGY-NJY"
  5.         station_cfg.pwd = "njy123456"
  6.         station_cfg.save = true
  7.         print(wifi.sta.getip())
  8.         wifi.setmode(wifi.STATION)
  9.         wifi.sta.config(station_cfg)
  10.         print(wifi.sta.getip())
  11.     end
只需要修改成你的WIFI名字和密码即可
  1.     -- OLED初始化
  2.     function init_oled()

  3.         i2c.setup(id, sda, scl, i2c.SLOW)
  4.         disp = u8g2.ssd1306_i2c_128x32_univision(id, sla)
  5.         -- 设置字体
  6.         disp:setFont(u8g2.font_unifont_t_symbols)
  7.                 --disp:setFont(u8g2.font_6x10_tf)

  8.         disp:setFontRefHeightExtendedText()
  9.         --disp:setDrawColor(1)
  10.         disp:setFontPosTop()
  11.         disp:setFontDirection(0)
  12.         -- 画边框
  13.         --disp:drawFrame(0, 0, 128, 32)
  14.     end
如果想要画个边框,可以去掉倒数第二行的注释“--”
接下来获取网络时间,先设置一下网络NTP服务器地址

  1. --NTP服务器列表
  2. NTP_SERVERS = {
  3.                                 "ntp.aliyun.com",
  4.                 "time.asia.apple.com",
  5.                 "cn.ntp.org.cn",
  6.                 "time.windows.com",
  7.                 "cn.pool.ntp.org"
  8.               }

  1. --获取网络时间   
  2.     function getNetTime()
  3.             sntp.sync(NTP_SERVERS,
  4.                         function(sec, usec, server, info)
  5.                         print('sync', sec, usec, server)
  6.                                                 getWeather()
  7.                         end,
  8.                         function()
  9.                         print('failed!')
  10.                         end
  11.                                         )
  12.     end
获取天气信息,可以根据你的需要找一个支持json数据返回的天气服务网站,接口,我这里用的心知天气
  1. --获取天气
  2. function getWeather()
  3.         http.get("http://api.seniverse.com/v3/weather/now.json?key=这里是你的私钥&location=nanjing&language=en&unit=c", nil,

  4.         function(code, data)
  5.                 if (code < 0) then
  6.                         print("HTTP request failed")
  7.                 else
  8.                 -- print(code, data)
  9.                  r = sjson.decode(data)
  10.                  print("City:                  \t"..r.results[1]["location"]["name"])
  11.                  print("TianQiCode:        \t"..r.results[1]["now"]["code"])
  12.                  print("\nTianQi:        \t"..r.results[1]["now"]["text"])
  13.                  print("Tem:                   \t"..r.results[1]["now"]["temperature"])
  14.                  --推送到OLED显示
  15.                  --disp:clearBuffer()
  16.                  --disp:drawStr(0, 0, r.results[1]["location"]["name"]..":"..r.results[1]["now"]["text"].."-"..r.results[1]["now"]["temperature"])
  17.                  --disp:sendBuffer()
  18.                  --该函数仅用于获取天气信息存储在全局变量里,方便显示函数调用
  19.                  city = r.results[1]["location"]["name"]
  20.                  weather_now_code = tonumber(r.results[1]["now"]["code"])
  21.                  now_temperature  = r.results[1]["now"]["temperature"]

  22.                 end
  23.          end)
  24. end
其他的部分应该会Lua的都知道怎么写了,关键函数已提供。
效果非常的奈斯。


我设置的是第一行每隔五秒切换一次。
最后给大家看看我的完整源码
  1.     -- 管脚定义
  2.     sda = 5 -- GPIO14
  3.     scl = 6 -- GPIO12
  4.     sla = 0x3c  -- oled的地址,一般为0x3c
  5.     id  = 0     -- iic总线 和 oled初始化,定义i2c编号id为0
  6.         i = 0     --一个计数器


  7.         --获取的信息
  8.         city =""
  9.         weather_now_code = 1
  10.         now_temperature=25
  11.         year=2022
  12.         month=09
  13.         day=07
  14.         yday=1
  15.         wday=1
  16.         hour=1
  17.         minute=1
  18.         second=1
  19.         
  20.         
  21. --字符宽和高
  22. width = 16
  23. height = 16

  24. bits = string.char(
  25. --sting.char()方法
  26. --下边就是字模
  27. 0x06,0x00,0x89,0x2F,0x69,0x30,0x36,0x20,
  28. 0x10,0x20,0x18,0x00,0x18,0x00,0x18,0x00,
  29. 0x18,0x00,0x18,0x00,0x18,0x00,0x10,0x00,
  30. 0x30,0x20,0x60,0x10,0x80,0x0F,0x00,0x00
  31. )

  32. --显示到oled上
  33. function print_sheshidu(x,y)
  34.                 --是否显示背景色
  35.        disp:setDrawColor(1)
  36.        disp:setBitmapMode(0)
  37. -----以位图的方式显示
  38.        disp:drawXBM(x,y,width,height,bits)
  39. end        
  40.         
  41.         
  42.         
  43. --NTP服务器列表
  44. NTP_SERVERS = {
  45.                                 "ntp.aliyun.com",
  46.                 "time.asia.apple.com",
  47.                 "cn.ntp.org.cn",
  48.                 "time.windows.com",
  49.                 "cn.pool.ntp.org"
  50.               }
  51.    
  52.     -- OLED初始化
  53.     function init_oled()

  54.         i2c.setup(id, sda, scl, i2c.SLOW)
  55.         disp = u8g2.ssd1306_i2c_128x32_univision(id, sla)
  56.         -- 设置字体
  57.         disp:setFont(u8g2.font_unifont_t_symbols)
  58.                 --disp:setFont(u8g2.font_6x10_tf)

  59.         disp:setFontRefHeightExtendedText()
  60.         --disp:setDrawColor(1)
  61.         disp:setFontPosTop()
  62.         disp:setFontDirection(0)
  63.         -- 画边框
  64.         --disp:drawFrame(0, 0, 128, 32)
  65.     end
  66.     -- 显示函数
  67.     function oled_show_msg()
  68.         -- 设置显示内容
  69.         disp:drawStr(0, 0,  " --Hello OLED-- ")
  70.         disp:drawStr(0, 16, "1234567890ABCDEF")
  71.         -- 将内容发送到oled
  72.         disp:sendBuffer()
  73.     end
  74.     -- 连接WiFi
  75.     function connectWiFi()
  76.         station_cfg={}
  77.         station_cfg.ssid = "XGY-NJY"
  78.         station_cfg.pwd = "njy123456"
  79.         station_cfg.save = true
  80.         print(wifi.sta.getip())
  81.         wifi.setmode(wifi.STATION)
  82.         wifi.sta.config(station_cfg)
  83.         print(wifi.sta.getip())
  84.     end
  85. --获取网络时间   
  86.     function getNetTime()
  87.             sntp.sync(NTP_SERVERS,
  88.                         function(sec, usec, server, info)
  89.                         print('sync', sec, usec, server)
  90.                                                 getWeather()
  91.                         end,
  92.                         function()
  93.                         print('failed!')
  94.                         end
  95.                                         )
  96.     end

  97.         function blinkTime()
  98.         --获取当前时间写到全局变量,方便调用
  99.                 tm        =        rtctime.epoch2cal(rtctime.get()+(8*3600))
  100.                 year=        tm["year"]
  101.                 month=        tm["mon"]
  102.                 day =        tm["day"]
  103.                 yday=        tm["yday"]
  104.                 wday=        tm["wday"]
  105.                 hour=        tm["hour"]
  106.                 minute=        tm["min"]
  107.                 second=        tm["sec"]
  108.         end

  109.         function disTIME()
  110.                 tm = rtctime.epoch2cal(rtctime.get()+(8*3600))
  111.       --打印转换后的年月日时分秒,以及今年的第几天,本周的第几天,本周第几天是从周日算第一天开始的,值的范围是1~7,周一是2
  112.         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"]))
  113.                 disp:clearBuffer()    --每次显示新内容前,清理一下缓冲区,保证不受上次显示内容影响

  114.       --推送给I2C显示年月日,今天是今年的第几天,今天是本周的第几天   
  115.                 disp:drawStr(0, 0,  string.format("%04d-%02d-%02d %03d %d", tm["year"], tm["mon"], tm["day"], tm["yday"], tm["wday"]))
  116.       --第二行显示时分秒
  117.                 disp:drawStr(0, 16, string.format("    %02d:%02d:%02d    ",     tm["hour"], tm["min"], tm["sec"]))
  118.         disp:sendBuffer()
  119.         end

  120. --获取天气
  121. function getWeather()
  122.         http.get("http://api.seniverse.com/v3/weather/now.json?key=换成你的私钥&location=nanjing&language=en&unit=c", nil,

  123.         function(code, data)
  124.                 if (code < 0) then
  125.                         print("HTTP request failed")
  126.                 else
  127.                 -- print(code, data)
  128.                  r = sjson.decode(data)
  129.                  print("City:                  \t"..r.results[1]["location"]["name"])
  130.                  print("TianQiCode:        \t"..r.results[1]["now"]["code"])
  131.                  print("\nTianQi:        \t"..r.results[1]["now"]["text"])
  132.                  print("Tem:                   \t"..r.results[1]["now"]["temperature"])

  133.                  --该函数仅用于获取天气信息存储在全局变量里,方便显示函数调用
  134.                  city = r.results[1]["location"]["name"]
  135.                  weather_now_code = tonumber(r.results[1]["now"]["code"])
  136.                  now_temperature  = r.results[1]["now"]["temperature"]

  137.                 end
  138.          end)
  139. end



  140. function display_OLED091_A()
  141.         disp:clearBuffer()
  142.         disp:drawStr(0, 0, string.format("%04d-%02d-%02d %03d %d", year,month,day,yday,wday))
  143.         disp:drawStr(0, 16,string.format("    %02d:%02d:%02d    ", hour,minute,second))
  144.         disp:sendBuffer()
  145. end

  146. function display_OLED091_B()
  147.         disp:clearBuffer()
  148.         --显示城市名
  149.         disp:drawStr(0, 0, city.." ")
  150.         --根据天气代码选择显示不同的图标表示天气信息
  151.         --在显示位置上先计算前面的内容占用了多少宽度
  152.                 if(weather_now_code<=3) then
  153.                 disp:drawGlyph((string.len(city)+1)*8,0,0x2600)
  154.                 elseif (weather_now_code<=8) then
  155.                 disp:drawGlyph((string.len(city)+1)*8,0,0x2601)               
  156.                 elseif (weather_now_code<=19) then
  157.                 disp:drawGlyph((string.len(city)+1)*8,0,0x2602)        
  158.                 elseif (weather_now_code<=25) then
  159.                 disp:drawGlyph((string.len(city)+1)*8,0,0x2603)
  160.                 else
  161.                 disp:drawGlyph((string.len(city)+1)*8,0,0x2604)
  162.                 end
  163.         --跟着显示温度        
  164.         disp:drawStr((string.len(city)+2)*8, 0, " "..now_temperature)
  165.         --绘制℃
  166.         print_sheshidu((string.len(city)+3+string.len(now_temperature))*8,0)
  167.         --在第二行显示时分秒
  168.         disp:drawStr(0, 16,string.format("    %02d:%02d:%02d    ", hour,minute,second))
  169.         disp:sendBuffer()
  170. end

  171.     -- 主函数
  172.     function main()
  173.         init_oled()
  174.                 --开机显示一些简单的测试信息
  175.                 oled_show_msg()
  176.                 disp:clearBuffer()

  177.                 connectWiFi()

  178.         getNetTime()
  179.                 --每1秒时间从本地取时间显示一次        
  180.         mytimer = tmr.create()
  181.         mytimer:register(1000, tmr.ALARM_AUTO, function()
  182.                                                                                                         blinkTime()
  183.                                                                                                         i=i+1
  184.                                                                                                         --print("This:", i)
  185. 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"]))
  186.                                                                                                         if (i>=60) then
  187.                                                                                                                 i=0
  188.                                                                                                                 print("a minute")
  189.                                                                                                         elseif(i%10>=5) then                                                                                       
  190.                                                                                                                 display_OLED091_A()
  191.                                                                                                         else
  192.                                                                                                                 --disTIME()
  193.                                                                                                                 display_OLED091_B()

  194.                                                                                                         end
  195.                                                                                                 end        )
  196.         mytimer:start()
  197.                 --定时每分钟从网络获取时间一次,每分钟获取一次天气信息
  198.         NetTimer = tmr.create()
  199.         NetTimer:register(60000, tmr.ALARM_AUTO,function()
  200.                                                                                                         getNetTime()
  201.                                                                                                         getWeather()
  202.                                                                                                         print("get NET TIME")
  203.                                                                                                 end )
  204.         NetTimer:start()
  205.         
  206.    end
  207.     -- 运行程序
  208.    main()
第四步:
将代码保存成init.lua
通过串口上传到ESP8266模块即可。
好了,你学会了吗?这应该是当前网上最简单好学的一个物联网天气时钟教程了。
做了一块连接板,本来还想做外壳呢,想想算了,哈哈,后面可能还会玩其他的功能
https://www.bilibili.com/video/bv1GP4y1o7ey

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×

打赏榜单

21小跑堂 打赏了 50.00 元 2022-09-09
理由:恭喜通过原创文章审核!请多多加油哦!

评论

得益于健康优质的开发生态,ESP8266可以很好的作为入门物联网的首选。通过简单的配置即可获取天气数据,作者不仅成功拉取了云端数据,还较好的显示出来,并动态刷新,实现效果较为良好。  发表于 2022-9-9 15:23
yiy 发表于 2022-9-10 12:34 | 显示全部楼层
MARK........
起点116 发表于 2022-9-17 21:50 | 显示全部楼层
起点116 发表于 2022-9-24 21:24 | 显示全部楼层
学习了
广州烂人 发表于 2022-10-21 17:10 | 显示全部楼层
高总厉害
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:如果你觉得我的分享或者答复还可以,请给我点赞,谢谢。

2052

主题

16403

帖子

222

粉丝
快速回复 在线客服 返回列表 返回顶部