{"id":16779684,"url":"https://github.com/lc044/network-clock","last_synced_at":"2025-04-10T20:52:52.114Z","repository":{"id":128060118,"uuid":"529502522","full_name":"LC044/network-clock","owner":"LC044","description":"esp32+python打造个性化桌面摆件","archived":false,"fork":false,"pushed_at":"2024-03-15T11:44:02.000Z","size":5786,"stargazers_count":26,"open_issues_count":1,"forks_count":11,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-24T18:21:37.381Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LC044.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-27T06:26:36.000Z","updated_at":"2025-03-24T01:38:11.000Z","dependencies_parsed_at":"2025-02-18T02:45:29.699Z","dependency_job_id":null,"html_url":"https://github.com/LC044/network-clock","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LC044%2Fnetwork-clock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LC044%2Fnetwork-clock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LC044%2Fnetwork-clock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LC044%2Fnetwork-clock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LC044","download_url":"https://codeload.github.com/LC044/network-clock/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248297011,"owners_count":21080309,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-13T07:31:40.918Z","updated_at":"2025-04-10T20:52:52.092Z","avatar_url":"https://github.com/LC044.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\r\n\r\n# esp32 + Python 打造个性化桌面时钟\r\n\r\n## 一、显示效果\r\n\r\n按下按键三种模式切换\r\n\r\n1. ![模式一](https://github.com/LC044/network-clock/blob/main/image/80da0e75a59bf6c452952c4b9b3feefd%2000_00_00-00_00_30.gif)\r\n2. ![模式二](https://pic1.zhimg.com/v2-17ce4d528bac4217ba75457f8de5e08c_r.jpg)\r\n3. ![模式三](https://pic1.zhimg.com/v2-321bbe2ba8dcbb4ca2a3fbcb5aec62e4_r.jpg)\r\n\r\n## 二、硬件准备\r\n\r\n### 硬件清单：\r\n\r\n|      |             名称             | 数量 |\r\n| :--: | :--------------------------: | :--: |\r\n|  1   |     30引脚esp32-s开发板      |  1   |\r\n|  2   |           1kΩ电阻            |  1   |\r\n|  3   |           按键开关           |  1   |\r\n|  4   | 240*240tft屏幕（st7789驱动） |  1   |\r\n|  5   |            杜邦线            | 若干 |\r\n\r\n### 线路连接：\r\n\r\n![接线](https://pic3.zhimg.com/v2-5f8a9038e7554468d3e7adad9acff41a_r.jpg)\r\n\r\n## 三、开源地址\r\n\r\nGitHub：https://github.com/LC044/network-clock\r\n\r\nGitee：https://gitee.com/shuaikang-zhou/network-clock\r\n\r\n## 四、原理分析\r\n\r\n### （一）st7789py.py驱动修改\r\n\r\n```python\r\n'''\r\n导入字体\r\n'''\r\nfrom package import GBfont\r\nfrom package import font_gb_16x16 as font_gb\r\nfrom package import VGAfont\r\nGB16 = GBfont.gb2312(16)\r\nGB24 = GBfont.gb2312(24)\r\nGB32 = font_gb.Font32(32)\r\nvga32 = VGAfont.VGAFONT(32)\r\nvga16 = VGAfont.VGAFONT(16)\r\nvga24 = VGAfont.VGAFONT(24)\r\nvga40 = VGAfont.VGAFONT(40)\r\nvga48 = VGAfont.VGAFONT(48)\r\n```\r\n\r\n新增PWM背光调节亮度：\r\n\r\n```python\r\n'''初始化'''\r\nif backlight is not None:\r\n    from machine import PWM\r\n    self.bl = PWM(self.backlight,freq=1000,duty=128)\r\ndef brightness(self,value):\r\n    '''\r\n    调节屏幕亮度\r\n    value: int 0-1023\r\n    '''\r\n    self.bl.duty(value)\r\n```\r\n\r\n支持更多字号字体，中英文字体，不同字号字体合并：\r\n\r\n```python\r\ndef text(self,size, text, x0, y0, color=WHITE, background=BLACK):\r\n        char = text[0].encode('utf-8')\r\n        if len(char) == 1:\r\n            if size == 0:\r\n                pass\r\n            elif size == 24:\r\n                font_gb = vga24.str(text)\r\n            elif size == 16:\r\n                font_gb = vga16.str(text)\r\n            elif size == 32:\r\n                font_gb = vga32.str(text)\r\n            elif size == 40:\r\n                font_gb = vga40.str(text)\r\n            elif size == 48:\r\n                font_gb = vga48.str(text)\r\n        else:\r\n            if size == 16:\r\n                font_gb = GB16.str(text)\r\n            elif size == 24:\r\n                font_gb = GB24.str(text)\r\n            elif size == 32:\r\n                font_gb = GB32\r\n            elif size == 48:\r\n                font_gb = GB48\r\n        self._text(font_gb,size, text, x0, y0, color, background)\r\n```\r\n\r\n提高显示速度，修复颜色bug：\r\n\r\n```python\r\ndef _text(self,font,size, text, x0, y0, color=WHITE, background=BLACK):\r\n        self.k = 0\r\n        self.num = 4\r\n        for char in text:\r\n            if (x0+font.WIDTH \u003c= self.width and y0+font.HEIGHT \u003c= self.height):\r\n                for line in range(self.num):  # 分两次显示，先显示上半边后显示下半边\r\n                        idx = line * (font.SIZE//self.num)\r\n                        buffer = b''\r\n                        for x in range(0,(font.SIZE//self.num),1):\r\n                            buffer += struct.pack('\u003e8H',\r\n                                color if font.FONT[char][idx+x] \u0026 _BIT7 else background,\r\n                                color if font.FONT[char][idx+x] \u0026 _BIT6 else background,\r\n                                color if font.FONT[char][idx+x] \u0026 _BIT5 else background,\r\n                                color if font.FONT[char][idx+x] \u0026 _BIT4 else background,\r\n                                color if font.FONT[char][idx+x] \u0026 _BIT3 else background,\r\n                                color if font.FONT[char][idx+x] \u0026 _BIT2 else background,\r\n                                color if font.FONT[char][idx+x] \u0026 _BIT1 else background,\r\n                                color if font.FONT[char][idx+x] \u0026 _BIT0 else background,\r\n                                )\r\n                        self.blit_buffer(buffer, x0, y0+(font.HEIGHT//self.num)*line, font.WIDTH, font.HEIGHT//self.num)\r\n                x0 += font.WIDTH  # 显示下一个字的时候x坐标增加字体宽度\r\n```\r\n\r\n### （二）连接WiFi，同步时钟（WiFi.py）\r\n\r\n```python\r\nimport ntptime\r\nimport network,time\r\nfrom machine import RTC,Pin\r\nrtc = RTC()\r\nssid = 'CU_1316'       # WiFi 账号\r\npassword = 'zhou1316'  # WiFi 密码\r\n# 联WIFI\r\ndef WIFI_Connect():\r\n    wlan = network.WLAN(network.STA_IF) #STA模式\r\n    wlan.active(True)                   #激活接口\r\n    start_time=time.time()              #记录时间做超时判断\r\n    if not wlan.isconnected():\r\n        print('connecting to network...')\r\n        wlan.connect(ssid, password) #输入WIFI账号密码\r\n        while not wlan.isconnected():\r\n            if time.time()-start_time \u003e 15 :\r\n                print('WIFI Connected Timeout!')\r\n                break\r\n    if wlan.isconnected():\r\n        print('connected!')\r\n        print('network information:', wlan.ifconfig())\r\n\r\n# 同步时间\r\ndef sync_ntp():\r\n     ntptime.NTP_DELTA = 3155644800\r\n     ntptime.host = 'ntp1.aliyun.com'\r\n     try:\r\n         ntptime.settime()\r\n     except:\r\n         print('同步失败')\r\n\r\nif __name__ == '__main__':\r\n    WIFI_Connect()\r\n    sync_ntp()\r\n    print(rtc.datetime())\r\n    print(\"同步后本地时间：%s\" %str(time.localtime()))\r\n```\r\n\r\n### （三）获取网络天气、诗词（weather.py）\r\n\r\n天气API：心知天气\r\n\r\n```python\r\ndef now_weather(self,city):\r\n    #34.40:114.96\r\n    url = \"https://api.seniverse.com/v3/weather/now.json?key={}\u0026location={}\u0026language=zh-Hans\u0026unit=c\".format(self.password,city)\r\n    result1=urequests.get(url)\r\n    j1=ujson.loads(result1.text)\r\n    _city = j1['results'][0]['location']['name']\r\n    weather = j1['results'][0]['now']['text']\r\n    tem = j1['results'][0]['now']['temperature']\r\n    code = j1['results'][0]['now']['code']\r\n    print(j1['results'][0]['location']['name'],end=' ')\r\n    print(j1['results'][0]['now']['text'],end=' ')\r\n    print(j1['results'][0]['now']['temperature'],end='℃ ')\r\n    print(j1['results'][0]['last_update'])\r\n    print('天气代码：',code)\r\n    return {\r\n        'city':_city,\r\n        'weather':weather,\r\n        'tem':tem,\r\n        'code':code,\r\n        'height':int(self.img_size(code)[3:]),\r\n        'width':int(self.img_size(code)[0:2])\r\n    }\r\ndef poet(self):\r\n    # 获取句子\r\n    url = 'https://api.a632079.me/?max_length=11'\r\n    res=urequests.get(url)\r\n    dic=ujson.loads(res.text)\r\n    print(len(dic['hitokoto']),dic['hitokoto'])\r\n    return dic['hitokoto'][:dic['length']]\r\ndef jinrishici(self):\r\n    # 获取诗词\r\n    url = 'https://v2.jinrishici.com/sentence'\r\n    headers = {\r\n        'X-User-Token':'EsnWc/+lex+Lgs7/m0clRHToBy/Eejy7'\r\n    }\r\n    res=urequests.get(url,headers=headers)\r\n    dic=ujson.loads(res.text)\r\n    if dic['status'] == 'success':\r\n        return dic['data']\r\n```\r\n\r\n### （四）天气时钟界面(clock.py)\r\n\r\n```python\r\ndef clock(self):\r\n    file = [open(\"./astronaut/img{}.dat\".format(i), \"rb\") for i in range(1, 14)]\r\n    #self.tft.brightness(64)\r\n    self.init_show()                   # 初始化显示画面\r\n    self.show_city(self.data['city'])  # 显示城市\r\n    self.show_tem(self.data['tem'])    # 显示温度\r\n    self.show_weather(self.data)       # 显示天气信息\r\n    self.init_time()                   # 初始化时间显示\r\n    while True:\r\n        if self.mod != _CLOCK:\r\n            return\r\n        self.t = time.localtime(time.time())\r\n        # 每十分钟天气更新一次\r\n        if self.t[4]%10==0 and 0\u003cself.t[5]\u003c2:\r\n            self.data = self.Weather.now_weather(self.city)\r\n            self.show_city(self.data['city'])\r\n            self.show_weather(self.data)\r\n            self.show_time(self.t)\r\n            # 显示太空人动画\r\n            for f in file:\r\n                f.seek(0)\r\n                for row in range(0, 80, 20):\r\n                    buffer = f.read(2800)\r\n                    self.tft._set_window(170,row,239,row+19)\r\n                    self.tft._write(None,buffer)\r\n                    time.sleep(0.1)\r\n```\r\n\r\n### （五）诗词界面(poet.py)\r\n\r\n```python\r\n    def show(self):\r\n        try:\r\n            poet_data = self.Weather.jinrishici()\r\n            author = poet_data['origin']['author']\r\n            dynasty = poet_data['origin']['dynasty']\r\n            title = poet_data['origin']['title']\r\n            poet = poet_data['origin']['content']\r\n        except:\r\n            pass\r\n        poem = []\r\n        for i in range(len(poet)):\r\n            if len(poet[i])\u003e15:\r\n                if '。' in poet[i]:\r\n                    se = poet[i].split('。')\r\n                elif '？' in poet[i]:\r\n                    se = poet[i].split('？')\r\n                elif '！' in poet[i]:\r\n                    se = poet[i].split('！')\r\n                else:\r\n                    se = poet[i].split('，')\r\n                for s in range(len(se)-1):\r\n                    if len(se[s]) \u003e 15:\r\n                        p = se[s].split('，')\r\n                        for pm in p:\r\n                            poem.append(pm)\r\n                    else:\r\n                        poem.append(se[s])\r\n            else:\r\n                poem.append(poet[i])\r\n        y = 50\r\n        del poet\r\n        self.tft.fill(self.WHITE)\r\n        offest = (240-24*len(title))//2\r\n        self.tft.text(24, title, offest, 0, self.BLACK, self.WHITE)\r\n        offest = 24*len(author)\r\n        self.tft.text(24, '{}'.format(author), 240-offest-24, 30, self.BLACK, self.WHITE)\r\n        for p in poem:\r\n            y = y + 20\r\n            offest = (240-16*len(p))//2\r\n            self.tft.text(16, p, offest, y, self.BLACK, self.WHITE)\r\n        while True:\r\n            if self.mod != 1:\r\n                return\r\n            else:\r\n                utime.sleep(0.5)\r\n                pass\r\n```\r\n\r\n### （六）日历界面（calendar.py）\r\n\r\n```python\r\ndef show(self,month):\r\n        begin = 1\r\n        y = 35 + 16 + 4+5+10\r\n        dx = 34\r\n        x = self.week\r\n        week_num = 1\r\n        while begin \u003c= self.days[month - 1]:\r\n            begin += 1\r\n            self.week = (self.week + 1) % 7\r\n            if self.week % 7 == 0:\r\n                week_num += 1\r\n        begin = 1\r\n        self.week = x\r\n        print(week_num)\r\n        size = 16 if week_num == 6 else 24\r\n        while begin \u003c= self.days[month - 1]:\r\n            color = self.RED if begin == self.t[2] else self.WHITE\r\n            bg = self.WHITE if begin == self.t[2] else self.BLACK\r\n            offest_x = 6 if begin\u003c10 else 0\r\n            self.tft.text(size, '{}'.format(begin), 5+dx*x+offest_x, y , color, bg)\r\n            begin += 1\r\n            x+=1\r\n            self.week = (self.week + 1) % 7\r\n            #print('%4d' % begin,end=',')\r\n            if self.week % 7 == 0:\r\n                x=0\r\n                y+=30\r\n```\r\n\r\n### （七）main.py\r\n\r\n```python\r\nfrom machine import Pin, SPI, RTC\r\nfrom package import st7789py as st\r\nfrom package import weather\r\nimport gc\r\nimport time\r\nimport clock\r\nimport poet\r\nimport calendar\r\n_CLOCK = 0\r\n_VERSE = 1\r\n_CALENDAR = 2\r\nclass Display():\r\n    def __init__(self):\r\n        self.tft = st.ST7789(SPI(2, 10000000), 240, 240, reset=Pin(5), dc=Pin(17), cs=Pin(16), backlight=Pin(4), rotation=0)\r\n        self.bt = Pin(34, Pin.IN,Pin.PULL_DOWN)\r\n        self.bt.irq(trigger=Pin.IRQ_FALLING, handler=self.model)\r\n        self.mod = 0\r\n        self.weather = weather.Weather()\r\n        self.clock = clock.CLOCK(self.tft,self.weather)\r\n        self.poem = poet.POET(self.tft,self.weather)\r\n        self.calendar = calendar.Calendar(self.tft)\r\n    def model(self,*argc):\r\n        self.mod=(self.mod+1)%4\r\n        self.clock.mod = self.mod\r\n        self.poem.mod = self.mod\r\n        self.calendar.mod = self.mod\r\n    def run(self):\r\n        while True:\r\n            print(self.mod)\r\n            if self.mod == _CLOCK:\r\n                self.tft.brightness(128)\r\n                self.clock.clock()\r\n            elif self.mod == _VERSE:\r\n                self.tft.brightness(256)\r\n                self.tft.fill(0)\r\n                self.poem.show()\r\n            elif self.mod == _CALENDAR:\r\n                self.tft.brightness(256)\r\n                self.calendar.display()\r\n            time.sleep(0.5)\r\n            gc.collect()\r\n    def __del__(self):\r\n        pass\r\nD = Display()\r\nD.run()\r\n```\r\n\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flc044%2Fnetwork-clock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flc044%2Fnetwork-clock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flc044%2Fnetwork-clock/lists"}