diff --git a/boards/HDK/MixGo_MINI-V1.7.pdf b/boards/HDK/MixGo_MINI-V1.7.pdf new file mode 100644 index 00000000..c1375140 Binary files /dev/null and b/boards/HDK/MixGo_MINI-V1.7.pdf differ diff --git a/boards/default_src/micropython/origin/build/lib/adxl345.py b/boards/default_src/micropython/origin/build/lib/adxl345.py index 3ea21da9..5ce18442 100644 --- a/boards/default_src/micropython/origin/build/lib/adxl345.py +++ b/boards/default_src/micropython/origin/build/lib/adxl345.py @@ -1,56 +1,52 @@ -#ADXL345 -import time -import ustruct -from micropython import const - -DATA_FORMAT = const(0x31) -BW_RATE = const(0x2c) -POWER_CTL = const(0x2d) -INT_ENABLE = const(0x2E) -OFSX = const(0x1e) -OFSY = const(0x1f) -OFSZ = const(0x20) - -class ADXL345: - def __init__(self, i2c, address=0X53): - self._device = i2c - self._address = address - for addr in self._device.scan(): - if self._device.readfrom_mem(addr, 0, 1)[0] == 0xe5: - self._address = addr - break - else: - raise AttributeError("Cannot find a ADXL345") - - self._wreg(DATA_FORMAT,0x2B) #16g量程 - self._wreg(BW_RATE,0x0A) #数据输出速度为100Hz - self._wreg(INT_ENABLE,0x00) #不使用中断 - - self._wreg(OFSX,0x00) - self._wreg(OFSY,0x00) - self._wreg(OFSZ,0x00) - self._wreg(POWER_CTL,0x08) #链接使能,测量模式 - time.sleep(0.5) - - def readXYZ(self): - x, = ustruct.unpack(' 5: break + _keys[i] = general[i] + else: + _keys[0] = general + # Pack the mouse state as described by the input report + self._ble.gatts_notify(self.conn_handle, self.h_rep, bytes([special & 0xFF, 0]) + _keys) + if release: + time.sleep_ms(10) + self._ble.gatts_notify(self.conn_handle, self.h_rep, b'\x00\x00\x00\x00\x00\x00\x00\x00') + + def notify_battery(self, level): + if self.is_connected(): + self.battery_level = max(min(level, 100), 0) + # Notifies the client by writing to the battery level handle. + self._ble.gatts_notify(self.conn_handle, self.h_bat, struct.pack("> 7 else 0x00, _KEYCODE[char] & 0x7F) + time.sleep_ms(20 + delay) + + @property + def mac(self): + '''Get mac address''' + return hexlify(self._ble.config('mac')[1]).decode() diff --git a/boards/default_src/micropython/origin/build/lib/ble_hid_mouse.py b/boards/default_src/micropython/origin/build/lib/ble_hid_mouse.py new file mode 100644 index 00000000..68e345ae --- /dev/null +++ b/boards/default_src/micropython/origin/build/lib/ble_hid_mouse.py @@ -0,0 +1,152 @@ +""" +Bluetooth-HID-Mouse + +Micropython library for the Bluetooth-HID-Mouse +======================================================= +#https://github.com/Heerkog/MicroPythonBLEHID + +@dahanzimin From the Mixly Team +""" +import bluetooth +import struct, time +from micropython import const +from ubinascii import hexlify +from ble_advertising import advertising_payload +from bluetooth import UUID, FLAG_READ, FLAG_WRITE ,FLAG_NOTIFY, FLAG_WRITE_NO_RESPONSE + +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_MTU_EXCHANGED = const(21) +_IRQ_CONNECTION_UPDATE = const(27) +_IRQ_PASSKEY_ACTION = const(31) +_PASSKEY_ACTION_INPUT = const(2) +_PASSKEY_ACTION_DISP = const(3) +_PASSKEY_ACTION_NUMCMP = const(4) + +_HID_INPUT_REPORT = const(b'\x05\x01\t\x02\xa1\x01\x85\x01\t\x01\xa1\x00\x05\t\x19\x01)\x03\x15\x00%\x01\x95\x03u\x01\x81\x02\x95\x01u\x05\x81\x03\x05\x01\t0\t1\t8\x15\x81%\x7fu\x08\x95\x03\x81\x06\xc0\xc0') + +_DIS = (UUID(0x180A), ( (UUID(0x2A24), FLAG_READ), + (UUID(0x2A25), FLAG_READ), + (UUID(0x2A26), FLAG_READ), + (UUID(0x2A27), FLAG_READ), + (UUID(0x2A28), FLAG_READ), + (UUID(0x2A29), FLAG_READ), + (UUID(0x2A50), FLAG_READ), ), ) + +_BAS = (UUID(0x180F), ( (UUID(0x2A19), FLAG_READ | FLAG_NOTIFY, ( + (UUID(0x2902), 0x01 | 0x02), + (UUID(0x2904), 0x01),)), ), ) + +_HIDS = (UUID(0x1812), ((UUID(0x2A4A), FLAG_READ), + (UUID(0x2A4B), FLAG_READ), + (UUID(0x2A4C), FLAG_READ | FLAG_WRITE | FLAG_WRITE_NO_RESPONSE), + (UUID(0x2A4D), FLAG_READ | FLAG_NOTIFY, ( + (UUID(0x2902), 0x01 | 0x02), + (UUID(0x2908), 0x01 | 0x02), )), + (UUID(0x2A4E), FLAG_READ | FLAG_WRITE | FLAG_WRITE_NO_RESPONSE), ), ) + +class Mouse: + def __init__(self, name=None, passkey=1234, battery_level=100): + if (name is '') or (name is None): + name = "Mixgo_" + self.mac[-6:].upper() + print("Mouse name:", name) + + self._ble = bluetooth.BLE() + self._ble.active(True) + self._ble.irq(self._irq) + self._ble.config(gap_name=name) + self._ble.config(mtu=23) + self.device_state = False + self.conn_handle = None + self.passkey = passkey + self.battery_level = battery_level + + handles = self._ble.gatts_register_services((_DIS, _BAS, _HIDS)) + self._service_characteristics(handles) + self._payload = advertising_payload(name=name, services=[UUID(0x1812)], appearance=const(962)) + self.advertise() + + def _irq(self, event, data): + # Interrupt request callback function + if event == _IRQ_CENTRAL_CONNECT: + self.conn_handle, _, _ = data + print("Mouse connected: ", self.conn_handle) + self.device_state = True + elif event == _IRQ_CENTRAL_DISCONNECT: + self.conn_handle = None + conn_handle, addr_type, addr = data + print("Mouse disconnected: ", conn_handle) + self.advertise() + self.device_state = False + elif event == _IRQ_MTU_EXCHANGED: + conn_handle, mtu = data + self._ble.config(mtu=mtu) + print("MTU exchanged: ", mtu) + elif event == _IRQ_CONNECTION_UPDATE: + self.conn_handle, _, _, _, _ = data + print("Connection update") + elif event == _IRQ_PASSKEY_ACTION: + conn_handle, action, passkey = data + print("Passkey action", conn_handle, action, passkey) + if action == _PASSKEY_ACTION_NUMCMP: + self._ble.gap_passkey(conn_handle, action, False) + elif action == _PASSKEY_ACTION_DISP: + print("Displaying passkey") + self._ble.gap_passkey(conn_handle, action, self.passkey) + elif action == _PASSKEY_ACTION_INPUT: + print(" Prompting for passkey") + self._ble.gap_passkey(conn_handle, action, None) + else: + print("unknown action") + #else: + #print("Unhandled IRQ event: ", event, data) + + def _service_characteristics(self, handles): + (h_mod, h_ser, h_fwr, h_hwr, h_swr, h_man, h_pnp) = handles[0] + (self.h_bat, h_ccc, h_bfmt,) = handles[1] + (h_info, h_hid, h_ctrl, self.h_rep, _, h_d1, h_proto) = handles[2] + + # Write DIS characteristics. + self._ble.gatts_write(h_mod, b'1') + self._ble.gatts_write(h_ser, b'1') + self._ble.gatts_write(h_fwr, b'1') + self._ble.gatts_write(h_hwr, b'1') + self._ble.gatts_write(h_swr, b'1') + self._ble.gatts_write(h_man, b'Homebrew') + self._ble.gatts_write(h_pnp, struct.pack("> 1 | 0x80 + t = -((~t + 1) & 0xFF) + else: + t = buf[0] >> 1 + return t - 0.25 + (buf[7] - buf[6]) / buf[7] + else: + t = buf[1] << 8 | buf[0] + if t & 0x8000: # sign bit set + t = -((t ^ 0xFFFF) + 1) + return t / 16 + + def temperature(self): + temp = [] + self.convert_temp() + for rom in self._roms: + temp.append(round(self.read_temp(rom), 2)) + return temp [0] if len(temp) == 1 else tuple(temp) diff --git a/boards/default_src/micropython/origin/build/lib/seniverse_api.py b/boards/default_src/micropython/origin/build/lib/seniverse_api.py index 78cf02eb..3d8beae2 100644 --- a/boards/default_src/micropython/origin/build/lib/seniverse_api.py +++ b/boards/default_src/micropython/origin/build/lib/seniverse_api.py @@ -20,115 +20,198 @@ _weather_alarm="http://api.seniverse.com/v3/weather/alarm.json?" #气象灾害 _life_suggestion="http://api.seniverse.com/v3/life/suggestion.json?" #生活指数 _air_now="http://api.seniverse.com/v3/air/now.json?" #空气质量实况 _air_daily="http://api.seniverse.com/v3/air/daily.json?" #逐日空气质量预报 -_tide_daily="http://api.seniverse.com/v3/tide/daily.json?" #逐小时潮汐预报 +_air_hourly="http://api.seniverse.com/v3/air/hourly.json?" #逐时空气质量预报 +_tide_daily="http://api.seniverse.com/v3/tide/daily.json?" #逐时潮汐预报 _geo_sun="http://api.seniverse.com/v3/geo/sun.json?" #日出日落 _geo_moon="http://api.seniverse.com/v3/geo/moon.json?" #月出月落和月相 _location_search="http://api.seniverse.com/v3/location/search.json?" #城市搜索 #数据请求 -def _urequests_api(url): - try: - results=json.loads(urequests.post(url).text) - except Exception as e: - raise RuntimeError("API request failed or WiFi is not connected",e) - - if "status" in results.keys(): - raise ValueError(results["status"]) - if "results" in results.keys(): - return results["results"] - -#天气实况 https://docs.seniverse.com/api/weather/now.html -def weather_now(key,location): - url="{}key={}&location={}".format(_weather_now,key,location) - results=_urequests_api(url)[0] - return results['now'] +class API_BASE: + _results = None + def _urequests_api(self, url): + try: + results=json.loads(urequests.post(url).text) + except Exception as e: + raise RuntimeError("API request failed or WiFi is not connected",e) + + if "status" in results.keys(): + raise ValueError(results["status"]) + if "results" in results.keys(): + return results["results"] +#天气实况 https://docs.seniverse.com/api/weather/now.html +class Weather_now(API_BASE): + def request(self, key, location): + url = "{}key={}&location={}".format(_weather_now, key, location) + self._results = self._urequests_api(url)[0]['now'] + + def analysis(self, key=None): + if key is None: + return self._results + else: + return self._results[key] + +Weather_now = Weather_now() #逐日天气预报 https://docs.seniverse.com/api/weather/daily.html -def weather_daily(key,location,days=1): - url="{}key={}&location={}&days={}".format(_weather_daily,key,location,days) - results=_urequests_api(url)[0] - data=[] - for i in range(len(results['daily'])): - data.append(results['daily'][i]) - return tuple(data) +class Weather_daily(API_BASE): + def request(self, key, location, days=1): + url = "{}key={}&location={}&days={}".format(_weather_daily, key, location, days) + self._results = self._urequests_api(url)[0]['daily'] + + def analysis(self, days=1, key=None): + if key is None: + return self._results[days] + else: + return self._results[days][key] + +Weather_daily = Weather_daily() #逐时天气预报 https://docs.seniverse.com/api/weather/hourly.html -def weather_hourly(key,location,hours=1): - url="{}key={}&location={}&hours={}".format(_weather_hourly,key,location,hours) - results=_urequests_api(url)[0] - data=[] - for i in range(len(results['hourly'])): - data.append(results['hourly'][i]) - return tuple(data) +class Weather_hourly(API_BASE): + def request(self, key, location, hours=1): + url = "{}key={}&location={}&hours={}".format(_weather_hourly, key, location, hours) + self._results = self._urequests_api(url)[0]['hourly'] -#气象灾害预警 https://docs.seniverse.com/api/weather/alarm.html -def weather_alarm(key,location): - url="{}key={}&location={}".format(_weather_alarm,key,location) - results=_urequests_api(url)[0] - data=[] - for i in range(len(results['alarms'])): - data.append(results['alarms'][i]) - return tuple(data) + def analysis(self, hours=1, key=None): + if key is None: + return self._results[hours] + else: + return self._results[hours][key] -#生活指数 https://docs.seniverse.com/api/life/suggestion.html -def life_suggestion(key,location,days=1): - url="{}key={}&location={}&days={}".format(_life_suggestion,key,location,days) - results=_urequests_api(url)[0] - data=[] - for i in range(len(results['suggestion'])): - data.append(results['suggestion'][i]) - return tuple(data) +#Weather_hourly = Weather_hourly() #暂不开启 #空气质量实况 https://docs.seniverse.com/api/air/now.html -def air_now(key,location): - url="{}key={}&location={}&scope=city".format(_air_now,key,location) - results=_urequests_api(url)[0] - return results['air']['city'] - -#逐日空气质量预报 https://docs.seniverse.com/api/air/daily5d.html -def air_daily(key,location,days=1): - url="{}key={}&location={}&days={}".format(_air_daily,key,location,days) - results=_urequests_api(url)[0] - data=[] - for i in range(len(results['daily'])): - data.append(results['daily'][i]) - return tuple(data) +class Air_now(API_BASE): + def request(self, key, location): + url = "{}key={}&location={}&scope=city".format(_air_now, key, location) + self._results = self._urequests_api(url)[0]['air']['city'] -#逐时潮汐预报 https://docs.seniverse.com/api/ocean/tide.html -def tide_daily(key,location): - url="{}key={}&location={}&days=1".format(_tide_daily,key,location) - results=_urequests_api(url)[0] - data=[] - for i in range(len(results['ports'])): - data.append({'port':results['ports'][i]['port'], - 'tide':results['ports'][i]['data'][0]['tide'], - 'range':results['ports'][i]['data'][0]['range']}) - return tuple(data) + def analysis(self, key=None): + if key is None: + return self._results + else: + return self._results[key] + +Air_now = Air_now() + +#逐日空气质量预报 https://docs.seniverse.com/api/air/daily5d.html +class Air_daily(API_BASE): + def request(self, key, location, days=1): + url = "{}key={}&location={}&days={}".format(_air_daily, key, location, days) + self._results = self._urequests_api(url)[0]['daily'] + + def analysis(self, days=1, key=None): + if key is None: + return self._results[days] + else: + return self._results[days][key] + +Air_daily= Air_daily() + +#逐时空气质量预报 https://docs.seniverse.com/api/air/hourly5d.html +class Air_hourly(API_BASE): + def request(self, key, location, hours=1): + url = "{}key={}&location={}&hours={}&days=1".format(_air_hourly, key, location, hours) + self._results = self._urequests_api(url)[0]['hourly'] + + def analysis(self, hours=1, key=None): + if key is None: + return self._results[hours] + else: + return self._results[hours][key] + +#Air_hourly = Air_hourly() #暂不开启 + +#气象灾害预警 https://docs.seniverse.com/api/weather/alarm.html +class Weather_alarm(API_BASE): + def request(self, key, location): + url = "{}key={}&location={}".format(_weather_alarm, key, location) + results = self._urequests_api(url)[0]['alarms'] + self._results = results[0] if results else {} + + def analysis(self, key=None): + if key is None: + return self._results + if key in self._results.keys(): + return self._results[key] + +Weather_alarm = Weather_alarm() + +#生活指数 https://docs.seniverse.com/api/life/suggestion.html +class Life_suggestion(API_BASE): + def request(self, key, location): + url = "{}key={}&location={}".format(_life_suggestion, key, location) + self._results = self._urequests_api(url)[0]['suggestion'] + + def analysis(self, key=None, brief=False): + if key is None: + return self._results + else: + return self._results[key]['brief'] if brief else self._results[key]['details'] + +Life_suggestion = Life_suggestion() + +#24时潮汐预报 https://docs.seniverse.com/api/ocean/tide.html +class Tide_daily(API_BASE): + def request(self, key, location): + url = "{}key={}&location={}&days=1".format(_tide_daily, key, location) + self._results = self._urequests_api(url)[0]['ports'][0]['data'][0] + + def analysis(self, key=None): + if key is None: + return self._results + else: + key = key.split(',') + if len(key) == 1: + return self._results[key[0]] + if len(key) == 2: + return self._results['range'][int(key[0])][key[1]] + +Tide_daily = Tide_daily() #日出日落 https://docs.seniverse.com/api/geo/sun.html -def geo_sun(key,location,days=1): - url="{}key={}&location={}&days={}".format(_geo_sun,key,location,days) - results=_urequests_api(url)[0] - data=[] - for i in range(len(results['sun'])): - data.append(results['sun'][i]) - return tuple(data) - +class Geo_sun(API_BASE): + def request(self, key, location, days=1): + url = "{}key={}&location={}&days={}".format(_geo_sun, key, location, days) + self._results = self._urequests_api(url)[0]['sun'] + + def analysis(self, days=1, key=None): + if key is None: + return self._results[days] + else: + return self._results[days][key] + +Geo_sun = Geo_sun() + #月出月落和月相 https://docs.seniverse.com/api/geo/moon.html -def geo_moon(key,location,days=1): - url="{}key={}&location={}&days={}".format(_geo_moon,key,location,days) - results=_urequests_api(url)[0] - data=[] - for i in range(len(results['moon'])): - data.append(results['moon'][i]) - return tuple(data) - +class Geo_moon(API_BASE): + def request(self, key, location, days=1): + url = "{}key={}&location={}&days={}".format(_geo_moon, key, location, days) + self._results = self._urequests_api(url)[0]['moon'] + + def analysis(self, days=1, key=None): + if key is None: + return self._results[days] + else: + return self._results[days][key] + +Geo_moon = Geo_moon() + #城市搜索 https://docs.seniverse.com/api/fct/search.html -def location_search(key,location): - url="{}key={}&q={}&limit=50".format(_location_search,key,location) - results=_urequests_api(url) - data=[] - for i in range(len(results)): - data.append(results[i]) - return tuple(data) +class Location_search(API_BASE): + def request(self, key, location): + url = "{}key={}&q={}&limit=50".format(_location_search, key, location) + results = self._urequests_api(url) + self._results = results[0] if results else {} + + def analysis(self, key=None): + if key is None: + return self._results + else: + if key in self._results.keys(): + return self._results[key] + +Location_search = Location_search() + \ No newline at end of file diff --git a/boards/default_src/micropython/origin/build/lib/urequests.py b/boards/default_src/micropython/origin/build/lib/urequests.py index 91358bb2..00c013f1 100644 --- a/boards/default_src/micropython/origin/build/lib/urequests.py +++ b/boards/default_src/micropython/origin/build/lib/urequests.py @@ -43,7 +43,7 @@ def request(method, url, data=None, json=None, headers={}, stream=None, parse_he if proto == "http:": port = 80 elif proto == "https:": - import ussl + import ssl port = 443 else: raise ValueError("Unsupported protocol: " + proto) @@ -63,7 +63,7 @@ def request(method, url, data=None, json=None, headers={}, stream=None, parse_he try: s.connect(ai[-1]) if proto == "https:": - s = ussl.wrap_socket(s, server_hostname=host) + s = ssl.wrap_socket(s, server_hostname=host) s.write(b"%s /%s HTTP/1.0\r\n" % (method, path)) if not "Host" in headers: s.write(b"Host: %s\r\n" % host) diff --git a/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini-v1.23.0.bin b/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini-v1.23.0.bin index 790701c3..dd021b73 100644 Binary files a/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini-v1.23.0.bin and b/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini-v1.23.0.bin differ diff --git a/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib-v1.23.0.bin b/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib-v1.23.0.bin index fb589758..e10fb064 100644 Binary files a/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib-v1.23.0.bin and b/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib-v1.23.0.bin differ diff --git a/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib_ble-v1.23.0.bin b/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib_ble-v1.23.0.bin new file mode 100644 index 00000000..92010fb0 Binary files /dev/null and b/boards/default_src/micropython_esp32c2/origin/build/Mixgo_Mini_lib_ble-v1.23.0.bin differ diff --git a/boards/default_src/micropython_esp32c2/origin/build/lib/mini_bot.py b/boards/default_src/micropython_esp32c2/origin/build/lib/mini_bot.py index 9689a037..e4adeac6 100644 --- a/boards/default_src/micropython_esp32c2/origin/build/lib/mini_bot.py +++ b/boards/default_src/micropython_esp32c2/origin/build/lib/mini_bot.py @@ -5,7 +5,7 @@ Micropython library for the MINI_WCH(TOUCH*2, MIC*1, Buzzer*1, PWM*2, Matrix8x12 ======================================================= @dahanzimin From the Mixly Team """ -import time +import time, math from esp import flash_read from micropython import const from framebuf import FrameBuffer, MONO_VLSB @@ -215,6 +215,12 @@ class BOT035(FrameBuffer): self.show() time.sleep_ms(speed) + def pointern(self, x=_LEDS_W // 2, y=_LEDS_H // 2, l=_LEDS_H // 2, angle=0): + radian = math.radians(angle) + self.fill(0) + self.line(x, y, round(x + l * math.sin(radian)), round(y - l * math.cos(radian)), 1) + self.show() + def _wreg(self, reg, val): '''Write memory address''' self._i2c.writeto_mem(_BOT035_ADDRESS, reg, val.to_bytes(1, 'little'))