极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 28351|回复: 6

(NodeMCU)用ESP8266改造LM8562数字钟互联网校时,比GPS省电,比NTP简单,比BPC快速

[复制链接]
发表于 2016-2-2 23:11:45 | 显示全部楼层 |阅读模式
本帖最后由 maidoo 于 2016-2-2 23:25 编辑



(1) 需求篇

家里的一个钟控收音机。2英寸的数码管,很大。放在冰箱上,老远都看得清楚,就当一个LED数字钟用,收音机功能闲置。开饭、出门的时间都靠它掌握,责任重大!
问题在于这个数字钟两周时间就能快十几分钟,平均每天一分种左右。隔十天半个月就要去手工调整一下,不胜其烦。但是看着这么大的屏幕和造型又舍不得换,改造它的念头由来已久。
这个数字种使用的是钟控专用芯片LM8562,类似的还有TMS3450,LM8560,LM8561等,上个世纪很流行。它使用市电50Hz的频率进行计时。有些还是采用专用的双阴极数码管,有闹钟/睡眠功能。市电频率不准确,走时就不准,这个问题也由来已久,所以也有个专门的芯片MM5369配上3.58MHz的晶振,可以产生很稳定的时基频率配合这类数字钟芯片。现在都讲究互联网思维了,不想用这个片子了。




(2) 方案篇
为了能给LM8562校时,有两个技术问题要解决:(一)对外:如何获取到准确时间;(二)对内:如何设置时间

(一)获取时间的方案
a) 通过GPS的方案。数码之家的坛子里已经有数不胜数的帖子了,电路图/PCB/源代码都有公开的,C51/AVR/STM32芯片的都齐全。由于不需要GPS的定位精度和速度,只要时间,所以最烂的GPS都可以,剪线GPS十多块钱就能买到。+5V供电也方便,UART串口操作代码也很经典,调试简单。实在是个不错的方案。就是要拖个尾巴到窗边略显麻烦。

b) 通过电波钟,获取商丘的国家授时中心发出的时间信号,这是中国的BPC格式,15元的模块。另外沿海地区还可以收到小鬼子的JJY格式的时间信号。但是目前还不太稳定,白天没夜间信号好,模块本身抗干扰不行,输出端无法直驱单片机,还需要一个运放整理信号,电路复杂些,调试较困难,坛子里的帖子不多。
thomas: 《永不消逝的电波——Arduino制作BCD码电波钟》
http://www.geek-workshop.com/thread-7797-1-1.html



c) 通过互联网获取时间,有个NTP协议专门是干这事的。我们的PC,无线路由器都在用这个协议,指定一个互联网上的NTP Server,一来一回两个报文,作为Client的我们就能计算出当前的准确时间。我本来是想用这个方案的,后来发现有更简单的,就是从服务器的http报文头里面获取当前时间。这个时间是明文字符串,处理起来更容易,虽然理论精度不及NTP,但是用在数字种上足够了。devcang和我想到一块去了。那个数字钟只显示时和分,不显示秒,所以没必要做到秒同步,只要每天校对一次,消除累积误差就可以了。这类方案的不多,搜到坛子里的几个帖子提及,但都没有完整实践。我就选定这个方案了。
renpeng009: 《基于NTP协议网络校时》
http://bbs.mydigit.cn/read.php?tid=1062532


d) 通过CDMA模块,从运营商的移动通信网获取时间。理论上有可能,也有不少困难,性价比不高。不研究。

e) 锁定广播电台的整点对时信号,"嘟嘟嘟嘟嘟,嘀" 的声音,通过锁相环LM567解出这个“嘀”信号后,给数字钟清零。管你快了还是慢了,到这一时刻,统统给我从整点开始。这还是我N多年前,从《中学科技》上看到的。这个方案无法知道当前到底是几点,只知道是个整点了。能消除累积误差,但是初始时间还需要手工设置一下。如果是电池供电的,设置一次就不会忘,问题不大。我的LM8562数字钟没装电池,掉电后时间就归零到凌晨12点了,有点麻烦。



(二)设置时间的方案
a) 废弃LM8562,保留LED屏幕,用单片机计时并驱动。获取到时间后,设置时间自然不在话下。不过改动工程量也是不少的,要不重新制作PCB,要不就会大量飞线。而且LM8562使用的正是双阴极数码管,驱动代码得好好找找。数码之家真是DIYer的大家庭,果然有mengfc网友贡献了双阴极数码管的驱动源代码。实在没有得意的办法时,这是一条退路。
《采用单片机驱动TMS3450数字钟的双阴极LED屏幕》
http://bbs.mydigit.cn/read.php?fpage=35&tid=1177273


