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

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

[复制链接]
595|4
手机看帖
扫描二维码
随时随地手机跟帖
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的相关库
26884631aac5d2c305.png
可以有以下字体库选择,非常的nice,如果没啥想法,第一个不点进去就行了,用默认的那两个字符库。
5337631aada1e8222.png
第二步:烧录固件,这个打开软件安装提示操作就行了
第三步:编写代码
该软件要实现以下功能:连接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的都知道怎么写了,关键函数已提供。
效果非常的奈斯。
69384631ab0084ef5a.png
10593631ab015626fe.png
我设置的是第一行每隔五秒切换一次。
最后给大家看看我的完整源码
    -- 管脚定义
    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
了解更多私有5G网络技术 提升系统性能和可靠性

使用特权

评论回复

打赏榜单

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

评论
21小跑堂 2022-9-9 15:23 回复TA
得益于健康优质的开发生态,ESP8266可以很好的作为入门物联网的首选。通过简单的配置即可获取天气数据,作者不仅成功拉取了云端数据,还较好的显示出来,并动态刷新,实现效果较为良好。 

相关帖子

yiy| | 2022-9-10 12:34 | 显示全部楼层
MARK........

使用特权

评论回复
起点116| | 2022-9-17 21:50 | 显示全部楼层
厉害

使用特权

评论回复
起点116| | 2022-9-24 21:24 | 显示全部楼层
学习了

使用特权

评论回复
发新帖 本帖赏金 50.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则