├── software ├── config.py ├── font5x8.bin ├── boot.py ├── week7x7.py ├── font3x8.py ├── bitmapfont.py ├── rx8111.py └── main.py ├── documents └── images │ ├── 1.png │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.png │ ├── 6.jpg │ └── 7.jpg ├── hardware ├── SCH_1.3a.pdf └── gerber_bom_pick │ ├── Gerber_PCB1_2_2023-08-18.zip │ └── BOM_1.3a_PCB1_2_2023-08-18.xlsx ├── mechanical ├── stl │ ├── 压板.stl │ ├── 外壳.stl │ └── 按键.stl └── Panel │ └── Panel_2023-08-08.epanm ├── firmware └── esp32c3-usb-20230426-v1.20.0.bin └── README.md /software/config.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #wifi 5 | ssid = " " 6 | password = " " 7 | -------------------------------------------------------------------------------- /software/font5x8.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/software/font5x8.bin -------------------------------------------------------------------------------- /documents/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/documents/images/1.png -------------------------------------------------------------------------------- /documents/images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/documents/images/2.jpg -------------------------------------------------------------------------------- /documents/images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/documents/images/3.jpg -------------------------------------------------------------------------------- /documents/images/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/documents/images/4.jpg -------------------------------------------------------------------------------- /documents/images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/documents/images/5.png -------------------------------------------------------------------------------- /documents/images/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/documents/images/6.jpg -------------------------------------------------------------------------------- /documents/images/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/documents/images/7.jpg -------------------------------------------------------------------------------- /hardware/SCH_1.3a.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/hardware/SCH_1.3a.pdf -------------------------------------------------------------------------------- /mechanical/stl/压板.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/mechanical/stl/压板.stl -------------------------------------------------------------------------------- /mechanical/stl/外壳.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/mechanical/stl/外壳.stl -------------------------------------------------------------------------------- /mechanical/stl/按键.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/mechanical/stl/按键.stl -------------------------------------------------------------------------------- /mechanical/Panel/Panel_2023-08-08.epanm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/mechanical/Panel/Panel_2023-08-08.epanm -------------------------------------------------------------------------------- /firmware/esp32c3-usb-20230426-v1.20.0.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/firmware/esp32c3-usb-20230426-v1.20.0.bin -------------------------------------------------------------------------------- /hardware/gerber_bom_pick/Gerber_PCB1_2_2023-08-18.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/hardware/gerber_bom_pick/Gerber_PCB1_2_2023-08-18.zip -------------------------------------------------------------------------------- /hardware/gerber_bom_pick/BOM_1.3a_PCB1_2_2023-08-18.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MakerM0/PixelTime/HEAD/hardware/gerber_bom_pick/BOM_1.3a_PCB1_2_2023-08-18.xlsx -------------------------------------------------------------------------------- /software/boot.py: -------------------------------------------------------------------------------- 1 | # This file is executed on every boot (including wake-boot from deepsleep) 2 | #import esp 3 | #esp.osdebug(None) 4 | #import webrepl 5 | #webrepl.start() 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 |
4 | 5 |
6 |

7 |

8 | GitHub release (latest by date) 9 | GitHub top language 10 |