b) 祭出IO模拟按键大法,这在我之前的《
三个零件,给大金家用中央空调加装遥控功能》中已经有成功应用。阅读LM8562的资料得知,它有一个时间调整的引脚,平时悬空(二分之一电源电压),接电源负极是分钟加,接电源正极是分钟减。没有单独的小时调整键,调整分钟,小时会自动进位。

举例来说,通过互联网获得当前时间是12:27分,那么单片机先通过IO脚控制LM8562断电再上电,这时LM8562复位到12:00开始计时。然后单片机控制另一个IO模拟按“时间调整”按键27次,LED时钟就调整到12:27了。同理,如果要调整到10:27分,就要模拟按键627次。

实际上,我设计为每天晚上12点到1点之间同步校时一次,这些按键所花费的时间可以忽略。


(3) 硬件篇




为了单片机能控制时钟芯片断电,在LM8562的电源负极串入一个NPN三极管Q1,平时由R1和D1注入基极电流,Q1导通,时钟得电。需要断电时,IOPWR输出0电平,Q1就能截止。设计D1为绿色的发光二极管,纯粹是为了抬高IO的有效控制电平。

Q2用来模拟“时间调整”按键,R3平时把基极电压拉低,Q2有效截止。IOCALI输出高电平(3.3V即可),Q2导通,相当于按键按下。

大名鼎鼎的ESP8266是专用于物联网的WIFI模块,所有功能通过串口即可操作。ESP8266模块的3.3V供电,我原本是用AMS1117-3.3一片LDO直接从原LM8562的10V电源降压的,但是会导致整机的电压波动,LED屏亮度闪烁明显,不可接受。更换为一片3.3V输出的DCDC模块解决问题。开关电源电源效率高,源端电流消耗小些。

ESP8266采用ESP-07模块,有全IO引出,方便扩展功能,比如加片DS18B20测温并上传到物联网平台,再比如通过物联网平台可任意设置定时时间,打开收音机听天气预报。反正CPU闲着也是闲着,给它找点活干。

至于前面提到的单片机嘛,也不需要一个实体的芯片了,单片机的活都交给ESP-07模块去完成


(4) 软件篇
ESP8266灌nodeMCU的固件后,可以用LUA语言编程。模块通过http协议请求百度服务器的页面,服务器返回的消息头里面的第二行Date开头的就是当前时间,不过是0时区的,加上8就是了。提取出小时、分钟的信息后,就可以通过GPIO调整时间了。

< HTTP/1.1 200 OK
< Date: Mon, 01 Feb 2016 09:05:59 GMT
< Server: Apache
< Cache-Control: max-age=86400
< Expires: Tue, 02 Feb 2016 09:05:59 GMT
< Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT
< ETag: "51-4b4c7d90"
< Accept-Ranges: bytes
< Content-Length: 81
< Content-Type: text/html

初次上电启动获取当前时间后,立即调整LM8562的时间。然后启动一个61分钟的定时器,每小时获取一次时间,发现小时等于12,才真正去设置一下当前时间。

nodeMCU中共加载2个文件:init.lua来配置家里的无线网络密码;netCaliLM8562.lua就是对应的单片机要干的活。

下载前,需要修改init.lua文件里这句SSID和密码为你自己家里的。我修改了官方的这个文件,让它循环尝试去获取IP地址,直到成功才能退出。
  1.     wifi.sta.config("ChinaNet","88888888")
复制代码
   
如果IO和我用的不一样,下载前需要修改netCaliLM8562.lua文件中的定义。需要说明的是,nodeMCU认定的IO编号和ESP-07的丝印是不一样的。有个对应关系,需要自己变换。比如说ESP-07的GPIO16在nodeMCU里面编号为0。下面定义的1对应ESP-07的GPIO5,2对应GPIO4。看一下nodeMCU官方小黄板的原理图就一切都明白了。
  1.         ioPWR  = 1
  2.         ioCali = 2
复制代码
   
然后重启ESP-07模块,正常的话,模块串口就能打印出运行信息了。








本帖子中包含更多资源

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

x
回复

使用道具 举报

 楼主| 发表于 2016-2-2 23:13:21 | 显示全部楼层
本帖最后由 maidoo 于 2016-2-2 23:19 编辑

软件附加在这里。
init.lua
[pre lang="LUA" line="1" file="init.lua"]
print("ower on, Setting wifi client connection....")
wifi.setmode(wifi.STATION)
wifi.sta.config("Chinanet","88888888")
wifi.sta.connect()
local cnt = 1
tmr.alarm(1, 1000, 1, function()
    if (wifi.sta.getip() == nil) and (cnt <= 20) then
        print("["..cnt.."] IP invalid, waitting...")
        cnt = cnt + 1
    else
        if (cnt <= 20) then print("Config done, IP is "..wifi.sta.getip())
            tmr.stop(1)
            dofile("netCaliLM8562.lua")
        else print("!!!! Wifi setup time more than 20s, reconnect it again....")
            cnt = 1
            wifi.sta.connect()
        end
    end
end)
[/code]

