mpython.py源码
mpython.py源码下载
1# labplus mPython library
2# MIT license; Copyright (c) 2018 labplus
3# V1.0 Zhang KaiHua(apple_eat@126.com)
4
5# mpython buildin periphers drivers
6
7# history:
8# V1.1 add oled draw function,add buzz.freq(). by tangliufeng
9# V1.2 add servo/ui class,by tangliufeng
10# ledong_pro 202411
11# v2.0 modified by zhaohuijiang for mpython V3 20250301
12
13from machine import I2C, PWM, Pin, ADC, TouchPad
14import esp, math, time, network
15import ustruct
16from neopixel import NeoPixel
17import time
18from micropython import schedule,const
19from esp32 import NVS
20from _ntptime import *
21from ltr308 import *
22import framebuf
23import lcd
24
25i2c = I2C(0, scl=Pin(43), sda=Pin(44), freq=400000)
26
27if '_print' not in dir(): _print = print
28
29def print(_t, *args, sep=' ', end='\n'):
30 _s = str(_t)[0:1]
31 if u'\u4e00' <= _s <= u'\u9fff':
32 _print(' ' + str(_t), *args, sep=sep, end=end)
33 else:
34 _print(_t, *args, sep=sep, end=end)
35
36def numberMap(inputNum, bMin, bMax, cMin, cMax):
37 outputNum = 0
38 outputNum = ((cMax - cMin) / (bMax - bMin)) * (inputNum - bMin) + cMin
39 return outputNum
40
41class TFT_displayer(framebuf.FrameBuffer):
42 def __init__(self):
43 self.lcd_width = 320
44 self.lcd_height = 172
45 self.buffer = bytearray(110080) # self.lcd_width*self.lcd_height*2
46 super().__init__(self.buffer, self.lcd_width, self.lcd_height, framebuf.RGB565)
47
48 def _mono2rgb65(self, ch_bitmap:bytes, ch_w:int, ch_h:int, char_col=0xffff)->list[int]:
49 """
50 黑白像素字符缓存点转化为RGB565缓存
51 param bitmap: 黑白像素点字符缓存
52 param ch_w ch_w: 缓存宽高
53 param char_col: 目标字体颜色
54 return: RGB565字符缓存
55 """
56 char_buf = bytearray(ch_w * ch_h * 2)
57 char_fb = framebuf.FrameBuffer(char_buf, ch_w, ch_h, framebuf.RGB565)
58
59 # 填充字符FB:将1bit位图映射为RGB565像素
60 for row in range(ch_h):
61 for col in range(ch_w):
62 # 计算当前像素在1bit位图中的字节/位位置
63 byte_idx = row * ((ch_w + 7) // 8) + (col // 8)
64 bit_idx = 7 - (col % 8) # 适配font-to-py的位序(bit7对应第一个像素)
65
66 # 读取1bit值(0=背景/透明,1=前景)
67 bit_val = (ch_bitmap[byte_idx] >> bit_idx) & 0x01 if byte_idx < len(ch_bitmap) else 0
68
69 # 映射为RGB565颜色并填充到字符FB
70 bg_color = super().background_color()
71 color = char_col if bit_val else bg_color
72 char_fb.pixel(col, row, color)
73
74 return char_fb
75
76 def DispChar(self, str, x, y, color=lcd.WHITE, auto_wrap = False):
77 """
78 显示内置思源24位黑体字符
79 :param str: 待显示字符
80 :param x y: 位置
81 :param color:字体颜色
82 :param auto_wrap:是否换行
83 """
84 self.dispChar(str, x, y, color, auto_wrap)
85
86 def DispChar_font(self, font, str, x, y, color=lcd.WHITE, auto_wrap = False):
87 """
88 显示用户自定义字体,参考: https://github.com/peterhinch/micropython-font-to-py
89 参数
90 :param font: 使用 font_to_py.py 从`ttf` or `otf`转换出的字体py文件,内置的相关字体位于font文件夹下.
91 :param str: 待显示字体,内置了数码管字体和相关ASCII字符字体,当然用户可以自已制作中文字体。
92 :param x y: 显示位置坐标
93 :param color: 字体颜色
94 :param auto_wrap: 是否自动换行,每行高度:char_height + 5,擦除一行时会用到此参数
95 """
96 is_x_change = False
97 for char in str:
98 char_buf, char_height, char_width = font.get_ch(char)
99
100 # 在数码管字体中,调整‘1’字符位置,在时钟显示中更美观
101 if font.fon_type() == 'DIGIFACE':
102 if char == '1':
103 x = x + char_width // 2
104 is_x_change = True
105 elif is_x_change:
106 x = x - char_width // 2
107 is_x_change = False
108
109 if x > self.lcd_width - char_width:
110 if auto_wrap:
111 x = 0
112 y += char_height + 5
113 else: # x超出屏幕宽度则不显示
114 return
115 if y > 240 - char_height: # y超出屏幕高度则不显示
116 return
117
118 fb = self._mono2rgb65(char_buf, char_width, char_height, color)
119 super().blit(fb, x, y)
120 x += char_width #+ 5
121
122 def clear(self, color = None):
123 if color is not None:
124 super().background_color(color)
125 super().fill(super().background_color())
126
127 def show(self):
128 lcd.show(self.buffer)
129
130display = TFT_displayer()
131
132# my_wifi = wifi()
133#多次尝试连接wifi
134def try_connect_wifi(_wifi, _ssid, _pass, _times):
135 if _times < 1: return False
136 try:
137 print("Try Connect WiFi ... {} Times".format(_times) )
138 _wifi.connectWiFi(_ssid, _pass)
139 if _wifi.sta.isconnected(): return True
140 else:
141 time.sleep(5)
142 return try_connect_wifi(_wifi, _ssid, _pass, _times-1)
143 except:
144 time.sleep(5)
145 return try_connect_wifi(_wifi, _ssid, _pass, _times-1)
146
147class wifi:
148 def __init__(self):
149 self.sta = network.WLAN(network.STA_IF)
150 self.ap = network.WLAN(network.AP_IF)
151
152 def connectWiFi(self, ssid, passwd, timeout=10000):
153 if self.sta.isconnected():
154 self.sta.disconnect()
155 self.sta.active(True)
156 list = self.sta.scan()
157 for i, wifi_info in enumerate(list):
158 try:
159 if wifi_info[0].decode() == ssid:
160 self.sta.connect(ssid, passwd)
161 wifi_dbm = wifi_info[3]
162 break
163 except UnicodeError:
164 self.sta.connect(ssid, passwd)
165 wifi_dbm = '?'
166 break
167 if i == len(list) - 1:
168 raise OSError("SSID invalid / failed to scan this wifi")
169 start = time.time()
170 print("Connection WiFi", end="")
171 while (self.sta.ifconfig()[0] == '0.0.0.0'):
172 if time.ticks_diff(time.time(), start) > timeout:
173 print("")
174 raise OSError("Timeout!,check your wifi password and keep your network unblocked")
175 print(".", end="")
176 time.sleep_ms(500)
177 print("")
178 print('WiFi(%s,%sdBm) Connection Successful, Config:%s' % (ssid, str(wifi_dbm), str(self.sta.ifconfig())))
179
180 def disconnectWiFi(self):
181 if self.sta.isconnected():
182 self.sta.disconnect()
183 self.sta.active(False)
184 print('disconnect WiFi...')
185
186 def enable_APWiFi(self, essid, password=b'',channel=10):
187 self.ap.active(True)
188 if password:
189 authmode=4
190 else:
191 authmode=0
192 self.ap.config(essid=essid,password=password,authmode=authmode, channel=channel)
193
194 def disable_APWiFi(self):
195 self.ap.active(False)
196 print('disable AP WiFi...')
197
198class MOTION(object):
199 def __init__(self):
200 self.i2c = i2c
201 addr = self.i2c.scan()
202 if 107 in addr:
203 MOTION.IIC_ADDR = 107
204 else:
205 raise OSError("MOTION init error")
206
207 MOTION._writeReg(0x60, 0x01) # soft reset regist value.
208 time.sleep_ms(20)
209 MOTION._writeReg(0x02, 0x60) # Enabe reg address auto increment auto
210 MOTION._writeReg(0x08, 0x03) # Enable accel and gyro
211 MOTION._writeReg(0x03, 0x1c) # accel range:4g ODR 128HZ
212 MOTION._writeReg(0x04, 0x40) # gyro ODR 8000HZ, FS 256dps
213 MOTION._writeReg(0x06, 0x55) # Enable accel and gyro Low-Pass Filter
214 # print('Motion init finished!')
215
216 # @staticmethod
217 def _readReg(reg, nbytes=1):
218 return i2c.readfrom_mem(MOTION.IIC_ADDR, reg, nbytes)
219
220 # @staticmethod
221 def _writeReg(reg, value):
222 i2c.writeto_mem(MOTION.IIC_ADDR, reg, value.to_bytes(1, 'little'))
223
224 def get_fw_version(self):
225 MOTION._writeReg(0x0a, 0x10) # send ctrl9R read FW cmd
226 while True:
227 if (MOTION._readReg(0x2F, 1)[0] & 0X01) == 0X01:
228 break
229 buf = MOTION._readReg(0X49, 3)
230
231 class Accelerometer():
232 """QMI8658C"""
233 # Range and resolustion
234 RANGE_2G = const(0)
235 RANGE_4G = const(1)
236 RANGE_8G = const(2)
237 RANGE_16G = const(3)
238 RES_14_BIT = const(0)
239 RES_12_BIT = const(1)
240 RES_10_BIT = const(2)
241
242 def __init__(self):
243 # 设置偏移值
244 self.x_offset = 0
245 self.y_offset = 0
246 self.z_offset = 0
247 self.get_nvs_offset()
248 try:
249 id = MOTION._readReg(0x0, 2)
250 except:
251 pass
252 self.set_range(MOTION.Accelerometer.RANGE_2G) #设置默认分辨率+-2g
253 self.int = Pin(45, Pin.IN)
254 self.int.irq(trigger=Pin.IRQ_FALLING, handler=self.irq)
255 # event handler
256 self.wom = None
257
258
259 def irq(self, arg):
260 flag = MOTION._readReg(0x2F, 1)[0]
261 if (flag & 0x04) == 0x04:
262 print('wom int trigged.')
263
264 def wom_config(self):
265 MOTION._writeReg(0x60, 0x01) # soft reset regist value.
266 time.sleep_ms(20)
267 MOTION._writeReg(0x08, 0x0) # disable all sensor
268 MOTION._writeReg(0x03, 0x1c) # accel range:4g ODR 128HZ
269 MOTION._writeReg(0x0B, 0xfF) # CAL_L WoM Threshold(1mg/LSB resolution)
270 MOTION._writeReg(0x0C, 0x8F) # CAL_H WoM (INT1 blank time 0x1f)
271 MOTION._writeReg(0x0A, 0x08)
272 while True:
273 if (MOTION._readReg(0x2F, 1)[0] & 0X01) == 0X01:
274 break
275 MOTION._writeReg(0x08, 0x01) # enable accel
276
277 def set_resolution(self, resolution):# set data output rate
278 self.odr = resolution
279 format = MOTION._readReg(0x03, 1)
280 format = format[0] & 0xf0
281 format |= (resolution & 0x0f)
282 MOTION._writeReg(0x03, format)
283
284 def set_range(self, range):
285 if(range==3):
286 range = 64 #0x40
287 else:
288 range = range << 4
289 self.FS = 2*(2**(range >> 4))
290 format = MOTION._readReg(0x03, 1)
291 format = format[0] & 0x8F
292 format |= range
293 MOTION._writeReg(0x03, format)
294
295 def set_offset(self, x=None, y=None, z=None):
296 for i in (x, y, z):
297 if i is not None:
298 if i < -16 or i > 16:
299 raise ValueError("超出调整范围!!!")
300 if x is not None:
301 self.x_offset = x
302 self.set_nvs_offset("x", x)
303 if y is not None:
304 self.y_offset = y
305 self.set_nvs_offset("y", y)
306 if z is not None:
307 self.z_offset = z
308 self.set_nvs_offset("z", z)
309
310 def get_x(self):
311 buf = MOTION._readReg(0x35, 2)
312 x = ustruct.unpack('<h', buf)[0]
313 return (x * self.FS) / 32768 + self.x_offset
314
315 def get_y(self):
316 buf = MOTION._readReg(0x37, 2)
317 y = ustruct.unpack('<h', buf)[0]
318 return (y * self.FS) / 32768 + self.y_offset
319
320 def get_z(self):
321 buf = MOTION._readReg(0x39, 2)
322 z = ustruct.unpack('<h', buf)[0]
323 return (z * self.FS) / 32768 + self.z_offset
324 # return -(z * self.FS) / 32768
325
326 def roll_pitch_angle(self):
327 x, y, z = self.get_x(), self.get_y(), -self.get_z()
328 # vector normalize
329 mag = math.sqrt(x ** 2 + y ** 2+z ** 2)
330 x /= mag
331 y /= mag
332 z /= mag
333 roll = math.degrees(-math.asin(y))
334 pitch = math.degrees(math.atan2(x, z))
335
336 return roll, pitch
337
338 def get_nvs_offset(self):
339 try:
340 tmp = NVS("offset_a")
341 self.x_offset = round(tmp.get_i32("x")/1e5, 5)
342 self.y_offset = round(tmp.get_i32("y")/1e5, 5)
343 self.z_offset = round(tmp.get_i32("z")/1e5, 5)
344 except OSError as e:
345 # print('Accelerometer get_nvs_offset:',e)
346 # self.x_offset = 0
347 # self.y_offset = 0
348 # self.z_offset = 0
349 self.set_offset(0,0,0)
350
351 def set_nvs_offset(self, key, value):
352 try:
353 nvs = NVS("offset_a")
354 nvs.set_i32(key, int(value*1e5))
355 nvs.commit()
356 except OSError as e:
357 print('Gyroscope set_nvs_offset error:',e)
358
359 class Gyroscope():
360 # gyro full scale
361 RANGE_16_DPS = const(0x00)
362 RANGE_32_DPS = const(0x10)
363 RANGE_64_DPS = const(0x20)
364 RANGE_128_DPS = const(0x30)
365 RANGE_256_DPS = const(0x40)
366 RANGE_512_DPS = const(0x50)
367 RANGE_1024_DPS = const(0x60)
368 RANGE_2048_DPS = const(0x70)
369
370 def __init__(self):
371 # 设置偏移值
372 self.x_offset = 0
373 self.y_offset = 0
374 self.z_offset = 0
375 self.get_nvs_offset()
376 self.set_range(MOTION.Gyroscope.RANGE_256_DPS)
377
378 def set_range(self, range):
379 self.FS = 16*(2**(range >> 4))
380 format = MOTION._readReg(0x04, 1)
381 format = format[0] & 0x8F
382 format |= range
383 MOTION._writeReg(0x04, format)
384
385 def set_ODR(self, odr): # set data output rate
386 self.odr = odr
387 format = MOTION._readReg(0x04, 1)
388 format = format[0] & 0xF0
389 format |= odr
390 MOTION._writeReg(0x04, format)
391
392 def get_x(self):
393 buf = MOTION._readReg(0x3b, 2)
394 x = ustruct.unpack('<h', buf)[0]
395 return (x * self.FS) / 32768 + self.x_offset
396
397 def get_y(self):
398 buf = MOTION._readReg(0x3d, 2)
399 y = ustruct.unpack('<h', buf)[0]
400 return (y * self.FS) / 32768 + self.y_offset
401
402 def get_z(self):
403 buf = MOTION._readReg(0x3f, 2)
404 z = ustruct.unpack('<h', buf)[0]
405 return (z * self.FS) / 32768 + self.z_offset
406
407 def set_offset(self, x=None, y=None, z=None):
408 for i in (x, y, z):
409 if i is not None:
410 if i < -4096 or i > 4096:
411 raise ValueError("超出调整范围!!!")
412 if x is not None:
413 self.x_offset = x
414 self.set_nvs_offset("x", x)
415 if y is not None:
416 self.y_offset = y
417 self.set_nvs_offset("y", y)
418 if z is not None:
419 self.z_offset = z
420 self.set_nvs_offset("z", z)
421
422 def get_nvs_offset(self):
423 try:
424 tmp = NVS("offset_g")
425 self.x_offset = round(tmp.get_i32("x")/1e5, 5)
426 self.y_offset = round(tmp.get_i32("y")/1e5, 5)
427 self.z_offset = round(tmp.get_i32("z")/1e5, 5)
428 except OSError as e:
429 # print('Gyroscope get_nvs_offset:',e)
430 self.set_offset(0,0,0)
431 # self.x_offset = 0
432 # self.y_offset = 0
433 # self.z_offset = 0
434
435 def set_nvs_offset(self, key, value):
436 try:
437 nvs = NVS("offset_g")
438 nvs.set_i32(key, int(value*1e5))
439 nvs.commit()
440 except OSError as e:
441 print('Gyroscope set_nvs_offset error:',e)
442
443motion = MOTION()
444accelerometer = motion.Accelerometer()
445gyroscope = motion.Gyroscope()
446
447class Magnetic(object):
448 """ MMC5983MA driver """
449 """ MMC5603NJ driver 20211028替换"""
450 def __init__(self):
451 self.addr = 48
452 self.i2c = i2c
453 self._judge_id()
454 time.sleep_ms(5)
455 if (self.product_ID==48):
456 pass # MMC5983MA
457 elif (self.product_ID==16):
458 pass # MMC5603NJ
459 else:
460 raise OSError("Magnetic init error")
461 """ MMC5983MA driver """
462 # 传量器裸数据,乘0.25后转化为mGS
463 self.raw_x = 0.0
464 self.raw_y = 0.0
465 self.raw_z = 0.0
466 # 校准后的偏移量, 基于裸数据
467 self.cali_offset_x = 0.0
468 self.cali_offset_y = 0.0
469 self.cali_offset_z = 0.0
470 # 去皮偏移量,类似电子秤去皮功能,基于裸数据。
471 self.peeling_x = 0.0
472 self.peeling_y = 0.0
473 self.peeling_z = 0.0
474 self.is_peeling = 0
475 if (self.chip==1):
476 self.i2c.writeto(self.addr, b'\x09\x20\xbd\x00', True)
477 """ MMC5603NJ driver """
478 if (self.chip==2):
479 self._writeReg(0x1C, 0x80)#软件复位
480 time.sleep_ms(100)
481 self._writeReg(0x1A, 255)
482 self._writeReg(0x1B, 0b10100001)
483 # self._writeReg(0x1C, 0b00000011)
484 self._writeReg(0x1C, 0b00000000)
485 self._writeReg(0x1D, 0b10010000)
486 time.sleep_ms(100)
487
488 def _readReg(self, reg, nbytes=1):
489 return i2c.readfrom_mem(self.addr, reg, nbytes)
490
491 def _writeReg(self, reg, value):
492 i2c.writeto_mem(self.addr, reg, value.to_bytes(1, 'little'))
493
494 def _set_offset(self):
495 if(self.chip == 1):
496 self.i2c.writeto(self.addr, b'\x09\x08', True) #set
497 self.i2c.writeto(self.addr, b'\x09\x01', True)
498 while True:
499 self.i2c.writeto(self.addr, b'\x08', False)
500 buf = self.i2c.readfrom(self.addr, 1)
501 status = ustruct.unpack('B', buf)[0]
502 if(status & 0x01):
503 break
504 self.i2c.writeto(self.addr, b'\x00', False)
505 buf = self.i2c.readfrom(self.addr, 6)
506 data = ustruct.unpack('>3H', buf)
507
508 self.i2c.writeto(self.addr, b'\x09\x10', True) #reset
509
510 self.i2c.writeto(self.addr, b'\x09\x01', True)
511 while True:
512 self.i2c.writeto(self.addr, b'\x08', False)
513 buf = self.i2c.readfrom(self.addr, 1)
514 status = ustruct.unpack('B', buf)[0]
515 if(status & 0x01):
516 break
517 self.i2c.writeto(self.addr, b'\x00', False)
518 buf = self.i2c.readfrom(self.addr, 6)
519 data1 = ustruct.unpack('>3H', buf)
520
521 self.x_offset = (data[0] + data1[0])/2
522 self.y_offset = (data[1] + data1[1])/2
523 self.z_offset = (data[2] + data1[2])/2
524 elif(self.chip == 2):
525 pass
526
527 def _get_raw(self):
528 if (self.chip == 1):
529 retry = 0
530 if (retry < 5):
531 try:
532 self.i2c.writeto(self.addr, b'\x09\x08', True) #set
533
534 self.i2c.writeto(self.addr, b'\x09\x01', True)
535 while True:
536 self.i2c.writeto(self.addr, b'\x08', False)
537 buf = self.i2c.readfrom(self.addr, 1)
538 status = ustruct.unpack('B', buf)[0]
539 if(status & 0x01):
540 break
541 self.i2c.writeto(self.addr, b'\x00', False)
542 buf = self.i2c.readfrom(self.addr, 6)
543 data = ustruct.unpack('>3H', buf)
544
545 self.i2c.writeto(self.addr, b'\x09\x10', True) #reset
546
547 self.i2c.writeto(self.addr, b'\x09\x01', True)
548 while True:
549 self.i2c.writeto(self.addr, b'\x08', False)
550 buf = self.i2c.readfrom(self.addr, 1)
551 status = ustruct.unpack('B', buf)[0]
552 if(status & 0x01):
553 break
554 self.i2c.writeto(self.addr, b'\x00', False)
555 buf = self.i2c.readfrom(self.addr, 6)
556 data1 = ustruct.unpack('>3H', buf)
557
558 self.raw_x = -((data[0] - data1[0])/2)
559 self.raw_y = -((data[1] - data1[1])/2)
560 self.raw_z = -((data[2] - data1[2])/2)
561 # print(str(self.raw_x) + " " + str(self.raw_y) + " " + str(self.raw_z))
562 except:
563 retry = retry + 1
564 else:
565 raise Exception("i2c read/write error!")
566 elif(self.chip == 2):
567 retry = 0
568 if (retry < 5):
569 try:
570 _raw_x = 0.0
571 _raw_y = 0.0
572 _raw_z = 0.0
573
574 self.i2c.writeto(self.addr, b'\x1B\x08', True) #set
575 self.i2c.writeto(self.addr, b'\x1B\x01', True)
576
577 while True:
578 time.sleep_ms(25)
579 buf = self._readReg(0x18, 1)
580 status = buf[0]
581 if(status & 0x40):
582 break
583
584 buf = self._readReg(0x00, 9)
585
586 _raw_x = (buf[0] << 12) | (buf[1] << 4) | (buf[6] >> 4)
587 _raw_y = (buf[2] << 12) | (buf[3] << 4) | (buf[7] >> 4)
588 _raw_z = (buf[4] << 12) | (buf[5] << 4) | (buf[8] >> 4)
589
590 self.raw_x = _raw_x
591 self.raw_y = _raw_y
592 self.raw_z = _raw_z
593 except:
594 retry = retry + 1
595 else:
596 raise Exception("i2c read/write error!")
597
598 def peeling(self):
599 '''
600 去除磁场环境
601 '''
602 self._get_raw()
603 self.peeling_x = self.raw_x
604 self.peeling_y = self.raw_y
605 self.peeling_z = self.raw_z
606 self.is_peeling = 1
607
608 def clear_peeling(self):
609 self.peeling_x = 0.0
610 self.peeling_y = 0.0
611 self.peeling_z = 0.0
612 self.is_peeling = 0
613
614 def get_x(self):
615 if (self.chip == 1):
616 self._get_raw()
617 return self.raw_x * 0.25
618 if (self.chip == 2):
619 self._get_raw()
620 if(self.cali_offset_x):
621 return -0.0625 * (self.raw_x - self.cali_offset_x)
622 else:
623 return -0.0625 * (self.raw_x - 524288)
624 # return -(self.raw_x - 524288)/16384
625
626 def get_y(self):
627 if (self.chip == 1):
628 self._get_raw()
629 return self.raw_y * 0.25
630 if (self.chip == 2):
631 self._get_raw()
632 if(self.cali_offset_y):
633 return -0.0625 * (self.raw_y - self.cali_offset_y)
634 else:
635 return -0.0625 * (self.raw_y - 524288)
636 # return -(self.raw_y - 524288)/16384
637
638 def get_z(self):
639 if (self.chip == 1):
640 self._get_raw()
641 return self.raw_z * 0.25
642 if (self.chip == 2):
643 self._get_raw()
644 if(self.cali_offset_z):
645 return 0.0625 * (self.raw_z - self.cali_offset_z)
646 else:
647 return 0.0625 * (self.raw_z - 524288)
648 # return (self.raw_z - 524288)/16384
649
650 def get_field_strength(self):
651 if(self.chip==1):
652 self._get_raw()
653 if self.is_peeling == 1:
654 return (math.sqrt((self.raw_x - self.peeling_x)*(self.raw_x - self.peeling_x) + (self.raw_y - self.peeling_y)*(self.raw_y - self.peeling_y) + (self.raw_z - self.peeling_z)*(self.raw_z - self.peeling_z)))*0.25
655 return (math.sqrt(self.raw_x * self.raw_x + self.raw_y * self.raw_y + self.raw_z * self.raw_z))*0.25
656 elif(self.chip==2):
657 self._get_raw()
658 if self.is_peeling == 1:
659 return (math.sqrt(math.pow(self.raw_x - self.peeling_x, 2) + pow(self.raw_y - self.peeling_y, 2) + pow(self.raw_z - self.peeling_z , 2)))*0.0625
660 return (math.sqrt(math.pow(self.get_x(), 2) + pow(self.get_y(), 2) + pow(self.get_z(), 2)))
661
662 def calibrate(self):
663 display.clear(lcd.WHITE)
664 w, h, buff = display.decode_png_internal(72)
665 fb = framebuf.FrameBuffer(buff, w, h, framebuf.RGB565)
666 display.blit(fb, 75, -17)
667 display.DispChar("步骤1:如图转几周", 50, 130, lcd.RED)
668 display.show()
669
670 self._get_raw()
671 min_x = max_x = self.raw_x
672 min_y = max_y = self.raw_y
673 min_z = max_z = self.raw_z
674 ticks_start = time.ticks_ms()
675 while (time.ticks_diff(time.ticks_ms(), ticks_start) < 15000) :
676 self._get_raw()
677 min_x = min(self.raw_x, min_x)
678 min_y = min(self.raw_y, min_y)
679 max_x = max(self.raw_x, max_x)
680 max_y = max(self.raw_y, max_y)
681 time.sleep_ms(100)
682 self.cali_offset_x = (max_x + min_x) / 2
683 self.cali_offset_y = (max_y + min_y) / 2
684 print('cali_offset_x: ' + str(self.cali_offset_x) + ' cali_offset_y: ' + str(self.cali_offset_y))
685
686 display.clear(lcd.WHITE)
687 w, h, buff = display.decode_png_internal(73)
688 fb = framebuf.FrameBuffer(buff, w, h, framebuf.RGB565)
689 display.blit(fb, 75, -20)
690 display.DispChar("步骤2:如图转几周", 50, 130, lcd.RED)
691 display.show()
692
693 time.sleep(5)
694 ticks_start = time.ticks_ms()
695 while (time.ticks_diff(time.ticks_ms(), ticks_start) < 15000) :
696 self._get_raw()
697 min_z = min(self.raw_z, min_z)
698 max_z = max(self.raw_z, max_z)
699 time.sleep_ms(100)
700 self.cali_offset_z = (max_z + min_z) / 2
701
702 print('cali_offset_z: ' + str(self.cali_offset_z))
703
704 display.clear(lcd.WHITE)
705 display.DispChar("校准完成!", 100, 60, lcd.RED)
706 display.show()
707
708 def get_heading(self):
709 if(self.chip==1):
710 self._get_raw()
711 temp_x = self.raw_x - self.cali_offset_x
712 temp_y = self.raw_y - self.cali_offset_y
713 # temp_z = self.raw_z - self.cali_offset_z
714 heading = math.atan2(temp_y, -temp_x) * (180 / 3.14159265) + 180 + 3
715 return heading
716 else:
717 if(self.cali_offset_x):
718 self._get_raw()
719 temp_x = -(self.raw_x - self.cali_offset_x)
720 temp_y = -(self.raw_y - self.cali_offset_y)
721 heading = math.atan2(temp_y, -temp_x) * (180 / 3.14159265) + 180 + 3
722 else:
723 heading = math.atan2(self.get_y(), -self.get_x()) * (180 / 3.14159265) + 180 + 3
724 return heading
725
726 def _get_temperature(self):
727 if(self.chip==1):
728 retry = 0
729 if (retry < 5):
730 try:
731 self.i2c.writeto(self.addr, b'\x09\x02', True)
732 while True:
733 self.i2c.writeto(self.addr, b'\x08', False)
734 buf = self.i2c.readfrom(self.addr, 1)
735 status = ustruct.unpack('B', buf)[0]
736 if(status & 0x02):
737 break
738 self.i2c.writeto(self.addr, b'\x07', False)
739 buf = self.i2c.readfrom(self.addr, 1)
740 temp = (ustruct.unpack('B', buf)[0])*0.8 -75
741 # print(data)
742 return temp
743 except:
744 retry = retry + 1
745 else:
746 raise Exception("i2c read/write error!")
747 elif(self.chip == 2):
748 pass
749
750 def _get_id(self):
751 if (self.chip==1):
752 retry = 0
753 if (retry < 5):
754 try:
755 self.i2c.writeto(self.addr, bytearray([0x2f]), False)
756 buf = self.i2c.readfrom(self.addr, 1, True)
757 print(buf)
758 id = ustruct.unpack('B', buf)[0]
759 return id
760 except:
761 retry = retry + 1
762 else:
763 raise Exception("i2c read/write error!")
764 elif (self.chip==2):
765 retry = 0
766 if (retry < 5):
767 try:
768 self.i2c.writeto(self.addr, bytearray([0x39]), False)
769 buf = self.i2c.readfrom(self.addr, 1, True)
770 id = ustruct.unpack('B', buf)[0]
771 return id
772 except:
773 retry = retry + 1
774 else:
775 raise Exception("i2c read/write error!")
776
777 def _judge_id(self):
778 """
779 判断product_ID
780 """
781 retry = 0
782 if (retry < 5):
783 try:
784 self.i2c.writeto(self.addr, bytearray([0x39]), False)
785 buf = self.i2c.readfrom(self.addr, 1, True)
786 id = ustruct.unpack('B', buf)[0]
787 if(id == 16):
788 self.chip = 2
789 self.product_ID = 16
790 else:
791 self.chip = 1
792 self.product_ID = 48
793 except:
794 retry = retry + 1
795 else:
796 raise Exception("i2c read/write error!")
797
798# Magnetic
799if 48 in i2c.scan():
800 magnetic = Magnetic()
801else:
802 print("Magnetic sensor not found.")
803
804class PinMode(object):
805 IN = 1
806 OUT = 2
807 PWM = 3
808 ANALOG = 4
809 OUT_DRAIN = 5
810
811# P3: 阻性器件
812# P5: A P11: B
813# P7: RGB LED
814# P10: sound
815# P12: buzzer
816# P0 P1 P2 P3 P4 P5 P6 P7 P8 P9 P10 P11 P12 P13 P14 P15 P16 P19 P20 P21 P22 P23 P24 P25 P26 P27 P28
817# * * * * * scl sda * P Y T H O N
818pins_remap_esp32 = (1, 2, 3, 4, 5, 0, 7, 8, 15, 16, 6, 46, 21, 17, 18, 48, 47, -1, -1, 43, 44, 45, 33, 9, 10, 11, 12, 13, 14)
819class MPythonPin():
820 def __init__(self, pin, mode=PinMode.IN, pull=None):
821 if mode not in [PinMode.IN, PinMode.OUT, PinMode.PWM, PinMode.ANALOG, PinMode.OUT_DRAIN]:
822 raise TypeError("mode must be 'IN, OUT, PWM, ANALOG,OUT_DRAIN'")
823 if pin == 10:
824 raise TypeError("P10 is used for internal sound sensor")
825 if pin == 5 or pin == 11:
826 raise TypeError("P5 or P11 is used for internal A B key.")
827 if pin == 7:
828 raise TypeError("P7 is used for internal RGB LED.")
829 if pin == 12:
830 raise TypeError("P12 is used for internal buzzer.")
831 try:
832 self.id = pins_remap_esp32[pin]
833 except IndexError:
834 raise IndexError("Out of Pin range")
835 if mode == PinMode.IN:
836 if pin not in [0, 1, 2, 3, 4, 6, 8, 9, 13, 14, 15, 16, 23, 24, 25, 26, 27, 28]:
837 raise TypeError('IN not supported on P%d' % pin)
838 self.Pin = Pin(self.id, Pin.IN, pull)
839 if mode == PinMode.OUT:
840 if pin not in [0, 1, 2, 3, 4, 6, 8, 9, 13, 14, 15, 16, 23, 24, 25, 26, 27, 28]:
841 raise TypeError('OUT not supported on P%d' % pin)
842 self.Pin = Pin(self.id, Pin.IN, pull)
843 time.sleep_ms(1)
844 self.Pin = Pin(self.id, Pin.OUT, pull)
845 if mode == PinMode.OUT_DRAIN:
846 if pin not in [0, 1, 2, 3, 4, 6, 8, 9, 13, 14, 15, 16, 23, 24, 25, 26, 27, 28]:
847 raise TypeError('OUT_DRAIN not supported on P%d' % pin)
848 self.Pin = Pin(self.id, Pin.IN, pull)
849 time.sleep_ms(1)
850 self.Pin = Pin(self.id, Pin.OPEN_DRAIN, pull)
851 if mode == PinMode.PWM:
852 if pin not in [0, 1, 2, 3, 4, 6, 8, 9, 13, 14, 15, 16, 23, 24, 25, 26, 27, 28]:
853 raise TypeError('PWM not supported on P%d' % pin)
854 self.Pin = Pin(self.id, Pin.IN, pull)
855 time.sleep_ms(1)
856 self.pwm = PWM(Pin(self.id), duty=0)
857 if mode == PinMode.ANALOG:
858 if pin not in [0, 1, 2, 3, 4, 6]:
859 raise TypeError('ANALOG not supported on P%d' % pin)
860 self.adc = ADC(Pin(self.id))
861 self.adc.atten(ADC.ATTN_11DB)
862 self.mode = mode
863
864 def irq(self, handler=None, trigger=Pin.IRQ_RISING):
865 if not self.mode == PinMode.IN:
866 raise TypeError('the pin is not in IN mode')
867 return self.Pin.irq(handler, trigger)
868
869 def read_digital(self):
870 if not self.mode == PinMode.IN:
871 raise TypeError('the pin is not in IN mode')
872 return self.Pin.value()
873
874 def write_digital(self, value):
875 if self.mode not in [PinMode.OUT, PinMode.OUT_DRAIN]:
876 raise TypeError('the pin is not in OUT or OUT_DRAIN mode')
877 self.Pin.value(value)
878
879 def read_analog(self):
880 if not self.mode == PinMode.ANALOG:
881 raise TypeError('the pin is not in ANALOG mode')
882 return self.adc.read()
883
884
885 def write_analog(self, duty, freq=1000):
886 if not self.mode == PinMode.PWM:
887 raise TypeError('the pin is not in PWM mode')
888 self.pwm.freq(freq)
889 self.pwm.duty(duty)
890
891
892
893# 3 rgb leds
894rgb = NeoPixel(Pin(8, Pin.OUT), 3, 3, 1, brightness=0.3)
895rgb.write()
896
897# light sensor LTR-308ALS
898if 83 in i2c.scan():
899 light = LTR_308ALS(i2c)
900else:
901 print("light sensor not found!")
902
903# sound sensor
904sound = ADC(Pin(6))
905sound.atten(sound.ATTN_11DB)
906
907# buttons
908class Button:
909 def __init__(self, pin_num, reverse=False):
910 self.__reverse = reverse
911 (self.__press_level, self.__release_level) = (0, 1) if not self.__reverse else (1, 0)
912 self.__pin = Pin(pin_num, Pin.IN, pull=Pin.PULL_UP)
913 self.__pin.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=self.__irq_handler)
914 # self.__user_irq = None
915 self.event_pressed = None
916 self.event_released = None
917 self.__pressed_count = 0
918 self.__was_pressed = False
919 # print("level: pressed is {}, released is {}." .format(self.__press_level, self.__release_level))
920
921
922 def __irq_handler(self, pin):
923 irq_falling = True if pin.value() == self.__press_level else False
924 # debounce
925 time.sleep_ms(10)
926 if self.__pin.value() == (self.__press_level if irq_falling else self.__release_level):
927 # new event handler
928 # pressed event
929 if irq_falling:
930 if self.event_pressed is not None:
931 schedule(self.event_pressed, self.__pin)
932 # key status
933 self.__was_pressed = True
934 if (self.__pressed_count < 100):
935 self.__pressed_count = self.__pressed_count + 1
936 # release event
937 else:
938 if self.event_released is not None:
939 schedule(self.event_released, self.__pin)
940
941
942 def is_pressed(self):
943 if self.__pin.value() == self.__press_level:
944 return True
945 else:
946 return False
947
948 def was_pressed(self):
949 r = self.__was_pressed
950 self.__was_pressed = False
951 return r
952
953 def get_presses(self):
954 r = self.__pressed_count
955 self.__pressed_count = 0
956 return r
957
958 def value(self):
959 return self.__pin.value()
960
961 def irq(self, *args, **kws):
962 self.__pin.irq(*args, **kws)
963
964 def status(self):
965 val = self.__pin.value()
966 if(val==0):
967 return 1
968 elif(val==1):
969 return 0
970
971button_a = Button(0)
972button_b = Button(46)
973
974class Touch:
975 def __init__(self, pin):
976 self.__touch_pad = TouchPad(pin)
977 self.__touch_pad.config(30000)
978 self.__touch_pad.irq(self.__irq_handler)
979 self.event_pressed = None
980 self.event_released = None
981 self.__pressed_count = 0
982 self.__was_pressed = False
983 self.__value = 0
984
985 def __irq_handler(self, value):
986 # when pressed
987 # print("%d" % value)
988 if value == 1:
989 if self.event_pressed is not None:
990 self.event_pressed(value)
991 self.__was_pressed = True
992 self.__value = 1
993 if (self.__pressed_count < 100):
994 self.__pressed_count = self.__pressed_count + 1
995 # when released
996 else:
997 self.__value = 0
998 if self.event_released is not None:
999 self.event_released(value)
1000
1001 def config(self, threshold):
1002 self.__touch_pad.config(threshold)
1003
1004 def is_pressed(self):
1005 if self.__value:
1006 return True
1007 else:
1008 return False
1009
1010 def was_pressed(self):
1011 r = self.__was_pressed
1012 self.__was_pressed = False
1013 return r
1014
1015 def get_presses(self):
1016 r = self.__pressed_count
1017 self.__pressed_count = 0
1018 return r
1019
1020 def read(self):
1021 return self.__touch_pad.read()
1022
1023# touchpad
1024touchpad_p = touchPad_P = Touch(Pin(9))
1025touchpad_y = touchPad_Y = Touch(Pin(10))
1026touchpad_t = touchPad_T = Touch(Pin(11))
1027touchpad_h = touchPad_H = Touch(Pin(12))
1028touchpad_o = touchPad_O = Touch(Pin(13))
1029touchpad_n = touchPad_N = Touch(Pin(14))
1030
1031from gui import *
1032
1033"""
1034uuid
1035"""
1036def uuid():
1037 import ubinascii
1038 import machine
1039 uuid = ''
1040 try:
1041 uuid = ubinascii.hexlify(machine.unique_id_custom()).decode().upper()
1042 except Exception as e:
1043 uuid = ubinascii.hexlify(machine.unique_id()).decode().upper()
1044
1045 if(uuid=='ffffffffffff'.upper() or uuid=='000000000000'.upper()):
1046 uuid = ubinascii.hexlify(machine.unique_id()).decode().upper()
1047
1048 return uuid
1049