11 | 12 | 13 | # [PixelTime](https://oshwhub.com/kakaka/PixelTime) 14 | a low power led watch based on ESP32-C3 & MicroPython 15 | 16 | [afdian.net](https://afdian.net/a/modular) [bilibili](https://www.bilibili.com/video/BV1Bj411i7ZR/) [OSHWHUB](https://oshwhub.com/kakaka/PixelTime) 17 | 18 | ![5](documents/images/5.png) 19 | 20 | ![7](documents/images/7.jpg) 21 | 22 | ![4](documents/images/4.jpg) 23 | 24 | ![2](documents/images/2.jpg) 25 | 26 | ![3](documents/images/3.jpg) 27 | 28 | ![6](documents/images/6.jpg) 29 | 30 | ![1](documents/images/1.png) 31 | 32 | ## Software 33 | 34 | based on MicroPython 35 | 36 | 37 | 38 | 39 | 40 | ## Mechanical 41 | 42 | 43 | 44 | ## License 45 | 46 | (hardware/mechanical)[Creative Commons — Attribution-NonCommercial-ShareAlike 4.0 International — CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) 47 | 48 | 49 | 50 | ## Extra 51 | 52 | 53 | -------------------------------------------------------------------------------- /software/week7x7.py: -------------------------------------------------------------------------------- 1 | WEEK7X8=( 2 | (0,1,1,1,1,1,0, 3 | 0,1,0,0,0,1,0, 4 | 0,1,0,0,0,1,0, 5 | 0,1,1,1,1,1,0, 6 | 0,1,0,0,0,1,0, 7 | 0,1,0,0,0,1,0, 8 | 0,1,1,1,1,1,0, 9 | 0,0,0,0,0,0,0, 10 | 11 | ),#日 12 | (0,0,0,0,0,0,0, 13 | 0,0,0,0,0,0,0, 14 | 0,0,0,0,0,1,0, 15 | 1,1,1,1,1,1,1, 16 | 0,0,0,0,0,0,0, 17 | 0,0,0,0,0,0,0, 18 | 0,0,0,0,0,0,0, 19 | 0,0,0,0,0,0,0, 20 | 21 | ),#一 22 | ( 23 | 0,0,0,0,0,0,0, 24 | 0,1,1,1,1,1,0, 25 | 0,0,0,0,0,0,0, 26 | 0,0,0,0,0,0,0, 27 | 0,0,0,0,0,0,0, 28 | 0,0,0,0,0,1,0, 29 | 1,1,1,1,1,1,1, 30 | 0,0,0,0,0,0,0, 31 | 32 | ),#二 33 | (0,0,0,0,0,0,0, 34 | 0,1,1,1,1,1,0, 35 | 0,0,0,0,0,0,0, 36 | 0,0,1,1,1,0,0, 37 | 0,0,0,0,0,0,0, 38 | 0,0,0,0,0,0,0, 39 | 1,1,1,1,1,1,1, 40 | 0,0,0,0,0,0,0, 41 | 42 | ),#三 43 | ( 44 | 1,1,1,1,1,1,1, 45 | 1,0,1,0,1,0,1, 46 | 1,0,1,0,1,0,1, 47 | 1,0,1,0,1,1,1, 48 | 1,1,0,0,0,0,1, 49 | 1,0,0,0,0,0,1, 50 | 1,1,1,1,1,1,1, 51 | 0,0,0,0,0,0,0, 52 | ),#四 53 | ( 54 | 0,1,1,1,1,1,0, 55 | 0,0,0,1,0,0,0, 56 | 0,0,0,1,0,0,0, 57 | 0,1,1,1,1,1,0, 58 | 0,0,1,0,0,1,0, 59 | 0,0,1,0,0,1,0, 60 | 1,1,1,1,1,1,1, 61 | 0,0,0,0,0,0,0, 62 | 63 | ),#五 64 | ( 65 | 0,0,0,1,0,0,0, 66 | 1,1,1,1,1,1,1, 67 | 0,0,0,0,0,0,0, 68 | 0,0,1,0,1,0,0, 69 | 0,0,1,0,1,0,0, 70 | 0,1,0,0,0,1,0, 71 | 1,0,0,0,0,0,1, 72 | 0,0,0,0,0,0,0, 73 | ),#六 74 | ( 75 | 0,1,1,1,1,1,1, 76 | 0,1,0,1,0,0,1, 77 | 0,1,1,1,1,0,1, 78 | 0,1,0,1,0,0,1, 79 | 0,1,1,1,1,1,1, 80 | 0,1,1,0,1,0,1, 81 | 1,0,1,1,1,0,1, 82 | 0,0,0,0,0,0,0, 83 | ),#周 84 | 85 | 86 | ) -------------------------------------------------------------------------------- /software/font3x8.py: -------------------------------------------------------------------------------- 1 | FONT3X8 = ( 2 | # 3 | # NUM0 = 4 | ( 5 | 1, 1, 1, 6 | 1, 0, 1, 7 | 1, 0, 1, 8 | 1, 0, 1, 9 | 1, 0, 1, 10 | 1, 0, 1, 11 | 1, 0, 1, 12 | 1, 1, 1, 13 | ), 14 | # 15 | # NUM1 = 16 | ( 17 | 0, 1, 0, 18 | 1, 1, 0, 19 | 0, 1, 0, 20 | 0, 1, 0, 21 | 0, 1, 0, 22 | 0, 1, 0, 23 | 0, 1, 0, 24 | 1, 1, 1, 25 | ), 26 | # 27 | # NUM2 = 28 | ( 29 | 1, 1, 1, 30 | 0, 0, 1, 31 | 0, 0, 1, 32 | 1, 1, 1, 33 | 1, 0, 0, 34 | 1, 0, 0, 35 | 1, 0, 0, 36 | 1, 1, 1, 37 | ), 38 | # 39 | # NUM3 = 40 | ( 41 | 1, 1, 1, 42 | 0, 0, 1, 43 | 0, 0, 1, 44 | 1, 1, 1, 45 | 0, 0, 1, 46 | 0, 0, 1, 47 | 0, 0, 1, 48 | 1, 1, 1, 49 | ), 50 | # 51 | # NUM4 = 52 | ( 53 | 1, 0, 1, 54 | 1, 0, 1, 55 | 1, 0, 1, 56 | 1, 1, 1, 57 | 0, 0, 1, 58 | 0, 0, 1, 59 | 0, 0, 1, 60 | 0, 0, 1, 61 | ), 62 | # 63 | # NUM5 = 64 | ( 65 | 1, 1, 1, 66 | 1, 0, 0, 67 | 1, 0, 0, 68 | 1, 1, 1, 69 | 0, 0, 1, 70 | 0, 0, 1, 71 | 0, 0, 1, 72 | 1, 1, 1, 73 | ), 74 | # 75 | # NUM6 = 76 | ( 77 | 1, 1, 1, 78 | 1, 0, 0, 79 | 1, 0, 0, 80 | 1, 1, 1, 81 | 1, 0, 1, 82 | 1, 0, 1, 83 | 1, 0, 1, 84 | 1, 1, 1, 85 | ), 86 | # 87 | # NUM7 = 88 | ( 89 | 1, 1, 1, 90 | 0, 0, 1, 91 | 0, 0, 1, 92 | 0, 0, 1, 93 | 0, 0, 1, 94 | 0, 0, 1, 95 | 0, 0, 1, 96 | 0, 0, 1, 97 | ), 98 | # 99 | # NUM8 = 100 | ( 101 | 1, 1, 1, 102 | 1, 0, 1, 103 | 1, 0, 1, 104 | 1, 1, 1, 105 | 1, 0, 1, 106 | 1, 0, 1, 107 | 1, 0, 1, 108 | 1, 1, 1, 109 | ), 110 | # 111 | # NUM9 = 112 | ( 113 | 1, 1, 1, 114 | 1, 0, 1, 115 | 1, 0, 1, 116 | 1, 1, 1, 117 | 0, 0, 1, 118 | 0, 0, 1, 119 | 0, 0, 1, 120 | 1, 1, 1, 121 | ) 122 | ) -------------------------------------------------------------------------------- /software/bitmapfont.py: -------------------------------------------------------------------------------- 1 | # MicroPython basic bitmap font renderer. 2 | # Author: Tony DiCola 3 | # License: MIT License (https://opensource.org/licenses/MIT) 4 | try: 5 | import ustruct 6 | except ImportError: 7 | import struct as ustruct 8 | 9 | 10 | class BitmapFont: 11 | 12 | def __init__(self, width, height, pixel, font_name='font5x8.bin'): 13 | # Specify the drawing area width and height, and the pixel function to 14 | # call when drawing pixels (should take an x and y param at least). 15 | # Optionally specify font_name to override the font file to use (default 16 | # is font5x8.bin). The font format is a binary file with the following 17 | # format: 18 | # - 1 unsigned byte: font character width in pixels 19 | # - 1 unsigned byte: font character height in pixels 20 | # - x bytes: font data, in ASCII order covering all 255 characters. 21 | # Each character should have a byte for each pixel column of 22 | # data (i.e. a 5x8 font has 5 bytes per character). 23 | self._width = width 24 | self._height = height 25 | self._pixel = pixel 26 | self._font_name = font_name 27 | 28 | def init(self): 29 | # Open the font file and grab the character width and height values. 30 | # Note that only fonts up to 8 pixels tall are currently supported. 31 | self._font = open(self._font_name, 'rb') 32 | self._font_width, self._font_height = ustruct.unpack('BB', self._font.read(2)) 33 | 34 | def deinit(self): 35 | # Close the font file as cleanup. 36 | self._font.close() 37 | 38 | def __enter__(self): 39 | self.init() 40 | return self 41 | 42 | def __exit__(self, exception_type, exception_value, traceback): 43 | self.deinit() 44 | 45 | def draw_char(self, ch, x, y, *args, **kwargs): 46 | # Don't draw the character if it will be clipped off the visible area. 47 | if x < -self._font_width or x >= self._width or \ 48 | y < -self._font_height or y >= self._height: 49 | return 50 | # Go through each column of the character. 51 | for char_x in range(self._font_width): 52 | # Grab the byte for the current column of font data. 53 | self._font.seek(2 + (ord(ch) * self._font_width) + char_x) 54 | line = ustruct.unpack('B', self._font.read(1))[0] 55 | # Go through each row in the column byte. 56 | for char_y in range(self._font_height): 57 | # Draw a pixel for each bit that's flipped on. 58 | if (line >> char_y) & 0x1: 59 | self._pixel(x + char_x, y + char_y, *args, **kwargs) 60 | 61 | def text(self, text, x, y, *args, **kwargs): 62 | # Draw the specified text at the specified location. 63 | for i in range(len(text)): 64 | self.draw_char(text[i], x + (i * (self._font_width + 1)), y, 65 | *args, **kwargs) 66 | 67 | def width(self, text): 68 | # Return the pixel width of the specified text message. 69 | return len(text) * (self._font_width + 1) 70 | -------------------------------------------------------------------------------- /software/rx8111.py: -------------------------------------------------------------------------------- 1 | ''' 2 | RX8111 RTC drive 3 | 4 | Author: M0dular 5 | Date: 2023-8-5 6 | Ver: 0.1.0 7 | 8 | https://oshwhub.com/kakaka 9 | ''' 10 | 11 | 12 | 13 | from micropython import const 14 | from math import log2 15 | from gc import collect 16 | # bus address 17 | RX8111_ADDR = const(0x32) 18 | 19 | # Basic time and calendar register 20 | RX8111_SEC = const(0x10) 21 | RX8111_MIN = const(0x11) 22 | RX8111_HOUR = const(0x12) 23 | RX8111_WEEK = const(0x13) 24 | RX8111_DAY = const(0x14) 25 | RX8111_MONTH = const(0x15) 26 | RX8111_YEAR = const(0x16) 27 | RX8111_MIN_ALARM = const(0x17) 28 | RX8111_HOUR_ALARM = const(0x18) 29 | RX8111_WEEK_DAY_ALARM = const(0x19) 30 | RX8111_TIMER_COUNTER0 = const(0x1A) 31 | RX8111_TIMER_COUNTER1 = const(0x1B) 32 | RX8111_TIMER_COUNTER2 = const(0x1C) 33 | RX8111_EXTENREG = const(0x1D) 34 | RX8111_FLAGREG = const(0x1E) 35 | RX8111_CTRLREG = const(0x1F) 36 | 37 | RX8111_TIMESTAMP_1_1000S = const(0x20) 38 | RX8111_TIMESTAMP_1_100S = const(0x21) 39 | RX8111_TIMESTAMP_SEC = const(0x22) 40 | RX8111_TIMESTAMP_MIN = const(0x23) 41 | RX8111_TIMESTAMP_HOUR = const(0x24) 42 | RX8111_TIMESTAMP_WEEK = const(0x25) 43 | RX8111_TIMESTAMP_DAY = const(0x26) 44 | RX8111_TIMESTAMP_MONTHS = const(0x27) 45 | RX8111_TIMESTAMP_YEAR = const(0x28) 46 | RX8111_STATUS_STAMP = const(0x29) 47 | 48 | RX8111_EVIN_SETTING = const(0x2B) 49 | RX8111_SEC_ALARM = const(0x2C) 50 | RX8111_TIMER_CONTROL = const(0x2D) 51 | RX8111_CMD_TRIG_CTRL = const(0x2E) 52 | RX8111_COMMAND_TRIGGER = const(0x2F) 53 | 54 | RX8111_PWR_SWITCH_CTRL = const(0x32) 55 | RX8111_STATUS_MONITOR = const(0x33) 56 | RX8111_TIME_STAMP_BUF_CTRL = const(0x34) 57 | RX8111_TIME_STAMP_TRIG_CTRL = const(0x35) 58 | RX8111_TIME_STAMP_DATA_STATUS = const(0x36) 59 | 60 | ''' 61 | RX8111_EXT_TSEL0 BIT(0) 62 | RX8111_EXT_TSEL1 BIT(1) 63 | RX8111_EXT_ECP BIT(2) 64 | RX8111_EXT_WADA BIT(3) 65 | RX8111_EXT_TE BIT(4) 66 | RX8111_EXT_USEL BIT(5) 67 | RX8111_EXT_FSEL0 BIT(6) 68 | RX8111_EXT_FSEL1 BIT(7) 69 | 70 | RX8111_FLAG_FSTOPF BIT(0) 71 | RX8111_FLAG_VLF BIT(1) 72 | RX8111_FLAG_EVF BIT(2) 73 | RX8111_FLAG_AF BIT(3) 74 | RX8111_FLAG_TF BIT(4) 75 | RX8111_FLAG_UF BIT(5) 76 | RX8111_FLAG_PORF BIT(7) 77 | ''' 78 | RX8111_CTRL_STOP =const(0) 79 | ''' 80 | RX8111_CTRL_EIE BIT(2) 81 | RX8111_CTRL_AIE BIT(3) 82 | RX8111_CTRL_TIE BIT(4) 83 | RX8111_CTRL_UIE BIT(5) 84 | 85 | RX8111_EVIN_EOVEN BIT(1) 86 | RX8111_EVIN_EPRUP_SEL0 BIT(2) 87 | RX8111_EVIN_EPRUP_SEL1 BIT(3) 88 | RX8111_EVIN_EPRDW_SEL BIT(4) 89 | RX8111_EVIN_ET0 BIT(5) 90 | RX8111_EVIN_ET1 BIT(6) 91 | RX8111_EVIN_EHL BIT(7) 92 | 93 | RX8111_TIMER_CTRL_TSTP BIT(0) 94 | RX8111_TIMER_CTRL_TMPIN BIT(1) 95 | RX8111_TIMER_CTRL_TBKE BIT(2) 96 | RX8111_TIMER_CTRL_TBKON BIT(3) 97 | 98 | RX8111_CMD_TRIG_DUMMY0 BIT(0) 99 | RX8111_CMD_TRIG_DUMMY1 BIT(1) 100 | RX8111_CMD_TRIG_DUMMY2 BIT(2) 101 | RX8111_CMD_TRIG_DUMMY3 BIT(3) 102 | RX8111_CMD_TRIG_DUMMY4 BIT(4) 103 | RX8111_CMD_TRIG_DUMMY5 BIT(5) 104 | RX8111_CMD_TRIG_DUMMY6 BIT(6) 105 | RX8111_CMD_TRIG_DUMMY7 BIT(7) 106 | 107 | RX8111_PSC_SMP_TSEL0 BIT(0) 108 | RX8111_PSC_SMP_TSEL1 BIT(1) 109 | RX8111_PSC_SMP_SWSEL0 BIT(2) 110 | RX8111_PSC_SMP_SWSEL1 BIT(3) 111 | RX8111_PSC_SMP_INIEN BIT(6) 112 | RX8111_PSC_SMP_CHGEN BIT(7) 113 | 114 | RX8111_PSC_SMP_CHGEN BIT(7) 115 | 116 | RX8111_STAT_M_FVLOW BIT(1) 117 | RX8111_STAT_M_FVCMP BIT(3) 118 | RX8111_STAT_M_EVINMON BIT(6) 119 | # Insert and Defined ALARM_AE 120 | RX8111_ALARM_AE BIT(7) 121 | 122 | ''' 123 | 124 | class RX8111(object): 125 | def __init__(self,i2c): 126 | self.i2c = i2c 127 | self.tb = bytearray(1) 128 | self.rb = bytearray(1) 129 | self.buf = bytearray(7) 130 | self.DT = [0] * 7 131 | # if RX8111_ADDR in self.i2c.scan(): 132 | # print('RTC: rx8111 find at address: 0x%x ' %(RX8111_ADDR)) 133 | # else: 134 | # print('RTC: rx8111 not found at address: 0x%x ' %(RX8111_ADDR)) 135 | # collect() # 136 | 137 | # set reg 138 | def setReg(self, reg, dat): 139 | self.tb[0] = dat 140 | self.i2c.writeto_mem(RX8111_ADDR, reg, self.tb) 141 | 142 | # get reg 143 | def getReg(self, reg): 144 | self.i2c.readfrom_mem_into(RX8111_ADDR, reg, self.rb) 145 | return self.rb[0] 146 | 147 | 148 | # 将二进制编码的十进制数转换为普通十进制数 149 | def _bcd2dec(self, bcd) : 150 | """Convert binary coded decimal (BCD) format to decimal""" 151 | return (((bcd & 0xf0) >> 4) * 10 + (bcd & 0x0f)) 152 | 153 | # 将十进制编码的二进制数转换为普通十进制数 154 | def _dec2bcd(self, dec): 155 | """Convert decimal to binary coded decimal (BCD) format""" 156 | tens, units = divmod(dec, 10) 157 | return (tens << 4) + units 158 | 159 | def WeekToBdc(self, val) : 160 | return 0x01 << val 161 | 162 | 163 | def WeekToNum(self, val) : 164 | return int(log2(val)) 165 | 166 | 167 | 168 | def setBit(self, reg, bit_addr): 169 | 170 | data = self.getReg(reg) 171 | data |= (0x01 << bit_addr) 172 | 173 | self.setReg(reg, data) 174 | 175 | 176 | def clearBit(self, reg, bit_addr): 177 | 178 | data = self.getReg(reg) 179 | data &= ~(0x01 << bit_addr) 180 | 181 | self.setReg(reg, data) 182 | 183 | def datetime(self,DT=None): 184 | ''' 185 | DT: year,month,day,hour,minute,second,weekday 186 | 在Python的time.localtime()函数中,stars from Monday (0) and ends on Sunday (6)。所以星期从0开始计数,0代表星期一。 187 | 而rx8111是从星期日开始计数,0代表星期日 188 | Sunday 0 0 0 0 0 0 0 1 01 h 189 | Monday 0 0 0 0 0 0 1 0 02 h 190 | Tuesday 0 0 0 0 0 1 0 0 04 h 191 | Wednesday 0 0 0 0 1 0 0 0 08 h 192 | Thursday 0 0 0 1 0 0 0 0 10 h 193 | Friday 0 0 1 0 0 0 0 0 20 h 194 | Saturday 0 1 0 0 0 0 0 0 40 h 195 | ''' 196 | if DT==None: 197 | self.i2c.readfrom_mem_into(RX8111_ADDR, RX8111_SEC, self.buf) 198 | 199 | self.DT[0] = self._bcd2dec(self.buf[6]) + 2000 # year 200 | self.DT[1] = self._bcd2dec(self.buf[5]& 0x1F)# month 201 | self.DT[2] = self._bcd2dec(self.buf[4] & 0x3F)# day 202 | 203 | self.DT[3] = self._bcd2dec(self.buf[2] & 0x3F)# hour 204 | self.DT[4] = self._bcd2dec(self.buf[1]& 0x7F)# minute 205 | self.DT[5] = self._bcd2dec(self.buf[0]& 0x7F)# second 206 | 207 | self.DT[6] = self.WeekToNum(self.buf[3]) # week day 208 | 209 | return tuple(self.DT) 210 | else: 211 | self.setBit(RX8111_CTRLREG, RX8111_CTRL_STOP) 212 | self.setReg(RX8111_FLAGREG, 0x00) 213 | 214 | self.buf[6] = self._dec2bcd(DT[0]%100) # year 215 | self.buf[5] = self._dec2bcd(DT[1]%13) # month 216 | self.buf[4] = self._dec2bcd(DT[2]%32) # day 217 | 218 | self.buf[2] = self._dec2bcd(DT[3]%24) # hour 219 | self.buf[1] = self._dec2bcd(DT[4]%60) # minute 220 | self.buf[0] = self._dec2bcd(DT[5]%60) # second 221 | 222 | self.buf[3] = self.WeekToBdc((DT[6]+1)%7) # week day 223 | 224 | self.i2c.writeto_mem(RX8111_ADDR, RX8111_SEC, self.buf) 225 | 226 | self.clearBit(RX8111_CTRLREG, RX8111_CTRL_STOP) 227 | 228 | 229 | 230 | 231 | 232 | if __name__ == "__main__": 233 | from machine import I2C, Pin 234 | import time 235 | print('rx8111 test') 236 | i2c = I2C(0, scl=Pin(7), sda=Pin(8), freq=400000) 237 | rtc = RX8111(i2c) 238 | rtc.datetime((2023,8,5,16,25,59,6)) 239 | while True: 240 | print(rtc.datetime()) 241 | time.sleep(1.0) 242 | 243 | 244 | -------------------------------------------------------------------------------- /software/main.py: -------------------------------------------------------------------------------- 1 | 2 | from machine import I2C, Pin 3 | import time 4 | import network 5 | import neopixel 6 | import gc 7 | import bitmapfont 8 | from font3x8 import * 9 | from week7x7 import * 10 | import rx8111 11 | 12 | import config 13 | 14 | ssid = config.ssid 15 | password =config.password 16 | 17 | zonetime = 8*3600 18 | 19 | NEOPIXEL_PIN = 5 20 | ROWS = 8 21 | COLS = 15 22 | NUMBER_PIXELS = ROWS * COLS 23 | 24 | C_BLACK=(0,0,0) 25 | C_GREEN=(0,35,0) 26 | C_BLUE=(0,0,35) 27 | C_RED=(35,0,0) 28 | C_YELLOW=(35,35,0) 29 | C_MONTH=(31,5,12) 30 | C_DAY=(35,31,31) 31 | 32 | np = neopixel.NeoPixel(Pin(NEOPIXEL_PIN), NUMBER_PIXELS) 33 | 34 | 35 | 36 | 37 | #15x8 38 | ICON_WIFI=( 39 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 40 | 1,0,1,0,0,1,0,0,1,1,1,0,0,1,0, 41 | 1,0,1,0,0,0,0,0,1,0,0,0,0,0,0, 42 | 1,0,1,0,0,1,0,0,1,1,0,0,0,1,0, 43 | 1,0,1,0,0,1,0,0,1,0,0,0,0,1,0, 44 | 1,1,1,0,0,1,0,0,1,0,0,0,0,1,0, 45 | 1,1,1,0,0,1,0,0,1,0,0,0,0,1,0, 46 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 47 | 48 | ) 49 | 50 | class Display(object): 51 | def __init__(self,np): 52 | self.np=np 53 | 54 | 55 | def write_pixel(self,x,y,value): 56 | if y >= 0 and y < ROWS and x >=0 and x < COLS: 57 | self.np[x*ROWS + y] = value 58 | 59 | def drawPixel(self,x,y,value): 60 | self.write_pixel(x,y,value) 61 | 62 | 63 | 64 | def clear(self): 65 | for i in range(NUMBER_PIXELS): 66 | np[i]=(0,0,0) 67 | self.np.write() 68 | 69 | def show(self): 70 | self.np.write() 71 | 72 | def draw1Num(self,x,y,num,color,bg=None): 73 | num=num%10 74 | for j in range(8): 75 | for i in range(3): 76 | if FONT3X8[num][i+j*3]==1: 77 | self.write_pixel(x+i,y+j,color) 78 | else : 79 | if bg != None: 80 | self.write_pixel(x+i,y+j,bg) 81 | 82 | def drawNum(self,x,y,num,color,bg=None) : 83 | self.draw1Num(x,y,num//1000%10,color,bg) 84 | self.draw1Num(x+4,y,num%1000//100,color,bg) 85 | self.draw1Num(x+4*2,y,num%100//10,color,bg) 86 | self.draw1Num(x+4*3,y,num%10,color,bg) 87 | 88 | def change1Num(self,x,y,num_src,num_dest,color,bg=None): 89 | num_src=num_src%10 90 | num_dest=num_dest%10 91 | 92 | for i in range(1,9): 93 | self.draw1Num(x,y-i,num_src,color,bg) 94 | self.draw1Num(x,y+8-i,num_dest,color,bg) 95 | time.sleep(0.01) 96 | self.show() 97 | 98 | def draw1Week(self,x,y,num,color,bg=None): 99 | for j in range(8): 100 | for i in range(7): 101 | if WEEK7X8[num][i+j*7]==1: 102 | self.write_pixel(x+i,y+j,color) 103 | else : 104 | if bg != None: 105 | self.write_pixel(x+i,y+j,bg) 106 | 107 | def drawWeek(self,x,y,watch,color,bg=None) : 108 | # num=num%7 109 | # print(num) 110 | self.draw1Week(x,y,7,color,bg) 111 | self.draw1Week(x+8,y,watch.time_now[6],color,bg) 112 | 113 | def drawTime(self,x,y,watch,color1,color2,bg=None): 114 | self.draw1Num(x+0,y,watch.time_now[3]//10,color1,bg) 115 | self.draw1Num(x+4,y,watch.time_now[3]%10,color1,bg) 116 | self.draw1Num(x+8,y,watch.time_now[4]//10,color2,bg) 117 | self.draw1Num(x+12,y,watch.time_now[4]%10,color2,bg) 118 | self.show() 119 | 120 | def drawDate(self,x,y,watch,color1,color2,bg=None): 121 | if watch.time_now[1]//10: 122 | self.draw1Num(x+0,y,watch.time_now[1]//10,color1,bg) 123 | else: 124 | self.draw1Num(x+0,y,watch.time_now[1]//10,bg,bg) 125 | 126 | self.draw1Num(x+4,y,watch.time_now[1]%10,color1,bg) 127 | if watch.time_now[2]//10: 128 | self.draw1Num(x+8,y,watch.time_now[2]//10,color2,bg) 129 | else: 130 | self.draw1Num(x+8,y,watch.time_now[2]//10,bg,bg) 131 | self.draw1Num(x+12,y,watch.time_now[2]%10,color2,bg) 132 | self.show() 133 | 134 | def drawIcon(self,x,y,width,height,icon,color,bg=None): 135 | for j in range(height): 136 | for i in range(width): 137 | if icon[i+j*width]==1: 138 | self.write_pixel(x+i,y+j,color) 139 | else : 140 | if bg != None: 141 | self.write_pixel(x+i,y+j,bg) 142 | 143 | 144 | 145 | 146 | disp = Display(np) 147 | 148 | import machine 149 | class Watch(object): 150 | def __init__(self,time_now): 151 | self.time_now=time_now 152 | self.time_old=time_now 153 | self.timeout=0 #关机超时时间,ms单位 154 | self.timeout_stamp=time.ticks_ms() 155 | 156 | 157 | def resetTimeout(self,timeout=5000): 158 | self.timeout= timeout 159 | self.timeout_stamp=time.ticks_ms() 160 | 161 | def checkTimeout(self): 162 | deadline = time.ticks_add(self.timeout_stamp, self.timeout) 163 | if time.ticks_diff(deadline, time.ticks_ms()) > 0: 164 | return False 165 | else: 166 | return True 167 | 168 | 169 | 170 | 171 | 172 | 173 | def poweroff(self): 174 | machine.deepsleep() 175 | 176 | 177 | 178 | 179 | 180 | INDEX_HOUR = 3 181 | INDEX_MINUTE = 4 182 | INDEX_SECOND = 5 183 | 184 | 185 | 186 | def updateTime(watch): 187 | watch.time_now = rtc.datetime() 188 | if watch.time_now != watch.time_old: 189 | if watch.time_now[INDEX_MINUTE]%10 != watch.time_old[INDEX_MINUTE]%10: 190 | disp.change1Num(12,0,watch.time_old[INDEX_MINUTE]%10,watch.time_now[INDEX_MINUTE]%10,C_YELLOW,C_BLACK) 191 | 192 | if watch.time_now[INDEX_MINUTE]//10 != watch.time_old[INDEX_MINUTE]//10: 193 | disp.change1Num(8,0,watch.time_old[INDEX_MINUTE]//10,watch.time_now[INDEX_MINUTE]//10,C_YELLOW,C_BLACK) 194 | 195 | if watch.time_now[INDEX_HOUR]%10 != watch.time_old[INDEX_HOUR]%10: 196 | disp.change1Num(4,0,watch.time_old[INDEX_HOUR]%10,watch.time_now[INDEX_HOUR]%10,C_GREEN,C_BLACK) 197 | 198 | if watch.time_now[INDEX_HOUR]//10 != watch.time_old[INDEX_HOUR]//10: 199 | disp.change1Num(0,0,watch.time_old[INDEX_HOUR]//10,watch.time_now[INDEX_HOUR]//10,C_GREEN,C_BLACK) 200 | 201 | watch.time_old = watch.time_now 202 | 203 | 204 | print('rx8111 test') 205 | i2c = I2C(0, scl=Pin(7), sda=Pin(8), freq=400000) 206 | rtc = rx8111.RX8111(i2c) 207 | 208 | week=('SUN','MON','TUE','WED','THU','FRI','SAT') 209 | 210 | 211 | 212 | def syncTimeByWifi(ssid,password,trycnt=20): 213 | if ssid == None : 214 | return 215 | import ntptime 216 | wlan = network.WLAN(network.STA_IF) 217 | wlan.active(True) 218 | wlan.connect(ssid,password) 219 | 220 | if not wlan.isconnected(): 221 | while not wlan.isconnected(): 222 | print("Waiting to connect:") 223 | time.sleep(1.0) 224 | trycnt = trycnt-1 225 | if trycnt%2 : 226 | disp.drawPixel(5,1,C_GREEN) 227 | else: 228 | disp.drawPixel(5,1,C_RED) 229 | 230 | disp.show() 231 | if trycnt<0: 232 | print("\nFail !!!\n") 233 | break 234 | if wlan.isconnected(): 235 | print(wlan.ifconfig()) 236 | while True: 237 | try: 238 | print('获取时间中') 239 | ntptime.host = 'ntp1.aliyun.com' 240 | ntptime.settime() 241 | print('成功获取') 242 | break 243 | except: 244 | print('获取失败') 245 | time.sleep(1) 246 | wlan.disconnect() 247 | wlan.active(False) 248 | watch.resetTimeout() 249 | 250 | 251 | 252 | charge = Pin(20, Pin.IN, Pin.PULL_UP) 253 | 254 | k1 = Pin(4, Pin.IN) 255 | k2 = Pin(0, Pin.IN) 256 | k3 = Pin(9, Pin.IN) 257 | 258 | 259 | class Event(): 260 | K1_PRESSED=1 261 | K1_RELEASED=2 262 | K2_PRESSED=3 263 | K2_RELEASED=4 264 | K3_PRESSED=5 265 | K3_RELEASED=6 266 | K1_LONG=7 267 | K2_LONG=8 268 | K3_LONG=9 269 | 270 | def __init__(self): 271 | self.event=[] 272 | 273 | def addEvent(self,evt): 274 | self.event.append(evt) 275 | 276 | def getEvent(self): 277 | if len(self.event)==0: 278 | return -1 279 | return self.event.pop(0) 280 | 281 | LongPressed = 1000 282 | key_time=0 283 | def fun(key): 284 | global key_time 285 | watch.resetTimeout() 286 | if key==k1: 287 | if key.value() == 0: 288 | key_time=time.ticks_ms() 289 | # print('k1 pressed') 290 | evt.addEvent(Event.K1_PRESSED) 291 | else: 292 | # print('k1 released') 293 | if time.ticks_diff(time.ticks_ms(),key_time)>LongPressed: 294 | evt.addEvent(Event.K1_LONG) 295 | else: 296 | evt.addEvent(Event.K1_RELEASED) 297 | if key==k2: 298 | if key.value() == 1: 299 | # print('k2 pressed') 300 | evt.addEvent(Event.K2_PRESSED) 301 | else: 302 | # print('k2 released') 303 | evt.addEvent(Event.K2_RELEASED) 304 | if key==k3: 305 | if key.value() == 0: 306 | # print('k3 pressed') 307 | evt.addEvent(Event.K3_PRESSED) 308 | else: 309 | # print('k3 released') 310 | evt.addEvent(Event.K3_RELEASED) 311 | 312 | 313 | 314 | 315 | 316 | k1.irq(fun,Pin.IRQ_FALLING | Pin.IRQ_RISING) 317 | k2.irq(fun,Pin.IRQ_FALLING | Pin.IRQ_RISING) 318 | k3.irq(fun,Pin.IRQ_FALLING | Pin.IRQ_RISING) 319 | 320 | currentPage=0 321 | 322 | 323 | 324 | 325 | # syncTimeByWifi(ssid,password) 326 | # sec=time.mktime(time.localtime())#获取时间戳 327 | # now = time.localtime(sec+zonetime)#获取新时区的时间 328 | 329 | # rtc.datetime(now) 330 | 331 | evt = Event() 332 | disp.clear() 333 | 334 | watch = Watch(rtc.datetime()) 335 | 336 | disp.drawTime( 0,0,watch,C_GREEN,C_YELLOW,C_BLACK) 337 | 338 | watch.resetTimeout() 339 | 340 | def switchPage(): 341 | global currentPage 342 | if currentPage==0: 343 | 344 | for j in range(8): 345 | disp.drawTime(0,-1-j,watch,C_GREEN,C_YELLOW,C_BLACK) 346 | disp.drawWeek(0,7-j,watch,C_GREEN,C_BLACK) 347 | disp.show() 348 | time.sleep(0.01) 349 | currentPage=1 350 | print(currentPage) 351 | return 352 | 353 | if currentPage==1: 354 | for j in range(8): 355 | disp.drawWeek(0,-1-j,watch,C_GREEN,C_BLACK) 356 | disp.drawDate(0,7-j,watch,C_MONTH,C_DAY,C_BLACK) 357 | disp.show() 358 | time.sleep(0.01) 359 | currentPage=2 360 | print(currentPage) 361 | return 362 | 363 | if currentPage==2: 364 | for j in range(8): 365 | disp.drawDate(0,-1-j,watch,C_MONTH,C_DAY,C_BLACK) 366 | disp.drawTime(0,7-j,watch,C_GREEN,C_YELLOW,C_BLACK) 367 | disp.show() 368 | time.sleep(0.01) 369 | currentPage=0 370 | print(currentPage) 371 | return 372 | 373 | def handleEvent(evt): 374 | global currentPage 375 | if evt<=0: 376 | return 377 | 378 | if evt==Event.K2_PRESSED: 379 | switchPage() 380 | pass 381 | if evt==Event.K3_PRESSED: 382 | watch.poweroff() 383 | 384 | if evt==Event.K1_LONG: 385 | print('syncTimeByWifi') 386 | 387 | disp.drawIcon(0,0,15,8,ICON_WIFI,C_BLUE,C_BLACK) 388 | disp.show() 389 | syncTimeByWifi(ssid,password) 390 | sec=time.mktime(time.localtime())#获取时间戳 391 | now = time.localtime(sec+zonetime)#获取新时区的时间 392 | 393 | rtc.datetime(now) 394 | if currentPage ==0: 395 | disp.drawTime(0,0,watch,C_GREEN,C_YELLOW,C_BLACK) 396 | updateTime(watch) 397 | elif currentPage ==1: 398 | disp.drawWeek(0,0,watch,C_GREEN,C_BLACK) 399 | elif currentPage ==2: 400 | disp.drawDate(0,0,watch,C_MONTH,C_DAY,C_BLACK) 401 | 402 | disp.show() 403 | 404 | print(now) 405 | pass 406 | 407 | 408 | 409 | timeout_en = False 410 | 411 | if charge.value()==0 : 412 | timeout_en = False 413 | else: 414 | timeout_en = True 415 | 416 | 417 | print(timeout_en) 418 | 419 | 420 | 421 | 422 | while True: 423 | if charge.value()==0 : 424 | time.sleep(1.0) 425 | disp.clear() 426 | 427 | if watch.checkTimeout() and timeout_en: 428 | print("timeout") 429 | watch.poweroff() 430 | watch.resetTimeout() 431 | 432 | 433 | handleEvent(evt.getEvent()) 434 | # Calculate deadline for operation and test for it 435 | deadline = time.ticks_add(time.ticks_ms(), 5000) 436 | if time.ticks_diff(deadline, time.ticks_ms()) > 0: 437 | deadline = time.ticks_add(time.ticks_ms(), 5000) 438 | if currentPage ==0: 439 | updateTime(watch) 440 | 441 | 442 | 443 | # print(watch.time_now) 444 | time.sleep(0.2) 445 | gc.collect() 446 | # print(f'mem: {gc.mem_free()}') 447 | --------------------------------------------------------------------------------