netCaliLM8562.lua
[pre lang="LUA" line="1" file="netCaliLM8562.lua"]
-- host = 'emouze.com'
host = 'baidu.com'
port = 80
--url = "/img/baidu_jgylogo1.gif"
url = '/'
httpPKG = "HEAD " .. url .. " HTTP/1.1\r\nHost: " .. host .. "\r\nConnection: close\r\n\r\n"
curGMT = ""
bConnected = false
calibratedAfterPowerOn = false
tzone = 8   -- 东8区时区
sentCnt = 0
t2cnt = 1
t2target = 0
tmrBtn = 2
tmrCycled = 4
ioPWR  = 1
ioCali = 2
gpio.mode (ioCali, gpio.OUTPUT)
gpio.write(ioCali, gpio.LOW)
-----------------------------
local function loopButton()
    if t2cnt <= t2target then
        if 1 == (t2cnt % 2) then
            gpio.write(ioCali, gpio.HIGH)
        else
            gpio.write(ioCali, gpio.LOW)
        end
        t2cnt = t2cnt + 1
    else
        tmr.stop(tmrBtn)
        gpio.write(ioCali, gpio.LOW)
    end
end
-----------------------------
function adjustClock(hour, minute)
    local cc
    hour = hour % 12
    minute = minute % 60
    cc = hour * 60 + minute
    print("========== Calibrated by Internet ================")
    print("    Adjust LM8562 clock to " .. hour .. ":" .. minute)
    gpio.mode (ioPWR, gpio.OUTPUT)
    gpio.write(ioPWR, gpio.LOW)   -- Clock power off
    tmr.delay(202000)
    gpio.mode (ioPWR, gpio.INPUT) -- Power it on again, with LM8562 it start from 12:00 AM
    tmr.delay(955000)
    tmr.wdclr()
    gpio.mode (ioCali, gpio.OUTPUT)
    t2cnt = 1
    if cc == 0 then cc = 1 end     -- At least press button once to calibrated clock
    t2target = cc * 2
    tmr.alarm(tmrBtn,50,1,function() loopButton() end)
end
function constructConnection()
    if bConnected == false then
        socket = nil
        socket = net.createConnection(net.TCP, 0)
        socketn("connection", function(sck, response)
            bConnected = true
            sentCnt = sentCnt + 1
            print("\r\n#### Conneted!\t\t&socket:", socket)
            socket:send(httpPKG)
        end)
        socketn("disconnection", function(sck, response)
            bConnected = false
            socket:close()
            socket = nil
            print("#### Socket disconneted, @Count=" .. sentCnt ..", Heap=" .. node.heap())
        end)
        socketn("receive", function(sck, response)
            --print("[Recv] " .. response)
            hh = nil
            curGMT = string.sub(response,string.find(response,"Date: "),string.find(response,"Date: ")+35)
            -- print(curGMT)    -- Date: Sat, 23 Jan 2016 09:10:20 GMT
            hh,mm,ss = string.match(curGMT, "(%d+)%d*%d+)%d*%d+)%d* GMT")
            if hh ~= nil then
                print("#### Get Internet time from " .. host, (hh+tzone)%24, mm, ss)
                --刚上电需要校准一次,之后仅在半夜12点到1点之间执行校准,这样按钮的次数少
                if (calibratedAfterPowerOn == false) or (hh + tzone == 24) then
                    calibratedAfterPowerOn = true
                    adjustClock(hh+tzone, mm)
                end
            end
        end)
        socket:connect(port, host)
    end
end
-- +++++++++++++++++++++++++++++++
constructConnection()
tmr.alarm(tmrCycled,3660000,1,function() constructConnection() end)
[/code]
回复 支持 反对

使用道具 举报

发表于 2016-2-4 09:08:43 | 显示全部楼层
不错,点赞!
回复 支持 反对

使用道具 举报

发表于 2016-2-4 15:01:07 | 显示全部楼层
很不错,真是高手
回复 支持 反对

使用道具 举报

发表于 2016-2-5 10:57:38 | 显示全部楼层
厉害,支持一下
回复 支持 反对

使用道具 举报

发表于 2016-3-8 23:43:36 | 显示全部楼层
本帖最后由 XEmperor 于 2016-3-8 23:46 编辑

楼主,可以把你用的那个固件,分享给我们吗????????或者发到我QQ邮箱[email protected]谢谢啦,我急用
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-3-16 17:24:59 | 显示全部楼层
XEmperor 发表于 2016-3-8 23:43
楼主,可以把你用的那个固件,分享给我们吗????????或者发到我QQ邮箱谢谢啦,我急用

没特别的,就是NodeMCU官方固件,去官网下载吧。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-24 17:15 , Processed in 0.050098 second(s), 23 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表