gaoyang9992006 发表于 2022-9-9 11:21

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

本帖最后由 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["location"]["name"])
               print("TianQiCode:      \t"..r.results["now"]["code"])
               print("\nTianQi:      \t"..r.results["now"]["text"])
               print("Tem:                   \t"..r.results["now"]["temperature"])
               --推送到OLED显示
               --disp:clearBuffer()
               --disp:drawStr(0, 0, r.results["location"]["name"]..":"..r.results["now"]["text"].."-"..r.results["now"]["temperature"])
               --disp:sendBuffer()
               --该函数仅用于获取天气信息存储在全局变量里,方便显示函数调用
               city = r.results["location"]["name"]
               weather_now_code = tonumber(r.results["now"]["code"])
               now_temperature= r.results["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["location"]["name"])
               print("TianQiCode:      \t"..r.results["now"]["code"])
               print("\nTianQi:      \t"..r.results["now"]["text"])
               print("Tem:                   \t"..r.results["now"]["temperature"])

               --该函数仅用于获取天气信息存储在全局变量里,方便显示函数调用
               city = r.results["location"]["name"]
               weather_now_code = tonumber(r.results["now"]["code"])
               now_temperature= r.results["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

yiy 发表于 2022-9-10 12:34

MARK........

起点116 发表于 2022-9-17 21:50

厉害

起点116 发表于 2022-9-24 21:24

学习了

广州烂人 发表于 2022-10-21 17:10

高总厉害
页: [1]
查看完整版本: ESP8266采用NodeMCU固件在Lua脚本加持下实现互联网天气时钟功能