├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── docs └── images │ ├── favicon.ico │ ├── hcsr04.jpg │ ├── hcsr04.png │ ├── micropython.png │ ├── mpython_ico_en.png │ ├── parrot.png │ ├── 掌控-立1.png │ ├── 掌控-立2.png │ ├── 透视正面.png │ └── 透视背面.png ├── library ├── README.md ├── bigiot │ ├── .gitignore │ ├── LICENSE │ ├── MANIFEST │ ├── MANIFEST.in │ ├── README.md │ ├── bigiot.py │ ├── examples │ │ └── bigiot_example.py │ ├── optimize_upip.py │ ├── sdist_upip.py │ ├── setup.cfg │ └── setup.py ├── ir_remote │ └── ir_remote.py ├── ledstrip │ ├── LICENSE │ ├── MANIFEST │ ├── README.md │ ├── examples │ │ └── ledstrip_simple.py │ ├── ledstrip.py │ ├── optimize_upip.py │ ├── sdist_upip.py │ ├── setup.cfg │ └── setup.py ├── microbit │ ├── README.md │ └── microbit.py ├── qqai │ ├── README.md │ ├── __init__.py │ ├── aai.py │ ├── base.py │ ├── nlp.py │ └── vision │ │ ├── __init__.py │ │ ├── face.py │ │ ├── ocr.py │ │ └── picture.py ├── urllib │ ├── README.md │ └── parse.py └── yeelight │ ├── LICENSE │ ├── MANIFEST │ ├── MANIFEST.in │ ├── README.md │ ├── examples │ └── yeelight_simple.py │ ├── images │ ├── mpython.png │ └── yeelight.png │ ├── optimize_upip.py │ ├── sdist_upip.py │ ├── setup.cfg │ ├── setup.py │ └── yeelight.py ├── mkdocs.yml ├── optimize_upip.py └── sdist_upip.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | .vscode/ 3 | !.gitkeep 4 | site/ 5 | docs/index.md -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "library/hcsr04"] 2 | path = library/hcsr04 3 | url = https://github.com/tangliufeng/micropython-hcsr04.git 4 | [submodule "library/ivPID"] 5 | path = library/ivPID 6 | url = https://github.com/tangliufeng/ivPID.git 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 labplus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BASEDIR=$(CURDIR) 2 | DOCDIR=$(BASEDIR)/docs 3 | 4 | install: 5 | pip install mkdocs mkdocs-material 6 | 7 | link: 8 | ln -sf $(BASEDIR)/README.md $(DOCDIR)/index.md 9 | 10 | serve: 11 | $(MAKE) link 12 | mkdocs serve 13 | 14 | deploy: 15 | $(MAKE) link 16 | mkdocs gh-deploy --clean -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mPython 精选资源 2 | 3 | 4 | 5 | 设立[aswsome-mpython](https://labplus-cn.github.io/awesome-mpython/)网页项目初衷,旨在收集、归纳掌控板MicroPython的一些网络上的资源。提供学习指南,库,软件和资源供掌控板MicroPython爱好者学习! 6 | 7 | `mPython`是`MicroPython`基础上的分支,旨在简化低成本微控制器上的实验和教学。它不需要预先安装Python环境或桌面软件,因此比以往更加轻松地上手。使用`mPython`,您可以编写简洁的Python代码来控制硬件,而不必使用复杂的底层语言(例如C或C ++)(Arduino用于编程的语言)。对初学者来说很棒! 8 | 9 | 10 | ## 精选驱动库 11 | 12 | ### 说明 13 | 14 |
15 | 16 | - 由于MicroPython与mPython会存在细微区别,并不一定完全能在mPython上运行。同时我们也会标识出哪些驱动可在mPython上正常运行。 17 | - 对应一些需要二次修改才能使用的驱动或经典的驱动,我们将会收收纳至[awesome-mpython/library](https://github.com/labplus-cn/awesome-mpython/tree/master/library)。 18 | 19 | 图标说明: 20 | 21 | - : 经验证可在掌控板上正常使用 22 | - : 收纳入awesome-mpython/library 23 | 24 | 25 | ### 传感器 26 | 27 | 28 | * [ADXL345-with-Pyboard](https://github.com/AbhinayBandaru/ADXL345-with-Pyboard) - ADXL345 16g 3轴加速度计 29 | * [adxl345_micropython](https://github.com/fanday/adxl345_micropython) - ADXL345 16g 3轴加速度计 30 | * [micropython-lis2hh12](https://github.com/tuupola/micropython-lis2hh12) - LIS2HH12 3轴加速度计 31 | * [MMA7660](https://github.com/Bucknalla/MicroPython-3-Axis-Accelerometer/blob/master/MMA7660.py) - MMA7660 1.5g 3轴加速度计 32 | 33 | * [CCS811](https://github.com/Ledbelly2142/CCS811) - CCS811 空气质量传感器 34 | * [upython-aq-monitor](https://github.com/ayoy/upython-aq-monitor) - PMS5003 颗粒物浓度传感器 35 | * [micropython-bme280](https://github.com/kevbu/micropython-bme280) - Bosch BME280 气象传感器(温度/湿度/气压) 36 | * [mpy_bme280_esp8266](https://github.com/catdog2/mpy_bme280_esp8266) - Bosch BME280 气象传感器(温度/湿度/气压) 37 | * [wipy_bme280](https://bitbucket.org/oscarBravo/wipy_bme280) - Bosch BME280 气象传感器(温度/湿度/气压) 38 | * [micropython-bmp180](https://github.com/micropython-IMU/micropython-bmp180) - Bosch BMP180 气象传感器(温度/气压/海拔) 39 | * [micropython-ov2640](https://github.com/namato/micropython-ov2640) - MicroPython class for OV2640 camera. 40 | * [micropython-esp8266-hmc5883l](https://github.com/gvalkov/micropython-esp8266-hmc5883l) - 3轴数字指南针 41 | * [pyb_ina219](https://github.com/chrisb2/pyb_ina219) - INA219 电压/电流传感器 42 | * [micropython-gp2y0e03](https://bitbucket.org/thesheep/micropython-gp2y0e03) - Sharp GP2Y0E03 红外测距传感器 43 | * [micropython-vl53l0x](https://bitbucket.org/thesheep/micropython-vl53l0x) - VL53L0X Time-of-Flight 激光测距模块 44 | * [micropython-vl6180](https://bitbucket.org/thesheep/micropython-vl6180) - vl6180近距离感测器(光学测距/环境光线) 45 | * [micropython-hcsr04](https://github.com/rsc1975/micropython-hcsr04) - HC-SR04/05 超声波测距 46 | * [ATM90E26_Micropython](https://github.com/whatnick/ATM90E26_Micropython) -ATM90E26 功率测量 47 | * [micropython-MQ](https://github.com/kartun83/micropython-MQ) - MQ系列烟雾传感器 48 | * [MQ135](https://github.com/rubfi/MQ135) - MQ135 烟雾传感器 49 | * [MicroPython-SI1145](https://github.com/neliogodoi/MicroPython-SI1145) - SI1145 光线传感器(紫外光/可见光/红外光) 50 | * [micropython-tsl2561](https://github.com/kfricke/micropython-tsl2561) - TSL2561 数字光强传感器 51 | * [mpy_bh1750fvi_esp8266](https://github.com/catdog2/mpy_bh1750fvi_esp8266) - BH1750FVI 数字光强传感器 52 | * [micropython-bmx055](https://github.com/micropython-IMU/micropython-bmx055) - Driver for Bosch BMX055 IMU sensor. 53 | * [micropython-bno055](https://github.com/deshipu/micropython-bno055) - Bosch Sensortec BNO055 9DOF IMU sensor, I2C interface. 54 | * [micropython-lsm9ds0](https://github.com/micropython-IMU/micropython-lsm9ds0) - LSM9DS0 g-force linear acceleration, gauss magnetic and dps angular rate sensors. 55 | * [micropython-mpu9250](https://github.com/tuupola/micropython-mpu9250) - I2C driver for MPU9250 9-axis motion tracking device. 56 | * [micropython-mpu9x50](https://github.com/micropython-IMU/micropython-mpu9x50) - Driver for the InvenSense MPU9250 inertial measurement unit. 57 | * [MPU6050-ESP8266-MicroPython](https://github.com/adamjezek98/MPU6050-ESP8266-MicroPython) - ESP8266 driver for MPU6050 accelerometer/gyroscope. 58 | * [py-mpu6050](https://github.com/larsks/py-mpu6050) - ESP8266 driver for MPU6050 accelerometer/gyroscope. 59 | 60 | * [micropython-chirp](https://github.com/robberwick/micropython-chirp) - Driver for the Chirp Soil Moisture Sensor. 61 | * [micropython-max31855](https://bitbucket.org/thesheep/micropython-max31855) - Thermocouple amplifier, SPI interface. 62 | * [max31856](https://github.com/alinbaltaru/max31856) - Precision thermocouple to digital converter with linearization, SPI interface. 63 | * [bme680-mqtt-micropython](https://github.com/robmarkcole/bme680-mqtt-micropython) - Driver for BME680 gas, pressure, temperature and humidity sensor. 64 | * [LM75-MicroPython](https://github.com/OldhamMade/LM75-MicroPython) - Driver for LM75 digital temperature sensor, I2C interface. 65 | * [micropython-am2320](https://github.com/mcauser/micropython-am2320) - Aosong AM2320 temperature and humidity sensor, I2C interface. 66 | * [micropython-dht12](https://github.com/mcauser/micropython-dht12) - Aosong DHT12 temperature and humidity sensor, I2C interface. 67 | * [micropython-hdc1008](https://github.com/kfricke/micropython-hdc1008) - Driver for the Texas Instruments HDC1008 humidity and temperature sensor. 68 | * [micropython-mcp9808](https://github.com/kfricke/micropython-mcp9808) - Driver for the Microchip MCP9808 temperature sensor. 69 | * [micropython-mpl115a2](https://github.com/khoulihan/micropython-mpl115a2) - Pyboard driver for the MPL115A2 barometric pressure sensor. 70 | * [micropython-sht30](https://github.com/rsc1975/micropython-sht30) - Driver for SHT30 temperature and humidity sensor. 71 | * [micropython-sht31](https://github.com/kfricke/micropython-sht31) - Driver for the SHT31 temperature and humidity sensor. 72 | * [micropython-Si7005](https://github.com/Smrtokvitek/micropython-Si7005) - Driver for Si7005 relative humidity and temperature sensor. 73 | * [micropython-si7021](https://bitbucket.org/thesheep/micropython-si7021) - SI7021 Temperature and humidity sensor, I2C interface. 74 | * [micropython-si7021](https://github.com/chrisbalmer/micropython-si7021) - SI7021 Temperature and humidity sensor, I2C interface. 75 | * [micropython-Si705x](https://github.com/billyrayvalentine/micropython-Si705x) - Silicon Labs Si705x series of temperature sensors, I2C interface. 76 | * [micropython-Si70xx](https://github.com/billyrayvalentine/micropython-Si70xx) - Silicon Labs Si70xx series of relative humidity and temperature sensors, I2C interface. 77 | * [micropython-tmp102](https://github.com/khoulihan/micropython-tmp102) - Driver for TMP102 digital temperature sensor. 78 | * [SHT10_uPython](https://github.com/Omgitskillah/SHT10_uPython) - Driver for SHT10 temperature and humidity sensor. 79 | * [sht25-micropython](https://github.com/Miceuz/sht25-micropython) - Driver for SHT25 temperature and humidity sensor. 80 | * [micropython-mlx90614](https://github.com/mcauser/micropython-mlx90614) - Driver for Melexis MLX90614 IR temperature sensor. 81 | * [micropython-mpr121](https://github.com/mcauser/micropython-mpr121) - Driver for MPR121 capacitive touch keypads and breakout boards. 82 | * [micropython-ttp223](https://github.com/mcauser/micropython-ttp223) - Examples using TTP223 capacitive touch module. 83 | * [XPT2046-touch-pad-driver-for-PyBoard](https://github.com/robert-hh/XPT2046-touch-pad-driver-for-PyBoard) - Driver for XPT2046 touch pad controller used in many TFT modules. 84 | * [micropython-nunchuck](https://github.com/kfricke/micropython-nunchuck) - Driver for Nunchuk game controller, I2C interface. 85 | 86 | ### 输出类 87 | 88 | * [micropython-adafruit-pca9685](https://github.com/adafruit/micropython-adafruit-pca9685) - 16-channel 12-bit PWM/servo driver. 89 | * [micropython-pca9685](https://bitbucket.org/thesheep/micropython-pca9685) - 16-channel 12-bit PWM/servo driver. 90 | * [L298N](https://github.com/GuyCarver/MicroPython/blob/master/lib/L298N.py) - Driver for the L298N dual h-bridge motor controller. 91 | * [micropython-upybbot](https://github.com/jeffmer/micropython-upybbot) - A4988 driver for bipolar stepper motors. 92 | * [JQ6500](https://github.com/rdagger/micropython-jq6500) - JQ6500 UART MP3 模块的驱动 93 | * [KT403A-MP3](https://github.com/jczic/KT403A-MP3) - Driver for KT403A, used by DFPlayer Mini and Grove MP3 v2.0. 94 | * [micropython-buzzer](https://github.com/fruch/micropython-buzzer) - Play nokia compose and mid files on buzzers. 95 | * [micropython-dfplayer](https://github.com/ShrimpingIt/micropython-dfplayer) - Driver for DFPlayer Mini using UART. 96 | * [micropython-longwave](https://github.com/MattMatic/micropython-longwave) - WAV player for MicroPython board. 97 | 98 | 99 | ### 显示类 100 | 101 | 102 | * LCD 103 | 104 | * [micropython-epaper](https://github.com/peterhinch/micropython-epaper) - Pyboard driver for Embedded Artists 2.7 inch e-paper display. 105 | * [micropython-ili9341](https://bitbucket.org/thesheep/micropython-ili9341) - SSD1606 active matrix epaper display 128x180. 106 | * [micropython-waveshare-epaper](https://github.com/mcauser/micropython-waveshare-epaper) - Drivers for various Waveshare e-paper modules. 107 | 108 | * [Grove_RGB_LCD](https://github.com/dda/MicroPython/blob/master/Grove_RGB_LCD.py) - Driver for SeeedStudio's Grove RGB LCD. 109 | * [lcdi2c](https://github.com/slothyrulez/lcdi2c) - Driver for HD44780 compatible dot matrix LCDs. 110 | * [micropython-charlcd](https://github.com/rdagger/micropython-charlcd) - Driver for HD44780 compatible LCDs. 111 | * [micropython-i2c-lcd](https://github.com/Bucknalla/micropython-i2c-lcd) - Driver for I2C 2x16 LCD Screens. 112 | * [micropython_grove_rgb_lcd_driver](https://github.com/KidVizious/micropython_grove_rgb_lcd_driver) - Driver for SeeedStudio's Grove RGB LCD. 113 | * [pyboard-LCD-character-display](https://github.com/scitoast/pyboard-LCD-character-display) - PyBoard driver for HDD44780 compatible 1602 LCDs. 114 | * [python_lcd](https://github.com/dhylands/python_lcd) - Driver for HD44780 compatible dot matrix LCDs. 115 | 116 | * [micropython-lcd-AQM1248A](https://github.com/forester3/micropython-lcd-AQM1248A) - ESP8266 driver for AQM1248A graphic LCD. 117 | * [micropython-lcd160cr-gui](https://github.com/peterhinch/micropython-lcd160cr-gui) - Simple touch driven event based GUI for the Pyboard and LCD160CR colour display. 118 | * [micropython-pcd8544](https://github.com/mcauser/micropython-pcd8544) - Driver for Nokia 5110 PCD8544 84x48 LCD modules. 119 | * [micropython-st7565](https://github.com/nquest/micropython-st7565) - Driver for ST7565 128x64 LCDs. 120 | * [micropython-st7920](https://github.com/ShrimpingIt/micropython-st7920) - Library for simple graphic primitives on ST7920 128x64 monochrome LCD panel using ESP8266 and SPI. 121 | * [MicroPython_PCD8544](https://github.com/AnthonyKNorman/MicroPython_PCD8544) - ESP8266 driver for Nokia 5110 PCD8544. 122 | * [Official LCD160CR](https://github.com/micropython/micropython/tree/master/drivers/display) - Driver for official MicroPython LCD160CR display with resistive touch sensor. 123 | 124 | 125 | * [micropython-ili9341](https://bitbucket.org/thesheep/micropython-ili9341) - Collection of drivers for TFT displays, ILI9341, SH1106, SSD1606, ST7735. 126 | * [micropython-ili934x](https://github.com/tuupola/micropython-ili934x) - SPI driver for ILI934X series based TFT / LCD displays. 127 | * [MicroPython-ST7735](https://github.com/boochow/MicroPython-ST7735) - ESP32 version of GuyCarvers's ST7735 TFT LCD driver. 128 | * [micropython-st7735](https://github.com/hosaka/micropython-st7735) - Driver for ST7735 TFT LCDs. 129 | * [MicroPython_ST7735](https://github.com/AnthonyKNorman/MicroPython_ST7735) - Driver for ST7735 128x128 TFT. 130 | * [SSD1963-TFT-Library-for-PyBoard](https://github.com/robert-hh/SSD1963-TFT-Library-for-PyBoard) - Driver for SSD1963 864x480 TFT LCDs. 131 | * [ST7735](https://github.com/GuyCarver/MicroPython/blob/master/lib/ST7735.py) - Driver for ST7735 TFT LCDs. 132 | 133 | 134 | * led matrix 135 | 136 | * [micropython-ht1632c](https://github.com/vrialland/micropython-ht1632c) - Driver for HT1632C 32x16 bicolor led matrix. 137 | * [micropython-matrix8x8](https://github.com/JanBednarik/micropython-matrix8x8) - Driver for AdaFruit 8x8 LED Matrix display with HT16K33 backpack. 138 | * [micropython-max7219](https://github.com/mcauser/micropython-max7219) - Driver for MAX7219 8x8 LED matrix modules. 139 | * [micropython-wemos-led-matrix-shield](https://github.com/mactijn/micropython-wemos-led-matrix-shield) - Driver for Wemos D1 Mini Matrix LED shield, using TM1640 chip. 140 | * [micropython-wemos-led-matrix](https://github.com/mattytrentini/micropython-wemos-led-matrix) - Driver for Wemos D1 Mini Matrix LED shield, using TM1640 chip. 141 | 142 | 143 | * 数码管 144 | * [LKM1638](https://github.com/arikb/LKM1638) - Driver for JY-LKM1638 displays based on TM1638 controller. 145 | * [max7219_8digit](https://github.com/pdwerryhouse/max7219_8digit) - Driver for MAX7219 8-digit 7-segment LED modules. 146 | * [micropython-max7219](https://github.com/JulienBacquart/micropython-max7219) - Driver for MAX7219 8-digit 7-segment LED modules. 147 | * [micropython-my9221](https://github.com/mcauser/micropython-my9221) - Driver for MY9221 10-segment LED bar graph modules. 148 | * [micropython-tm1637](https://github.com/mcauser/micropython-tm1637) - Driver for TM1637 quad 7-segment LED modules. 149 | * [micropython-tm1638](https://github.com/mcauser/micropython-tm1638) - Driver for TM1638 dual quad 7-segment LED modules with switches. 150 | * [micropython-tm1640](https://github.com/mcauser/micropython-tm1640) - Driver for TM1740 8x8 LED matrix modules. 151 | 152 | * 灯带 153 | * [micropython-morsecode](https://github.com/mampersat/micropython-morsecode) - Blink an LED with morse coded message. 154 | * [micropython-p9813](https://github.com/mcauser/micropython-p9813) - Driver for P9813 RGB LED used in SeeedStudio's Grove Chainable RGB LED. 155 | * [micropython-ws2812-7seg](https://github.com/HubertD/micropython-ws2812-7seg) - 7-segment display using WS2812 RGB LEDs. 156 | * [micropython-ws2812](https://github.com/JanBednarik/micropython-ws2812) - Driver for WS2812 RGB LEDs. 157 | * [Official APA102](http://docs.micropython.org/en/latest/esp8266/quickref.html#apa102-driver) - ESP8266 APA102/DotStar RGB LED driver. 158 | * [Official WS2811](http://docs.micropython.org/en/latest/esp8266/quickref.html#neopixel-driver) - ESP8266 WS2811/NeoPixel RGB LED driver. 159 | * [tlc5940-micropython](https://github.com/oysols/tlc5940-micropython) - Driver for TLC5940 16 channel LED driver. 160 | * [ledstrip](https://github.com/labplus-cn/awesome-mpython/tree/master/library/ledstrip) - micropython neopixel module的增强版. 161 | 162 | * OLED 163 | * [Grove_OLED](https://github.com/dda/MicroPython/blob/master/Grove_OLED.py) - Driver for SSD1327 used by SeeedStudio's Grove OLED Display 1.12" v1.0. 164 | * [micropython-oled](https://bitbucket.org/thesheep/micropython-oled) - Collection of drivers for monochrome OLED displays, PCD8544, SH1106, SSD1306, UC1701X. 165 | * [micropython-ssd1327](https://github.com/mcauser/micropython-ssd1327) - Driver for SSD1327 128x128 4-bit greyscale OLED displays. 166 | * [micropython-ssd1351](https://github.com/rdagger/micropython-ssd1351) - Driver for SSD1351 OLED displays. 167 | * [MicroPython_SSD1306](https://github.com/AnthonyKNorman/MicroPython_SSD1306) - ESP8266 driver for SSD1306 OLED 128x64 displays. 168 | * [Official SSD1306](https://github.com/micropython/micropython/tree/master/drivers/display) - Driver for SSD1306 128x64 OLED displays. 169 | * [SH1106](https://github.com/robert-hh/SH1106) - 170 | 171 | ### 通讯类 172 | 173 | 174 | * [PyBoard-HC05-Android](https://github.com/KipCrossing/PyBoard-HC05-Android) - Pyboard HC05 Bluetooth adaptor example application. 175 | * [Official wiznet5k](https://github.com/micropython/micropython/tree/master/drivers/wiznet5k) - Official driver for the WIZnet5x00 series of Ethernet controllers. 176 | * [micropyGPS](https://github.com/inmcm/micropyGPS) - Full featured GPS NMEA sentence parser. 177 | * [micropython-gnssl76l](https://github.com/tuupola/micropython-gnssl76l) - MicroPython I2C driver for Quectel GNSS L76-L (GPS). 178 |
179 |
180 | * 红外 181 | * [micropython-necir](https://github.com/MattMatic/micropython-necir) - NEC infrared capture for TL1838 IR receiver LEDs. 182 | * [Micropython-IR](https://github.com/designerPing/Micropython-IR) - Pyboard infrared remote sniff and replay. 183 | * [IR Remote](https://github.com/labplus-cn/awesome-mpython/tree/master/library/ir_remote) - 红外编解码(NEC)的驱动 184 |
185 |
186 | 187 | * [Official OneWire](https://github.com/micropython/micropython/tree/master/drivers/onewire) - For devices using the OneWire bus, eg Dallas ds18x20. 188 | 189 | 190 | * [micropython-radio](https://github.com/peterhinch/micropython-radio) - Protocols for nRF24L01 2.4Ghz radio modules. 191 | * [micropython-rfsocket](https://github.com/wuub/micropython-rfsocket) - Micropython implementation of popular 433MHzn based RFSockets. 192 | * [Official nRF24L01](https://github.com/micropython/micropython/tree/master/drivers/nrf24l01) - Official driver for nRF24L01 2.4Ghz radio modules. 193 | 194 | * [micropython-mfrc522](https://github.com/wendlers/micropython-mfrc522) - Driver for NXP MFRC522 RFID reader/writer. 195 | * [micropython-wiegand](https://github.com/pjz/micropython-wiegand) - Wiegand protocol reader. 196 | 197 | * [micropython-tinyrtc-i2c](https://github.com/mcauser/micropython-tinyrtc-i2c) - Driver for DS1307 RTC and AT24C32N EEPROM. 198 | * [Micropython_TinyRTC](https://github.com/AnthonyKNorman/Micropython_TinyRTC) - Driver for DS1307 RTC. 199 | 200 | * [HueBridge](https://github.com/FRC4564/HueBridge) - Philips Hue Bridge. 201 | 202 | ### 功能类 203 | 204 | 205 | * [ads1x15](https://github.com/robert-hh/ads1x15) - Driver for the ADS1015/ADS1115 ADC, I2C interface. 206 | * [micropython-ads1015](https://bitbucket.org/thesheep/micropython-ads1015) - ADS1015 12-Bit and ADS1115 16-bit ADC, 4 channels with programmable gain, I2C interface. 207 | * [Micropython_ADS1115](https://github.com/AnthonyKNorman/Micropython_ADS1115) - ADS1115 16-bit ADC, 4 channels with programmable gain, I2C interface. 208 | * [micropython-mcp4725](https://github.com/wayoda/micropython-mcp4725) - Driver for the MCP4725 I2C DAC. 209 | * [MCP23017-ESP8266-Miniature-Driver](https://github.com/forkachild/MCP23017-ESP8266-Miniature-Driver) - Driver for MCP23017 16-bit I/O Expander. 210 | * [micropython-mcp230xx](https://github.com/ShrimpingIt/micropython-mcp230xx) - Driver for MCP23017 and MCP23008 GPIO expanders. 211 | * [Micropython-AD9833](https://github.com/KipCrossing/Micropython-AD9833) - Pyboard driver for AD9833, spi interface. 212 | * [microbit](https://github.com/labplus-cn/awesome-mpython/tree/master/library/microbit) - 掌控板兼容MicroBit 213 | * [ivPID](https://github.com/labplus-cn/awesome-mpython/tree/master/library/ivPID) - PID控制器 214 | 215 | 216 | 217 | 218 | ### 网络类 219 | 220 | * [urllib.parse](https://github.com/labplus-cn/awesome-mpython/tree/master/library/urllib) - URL编码 (MicroPython-lib@urllib.parse精简版) 221 | * [bigiot](https://github.com/labplus-cn/awesome-mpython/tree/master/library/bigiot) - 贝壳物联 222 | * [yeelight](https://github.com/labplus-cn/awesome-mpython/tree/master/library/yeelight) - YeeLight 223 | * [qqai](https://github.com/labplus-cn/awesome-mpython/tree/master/library/qqai) - [腾讯AI开放平台](https://ai.qq.com/) 224 | 225 | 226 | ------------------------------ 227 | 228 | ## 软件 229 | 230 | ## 社区 231 | 232 | ## 贡献 233 | 234 | 始终欢迎您提供意见和建议!请提出请求以修改Awesome mPython。 235 | 236 | - 维护者:[tangliufeng](https://github.com/tangliufeng) 237 | - 共享者: 238 | 239 | 240 | ## 许可证 241 | 由[tangliufeng@LabPlus](https://github.com/tangliufeng)编译和维护。 -------------------------------------------------------------------------------- /docs/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/docs/images/favicon.ico -------------------------------------------------------------------------------- /docs/images/hcsr04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/docs/images/hcsr04.jpg -------------------------------------------------------------------------------- /docs/images/hcsr04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/docs/images/hcsr04.png -------------------------------------------------------------------------------- /docs/images/micropython.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/docs/images/micropython.png -------------------------------------------------------------------------------- /docs/images/mpython_ico_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/docs/images/mpython_ico_en.png -------------------------------------------------------------------------------- /docs/images/parrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/docs/images/parrot.png -------------------------------------------------------------------------------- /docs/images/掌控-立1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/docs/images/掌控-立1.png -------------------------------------------------------------------------------- /docs/images/掌控-立2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/docs/images/掌控-立2.png -------------------------------------------------------------------------------- /docs/images/透视正面.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/docs/images/透视正面.png -------------------------------------------------------------------------------- /docs/images/透视背面.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/docs/images/透视背面.png -------------------------------------------------------------------------------- /library/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Library Management 3 | 4 | 库文件应建立独立文件夹存放,需要说明使用方法,可添加`README`加以说明! 5 | 6 | ## License 7 | 8 | 所有代码均在MIT许可下发布。 -------------------------------------------------------------------------------- /library/bigiot/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | *.egg-info/ -------------------------------------------------------------------------------- /library/bigiot/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 labplus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /library/bigiot/MANIFEST: -------------------------------------------------------------------------------- 1 | # file GENERATED by distutils, do NOT edit 2 | README.md 3 | setup.cfg 4 | setup.py 5 | -------------------------------------------------------------------------------- /library/bigiot/MANIFEST.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/library/bigiot/MANIFEST.in -------------------------------------------------------------------------------- /library/bigiot/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 概述 4 | 5 | 贝壳物联是一个让你与智能设备沟通更方便的物联网云平台,你可以通过互联网以对话、遥控器等形式与你的智能设备聊天、发送指令,查看实时数据,跟实际需求设置报警条件,通过APP、邮件、短信、微博、微信等方式通知用户。 6 | 7 | repo 提供掌控板或micropython连接贝壳物联平台功能,你可以设备与设备间的通讯、上传传感器数据、或者通过web端或微信发送指令给设备。也可以通过智能语音助手(天猫),控制设备。实现语音控制。 8 | 9 | - mPython library GitHub:https://github.com/labplus-cn/mPython-lib 10 | - mPython Library Documentation:https://mpython-lib.readthedocs.io 11 | 12 | ## 库的安装方法 13 | 14 | 可通过以下任一方法进行安装。 15 | 1. 将项目中的`bigiot.py` 16 | 2. 在掌控板REPL界面中,使用upip安装,步骤如下: 17 | * 前置条件需要掌控板连接网络 18 | * 导入upip模块,执行`import upip` 19 | * 执行`upip.install('mPython-bigiot') 20 | 21 | ```python 22 | >>> import upip 23 | >>> upip.install('mPython-bigiot') 24 | ``` 25 | 26 | 27 | ## 执照 28 | 29 | 所有代码均在MIT许可下发布。 -------------------------------------------------------------------------------- /library/bigiot/bigiot.py: -------------------------------------------------------------------------------- 1 | """ 2 | 贝壳物联是一个让你与智能设备沟通更方便的物联网云平台,你可以通过互联网以对话、遥控器等形式与你的智能设备聊天、发送指令,查看实时数据, 3 | 跟实际需求设置报警条件,通过APP、邮件、短信、微博、微信等方式通知用户。 4 | 5 | | 在使用前,需要先到贝壳物联注册账号,并增加设备 https://www.bigiot.net 6 | | 贝壳物联平台通讯协议:https://www.bigiot.net/help/1.html 7 | """ 8 | """ 9 | The MIT License (MIT) 10 | 11 | Copyright (c) 2019, labplus@Tangliufeng 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in 21 | all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | THE SOFTWARE. 30 | """ 31 | import _thread 32 | import socket 33 | import json 34 | import time 35 | 36 | Server_IP = "www.bigiot.net" 37 | Server_Port = 8282 38 | 39 | 40 | class Device: 41 | """ 42 | 构建bigiot设备 43 | 44 | :param id(int): 智能设备ID号,类型为整形 45 | :param api_key(str): 智能设备APIKEY,类型为字符串 46 | """ 47 | 48 | def __init__(self, id, api_key): 49 | self.ID = str(id) 50 | self.K = api_key 51 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 52 | self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 53 | self.socket.setblocking(True) 54 | self.socket.connect((Server_IP, Server_Port)) 55 | _thread.start_new_thread(self._reciver_loop, ()) 56 | self.say_cb = None 57 | self.checkinOK = False 58 | self._send_return = None 59 | self.check_out() 60 | for i in range(3): 61 | self.check_in() 62 | if self.checkinOK: 63 | break 64 | time.sleep(1) 65 | 66 | # Send data, and wait for a return 67 | 68 | def _socket_send(self, str): 69 | self._send_return = None 70 | try: 71 | self.socket.send(str) 72 | # print("Send: %s" % str) 73 | except OSError as e: 74 | if e.args[0] == 104: 75 | self.__init__(self.ID, self.K) 76 | self.socket.send(str) 77 | else: 78 | raise e 79 | 80 | def say_callback(self, f): 81 | """ 82 | 接收设备通讯的回调函数 83 | 84 | :param f(function): 回调函数,f(msg,id,name)。``msg`` 参数为接收消息, ``id`` 参数为发送设备ID, ``name`` 参数为设备名称。 85 | """ 86 | self.say_cb = f 87 | 88 | # Sockets receive data 89 | 90 | def _reciver_loop(self): 91 | 92 | while True: 93 | 94 | res = self.socket.recv(512).decode() 95 | 96 | if len(res) == 0: 97 | continue 98 | dict_res = json.loads(res) 99 | # 应答服务器的心跳包 100 | if dict_res["M"] == 'b': 101 | self.socket.send('{"M":"beat"}\n') 102 | continue 103 | # 服务器连接成功 104 | elif dict_res["M"] == "WELCOME TO BIGIOT": 105 | print("WELCOME TO BIGIOT !") 106 | continue 107 | 108 | self.respon = dict_res 109 | # print(self.respon) 110 | 111 | method = dict_res["M"] 112 | 113 | # check login 114 | if method == "checkinok": 115 | self.checkinOK = True 116 | 117 | if method == "isOL": 118 | self._send_return = dict_res["R"] 119 | 120 | if method == "checked" or method == "connected": 121 | self._send_return = dict_res["M"] 122 | 123 | if method == "time": 124 | self._send_return = dict_res["T"] 125 | 126 | # say 指令 127 | if method == "say" and dict_res["C"] is not None: 128 | msg = (dict_res["C"], dict_res["ID"], dict_res["NAME"]) 129 | self.say_cb(msg) 130 | 131 | self.isRecv = True 132 | 133 | def check_in(self): 134 | """ 135 | 设备登录 136 | """ 137 | obj = {"M": "checkin", "ID": self.ID, "K": self.K} 138 | obj = json.dumps(obj) + "\n" 139 | self._socket_send(obj) 140 | 141 | def check_out(self): 142 | """ 143 | 设备下线 144 | """ 145 | obj = {"M": "checkout", "ID": self.ID, "K": self.K} 146 | obj = json.dumps(obj) + "\n" 147 | self._socket_send(obj) 148 | 149 | def say(self, user_id=None, group_id=None, device_id=None, msg=None): 150 | """ 151 | 设备间的通讯 152 | 153 | :param user_id(int): 用户ID。如你的用户ID为U5600,则user_id=5600。可用于web、微信、app平台间通讯。 154 | :param group_id(int): 群组ID。你可以在平台设置多个设备为一个群组,编译相互通讯。 155 | :param device_id(int): 设备ID。 156 | :param msg(str):发送消息。 157 | """ 158 | while True: 159 | if user_id is not None: 160 | ID = "U" + str(user_id) 161 | break 162 | if group_id is not None: 163 | ID = "G" + str(group_id) 164 | break 165 | if device_id is not None: 166 | ID = "D" + str(device_id) 167 | break 168 | 169 | obj = {"M": "say", "ID": ID, "C": msg} 170 | obj = json.dumps(obj) + "\n" 171 | self._socket_send(obj) 172 | 173 | # Submit data to the data interface 174 | 175 | def update(self, id, data): 176 | """ 177 | 向接口发送数据。先在平台新增并设置接口。 178 | 179 | :param id(int): 接口ID,类型为整形 180 | :param data(str): 发送数据,类型为字符串,一般用于上传传感器数据。 181 | """ 182 | dict_data = {} 183 | dict_data[str(id)] = data 184 | obj = {"M": "update", "ID": self.ID, "V": dict_data} 185 | obj = json.dumps(obj) + "\n" 186 | self._socket_send(obj) 187 | 188 | # Check online 189 | 190 | def is_online(self, device_id): 191 | """ 192 | 查询设备是否在线 193 | 194 | :param device_id(int): 你要查询的设备ID 195 | :return: 返回设备状态。0代表不在线,1代表在线 196 | """ 197 | self.isRecv = False 198 | obj = {"M": "isOL", "ID": "D" + str(device_id)} 199 | obj = json.dumps(obj) + "\n" 200 | start_time = time.time() 201 | self._socket_send(obj) 202 | while not self.isRecv: 203 | if time.time() - start_time > 2: 204 | return None 205 | return int(self._send_return["D" + str(device_id)]) 206 | 207 | def status(self): 208 | """ 209 | 查询当前设备状态,两次查询间隔不得小于10s 210 | 211 | :return: connected代表已连接服务器尚未登录,checked代表已连接且登录成功 212 | """ 213 | self.isRecv = False 214 | obj = {"M": "status"} 215 | obj = json.dumps(obj) + "\n" 216 | self._socket_send(obj) 217 | start_time = time.time() 218 | while not self.isRecv: 219 | if time.time() - start_time > 1: 220 | return None 221 | return self._send_return 222 | 223 | def time(self, format="stamp"): 224 | """ 225 | 查询服务器时间 226 | 227 | :param format(str): stamp(1466659300)、"Y-m-d"(2016-06-21)、Y.m.d(2016.06.21)、Y-m-d H:i:s(2016-06-21 10:25:30) 228 | :return: 返回时间 229 | """ 230 | self.isRecv = False 231 | obj = {"M": "time", "F": format} 232 | obj = json.dumps(obj) + "\n" 233 | self._socket_send(obj) 234 | start_time = time.time() 235 | while not self.isRecv: 236 | if time.time() - start_time > 1: 237 | raise bigiotException("Bigiot Server no response ") 238 | return self._send_return 239 | 240 | 241 | # #Send an alert 242 | # def alert(self, C, B): 243 | # obj = {"M": "alert", "C": C, "B": B} 244 | # obj = json.dumps(obj)+"\n" 245 | # self.socket.send(obj) 246 | 247 | 248 | -------------------------------------------------------------------------------- /library/bigiot/examples/bigiot_example.py: -------------------------------------------------------------------------------- 1 | from mpython import * 2 | from bigiot import Device 3 | 4 | mywifi = wifi() # 连接wifi 5 | mywifi.connectWiFi('', '') 6 | 7 | 8 | 9 | ID = "" # 设备ID 10 | API_KEY = "" # 设备APIKEY 11 | 12 | def say_cb(msg): # 回调函数 13 | print(msg) 14 | 15 | device = Device(ID, API_KEY) # 构建bigiot 设备 16 | 17 | device.say_callback(say_cb) # 设置say通讯的回调函数 18 | 19 | device.check_in() # 登录 20 | 21 | device.say(user_id=0000,msg="hello I am mpython!") # 向web,app端发送消息 22 | -------------------------------------------------------------------------------- /library/bigiot/optimize_upip.py: -------------------------------------------------------------------------------- 1 | # 2 | # This script optimizes a Python source distribution tarball as produced by 3 | # "python3 setup.py sdist" command for MicroPython's native package manager, 4 | # upip. Optimization includes: 5 | # * Removing metadata files not used by upip (this includes setup.py) 6 | # * Recompressing gzip archive with 4K dictionary size so it can be 7 | # installed even on low-heap targets. 8 | # 9 | import sys 10 | import os 11 | import zlib 12 | from subprocess import Popen, PIPE 13 | import glob 14 | import tarfile 15 | import re 16 | import io 17 | 18 | 19 | def gzip_4k(inf, fname): 20 | comp = zlib.compressobj(level=9, wbits=16 + 12) 21 | with open(fname + ".out", "wb") as outf: 22 | while 1: 23 | data = inf.read(1024) 24 | if not data: 25 | break 26 | outf.write(comp.compress(data)) 27 | outf.write(comp.flush()) 28 | os.rename(fname, fname + ".orig") 29 | os.rename(fname + ".out", fname) 30 | 31 | 32 | def recompress(fname): 33 | with Popen(["gzip", "-d", "-c", fname], stdout=PIPE).stdout as inf: 34 | gzip_4k(inf, fname) 35 | 36 | def find_latest(dir): 37 | res = [] 38 | for fname in glob.glob(dir + "/*.gz"): 39 | st = os.stat(fname) 40 | res.append((st.st_mtime, fname)) 41 | res.sort() 42 | latest = res[-1][1] 43 | return latest 44 | 45 | 46 | def recompress_latest(dir): 47 | latest = find_latest(dir) 48 | print(latest) 49 | recompress(latest) 50 | 51 | 52 | FILTERS = [ 53 | # include, exclude, repeat 54 | (r".+\.egg-info/(PKG-INFO|requires\.txt)", r"setup.py$"), 55 | (r".+\.py$", r"[^/]+$"), 56 | (None, r".+\.egg-info/.+"), 57 | ] 58 | 59 | 60 | outbuf = io.BytesIO() 61 | 62 | def filter_tar(name): 63 | fin = tarfile.open(name, "r:gz") 64 | fout = tarfile.open(fileobj=outbuf, mode="w") 65 | for info in fin: 66 | # print(info) 67 | if not "/" in info.name: 68 | continue 69 | fname = info.name.split("/", 1)[1] 70 | include = None 71 | 72 | for inc_re, exc_re in FILTERS: 73 | if include is None and inc_re: 74 | if re.match(inc_re, fname): 75 | include = True 76 | 77 | if include is None and exc_re: 78 | if re.match(exc_re, fname): 79 | include = False 80 | 81 | if include is None: 82 | include = True 83 | 84 | if include: 85 | print("Including:", fname) 86 | else: 87 | print("Excluding:", fname) 88 | continue 89 | 90 | farch = fin.extractfile(info) 91 | fout.addfile(info, farch) 92 | fout.close() 93 | fin.close() 94 | 95 | 96 | 97 | from setuptools import Command 98 | 99 | class OptimizeUpip(Command): 100 | 101 | user_options = [] 102 | 103 | def run(self): 104 | latest = find_latest("dist") 105 | filter_tar(latest) 106 | outbuf.seek(0) 107 | gzip_4k(outbuf, latest) 108 | 109 | def initialize_options(self): 110 | pass 111 | 112 | def finalize_options(self): 113 | pass 114 | 115 | 116 | # For testing only 117 | if __name__ == "__main__": 118 | # recompress_latest(sys.argv[1]) 119 | filter_tar(sys.argv[1]) 120 | outbuf.seek(0) 121 | gzip_4k(outbuf, sys.argv[1]) 122 | -------------------------------------------------------------------------------- /library/bigiot/sdist_upip.py: -------------------------------------------------------------------------------- 1 | # 2 | # This module overrides distutils (also compatible with setuptools) "sdist" 3 | # command to perform pre- and post-processing as required for MicroPython's 4 | # upip package manager. 5 | # 6 | # Preprocessing steps: 7 | # * Creation of Python resource module (R.py) from each top-level package's 8 | # resources. 9 | # Postprocessing steps: 10 | # * Removing metadata files not used by upip (this includes setup.py) 11 | # * Recompressing gzip archive with 4K dictionary size so it can be 12 | # installed even on low-heap targets. 13 | # 14 | import sys 15 | import os 16 | import zlib 17 | from subprocess import Popen, PIPE 18 | import glob 19 | import tarfile 20 | import re 21 | import io 22 | 23 | from distutils.filelist import FileList 24 | from setuptools.command.sdist import sdist as _sdist 25 | 26 | 27 | def gzip_4k(inf, fname): 28 | comp = zlib.compressobj(level=9, wbits=16 + 12) 29 | with open(fname + ".out", "wb") as outf: 30 | while 1: 31 | data = inf.read(1024) 32 | if not data: 33 | break 34 | outf.write(comp.compress(data)) 35 | outf.write(comp.flush()) 36 | os.rename(fname, fname + ".orig") 37 | os.rename(fname + ".out", fname) 38 | 39 | 40 | FILTERS = [ 41 | # include, exclude, repeat 42 | (r".+\.egg-info/(PKG-INFO|requires\.txt)", r"setup.py$"), 43 | (r".+\.py$", r"[^/]+$"), 44 | (None, r".+\.egg-info/.+"), 45 | ] 46 | 47 | 48 | outbuf = io.BytesIO() 49 | 50 | def filter_tar(name): 51 | fin = tarfile.open(name, "r:gz") 52 | fout = tarfile.open(fileobj=outbuf, mode="w") 53 | for info in fin: 54 | # print(info) 55 | if not "/" in info.name: 56 | continue 57 | fname = info.name.split("/", 1)[1] 58 | include = None 59 | 60 | for inc_re, exc_re in FILTERS: 61 | if include is None and inc_re: 62 | if re.match(inc_re, fname): 63 | include = True 64 | 65 | if include is None and exc_re: 66 | if re.match(exc_re, fname): 67 | include = False 68 | 69 | if include is None: 70 | include = True 71 | 72 | if include: 73 | print("including:", fname) 74 | else: 75 | print("excluding:", fname) 76 | continue 77 | 78 | farch = fin.extractfile(info) 79 | fout.addfile(info, farch) 80 | fout.close() 81 | fin.close() 82 | 83 | 84 | def make_resource_module(manifest_files): 85 | resources = [] 86 | # Any non-python file included in manifest is resource 87 | for fname in manifest_files: 88 | ext = fname.rsplit(".", 1)[1] 89 | if ext != "py": 90 | resources.append(fname) 91 | 92 | if resources: 93 | print("creating resource module R.py") 94 | resources.sort() 95 | last_pkg = None 96 | r_file = None 97 | for fname in resources: 98 | try: 99 | pkg, res_name = fname.split("/", 1) 100 | except ValueError: 101 | print("not treating %s as a resource" % fname) 102 | continue 103 | if last_pkg != pkg: 104 | last_pkg = pkg 105 | if r_file: 106 | r_file.write("}\n") 107 | r_file.close() 108 | r_file = open(pkg + "/R.py", "w") 109 | r_file.write("R = {\n") 110 | 111 | with open(fname, "rb") as f: 112 | r_file.write("%r: %r,\n" % (res_name, f.read())) 113 | 114 | if r_file: 115 | r_file.write("}\n") 116 | r_file.close() 117 | 118 | 119 | class sdist(_sdist): 120 | 121 | def run(self): 122 | self.filelist = FileList() 123 | self.get_file_list() 124 | make_resource_module(self.filelist.files) 125 | 126 | r = super().run() 127 | 128 | assert len(self.archive_files) == 1 129 | print("filtering files and recompressing with 4K dictionary") 130 | filter_tar(self.archive_files[0]) 131 | outbuf.seek(0) 132 | gzip_4k(outbuf, self.archive_files[0]) 133 | 134 | return r 135 | 136 | 137 | # For testing only 138 | if __name__ == "__main__": 139 | filter_tar(sys.argv[1]) 140 | outbuf.seek(0) 141 | gzip_4k(outbuf, sys.argv[1]) 142 | -------------------------------------------------------------------------------- /library/bigiot/setup.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/library/bigiot/setup.cfg -------------------------------------------------------------------------------- /library/bigiot/setup.py: -------------------------------------------------------------------------------- 1 | # import sys 2 | # sys.path.pop(0) 3 | # sys.path.append("..") 4 | from setuptools import setup 5 | import sdist_upip 6 | from os import path 7 | 8 | # read the contents of your README file 9 | from os import path 10 | this_directory = path.abspath(path.dirname(__file__)) 11 | with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f: 12 | long_description = f.read() 13 | 14 | VERSION= '0.0.3' 15 | 16 | setup(name='mPython-bigiot', 17 | version=VERSION, 18 | description='bigiot for mPython library', 19 | long_description=long_description, 20 | long_description_content_type="text/markdown", 21 | url='https://github.com/labplus-cn/mPython-lib', 22 | author='tangliufeng@LabPlus', 23 | author_email='137513285@qq.com', 24 | maintainer='LabPlus Developers', 25 | license='MIT', 26 | cmdclass={'sdist': sdist_upip.sdist}, 27 | py_modules=['bigiot'], 28 | # packages=['email'], 29 | # install_requires=[''] 30 | ) 31 | 32 | -------------------------------------------------------------------------------- /library/ir_remote/ir_remote.py: -------------------------------------------------------------------------------- 1 | 2 | # The MIT License (MIT) 3 | 4 | # Copyright (c) 2019, Tangliufeng for labplus Industries 5 | 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | 24 | # NEC infrared receiving and sending library of the mPython or MicropPython, please note that sending and receiving function cannot be used at the same time. 25 | # Both have an effect,only choose one. 26 | # 27 | ''' 28 | # IR Receive example: 29 | from ir_remote import IRDecode 30 | from machine import Pin 31 | 32 | def remote_callback(address,command): 33 | print(address,command) 34 | 35 | remote = IRDecode( Pin.P1) 36 | remote.set_callback(remote_callback) 37 | # enable print debug 38 | # remote.set_debug(True) 39 | ''' 40 | 41 | ''' 42 | # IR send example: 43 | from ir_remote import IRTransmit 44 | from machine import Pin 45 | 46 | ir = IRTransmit(Pin.P0) 47 | 48 | ir.send(0,01) # format (address, command). or ir.send(b'\x00',b'\x01') 49 | 50 | ''' 51 | 52 | 53 | 54 | def remote_callback(address,command): 55 | print(address,command) 56 | from machine import Pin, PWM 57 | import time 58 | import _thread 59 | import gc 60 | 61 | class IRDecodeException(Exception): 62 | """Generic decode exception""" 63 | pass 64 | 65 | 66 | class _Const: 67 | NEC_HDR_MARK = 9000 68 | NEC_HDR_SPACE = 4500 69 | NEC_BIT_MARK = 560 70 | 71 | class IRDecode: 72 | 73 | 74 | def __init__(self, pin): 75 | 76 | self.pulse_buffer = [] 77 | self._prev_time = 0 78 | self.callback = None 79 | self.debug = False 80 | self.waittime = 150 # time in milliseconds 81 | self.recv = Pin(pin, Pin.IN, Pin.PULL_UP) 82 | self.recv.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, 83 | handler=self._pulse_width_record) 84 | _thread.start_new_thread(self._ir_recv_thread, ()) 85 | 86 | def _pulse_width_record(self, pin): 87 | """record the width of th IR remote signal.""" 88 | self._time = time.ticks_us() 89 | if self._prev_time == 0: 90 | self._prev_time = self._time 91 | return 92 | self.pulse_buffer.append(self._time - self._prev_time) 93 | self._prev_time = self._time 94 | 95 | def _lead_cheak(self): 96 | """function to cheak the lead code """ 97 | offset = 0.3 98 | return int(_Const.NEC_HDR_MARK * (1-offset)) < self.pulse_buffer[0] < int(_Const.NEC_HDR_MARK*(1+offset)) \ 99 | and int(_Const.NEC_HDR_SPACE*(1-offset)) < self.pulse_buffer[1] < int(_Const.NEC_HDR_SPACE*(1+offset)) 100 | 101 | def decode_pulse_nec(self): 102 | """decode IR signal of NEC""" 103 | recv_bytes = bytearray(0) 104 | index = 0 105 | if self._lead_cheak(): 106 | for i in range(4): 107 | byte = 0 108 | for j in range(8): 109 | index = 16 * i + 2 * j + 2 110 | _Const.NEC_BIT_MARK = min( 111 | _Const.NEC_BIT_MARK, self.pulse_buffer[index]) 112 | if (self.pulse_buffer[index] + 113 | self.pulse_buffer[index + 1]) > _Const.NEC_BIT_MARK * 4: 114 | byte |= 1 << j 115 | 116 | recv_bytes.append(byte) 117 | return bytes(recv_bytes) 118 | 119 | def _print_debug(self,decode): 120 | """""" 121 | print('---------Received pulse width buffer---------') 122 | print('Lead Code: (%d %d)' % 123 | (self.pulse_buffer[0], self.pulse_buffer[1])) 124 | index = 0 125 | for i in range(4): 126 | print('Byte %d:' % i, end='') 127 | for j in range(8): 128 | index = 16 * i + 2 * j + 2 129 | print('(%d %d)' % (self.pulse_buffer[index], self.pulse_buffer[index + 1]), 130 | end=', ') 131 | print('') 132 | print('Buffer:{} ,length:{}' .format( 133 | decode, len(self.pulse_buffer))) 134 | print('--------------------------------------------') 135 | 136 | def _ir_recv_thread(self): 137 | while True: 138 | if (time.ticks_us()-self._prev_time) > self.waittime * 1000 and self.pulse_buffer != []: 139 | if self._lead_cheak(): 140 | if len(self.pulse_buffer) >= 66: 141 | decode = self.decode_pulse_nec() 142 | if self.debug: 143 | self._print_debug(decode) 144 | self.pulse_buffer = [] 145 | self._prev_time = 0 146 | 147 | if self.callback != None: 148 | self.callback(decode[:1],decode[2:3]) # Address ,Command 149 | else: 150 | print("Warning: Buffer length too short!") 151 | self.pulse_buffer = [] 152 | self._prev_time = 0 153 | 154 | else: 155 | print("Warning: Buffer lead code error!") 156 | self.pulse_buffer = [] 157 | self._prev_time = 0 158 | 159 | gc.collect() 160 | 161 | def set_callback(self, callback=None): 162 | """function to allow the user to set 163 | or change the callback function. farmat,callback(address ,command) """ 164 | self.callback = callback 165 | 166 | def remove_callback(self): 167 | """remove_callback, function to allow the user to remove 168 | the callback function used at any time""" 169 | self.callback = None 170 | 171 | def set_debug(self, debug=True): 172 | """set_debug, function to turn verbose mode 173 | on or off. Used to print out pulse width list 174 | """ 175 | self.debug = debug 176 | 177 | class IRTransmit: 178 | """IR NEC signal transmits""" 179 | def __init__(self,pin): 180 | self.ir_transmit = PWM(Pin(pin), duty=0, freq=38000) 181 | 182 | def _mark(self,us): 183 | self.ir_transmit.duty(512) 184 | time.sleep_us(us) 185 | 186 | def _space(self,us): 187 | self.ir_transmit.duty(0) 188 | time.sleep_us(us) 189 | 190 | def _bit(self, bit): 191 | # print(bit) 192 | self._mark(_Const.NEC_BIT_MARK) 193 | if bit: 194 | self._space(_Const.NEC_BIT_MARK*3) 195 | else: 196 | self._space(_Const.NEC_BIT_MARK) 197 | 198 | def _generate_bits(self,addr,cmd): 199 | if type(addr) == bytes : 200 | addr = int.from_bytes(addr,'little')&0xff 201 | if type(cmd) == bytes : 202 | cmd = int.from_bytes(cmd,'little')&0xff 203 | self._payload =[] 204 | for i in range(4): 205 | self._payload.append([]) 206 | for i in range(8): 207 | if (addr>>i)&0x01: 208 | self._payload[0].append(1) 209 | self._payload[1].append(0) 210 | else: 211 | self._payload[0].append(0) 212 | self._payload[1].append(1) 213 | for i in range(8): 214 | if (cmd>>i)&0x01: 215 | self._payload[2].append(1) 216 | self._payload[3].append(0) 217 | else: 218 | self._payload[2].append(0) 219 | self._payload[3].append(1) 220 | 221 | def send(self,address,command): 222 | """send NEC data""" 223 | self._generate_bits(address,command) 224 | print(self._payload) 225 | self._mark(_Const.NEC_HDR_MARK) 226 | self._space(_Const.NEC_HDR_SPACE) 227 | for i in range(4): 228 | for j in range(8): 229 | self._bit(self._payload[i][j]) 230 | self._mark(560) 231 | self._space(40000) 232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /library/ledstrip/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 labplus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /library/ledstrip/MANIFEST: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/library/ledstrip/MANIFEST -------------------------------------------------------------------------------- /library/ledstrip/README.md: -------------------------------------------------------------------------------- 1 | ledstrip module 是 micropython neopixel module的增强版,在neopixel基础功能上封装多种的灯带显示效果。 2 | 支持掌控板或micropython的使用。 3 | 4 | 5 | ## 库的安装方法 6 | 7 | 可通过以下任一方法进行安装。 8 | 1. 将项目中的`ledstrip.py`拷到掌控板文件系统上 9 | 2. 在掌控板REPL界面中,使用upip安装,步骤如下: 10 | * 前置条件需要掌控板连接网络 11 | * 导入upip模块,执行`import upip` 12 | * 执行`upip.install('mPython-ledstrip') 13 | 14 | ```python 15 | >>> import upip 16 | >>> upip.install('mPython-ledstrip') 17 | ``` 18 | ## 简单示例 19 | 20 | ```python 21 | from ledstrip import * 22 | from machine import Pin 23 | 24 | strip=LedStrip(pin=Pin.P15,n=24,brightness=0.5) 25 | 26 | print("rainbow") 27 | strip.rainbow() 28 | print("rainbow_cycle") 29 | strip.rainbow_cycle() 30 | print("cycle") 31 | strip.cycle((50,50,50)) 32 | print("bounce") 33 | strip.bounce((0,0,50)) 34 | strip.clear() 35 | print("colorWipe") 36 | strip.colorWipe((0,50,0)) 37 | print("theaterChase") 38 | strip.theaterChase((50,0,0)) 39 | print("theaterChaseRainbow") 40 | strip.theaterChaseRainbow(wait=5) 41 | print("cylonBounce") 42 | strip.cylonBounce((0,0,50),4,10,50) 43 | print("runningLight") 44 | strip.runningLight((50,50,0),20) 45 | 46 | print("meteorRain") 47 | for i in range(5): 48 | strip.meteorRain((100,100,100),8,60,True,20) 49 | ``` 50 | 51 | 52 | ## API 说明 53 | 54 | | 函数 | 功能说明 | 参数 | 55 | | :------ | :------ | :------ | 56 | | hsv2rgb(hsv) |将HSV颜色三元组转换为RGB三元组 | ``hsv`` - 三元组 | 57 | | rgb2hsv(rgb) |将RGB颜色三元组转换为HSV三元组 | ``rgb`` - 三元组 | 58 | | wheel(pos) |彩轮,将0~255值转换为RGB三元组 | ``pos`` - 0~255 | 59 | |LedStrip( pin, n=24, brightness=1.0, timing=1) |LedStrip类初始化 | ``pin`` - 引脚; ``n`` - 灯数
``brightness`` - 亮度设置,范围0~1.0;``timing`` -速率,1为800Khz,0为400kHz | 60 | | LedStrip.clear() | 熄灭所有灯,不用write()即可生效| /| 61 | | LedStrip.brightness(brightness) | 设置灯带亮度| ``brightness`` - 0~1.0| 62 | | LedStrip.rainbow(wait_us=20) | 彩虹效果| ``wait_us`` - 等待时间,默认20毫秒| 63 | | LedStrip.rainbow_cycle(wait_us=20) | 彩虹环效果:与rainbow略有不同,彩虹在整个过程中均匀分布| ``wait_us`` - 等待时间,默认20毫秒| 64 | | LedStrip.cycle(c, wait=20) |循环效果:有一个像素在所有灯带位置上运行,而其他像素关闭。 | ``c`` - 显示灯RGB颜色,(r,g,b)三元组; ``wait`` - 等待时间,单位毫秒,默认20| 65 | | LedStrip.bounce(c, wait=20) |弹跳效果:弹跳效应和接受(R,G,B)来设置颜色,以及等待时间。等待时间决定了弹跳效果的速度。 | ``c`` - 显示灯RGB颜色,(r,g,b)三元组;``wait`` - 等待时间,单位毫秒,默认20| 66 | | LedStrip.colorWipe(c, wait=20) |逐个填充颜色 | ``c`` - 填充RGB颜色,(r,g,b)三元组;``wait`` - 等待时间,单位毫秒,默认20| 67 | | LedStrip.theaterChase(c, wait=20) |剧院风格的追逐灯效果 | ``c`` - 填充RGB颜色,(r,g,b)三元组;``wait`` - 等待时间,单位毫秒,默认20| 68 | | LedStrip.theaterChaseRainbow(wait=20) |剧院风格的追逐灯效果 | ``wait`` - 等待时间,单位毫秒,默认20| 69 | | LedStrip.cylonBounce(c, eye_size, spee_delay, return_delay) |Cylon效果:模拟Cylon移动“眼睛”的效果:一个红色的“眼睛”从左到右,一次又一次地向后移动 | ``eye_size`` - 运行的LED数量,或:“眼睛”的宽度(外部2,褪色,LED未计数);``spee_delay`` -影响眼睛移动的速度,较高的值意味着移动缓慢;
``return_delay`` - 设置应该等待反弹的时间| 70 | | LedStrip.runningLight(c,wait) |行走灯效果:多组LED相互追逐。亲切 - 就像你在节日期间用来在商店看到的行车灯一样 | ``c`` - 显示灯RGB颜色,(r,g,b)三元组; ``wait`` - 等待时间,单位毫秒0 | 71 | | LedStrip.meteorRain(c,size,trail_decay,random_decay,delay) |流星雨效果 | ``c`` - 显示灯RGB颜色,(r,g,b)三元组;``size`` - 设置流星大小代表流星的LED数量,不计算流星的尾部;
``trail_decay`` - 流星尾部衰减/消失的速度。数字越大,尾部越短和/或消失得越快。理论上,值为64时,每次流星绘制时亮度都会降低25%; ``delay`` - 延时 | 72 | 73 | ## 执照 74 | 75 | 所有代码均在MIT许可下发布。 -------------------------------------------------------------------------------- /library/ledstrip/examples/ledstrip_simple.py: -------------------------------------------------------------------------------- 1 | from ledstrip import * 2 | from machine import Pin 3 | 4 | strip=LedStrip(pin=Pin.P15,n=24,brightness=0.5) 5 | 6 | print("rainbow") 7 | strip.rainbow() 8 | print("rainbow_cycle") 9 | strip.rainbow_cycle() 10 | print("cycle") 11 | strip.cycle((50,50,50)) 12 | print("bounce") 13 | strip.bounce((0,0,50)) 14 | strip.clear() 15 | print("colorWipe") 16 | strip.colorWipe((0,50,0)) 17 | print("theaterChase") 18 | strip.theaterChase((50,0,0)) 19 | print("theaterChaseRainbow") 20 | strip.theaterChaseRainbow(wait=5) 21 | print("cylonBounce") 22 | strip.cylonBounce((0,0,50),4,10,50) 23 | print("runningLight") 24 | strip.runningLight((50,50,0),20) 25 | 26 | print("meteorRain") 27 | for i in range(5): 28 | strip.meteorRain((100,100,100),8,60,True,20) -------------------------------------------------------------------------------- /library/ledstrip/ledstrip.py: -------------------------------------------------------------------------------- 1 | 2 | from neopixel import * 3 | from machine import Pin 4 | from time import sleep_us, sleep_ms 5 | import math 6 | from random import randint 7 | 8 | def hsv2rgb(hsv): 9 | h = float(hsv[0]) 10 | s = float(hsv[1]) 11 | v = float(hsv[2]) 12 | h60 = h / 60.0 13 | h60f = math.floor(h60) 14 | hi = int(h60f) % 6 15 | f = h60 - h60f 16 | p = v * (1 - s) 17 | q = v * (1 - f * s) 18 | t = v * (1 - (1 - f) * s) 19 | r, g, b = 0, 0, 0 20 | if hi == 0: 21 | r, g, b = v, t, p 22 | elif hi == 1: 23 | r, g, b = q, v, p 24 | elif hi == 2: 25 | r, g, b = p, v, t 26 | elif hi == 3: 27 | r, g, b = p, q, v 28 | elif hi == 4: 29 | r, g, b = t, p, v 30 | elif hi == 5: 31 | r, g, b = v, p, q 32 | r, g, b = int(r * 255), int(g * 255), int(b * 255) 33 | return r, g, b 34 | 35 | 36 | def rgb2hsv(rgb): 37 | r, g, b = rgb[0]/255.0, rgb[1]/255.0, rgb[2]/255.0 38 | mx = max(r, g, b) 39 | mn = min(r, g, b) 40 | df = mx-mn 41 | if mx == mn: 42 | h = 0 43 | elif mx == r: 44 | h = (60 * ((g-b)/df) + 360) % 360 45 | elif mx == g: 46 | h = (60 * ((b-r)/df) + 120) % 360 47 | elif mx == b: 48 | h = (60 * ((r-g)/df) + 240) % 360 49 | if mx == 0: 50 | s = 0 51 | else: 52 | s = df/mx 53 | v = mx 54 | return round(h,2), round(s,2), round(v,2) 55 | 56 | 57 | def wheel(pos): 58 | # Input a value 0 to 255 to get a color value. 59 | # The colours are a transition r - g - b - back to r. 60 | if pos < 0 or pos > 255: 61 | r = g = b = 0 62 | elif pos < 85: 63 | r = int(pos * 3) 64 | g = int(255 - pos*3) 65 | b = 0 66 | elif pos < 170: 67 | pos -= 85 68 | r = int(255 - pos*3) 69 | g = 0 70 | b = int(pos*3) 71 | else: 72 | pos -= 170 73 | r = 0 74 | g = int(pos*3) 75 | b = int(255 - pos*3) 76 | return (r, g, b) 77 | 78 | 79 | class LedStrip(NeoPixel): 80 | 81 | def __init__(self, pin, n=24, brightness=0.5, timing=1): 82 | self._brightness = brightness 83 | useable_pin = [Pin.P5, Pin.P6, Pin.P8, Pin.P9, Pin.P11, 84 | Pin.P13, Pin.P14, Pin.P15, Pin.P16, Pin.P19, Pin.P20] 85 | if pin not in useable_pin: 86 | raise TypeError('neopixel not supported in IO%d' % pin) 87 | super().__init__(Pin(pin), n=n, bpp=3, timing=timing) 88 | 89 | def clear(self): 90 | # 熄灭所有RGB 91 | for i in range(self.n): 92 | self[i] = (0, 0, 0) 93 | self.write() 94 | 95 | def write(self): 96 | if self._brightness > 0.99: 97 | neopixel_write(self.pin, self.buf, self.timing) 98 | else: 99 | neopixel_write(self.pin, bytearray([int(i * self._brightness) for i in self.buf]), self.timing) 100 | 101 | def brightness(self, brightness): 102 | self._brightness = min(max(brightness, 0.0), 1.0) 103 | 104 | def rainbow(self, wait_us=20): 105 | # 彩虹效果 106 | for j in range(255): 107 | 108 | for i in range(self.n): 109 | self[i] = wheel((i+j) & 255) 110 | self.write() 111 | sleep_us(wait_us) 112 | 113 | def rainbow_cycle(self, wait_us=20): 114 | # 与rainbow略有不同,这使得彩虹在整个过程中均匀分布 115 | for j in range(255): 116 | for i in range(self.n): 117 | pixel_index = (i * 256 // self.n) + j 118 | self[i] = wheel(pixel_index & 255) 119 | self.write() 120 | sleep_us(wait_us) 121 | 122 | def cycle(self, c, wait=20): 123 | # 循环效果,有一个像素在所有灯带位置上运行,而其他像素关闭。 124 | for i in range(4 * self.n): 125 | for j in range(self.n): 126 | self[j] = (0, 0, 0) 127 | self[i % self.n] = c 128 | self.write() 129 | sleep_ms(wait) 130 | 131 | def bounce(self, c, wait=20): 132 | # 弹跳效果,等待时间决定了弹跳效果的速度 133 | for i in range(4 * self.n): 134 | for j in range(self.n): 135 | self[j] = c 136 | if (i // self.n) % 2 == 0: 137 | self[i % self.n] = (0, 0, 0) 138 | else: 139 | self[self.n - 1 - (i % self.n)] = (0, 0, 0) 140 | self.write() 141 | sleep_ms(wait) 142 | 143 | def colorWipe(self, c, wait=10): 144 | # 逐个填充颜色 145 | # Fill the dots one after the other with a color 146 | for i in range(self.n): 147 | self[i] = c 148 | sleep_ms(wait) 149 | self.write() 150 | sleep_ms(wait) 151 | 152 | def theaterChase(self, c, wait=50): 153 | # 剧院风格的追逐灯 154 | for j in range(10): 155 | for q in range(3): 156 | for i in range(0, self.n, 3): 157 | self[i+q] = c 158 | self.write() 159 | sleep_ms(wait) 160 | for i in range(0, self.n, 3): 161 | self[i+q] = (0, 0, 0) 162 | 163 | def theaterChaseRainbow(self, wait=50): 164 | # 剧院风格的追逐灯带rainbow效果 165 | for j in range(256): 166 | for q in range(3): 167 | for i in range(0, self.n, 3): 168 | 169 | self[i+q] = wheel((i+j) % 255) 170 | self.write() 171 | sleep_ms(wait) 172 | for i in range(0, self.n, 3): 173 | self[i+q] = (0, 0, 0) 174 | 175 | def cylonBounce(self, c, eye_size, spee_delay, return_delay): 176 | """ 177 | 我想我们并不是所有人都知道Cylon是什么,但我和那些很酷的机器人一起长大,我肯定想拥有一个。我熟悉骑士骑士(尽管那个时代差不多)?它很亲切 - 类似。这种类型的“扫描仪”通常被称为拉尔森扫描仪, 178 | 以Glen Larson的名字命名,Glen Larson是负责制作原始太空堡垒卡拉狄加和骑士骑士电视节目的人。无论如何,这里有一个模拟Cylon移动“眼睛”的效果:一个红色的“眼睛”从左到右,一次又一次地向后移动。 179 | 亲切 - 一个充满弹性的球哈哈。 180 | :param eye_size-确定运行的LED数量,或:“眼睛”的宽度(外部2,褪色,LED未计数) 181 | :param spee_delay-影响眼睛移动的速度,较高的值意味着移动缓慢。 182 | :param return_delay-设置应该等待反弹的时间 183 | """ 184 | for i in range(self.n-eye_size - 2): 185 | self.fill((0, 0, 0)) 186 | self[i] = (int(c[0]/10), int(c[1]/10), int(c[2]/10)) 187 | for j in range(1, eye_size+1): 188 | self[i+j] = c 189 | self[i+eye_size+1] = (int(c[0]/10), int(c[1]/10), int(c[2]/10)) 190 | self.write() 191 | sleep_ms(spee_delay) 192 | 193 | sleep_ms(return_delay) 194 | 195 | for i in range(self.n-eye_size - 2, 0, -1): 196 | self.fill((0, 0, 0)) 197 | self[i] = (int(c[0]/10), int(c[1]/10), int(c[2]/10)) 198 | for j in range(1, eye_size+1): 199 | self[i+j] = c 200 | self[i+eye_size+1] = (int(c[0]/10), int(c[1]/10), int(c[2]/10)) 201 | self.write() 202 | sleep_ms(spee_delay) 203 | 204 | sleep_ms(return_delay) 205 | 206 | def runningLight(self, c, wait): 207 | """ 208 | 这种效应使得多组LED相互追逐。亲切 - 就像你在节日期间用来在商店看到的行车灯一样。 209 | :param wait表示循环中放入了多少延迟,数字越大,它就越慢。 210 | """ 211 | position = 0 212 | for j in range(self.n * 2): 213 | position += 1 214 | for i in range(self.n): 215 | self[i] = (int(((math.sin(i+position) * 127 + 128)/255)*c[0]), 216 | int(((math.sin(i+position) * 127 + 128)/255)*c[1]), 217 | int(((math.sin(i+position) * 127 + 128)/255)*c[2])) 218 | self.write() 219 | sleep_ms(wait) 220 | 221 | def meteorRain(self,c,size,trail_decay,random_decay,delay): 222 | """ 223 | 流星雨效果 224 | :param size - 设置流星大小代表流星的LED数量,不计算流星的尾部 225 | :param trail_decay - 流星尾部衰减/消失的速度。数字越大,尾部越短和/或消失得越快。理论上,值为64时,每次流星绘制时亮度都会降低25% 226 | :param random_decay - 随机衰变-通过使衰变有点随机来模仿碎片中的某些差异。如果此值设置为“true”,则会向轨道添加一些随机性。如果将值设置为“false”,则尾部将非常平滑 227 | :param delay - 延时 228 | 229 | """ 230 | 231 | self.fill((0,0,0)) 232 | def fadeToBlack(ledNo, fadeValue): 233 | 234 | r = self.buf[ledNo*3+0] 235 | g = self.buf[ledNo*3+1] 236 | b = self.buf[ledNo*3+2] 237 | 238 | r = (0 if (r<=10) else r-int(r*fadeValue/256 )) 239 | g = (0 if (g<=10) else g-int(g*fadeValue/256 )) 240 | b = (0 if (b<=10) else b-int(b*fadeValue/256 )) 241 | 242 | self[ledNo]=(r,g,b) 243 | 244 | for i in range(self.n*2): 245 | 246 | for j in range(self.n): 247 | if (not random_decay) or (randint(0,10) > 5) : 248 | fadeToBlack(j, trail_decay ) 249 | 250 | for j in range(size): 251 | if ( i-j =0) : 252 | 253 | self[i-j]=c 254 | 255 | self.write() 256 | sleep_ms(delay) 257 | -------------------------------------------------------------------------------- /library/ledstrip/optimize_upip.py: -------------------------------------------------------------------------------- 1 | # 2 | # This script optimizes a Python source distribution tarball as produced by 3 | # "python3 setup.py sdist" command for MicroPython's native package manager, 4 | # upip. Optimization includes: 5 | # * Removing metadata files not used by upip (this includes setup.py) 6 | # * Recompressing gzip archive with 4K dictionary size so it can be 7 | # installed even on low-heap targets. 8 | # 9 | import sys 10 | import os 11 | import zlib 12 | from subprocess import Popen, PIPE 13 | import glob 14 | import tarfile 15 | import re 16 | import io 17 | 18 | 19 | def gzip_4k(inf, fname): 20 | comp = zlib.compressobj(level=9, wbits=16 + 12) 21 | with open(fname + ".out", "wb") as outf: 22 | while 1: 23 | data = inf.read(1024) 24 | if not data: 25 | break 26 | outf.write(comp.compress(data)) 27 | outf.write(comp.flush()) 28 | os.rename(fname, fname + ".orig") 29 | os.rename(fname + ".out", fname) 30 | 31 | 32 | def recompress(fname): 33 | with Popen(["gzip", "-d", "-c", fname], stdout=PIPE).stdout as inf: 34 | gzip_4k(inf, fname) 35 | 36 | def find_latest(dir): 37 | res = [] 38 | for fname in glob.glob(dir + "/*.gz"): 39 | st = os.stat(fname) 40 | res.append((st.st_mtime, fname)) 41 | res.sort() 42 | latest = res[-1][1] 43 | return latest 44 | 45 | 46 | def recompress_latest(dir): 47 | latest = find_latest(dir) 48 | print(latest) 49 | recompress(latest) 50 | 51 | 52 | FILTERS = [ 53 | # include, exclude, repeat 54 | (r".+\.egg-info/(PKG-INFO|requires\.txt)", r"setup.py$"), 55 | (r".+\.py$", r"[^/]+$"), 56 | (None, r".+\.egg-info/.+"), 57 | ] 58 | 59 | 60 | outbuf = io.BytesIO() 61 | 62 | def filter_tar(name): 63 | fin = tarfile.open(name, "r:gz") 64 | fout = tarfile.open(fileobj=outbuf, mode="w") 65 | for info in fin: 66 | # print(info) 67 | if not "/" in info.name: 68 | continue 69 | fname = info.name.split("/", 1)[1] 70 | include = None 71 | 72 | for inc_re, exc_re in FILTERS: 73 | if include is None and inc_re: 74 | if re.match(inc_re, fname): 75 | include = True 76 | 77 | if include is None and exc_re: 78 | if re.match(exc_re, fname): 79 | include = False 80 | 81 | if include is None: 82 | include = True 83 | 84 | if include: 85 | print("Including:", fname) 86 | else: 87 | print("Excluding:", fname) 88 | continue 89 | 90 | farch = fin.extractfile(info) 91 | fout.addfile(info, farch) 92 | fout.close() 93 | fin.close() 94 | 95 | 96 | 97 | from setuptools import Command 98 | 99 | class OptimizeUpip(Command): 100 | 101 | user_options = [] 102 | 103 | def run(self): 104 | latest = find_latest("dist") 105 | filter_tar(latest) 106 | outbuf.seek(0) 107 | gzip_4k(outbuf, latest) 108 | 109 | def initialize_options(self): 110 | pass 111 | 112 | def finalize_options(self): 113 | pass 114 | 115 | 116 | # For testing only 117 | if __name__ == "__main__": 118 | # recompress_latest(sys.argv[1]) 119 | filter_tar(sys.argv[1]) 120 | outbuf.seek(0) 121 | gzip_4k(outbuf, sys.argv[1]) 122 | -------------------------------------------------------------------------------- /library/ledstrip/sdist_upip.py: -------------------------------------------------------------------------------- 1 | # 2 | # This module overrides distutils (also compatible with setuptools) "sdist" 3 | # command to perform pre- and post-processing as required for MicroPython's 4 | # upip package manager. 5 | # 6 | # Preprocessing steps: 7 | # * Creation of Python resource module (R.py) from each top-level package's 8 | # resources. 9 | # Postprocessing steps: 10 | # * Removing metadata files not used by upip (this includes setup.py) 11 | # * Recompressing gzip archive with 4K dictionary size so it can be 12 | # installed even on low-heap targets. 13 | # 14 | import sys 15 | import os 16 | import zlib 17 | from subprocess import Popen, PIPE 18 | import glob 19 | import tarfile 20 | import re 21 | import io 22 | 23 | from distutils.filelist import FileList 24 | from setuptools.command.sdist import sdist as _sdist 25 | 26 | 27 | def gzip_4k(inf, fname): 28 | comp = zlib.compressobj(level=9, wbits=16 + 12) 29 | with open(fname + ".out", "wb") as outf: 30 | while 1: 31 | data = inf.read(1024) 32 | if not data: 33 | break 34 | outf.write(comp.compress(data)) 35 | outf.write(comp.flush()) 36 | os.rename(fname, fname + ".orig") 37 | os.rename(fname + ".out", fname) 38 | 39 | 40 | FILTERS = [ 41 | # include, exclude, repeat 42 | (r".+\.egg-info/(PKG-INFO|requires\.txt)", r"setup.py$"), 43 | (r".+\.py$", r"[^/]+$"), 44 | (None, r".+\.egg-info/.+"), 45 | ] 46 | 47 | 48 | outbuf = io.BytesIO() 49 | 50 | def filter_tar(name): 51 | fin = tarfile.open(name, "r:gz") 52 | fout = tarfile.open(fileobj=outbuf, mode="w") 53 | for info in fin: 54 | # print(info) 55 | if not "/" in info.name: 56 | continue 57 | fname = info.name.split("/", 1)[1] 58 | include = None 59 | 60 | for inc_re, exc_re in FILTERS: 61 | if include is None and inc_re: 62 | if re.match(inc_re, fname): 63 | include = True 64 | 65 | if include is None and exc_re: 66 | if re.match(exc_re, fname): 67 | include = False 68 | 69 | if include is None: 70 | include = True 71 | 72 | if include: 73 | print("including:", fname) 74 | else: 75 | print("excluding:", fname) 76 | continue 77 | 78 | farch = fin.extractfile(info) 79 | fout.addfile(info, farch) 80 | fout.close() 81 | fin.close() 82 | 83 | 84 | def make_resource_module(manifest_files): 85 | resources = [] 86 | # Any non-python file included in manifest is resource 87 | for fname in manifest_files: 88 | ext = fname.rsplit(".", 1)[1] 89 | if ext != "py": 90 | resources.append(fname) 91 | 92 | if resources: 93 | print("creating resource module R.py") 94 | resources.sort() 95 | last_pkg = None 96 | r_file = None 97 | for fname in resources: 98 | try: 99 | pkg, res_name = fname.split("/", 1) 100 | except ValueError: 101 | print("not treating %s as a resource" % fname) 102 | continue 103 | if last_pkg != pkg: 104 | last_pkg = pkg 105 | if r_file: 106 | r_file.write("}\n") 107 | r_file.close() 108 | r_file = open(pkg + "/R.py", "w") 109 | r_file.write("R = {\n") 110 | 111 | with open(fname, "rb") as f: 112 | r_file.write("%r: %r,\n" % (res_name, f.read())) 113 | 114 | if r_file: 115 | r_file.write("}\n") 116 | r_file.close() 117 | 118 | 119 | class sdist(_sdist): 120 | 121 | def run(self): 122 | self.filelist = FileList() 123 | self.get_file_list() 124 | make_resource_module(self.filelist.files) 125 | 126 | r = super().run() 127 | 128 | assert len(self.archive_files) == 1 129 | print("filtering files and recompressing with 4K dictionary") 130 | filter_tar(self.archive_files[0]) 131 | outbuf.seek(0) 132 | gzip_4k(outbuf, self.archive_files[0]) 133 | 134 | return r 135 | 136 | 137 | # For testing only 138 | if __name__ == "__main__": 139 | filter_tar(sys.argv[1]) 140 | outbuf.seek(0) 141 | gzip_4k(outbuf, sys.argv[1]) 142 | -------------------------------------------------------------------------------- /library/ledstrip/setup.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/library/ledstrip/setup.cfg -------------------------------------------------------------------------------- /library/ledstrip/setup.py: -------------------------------------------------------------------------------- 1 | # import sys 2 | # sys.path.pop(0) 3 | # sys.path.append("..") 4 | from setuptools import setup 5 | import sdist_upip 6 | from os import path 7 | 8 | # read the contents of your README file 9 | from os import path 10 | this_directory = path.abspath(path.dirname(__file__)) 11 | with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f: 12 | long_description = f.read() 13 | 14 | setup(name='mPython-ledstrip', 15 | version='0.0.4', 16 | description='', 17 | long_description=long_description, 18 | long_description_content_type="text/markdown", 19 | url='https://github.com/labplus-cn/mPython_ledstrip', 20 | author='tangliufeng', 21 | author_email='137513285@qq.com', 22 | maintainer='LabPlus Developers', 23 | license='MIT', 24 | cmdclass={'sdist': sdist_upip.sdist}, 25 | py_modules=['ledstrip'], 26 | # spackages=['email'], 27 | # install_requires=[''] 28 | ) 29 | 30 | -------------------------------------------------------------------------------- /library/microbit/README.md: -------------------------------------------------------------------------------- 1 | 使用该库,Micro:Bit程序可在掌控板上运行。 2 | 3 | ### 已实现Micro:Bit 功能 4 | 5 | - MicrBit Module 6 | - Button 7 | - Pin 8 | - Display 9 | - Image 10 | - UART 11 | - I2C 12 | - Accelerometer 13 | - Compass 14 | 15 | 16 | ## 使用 17 | 18 | 例: 19 | 20 | ``` 21 | from microbit import * 22 | display.show(Image.HAPPY) 23 | 24 | ``` 25 | -------------------------------------------------------------------------------- /library/microbit/microbit.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | # Copyright (c) 2019, labplus@Tangliufeng 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # 掌控板兼容Microbit库,已实现功能有以下: 24 | # - MicrBit Module 基础函数 25 | # - MicroBitDispl 26 | # - 内置Image图片和构建类 27 | 28 | import os,sys,time,_thread,esp32,framebuf,machine,gc 29 | from framebuf import FrameBuffer 30 | from machine import UART 31 | from mpython import oled, MPythonPin, PinMode, ADC, button_a, button_b, accelerometer 32 | 33 | pendolino = [0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x0, 0x8, 0xa, 0x4a, 0x40, 0x0, 0x0, 0xa, 0x5f, 0xea, 0x5f, 0xea, 0xe, 0xd9, 0x2e, 0xd3, 0x6e, 0x19, 0x32, 0x44, 0x89, 0x33, 0xc, 0x92, 0x4c, 0x92, 0x4d, 0x8, 0x8, 0x0, 0x0, 0x0, 0x4, 0x88, 0x8, 0x8, 0x4, 0x8, 0x4, 0x84, 0x84, 0x88, 0x0, 0xa, 0x44, 0x8a, 0x40, 0x0, 0x4, 0x8e, 0xc4, 0x80, 0x0, 0x0, 0x0, 0x4, 0x88, 0x0, 0x0, 0xe, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x1, 0x22, 0x44, 0x88, 0x10, 0xc, 0x92, 0x52, 0x52, 0x4c, 0x4, 0x8c, 0x84, 0x84, 0x8e, 0x1c, 0x82, 0x4c, 0x90, 0x1e, 0x1e, 0xc2, 0x44, 0x92, 0x4c, 0x6, 0xca, 0x52, 0x5f, 0xe2, 0x1f, 0xf0, 0x1e, 0xc1, 0x3e, 0x2, 0x44, 0x8e, 0xd1, 0x2e, 0x1f, 0xe2, 0x44, 0x88, 0x10, 0xe, 0xd1, 0x2e, 0xd1, 0x2e, 0xe, 0xd1, 0x2e, 0xc4, 0x88, 0x0, 0x8, 0x0, 0x8, 0x0, 0x0, 0x4, 0x80, 0x4, 0x88, 0x2, 0x44, 0x88, 0x4, 0x82, 0x0, 0xe, 0xc0, 0xe, 0xc0, 0x8, 0x4, 0x82, 0x44, 0x88, 0xe, 0xd1, 0x26, 0xc0, 0x4, 0xe, 0xd1, 0x35, 0xb3, 0x6c, 0xc, 0x92, 0x5e, 0xd2, 0x52, 0x1c, 0x92, 0x5c, 0x92, 0x5c, 0xe, 0xd0, 0x10, 0x10, 0xe, 0x1c, 0x92, 0x52, 0x52, 0x5c, 0x1e, 0xd0, 0x1c, 0x90, 0x1e, 0x1e, 0xd0, 0x1c, 0x90, 0x10, 0xe, 0xd0, 0x13, 0x71, 0x2e, 0x12, 0x52, 0x5e, 0xd2, 0x52, 0x1c, 0x88, 0x8, 0x8, 0x1c, 0x1f, 0xe2, 0x42, 0x52, 0x4c, 0x12, 0x54, 0x98, 0x14, 0x92, 0x10, 0x10, 0x10, 0x10, 0x1e, 0x11, 0x3b, 0x75, 0xb1, 0x31, 0x11, 0x39, 0x35, 0xb3, 0x71, 0xc, 0x92, 0x52, 0x52, 0x4c, 0x1c, 0x92, 0x5c, 0x90, 0x10, 0xc, 0x92, 0x52, 0x4c, 0x86, 0x1c, 0x92, 0x5c, 0x92, 0x51, 0xe, 0xd0, 0xc, 0x82, 0x5c, 0x1f, 0xe4, 0x84, 0x84, 0x84, 0x12, 0x52, 0x52, 0x52, 0x4c, 0x11, 0x31, 0x31, 0x2a, 0x44, 0x11, 0x31, 0x35, 0xbb, 0x71, 0x12, 0x52, 0x4c, 0x92, 0x52, 0x11, 0x2a, 0x44, 0x84, 0x84, 0x1e, 0xc4, 0x88, 0x10, 0x1e, 0xe, 0xc8, 0x8, 0x8, 0xe, 0x10, 0x8, 0x4, 0x82, 0x41, 0xe, 0xc2, 0x42, 0x42, 0x4e, 0x4, 0x8a, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x8, 0x4, 0x80, 0x0, 0x0, 0x0, 0xe, 0xd2, 0x52, 0x4f, 0x10, 0x10, 0x1c, 0x92, 0x5c, 0x0, 0xe, 0xd0, 0x10, 0xe, 0x2, 0x42, 0x4e, 0xd2, 0x4e, 0xc, 0x92, 0x5c, 0x90, 0xe, 0x6, 0xc8, 0x1c, 0x88, 0x8, 0xe, 0xd2, 0x4e, 0xc2, 0x4c, 0x10, 0x10, 0x1c, 0x92, 0x52, 0x8, 0x0, 0x8, 0x8, 0x8, 0x2, 0x40, 0x2, 0x42, 0x4c, 0x10, 0x14, 0x98, 0x14, 0x92, 0x8, 0x8, 0x8, 0x8, 0x6, 0x0, 0x1b, 0x75, 0xb1, 0x31, 0x0, 0x1c, 0x92, 0x52, 0x52, 0x0, 0xc, 0x92, 0x52, 0x4c, 0x0, 0x1c, 0x92, 0x5c, 0x90, 0x0, 0xe, 0xd2, 0x4e, 0xc2, 0x0, 0xe, 0xd0, 0x10, 0x10, 0x0, 0x6, 0xc8, 0x4, 0x98, 0x8, 0x8, 0xe, 0xc8, 0x7, 0x0, 0x12, 0x52, 0x52, 0x4f, 0x0, 0x11, 0x31, 0x2a, 0x44, 0x0, 0x11, 0x31, 0x35, 0xbb, 0x0, 0x12, 0x4c, 0x8c, 0x92, 0x0, 0x11, 0x2a, 0x44, 0x98, 0x0, 0x1e, 0xc4, 0x88, 0x1e, 0x6, 0xc4, 0x8c, 0x84, 0x86, 0x8, 0x8, 0x8, 0x8, 0x8, 0x18, 0x8, 0xc, 0x88, 0x18, 0x0, 0x0, 0xc, 0x83, 0x60] 34 | pendolino = bytearray(pendolino) 35 | 36 | # microbit引脚映射 37 | def pin_esp2bit(pin): 38 | pins_remap_esp32 = (33, 32, 35, 34, 39, 0, 16, 17, 26, 25, 36, 2, -1, 18, 19, 21, 5, -1, -1, 22, 23, -1, -1, 27, 14, 12, 39 | 13, 15, 4) 40 | if pin == None: 41 | return None 42 | else: 43 | return pins_remap_esp32[pin] 44 | 45 | # 芯片温度 46 | def temperature(): 47 | return int((esp32.raw_temperature()-32)/1.8) 48 | 49 | # 复位 50 | def reset(): 51 | machine.reset() 52 | 53 | # 毫秒延时 54 | def sleep(n): 55 | time.sleep_ms(n) 56 | 57 | # 运行时间ms 58 | def running_time(): 59 | return time.ticks_ms() 60 | 61 | 62 | class MicroBitUART(UART): 63 | 64 | def __init__(self): 65 | super().__init__(1) 66 | self.redirect = False 67 | 68 | def init(self, *arg, **kws): 69 | try: 70 | tx = kws["tx"] 71 | rx = kws["rx"] 72 | except KeyError: 73 | pass 74 | else: 75 | kws['tx'] = pin_esp2bit(tx.id) 76 | kws['rx'] = pin_esp2bit(rx.id) 77 | self.redirect = True 78 | super().init(*arg, **kws) 79 | 80 | def any(self): 81 | if not self.redirect: super().init(115200, tx=1, rx=3) 82 | return super().any() 83 | 84 | def read(self,*arg): 85 | if not self.redirect: super().init(115200, tx=1, rx=3) 86 | return super().read(*arg) 87 | 88 | def readall(self): 89 | if not self.redirect: super().init(115200, tx=1, rx=3) 90 | return super().read() 91 | 92 | def readinto(self,*arg): 93 | if not self.redirect: super().init(115200, tx=1, rx=3) 94 | return super().readinto(*arg) 95 | 96 | def readline(self): 97 | if not self.redirect: super().init(115200, tx=1, rx=3) 98 | return super().readline() 99 | 100 | def write(self,*arg): 101 | if not self.redirect: super().init(115200, tx=1, rx=3) 102 | return super().write(*arg) 103 | 104 | 105 | class Matrix(FrameBuffer): 106 | 107 | def __init__(self): 108 | self.buffer = bytearray(5) 109 | super().__init__(self.buffer, 5, 5, framebuf.MONO_VLSB) 110 | self.grid = 12 111 | self._pixel_o = (37, 5) 112 | 113 | def draw_pixel(self,x,y,c): 114 | pixel = (self._pixel_o[0]+x*self.grid, self._pixel_o[1]+y*self.grid) 115 | oled.fill_rect(pixel[0],pixel[1],int(self.grid/2),int(self.grid/2),c) 116 | 117 | def draw_matrix(self,buf): 118 | temp=bytearray(5) 119 | for i in range(5): 120 | temp[i]=(buf[i]<<3)&0xff 121 | for j in range(5): 122 | if temp[i] << j & 0x80: 123 | self.draw_pixel(i, 4-j, 1) 124 | else: 125 | self.draw_pixel(i,4-j,0) 126 | 127 | def get_char(self,char): 128 | buf = bytearray(5) 129 | buf_v = bytearray(5) 130 | index = 5*(ord(char)-32) 131 | temp = pendolino[index:index+5] 132 | for i in range(5): 133 | buf[i] = (temp[i] << 3) & 0xff 134 | for i in range(5): 135 | for j in range(5): 136 | buf_v[4-i] |= ((buf[j] >> (3+i) & 0x01) << j) & 0xff 137 | return buf_v 138 | 139 | def text(self,text): 140 | self.buffer_text= [] 141 | text=str(text) 142 | self.text_length=len(text) 143 | for i in range(self.text_length): 144 | temp=self.get_char(text[i]) 145 | temp_list=list(temp) 146 | self.buffer_text.extend(temp_list) 147 | self.buffer_text.append(0) 148 | self.buffer[:]=bytearray(self.buffer_text[0:5])[:] 149 | 150 | def left_shift(self, step): 151 | for i in range(step): 152 | self.buffer_text.append(self.buffer_text[0]) 153 | self.buffer_text.pop(0) 154 | self.buffer[:]=bytearray(self.buffer_text[0:5])[:] 155 | self.show() 156 | 157 | def show(self): 158 | self.draw_matrix(self.buffer) 159 | oled.show() 160 | 161 | class MicroBitImage(): 162 | 163 | def __init__(self,*arg): 164 | self.buffer = bytearray() 165 | if len(arg)==1: 166 | if type(arg[0]) == str: 167 | self.buffer = self.str2bytes(arg[0]) 168 | self._str = arg[0] 169 | else: 170 | raise TypeError("The argument must be a string" ) 171 | else: 172 | self._weight =arg[0] 173 | self._height =arg[1] 174 | self.buffer = bytearray(arg[2]) 175 | self._str = self.bytes2str(*arg) 176 | 177 | def __repr__(self): 178 | return 'Image({!r})'.format(self._str) 179 | 180 | def str2bytes(self,string): 181 | if string.find(":") ==-1: 182 | list_=string.split() 183 | else: 184 | list_ = string.split(":") 185 | self._height = len(list_) 186 | self._weight = len(list_[0]) 187 | buf=bytearray() 188 | for raw in range(self._height): 189 | for col in range(self._weight): 190 | if int(list_[raw][col]): 191 | buf.append(1) 192 | else: 193 | buf.append(0) 194 | return buf 195 | 196 | def bytes2str(self,width,height,buf): 197 | list_=[""]*height 198 | for h in range(height): 199 | for w in range(width): 200 | if buf[h*height+w]: 201 | list_[h]+="9" 202 | else: 203 | list_[h]+="0" 204 | return ":".join(list_) 205 | 206 | def width(self): 207 | return self._weight 208 | 209 | def height(self): 210 | return self._height 211 | 212 | class Image(MicroBitImage): 213 | 214 | HEART = MicroBitImage(5, 5, [ 215 | 0, 1, 0, 1, 0, 216 | 1, 1, 1, 1, 1, 217 | 1, 1, 1, 1, 1, 218 | 0, 1, 1, 1, 0, 219 | 0, 0, 1, 0, 0 220 | ]) 221 | HEART_SMALL = MicroBitImage(5, 5, [ 222 | 0, 0, 0, 0, 0, 223 | 0, 1, 0, 1, 0, 224 | 0, 1, 1, 1, 0, 225 | 0, 0, 1, 0, 0, 226 | 0, 0, 0, 0, 0 227 | ]) 228 | HAPPY = MicroBitImage(5, 5, [ 229 | 0, 0, 0, 0, 0, 230 | 0, 1, 0, 1, 0, 231 | 0, 0, 0, 0, 0, 232 | 1, 0, 0, 0, 1, 233 | 0, 1, 1, 1, 0 234 | ]) 235 | 236 | SMILE = MicroBitImage(5, 5, [ 237 | 0, 0, 0, 0, 0, 238 | 0, 1, 0, 1, 0, 239 | 0, 0, 0, 0, 0, 240 | 1, 0, 0, 0, 1, 241 | 0, 1, 1, 1, 0 242 | ]) 243 | SAD = MicroBitImage(5, 5, [ 244 | 0, 0, 0, 0, 0, 245 | 0, 1, 0, 1, 0, 246 | 0, 0, 0, 0, 0, 247 | 0, 1, 1, 1, 0, 248 | 1, 0, 0, 0, 1 249 | ]) 250 | 251 | CONFUSED = MicroBitImage(5, 5, [ 252 | 0, 0, 0, 0, 0, 253 | 0, 1, 0, 1, 0, 254 | 0, 0, 0, 0, 0, 255 | 0, 1, 0, 1, 0, 256 | 1, 0, 1, 0, 1 257 | ]) 258 | 259 | ANGRY = MicroBitImage(5, 5, [ 260 | 1, 0, 0, 0, 1, 261 | 0, 1, 0, 1, 0, 262 | 0, 0, 0, 0, 0, 263 | 1, 1, 1, 1, 1, 264 | 1, 0, 1, 0, 1 265 | ]) 266 | 267 | ASLEEP = MicroBitImage(5, 5, [ 268 | 0, 0, 0, 0, 0, 269 | 1, 1, 0, 1, 1, 270 | 0, 0, 0, 0, 0, 271 | 0, 1, 1, 1, 0, 272 | 0, 0, 0, 0, 0 273 | ]) 274 | 275 | SURPRISED = MicroBitImage(5, 5, [ 276 | 0, 1, 0, 1, 0, 277 | 0, 0, 0, 0, 0, 278 | 0, 0, 1, 0, 0, 279 | 0, 1, 0, 1, 0, 280 | 0, 0, 1, 0, 0 281 | ]) 282 | 283 | SILLY = MicroBitImage(5, 5, [ 284 | 1, 0, 0, 0, 1, 285 | 0, 0, 0, 0, 0, 286 | 1, 1, 1, 1, 1, 287 | 0, 0, 1, 0, 1, 288 | 0, 0, 1, 1, 1 289 | ]) 290 | FABULOUS = MicroBitImage(5, 5, [ 291 | 1, 1, 1, 1, 1, 292 | 1, 1, 0, 1, 1, 293 | 0, 0, 0, 0, 0, 294 | 0, 1, 0, 1, 0, 295 | 0, 1, 1, 1, 0 296 | ]) 297 | 298 | MEH = MicroBitImage(5, 5, [ 299 | 0, 1, 0, 1, 0, 300 | 0, 0, 0, 0, 0, 301 | 0, 0, 0, 1, 0, 302 | 0, 0, 1, 0, 0, 303 | 0, 1, 0, 0, 0 304 | ]) 305 | 306 | YES = MicroBitImage(5, 5, [ 307 | 0, 0, 0, 0, 0, 308 | 0, 0, 0, 0, 1, 309 | 0, 0, 0, 1, 0, 310 | 1, 0, 1, 0, 0, 311 | 0, 1, 0, 0, 0 312 | ]) 313 | 314 | NO = MicroBitImage(5, 5, [ 315 | 1, 0, 0, 0, 1, 316 | 0, 1, 0, 1, 0, 317 | 0, 0, 1, 0, 0, 318 | 0, 1, 0, 1, 0, 319 | 1, 0, 0, 0, 1 320 | ]) 321 | 322 | CLOCK12 = MicroBitImage(5, 5, [ 323 | 0, 0, 1, 0, 0, 324 | 0, 0, 1, 0, 0, 325 | 0, 0, 1, 0, 0, 326 | 0, 0, 0, 0, 0, 327 | 0, 0, 0, 0, 0 328 | ]) 329 | 330 | CLOCK1 = MicroBitImage(5, 5, [ 331 | 0, 0, 0, 1, 0, 332 | 0, 0, 0, 1, 0, 333 | 0, 0, 1, 0, 0, 334 | 0, 0, 0, 0, 0, 335 | 0, 0, 0, 0, 0 336 | ]) 337 | 338 | CLOCK2 = MicroBitImage(5, 5, [ 339 | 0, 0, 0, 0, 0, 340 | 0, 0, 0, 1, 1, 341 | 0, 0, 1, 0, 0, 342 | 0, 0, 0, 0, 0, 343 | 0, 0, 0, 0, 0 344 | ]) 345 | 346 | CLOCK3 = MicroBitImage(5, 5, [ 347 | 0, 0, 0, 0, 0, 348 | 0, 0, 0, 0, 0, 349 | 0, 0, 1, 1, 1, 350 | 0, 0, 0, 0, 0, 351 | 0, 0, 0, 0, 0 352 | ]) 353 | 354 | CLOCK4 = MicroBitImage(5, 5, [ 355 | 0, 0, 0, 0, 0, 356 | 0, 0, 0, 0, 0, 357 | 0, 0, 1, 0, 0, 358 | 0, 0, 0, 1, 1, 359 | 0, 0, 0, 0, 0 360 | ]) 361 | 362 | CLOCK5 = MicroBitImage(5, 5, [ 363 | 0, 0, 0, 0, 0, 364 | 0, 0, 0, 0, 0, 365 | 0, 0, 1, 0, 0, 366 | 0, 0, 0, 1, 0, 367 | 0, 0, 0, 1, 0 368 | ]) 369 | 370 | CLOCK6 = MicroBitImage(5, 5, [ 371 | 0, 0, 0, 0, 0, 372 | 0, 0, 0, 0, 0, 373 | 0, 0, 1, 0, 0, 374 | 0, 0, 1, 0, 0, 375 | 0, 0, 1, 0, 0 376 | ]) 377 | 378 | CLOCK7 = MicroBitImage(5, 5, [ 379 | 0, 0, 0, 0, 0, 380 | 0, 0, 0, 0, 0, 381 | 0, 0, 1, 0, 0, 382 | 0, 1, 0, 0, 0, 383 | 0, 1, 0, 0, 0 384 | ]) 385 | 386 | CLOCK8 = MicroBitImage(5, 5, [ 387 | 0, 0, 0, 0, 0, 388 | 0, 0, 0, 0, 0, 389 | 0, 0, 1, 0, 0, 390 | 1, 1, 0, 0, 0, 391 | 0, 0, 0, 0, 0 392 | ]) 393 | 394 | CLOCK9 = MicroBitImage(5, 5, [ 395 | 0, 0, 0, 0, 0, 396 | 0, 0, 0, 0, 0, 397 | 1, 1, 1, 0, 0, 398 | 0, 0, 0, 0, 0, 399 | 0, 0, 0, 0, 0 400 | ]) 401 | 402 | CLOCK10 = MicroBitImage(5, 5, [ 403 | 0, 0, 0, 0, 0, 404 | 1, 1, 0, 0, 0, 405 | 0, 0, 1, 0, 0, 406 | 0, 0, 0, 0, 0, 407 | 0, 0, 0, 0, 0 408 | ]) 409 | 410 | CLOCK11 = MicroBitImage(5, 5, [ 411 | 0, 1, 0, 0, 0, 412 | 0, 1, 0, 0, 0, 413 | 0, 0, 1, 0, 0, 414 | 0, 0, 0, 0, 0, 415 | 0, 0, 0, 0, 0 416 | ]) 417 | 418 | ARROW_N = MicroBitImage(5, 5, [ 419 | 0, 0, 1, 0, 0, 420 | 0, 1, 1, 1, 0, 421 | 1, 0, 1, 0, 1, 422 | 0, 0, 1, 0, 0, 423 | 0, 0, 1, 0, 0 424 | ]) 425 | 426 | ARROW_NE = MicroBitImage(5, 5, [ 427 | 0, 0, 1, 1, 1, 428 | 0, 0, 0, 1, 1, 429 | 0, 0, 1, 0, 1, 430 | 0, 1, 0, 0, 0, 431 | 1, 0, 0, 0, 0 432 | ]) 433 | 434 | ARROW_E = MicroBitImage(5, 5, [ 435 | 0, 0, 1, 0, 0, 436 | 0, 0, 0, 1, 0, 437 | 1, 1, 1, 1, 1, 438 | 0, 0, 0, 1, 0, 439 | 0, 0, 1, 0, 0 440 | ]) 441 | 442 | ARROW_SE = MicroBitImage(5, 5, [ 443 | 1, 0, 0, 0, 0, 444 | 0, 1, 0, 0, 0, 445 | 0, 0, 1, 0, 1, 446 | 0, 0, 0, 1, 1, 447 | 0, 0, 1, 1, 1 448 | ]) 449 | 450 | ARROW_S = MicroBitImage(5, 5, [ 451 | 0, 0, 1, 0, 0, 452 | 0, 0, 1, 0, 0, 453 | 1, 0, 1, 0, 1, 454 | 0, 1, 1, 1, 0, 455 | 0, 0, 1, 0, 0 456 | ]) 457 | 458 | ARROW_SW = MicroBitImage(5, 5, [ 459 | 0, 0, 0, 0, 1, 460 | 0, 0, 0, 1, 0, 461 | 1, 0, 1, 0, 0, 462 | 1, 1, 0, 0, 0, 463 | 1, 1, 1, 0, 0 464 | ]) 465 | ARROW_W = MicroBitImage(5, 5, [ 466 | 0, 0, 1, 0, 0, 467 | 0, 1, 0, 0, 0, 468 | 1, 1, 1, 1, 1, 469 | 0, 1, 0, 0, 0, 470 | 0, 0, 1, 0, 0 471 | ]) 472 | ARROW_NW = MicroBitImage(5, 5, [ 473 | 1, 1, 1, 0, 0, 474 | 1, 1, 0, 0, 0, 475 | 1, 0, 1, 0, 0, 476 | 0, 0, 0, 1, 0, 477 | 0, 0, 0, 0, 1 478 | ]) 479 | TRIANGLE = MicroBitImage(5, 5, [ 480 | 0, 0, 0, 0, 0, 481 | 0, 0, 1, 0, 0, 482 | 0, 1, 0, 1, 0, 483 | 1, 1, 1, 1, 1, 484 | 0, 0, 0, 0, 0 485 | ]) 486 | TRIANGLE_LEFT = MicroBitImage(5, 5, [ 487 | 1, 0, 0, 0, 0, 488 | 1, 1, 0, 0, 0, 489 | 1, 0, 1, 0, 0, 490 | 1, 0, 0, 1, 0, 491 | 1, 1, 1, 1, 1 492 | ]) 493 | CHESSBOARD = MicroBitImage(5, 5, [ 494 | 0, 1, 0, 1, 0, 495 | 1, 0, 1, 0, 1, 496 | 0, 1, 0, 1, 0, 497 | 1, 0, 1, 0, 1, 498 | 0, 1, 0, 1, 0 499 | ]) 500 | DIAMOND_SMALL = MicroBitImage(5, 5, [ 501 | 0, 0, 0, 0, 0, 502 | 0, 0, 1, 0, 0, 503 | 0, 1, 0, 1, 0, 504 | 0, 0, 1, 0, 0, 505 | 0, 0, 0, 0, 0 506 | ]) 507 | SQUARE = MicroBitImage(5, 5, [ 508 | 1, 1, 1, 1, 1, 509 | 1, 0, 0, 0, 1, 510 | 1, 0, 0, 0, 1, 511 | 1, 0, 0, 0, 1, 512 | 1, 1, 1, 1, 1 513 | ]) 514 | SQUARE_SMALL = MicroBitImage(5, 5, [ 515 | 0, 0, 0, 0, 0, 516 | 0, 1, 1, 1, 0, 517 | 0, 1, 0, 1, 0, 518 | 0, 1, 1, 1, 0, 519 | 0, 0, 0, 0, 0 520 | ]) 521 | RABBIT = MicroBitImage(5, 5, [ 522 | 1, 0, 1, 0, 0, 523 | 1, 0, 1, 0, 0, 524 | 1, 1, 1, 1, 0, 525 | 1, 1, 0, 1, 0, 526 | 1, 1, 1, 1, 0 527 | ]) 528 | COW = MicroBitImage(5, 5, [ 529 | 1, 0, 0, 0, 1, 530 | 1, 0, 0, 0, 1, 531 | 1, 1, 1, 1, 1, 532 | 0, 1, 1, 1, 0, 533 | 0, 0, 1, 0, 0 534 | ]) 535 | CROTCHET = MicroBitImage(5, 5, [ 536 | 0, 0, 1, 0, 0, 537 | 0, 0, 1, 0, 0, 538 | 0, 0, 1, 0, 0, 539 | 1, 1, 1, 0, 0, 540 | 1, 1, 1, 0, 0 541 | ]) 542 | 543 | QUAVER = MicroBitImage(5, 5, [ 544 | 0, 0, 1, 0, 0, 545 | 0, 0, 1, 1, 0, 546 | 0, 0, 1, 0, 1, 547 | 1, 1, 1, 0, 0, 548 | 1, 1, 1, 0, 0 549 | ]) 550 | 551 | QUAVERS = MicroBitImage(5, 5, [ 552 | 0, 1, 1, 1, 1, 553 | 0, 1, 0, 0, 1, 554 | 0, 1, 0, 0, 1, 555 | 1, 1, 0, 1, 1, 556 | 1, 1, 0, 1, 1 557 | ]) 558 | PITCHFORK = MicroBitImage(5, 5, [ 559 | 1, 0, 1, 0, 1, 560 | 1, 0, 1, 0, 1, 561 | 1, 1, 1, 1, 1, 562 | 0, 0, 1, 0, 0, 563 | 0, 0, 1, 0, 0 564 | ]) 565 | XMAS = MicroBitImage(5, 5, [ 566 | 0, 0, 1, 0, 0, 567 | 0, 1, 1, 1, 0, 568 | 0, 0, 1, 0, 0, 569 | 0, 1, 1, 1, 0, 570 | 1, 1, 1, 1, 1 571 | ]) 572 | PACMAN = MicroBitImage(5, 5, [ 573 | 0, 1, 1, 1, 1, 574 | 1, 1, 0, 1, 0, 575 | 1, 1, 1, 0, 0, 576 | 1, 1, 1, 1, 0, 577 | 0, 1, 1, 1, 1 578 | ]) 579 | TARGET = MicroBitImage(5, 5, [ 580 | 0, 0, 1, 0, 0, 581 | 0, 1, 1, 1, 0, 582 | 1, 1, 0, 1, 1, 583 | 0, 1, 1, 1, 0, 584 | 0, 0, 1, 0, 0 585 | ]) 586 | TSHIRT = MicroBitImage(5, 5, [ 587 | 1, 1, 0, 1, 1, 588 | 1, 1, 1, 1, 1, 589 | 0, 1, 1, 1, 0, 590 | 0, 1, 1, 1, 0, 591 | 0, 1, 1, 1, 0 592 | ]) 593 | ROLLERSKATE = MicroBitImage(5, 5, [ 594 | 0, 0, 0, 1, 1, 595 | 0, 0, 0, 1, 1, 596 | 1, 1, 1, 1, 1, 597 | 1, 1, 1, 1, 1, 598 | 0, 1, 0, 1, 0 599 | ]) 600 | DUCK = MicroBitImage(5, 5, [ 601 | 0, 1, 1, 0, 0, 602 | 1, 1, 1, 0, 0, 603 | 0, 1, 1, 1, 1, 604 | 0, 1, 1, 1, 0, 605 | 0, 0, 0, 0, 0 606 | ]) 607 | HOUSE = MicroBitImage(5, 5, [ 608 | 0, 0, 1, 0, 0, 609 | 0, 1, 1, 1, 0, 610 | 1, 1, 1, 1, 1, 611 | 0, 1, 1, 1, 0, 612 | 0, 1, 0, 1, 0 613 | ]) 614 | TORTOISE = MicroBitImage(5, 5, [ 615 | 0, 0, 0, 0, 0, 616 | 0, 1, 1, 1, 0, 617 | 1, 1, 1, 1, 1, 618 | 0, 1, 0, 1, 0, 619 | 0, 0, 0, 0, 0 620 | ]) 621 | BUTTERFLY = MicroBitImage(5, 5, [ 622 | 1, 1, 0, 1, 1, 623 | 1, 1, 1, 1, 1, 624 | 0, 0, 1, 0, 0, 625 | 1, 1, 1, 1, 1, 626 | 1, 1, 0, 1, 1 627 | ]) 628 | STICKFIGURE = MicroBitImage(5, 5, [ 629 | 0, 0, 1, 0, 0, 630 | 1, 1, 1, 1, 1, 631 | 0, 0, 1, 0, 0, 632 | 0, 1, 0, 1, 0, 633 | 1, 0, 0, 0, 1 634 | ]) 635 | GHOST = MicroBitImage(5, 5, [ 636 | 1, 1, 1, 1, 1, 637 | 1, 0, 1, 0, 1, 638 | 1, 1, 1, 1, 1, 639 | 1, 1, 1, 1, 1, 640 | 1, 0, 1, 0, 1 641 | ]) 642 | SWORD = MicroBitImage(5, 5, [ 643 | 0, 0, 1, 0, 0, 644 | 0, 0, 1, 0, 0, 645 | 0, 0, 1, 0, 0, 646 | 0, 1, 1, 1, 0, 647 | 0, 0, 1, 0, 0 648 | ]) 649 | GIRAFFE = MicroBitImage(5, 5, [ 650 | 1, 1, 0, 0, 0, 651 | 0, 1, 0, 0, 0, 652 | 0, 1, 0, 0, 0, 653 | 0, 1, 1, 1, 0, 654 | 0, 1, 0, 1, 0 655 | ]) 656 | SKULL = MicroBitImage(5, 5, [ 657 | 0, 1, 1, 1, 0, 658 | 1, 0, 1, 0, 1, 659 | 1, 1, 1, 1, 1, 660 | 0, 1, 1, 1, 0, 661 | 0, 1, 1, 1, 0 662 | ]) 663 | UMBRELLA = MicroBitImage(5, 5, [ 664 | 0, 1, 1, 1, 0, 665 | 1, 1, 1, 1, 1, 666 | 0, 0, 1, 0, 0, 667 | 1, 0, 1, 0, 0, 668 | 0, 1, 1, 0, 0 669 | ]) 670 | SNAKE = MicroBitImage(5, 5, [ 671 | 1, 1, 0, 0, 0, 672 | 1, 1, 0, 1, 1, 673 | 0, 1, 0, 1, 0, 674 | 0, 1, 1, 1, 0, 675 | 0, 0, 0, 0, 0 676 | ]) 677 | 678 | class MicroBitDisplay(): 679 | 680 | def __init__(self): 681 | self.matrix = Matrix() 682 | self.ison = True 683 | _thread.start_new_thread(self._loop, ()) 684 | self.lock=_thread.allocate_lock() 685 | self.clear() 686 | 687 | def get_pixel(self, x, y): 688 | temp = (self.matrix.buffer[x] << 3) & 0xff 689 | if temp << 4-y & 0x80: 690 | return True 691 | else: 692 | return False 693 | 694 | def set_pixel(self, x, y, value): 695 | self.matrix.pixel(x,y,value) 696 | self.matrix.show() 697 | 698 | def clear(self): 699 | self.matrix.fill(0) 700 | self.matrix.show() 701 | 702 | def hline(self, *arg ,**kws): 703 | self.matrix.hline(*arg ,**kws) 704 | self.matrix.show() 705 | 706 | def vline(self, *arg ,**kws): 707 | self.matrix.vline(*arg ,**kws) 708 | self.matrix.show() 709 | 710 | def show(self, value,delay = 150,*,wait = True,loop = False,clear = False): 711 | if type(value) == MicroBitImage: 712 | img=value 713 | for h in range(5): 714 | for w in range(5): 715 | if img.buffer[h*5+w]: 716 | self.matrix.pixel(w,h,1) 717 | else: 718 | self.matrix.pixel(w,h,0) 719 | self.matrix.show() 720 | else: 721 | self.scroll(value,delay,wait=wait,loop=loop) 722 | if clear: 723 | self.clear() 724 | 725 | def scroll(self, value, delay=150, *, wait=True, loop=False, monospace=False): 726 | self._wait =wait 727 | self._value = value 728 | self._delay = delay 729 | while loop: 730 | self._text(value,delay) 731 | if self._wait and (not loop): 732 | self._text(value,delay) 733 | 734 | 735 | def _text(self,value,delay): 736 | self.matrix.text(value) 737 | self.matrix.show() 738 | time.sleep_ms(delay) 739 | if self.matrix.text_length >1: 740 | for i in range(len(self.matrix.buffer_text)-5): 741 | self.matrix.left_shift(1) 742 | time.sleep_ms(delay) 743 | 744 | 745 | def _loop(self): 746 | self._wait =True 747 | while True: 748 | if not self._wait: 749 | self._text(self._value,self._delay) 750 | self._wait= True 751 | 752 | def on(self): 753 | oled.poweron() 754 | self.ison = True 755 | 756 | def off(self): 757 | oled.poweroff() 758 | self.ison = False 759 | 760 | def is_on(self): 761 | return self.ison 762 | 763 | class MicroBitPin: 764 | IN = 1 765 | OUT = 2 766 | PWM = 3 767 | ANALOG = 4 768 | 769 | def __init__(self, id): 770 | self.id = id 771 | self.current_mode = None 772 | self.mpython_pin = None 773 | 774 | def __change_pin_mode(self, pinmode): 775 | if self.current_mode is not pinmode: 776 | self.mpython_pin = MPythonPin(self.id, mode=pinmode) 777 | if pinmode == MicroBitPin.ANALOG: 778 | self.mpython_pin.adc.width(ADC.WIDTH_10BIT) 779 | self.current_mode = pinmode 780 | 781 | def read_digital(self): 782 | self.__change_pin_mode(MicroBitPin.IN) 783 | return self.mpython_pin.read_digital() 784 | 785 | def write_digital(self, value): 786 | self.__change_pin_mode(MicroBitPin.OUT) 787 | self.mpython_pin.write_digital(value) 788 | 789 | def read_analog(self): 790 | self.__change_pin_mode(MicroBitPin.ANALOG) 791 | return self.mpython_pin.read_analog() 792 | 793 | def write_analog(self, value): 794 | self.__change_pin_mode(MicroBitPin.PWM) 795 | self.mpython_pin.write_analog(value) 796 | 797 | def set_analog_period(period): 798 | #TODO 799 | pass 800 | 801 | def set_analog_period_microseconds(period): 802 | #TODO 803 | pass 804 | 805 | uart = MicroBitUART() 806 | display=MicroBitDisplay() 807 | # Pin 808 | pin0 = MicroBitPin(0) 809 | pin1 = MicroBitPin(1) 810 | pin2 = MicroBitPin(2) 811 | pin3 = MicroBitPin(3) 812 | pin4 = MicroBitPin(4) 813 | pin5 = MicroBitPin(5) 814 | pin6 = MicroBitPin(6) 815 | pin7 = MicroBitPin(7) 816 | pin8 = MicroBitPin(8) 817 | pin9 = MicroBitPin(9) 818 | pin10 = MicroBitPin(10) 819 | pin11 = MicroBitPin(11) 820 | 821 | pin13 = MicroBitPin(13) 822 | pin14 = MicroBitPin(14) 823 | pin15 = MicroBitPin(15) 824 | pin16 = MicroBitPin(16) 825 | 826 | pin19 = MicroBitPin(19) 827 | pin20 = MicroBitPin(20) 828 | 829 | def accelerometer_get_gestures(): 830 | return (accelerometer.get_x(), accelerometer.get_y(), accelerometer.get_z()) 831 | 832 | accelerometer.get_gestures = accelerometer_get_gestures 833 | 834 | try: 835 | from mpython import magnetic 836 | except ImportError as e: 837 | pass 838 | else: 839 | compass = magnetic 840 | compass.heading = magnetic.get_heading 841 | 842 | gc.collect() 843 | 844 | -------------------------------------------------------------------------------- /library/qqai/README.md: -------------------------------------------------------------------------------- 1 | # qqai 2 | 3 | [腾讯AI](https://ai.qq.com/)开放平台的掌控板(mPython)、MicroPython API库。 4 | 5 | ## 依赖 6 | 7 | - `urequests` 8 | - `urllib.parse` 9 | 10 | ## 目前完成的功能 11 | 12 | - [ ] 机器视觉 13 | - [ ] OCR 14 | - [x] 通用OCR 15 | - [x] 行驶证驾驶证OCR 16 | - [x] 行驶证驾驶证OCR 17 | - [x] 手写体OCR 18 | - [x] 车牌OCR 19 | - [x] 名片OCR 20 | - [x] 银行卡OCR 21 | - [ ] 身份证OCR 22 | - [ ] 营业执照OCR 23 | 24 | - [ ] 人脸与人体识别 25 | - [ ] 人脸识别 26 | - [ ] 人脸检测与分析 27 | - [ ] 多人脸检测 28 | - [ ] 人脸对比 29 | - [ ] 跨年龄人脸识别 30 | - [ ] 五官定位 31 | - [ ] 人脸识别 32 | - [ ] 人脸验证 33 | - [ ] 个体管理 34 | - [ ] 个体创建 35 | - [ ] 删除个体 36 | - [ ] 增加人脸 37 | - [ ] 删除人脸 38 | - [ ] 设置信息 39 | - [ ] 获取信息 40 | - [ ] 信息查询 41 | - [ ] 获取组列表 42 | - [ ] 获取个体列表 43 | - [ ] 获取人脸列表 44 | - [ ] 获取人脸信息 45 | 46 | - [ ] 图片特效 47 | - [x] 人脸美妆 48 | - [x] 人脸变妆 49 | - [x] 滤镜 50 | - [x] 滤镜(天天P图) 51 | - [x] 滤镜(AI Lab) 52 | - [x] 人脸融合 53 | - [x] 大头贴 54 | - [x] 颜龄检测 55 | 56 | - [ ] 图片识别 57 | - [ ] 物体场景识别 58 | - [ ] 场景识别 59 | - [ ] 物体识别 60 | - [x] 图片标签识别 61 | - [x] 看图说话 62 | - [x] 模糊图片检测 63 | - [x] 美食图片识别 64 | 65 | - [x] 敏感信息审核 66 | - [x] 暴恐识别 67 | - [x] 图片鉴黄 68 | 69 | - [ ] 自然语言处理 70 | - [x] 智能闲聊 71 | - [x] 机器翻译 72 | - [x] 文本翻译 73 | - [x] 文本翻译(AI Lab) 74 | - [x] 文本翻译(翻译君) 75 | - [x] 图片翻译 76 | - [ ] 语音翻译 77 | - [x] 语种识别 78 | - [ ] 基础文本分析 79 | - [ ] 分词 80 | - [ ] 词性标注 81 | - [ ] 专有名词识别 82 | - [ ] 同义词识别 83 | - [ ] 语义解析 84 | - [ ] 意图成分识别 85 | - [ ] 情感分析 86 | - [ ] 情感分析识别 87 | 88 | - [ ] 智能语音 89 | - [ ] 语音识别 90 | - [x] 语音识别-echo版 91 | - [ ] 语音识别-流式版(AI Lab) 92 | - [ ] 语音识别-流式版(WeChat AI) 93 | - [ ] 长语音识别 94 | - [ ] 关键词检索 95 | - [ ] 语音合成 96 | - [x] 语音合成(AI Lab) 97 | - [ ] 语音合成(优图) -------------------------------------------------------------------------------- /library/qqai/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['nlp', 'aai', 'vision'] 2 | 3 | from qqai.base import * 4 | import qqai.nlp 5 | import qqai.vision 6 | import qqai.aai 7 | from qqai import nlp ,aai, vision 8 | -------------------------------------------------------------------------------- /library/qqai/aai.py: -------------------------------------------------------------------------------- 1 | from qqai.base import * 2 | 3 | 4 | class AAI(QQAIBase): 5 | 6 | def aai_asr(self, speech, format=2, rate=16000): 7 | """语音识别-echo版""" 8 | self.api = 'https://api.ai.qq.com/fcgi-bin/aai/aai_asr' 9 | self.params = {'app_id': self.app_id, 10 | 'time_stamp': self._time_stamp(), 11 | 'nonce_str': self._time_stamp(), 12 | 'format': format, 13 | 'rate': rate, 14 | 'speech': self.get_base64(speech) 15 | } 16 | self.params['sign'] = self.get_sign(self.params) 17 | s = self.call_api(self.params) 18 | contants = s.read() 19 | s.close() 20 | return json.loads(contants) 21 | 22 | def aai_tts(self, text, save_path ,speaker =1, format=3, volume=10, speed=100, aht=0, apc=58): 23 | """语音合成(AI Lab)""" 24 | self.api = 'https://api.ai.qq.com/fcgi-bin/aai/aai_tts' 25 | self.params = {'app_id': self.app_id, 26 | 'time_stamp': self._time_stamp(), 27 | 'nonce_str': self._time_stamp(), 28 | 'text': text, 29 | 'speaker': speaker, 30 | 'format': format, 31 | 'volume': volume, 32 | 'speed': speed, 33 | 'aht': aht, 34 | 'apc': apc 35 | } 36 | self.params['sign'] = self.get_sign(self.params) 37 | s = self.call_api(self.params) 38 | resp = self.response_base64_decode(s,b"\"speech\": \"",save_path) 39 | return resp 40 | -------------------------------------------------------------------------------- /library/qqai/base.py: -------------------------------------------------------------------------------- 1 | from urllib import parse 2 | import hashlib 3 | import ubinascii 4 | import urequests 5 | import usocket 6 | import ussl 7 | import gc 8 | import time 9 | import os 10 | import sys 11 | import time 12 | import json 13 | sys.path.append('../') 14 | 15 | 16 | class QQAIBase: 17 | headers = {'Content-Type': 'application/x-www-form-urlencoded'} 18 | 19 | def __init__(self, app_id, app_key): 20 | self.app_id = app_id 21 | self.app_key = app_key 22 | self.params = {} 23 | 24 | def get_base64(self, media_path,save='.qqai_base64'): 25 | """获取媒体的Base64字符串 26 | 27 | :param media_param 媒体URL或文件路径 28 | """ 29 | gc.collect() 30 | BLOCK_SZ = 1024 * 6 31 | with open(save, 'wb') as tmpfile: 32 | if type(media_path) == str: 33 | if 'https://' in media_path or 'http://' in media_path: 34 | rsp = urequests.get(media_path) 35 | while True: 36 | chunk_data = rsp.raw.read(BLOCK_SZ) 37 | if chunk_data: 38 | chunk_data = ubinascii.b2a_base64( 39 | chunk_data).strip() 40 | # print(chunk_data.decode(),end='') 41 | tmpfile.write(chunk_data) 42 | else: 43 | rsp.close() 44 | break 45 | else: 46 | with open(media_path, 'rb') as media_file: 47 | while True: 48 | chunk_data = media_file.read(BLOCK_SZ) 49 | if chunk_data: 50 | chunk_data = ubinascii.b2a_base64( 51 | chunk_data).strip() 52 | # print(chunk_data.decode(),end='') 53 | tmpfile.write(chunk_data) 54 | else: 55 | break 56 | else: 57 | raise TypeError('URL or file path must be str type') 58 | print("Base64 temporary files size: %0.3fKB" % 59 | (os.stat(save)[6]//1000)) 60 | return tmpfile 61 | 62 | def get_sign(self, params): 63 | """获取签名""" 64 | gc.collect() 65 | uri_str = '' 66 | BLOCK_SZ = 1024*3 67 | # print("sign_str: ",end='') 68 | hash_str = hashlib.md5() 69 | for key in sorted(params.keys()): 70 | if not hasattr(params[key], 'read'): 71 | uri_str = '{}={}&'.format( 72 | key, parse.quote_plus(str(params[key]), safe='')) 73 | hash_str.update(uri_str) 74 | # print(uri_str,end='') 75 | else: 76 | uri_str = '{}=' .format(key) 77 | hash_str.update(uri_str) 78 | # print(uri_str,end='') 79 | with open('.qqai_base64', 'r') as file_base64: 80 | while True: 81 | chunk_data = file_base64.read(BLOCK_SZ) 82 | if chunk_data: 83 | uri_str = parse.quote_plus(chunk_data, safe='') 84 | hash_str.update(uri_str) 85 | # print(uri_str,end='') 86 | else: 87 | break 88 | hash_str.update('&') 89 | # print('&',end='') 90 | uri_str = 'app_key='+self.app_key 91 | hash_str.update(uri_str) 92 | # print(uri_str) 93 | return ubinascii.hexlify(hash_str.digest()).decode().upper() 94 | 95 | def call_api(self, params, api=None): 96 | gc.collect() 97 | if api is None: 98 | api = self.api 99 | rsp = self._qqai_post(api, params) 100 | gc.collect() 101 | return rsp 102 | 103 | def _time_stamp(self): 104 | return str(time.time() + 946656001) 105 | 106 | def _qqai_post(self, url, params): 107 | gc.collect() 108 | port = 443 109 | proto, dummy, host, path = url.split("/", 3) 110 | ai = usocket.getaddrinfo(host, port) 111 | # print(ai) 112 | addr = ai[0][4] 113 | s = usocket.socket() 114 | s.connect(addr) 115 | s = ussl.wrap_socket(s) 116 | s.write(b"%s /%s HTTP/1.0\r\n" % ('POST', path)) 117 | s.write(b"Host: %s\r\n" % host) 118 | s.write(b"Connection: keep-alive\r\n") 119 | s.write(b"Content-Type: application/x-www-form-urlencoded\r\n") 120 | s.write(b"Transfer-Encoding: chunked\r\n") 121 | 122 | s.write(b"\r\n") 123 | temp_str = '' 124 | list_key = list(params.keys()) 125 | for k in list_key: 126 | if not hasattr(params[k], 'read'): 127 | temp_str = k+'='+parse.quote_plus(str(params[k]), safe='') 128 | if k is not list_key[-1]: 129 | temp_str = temp_str + '&' 130 | 131 | chunk_size = hex(len(temp_str))[2:] 132 | s.write(chunk_size.encode()) 133 | # print(chunk_size,end='') 134 | s.write(b'\r\n') 135 | # print() 136 | s.write(temp_str.encode()) 137 | # print(temp_str,end='') 138 | s.write(b'\r\n') 139 | # print() 140 | 141 | else: 142 | temp_str = k+'=' 143 | chunk_size = hex(len(temp_str))[2:] 144 | s.write(chunk_size.encode()) 145 | s.write(b'\r\n') 146 | # print(chunk_size) 147 | s.write(temp_str.encode()) 148 | s.write(b'\r\n') 149 | # print(temp_str) 150 | with open('.qqai_base64', 'r') as file_base64: 151 | while True: 152 | temp_str = file_base64.read(1024*3) 153 | if temp_str: 154 | temp_str = parse.quote_plus(temp_str, safe='') 155 | chunk_size = hex(len(temp_str))[2:] 156 | s.write((chunk_size+'\r\n').encode()) 157 | # print(chunk_size) 158 | s.write((temp_str+'\r\n').encode()) 159 | # print(temp_str) 160 | else: 161 | break 162 | if k is not list_key[-1]: 163 | s.write(b'1\r\n') 164 | # print('1') 165 | s.write(b'&\r\n') 166 | # print('&') 167 | # chunked end 168 | s.write(b'0\r\n') 169 | # print('0') 170 | s.write(b'\r\n') 171 | # print('') 172 | 173 | l = s.readline() 174 | protover, status, msg = l.split(None, 2) 175 | # print(protover, status, msg) 176 | status = int(status) 177 | while True: 178 | l = s.readline() 179 | # print(l) 180 | if not l or l == b"\r\n": 181 | break 182 | return s 183 | 184 | def response_base64_decode(self, socket, key_str, path): 185 | gc.collect() 186 | _socket = socket 187 | BlOCK_SZ = 512*4 188 | chunked = _socket.read(1024) 189 | if len(chunked) < 1024 and chunked[-1:] == b'\n': 190 | _socket.close() 191 | return json.loads(chunked.decode()) 192 | # print(json.loads(chunked.decode())) 193 | else: 194 | with open(path, 'wb') as file_image: 195 | chunked = chunked.replace(b'\\', b'') 196 | index = chunked.find(key_str) 197 | if index != -1: 198 | chunked = chunked[index+len(key_str):] 199 | while (BlOCK_SZ-len(chunked)) != 0: 200 | miss_len = BlOCK_SZ-len(chunked) 201 | miss_str = _socket.read(miss_len).replace(b'\\', b'') 202 | chunked = chunked+miss_str 203 | # print(chunked.decode(),end='') 204 | file_image.write(ubinascii.a2b_base64(chunked)) 205 | while True: 206 | chunked = _socket.read(BlOCK_SZ).replace(b'\\', b'') 207 | while (BlOCK_SZ-len(chunked)) != 0: 208 | miss_len = BlOCK_SZ-len(chunked) 209 | miss_str = _socket.read(miss_len).replace(b'\\', b'') 210 | chunked = chunked+miss_str 211 | index_end = chunked.find(b"\"") 212 | if index_end != -1: 213 | chunked = chunked[:index_end] 214 | # print(chunked.decode(),end='') 215 | file_image.write(ubinascii.a2b_base64(chunked)) 216 | _socket.close() 217 | return True 218 | # print(chunked.decode(),end='') 219 | file_image.write(ubinascii.a2b_base64(chunked)) 220 | -------------------------------------------------------------------------------- /library/qqai/nlp.py: -------------------------------------------------------------------------------- 1 | from qqai.base import * 2 | 3 | class Nlp(QQAIBase): 4 | """自然语言""" 5 | 6 | def text_translate_ailab(self,text,type=0): 7 | """文本翻译(AI Lab)""" 8 | self.api = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_texttrans' 9 | 10 | self.params = {'app_id': self.app_id, 11 | 'time_stamp': self._time_stamp(), 12 | 'nonce_str': self._time_stamp(), 13 | 'type': type, 14 | 'text': text, 15 | } 16 | self.params['sign'] = self.get_sign(self.params) 17 | s = self.call_api(self.params) 18 | contants = s.read() 19 | s.close() 20 | return json.loads(contants) 21 | 22 | 23 | def text_translate_fanyi(self,text,source='auto', target='en'): 24 | """文本翻译(翻译君)""" 25 | self.api = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_texttranslate' 26 | self.params = {'app_id': self.app_id, 27 | 'time_stamp': self._time_stamp(), 28 | 'nonce_str': self._time_stamp(), 29 | 'text': text, 30 | 'source': source, 31 | 'target': target, 32 | } 33 | self.params['sign'] = self.get_sign(self.params) 34 | s = self.call_api(self.params) 35 | contants = s.read() 36 | s.close() 37 | return json.loads(contants) 38 | 39 | def text_detect(self, text,candidate_langs=None, force=0): 40 | """语种识别""" 41 | self.api = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_textdetect' 42 | if candidate_langs is None: 43 | candidate_langs = ['zh', 'en', 'jp', 'kr'] 44 | if type(candidate_langs) == str: 45 | candidate_langs_param = candidate_langs 46 | else: 47 | candidate_langs_param = '|'.join(candidate_langs) 48 | self.params = {'app_id': self.app_id, 49 | 'time_stamp': self._time_stamp(), 50 | 'nonce_str': self._time_stamp(), 51 | 'text': text, 52 | 'candidate_langs': candidate_langs_param, 53 | 'force': force 54 | } 55 | self.params['sign'] = self.get_sign(self.params) 56 | s = self.call_api(self.params) 57 | contants = s.read() 58 | s.close() 59 | return json.loads(contants) 60 | 61 | 62 | 63 | def image_translate(self,image_path, scene='doc', source='auto', target='auto'): 64 | """图片翻译""" 65 | self.api = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_imagetranslate' 66 | self.params = {'app_id': self.app_id, 67 | 'time_stamp': self._time_stamp(), 68 | 'nonce_str': self._time_stamp(), 69 | 'image': self.get_base64(image_path), 70 | 'session_id': self._time_stamp(), 71 | 'scene': scene, 72 | 'source': source, 73 | 'target': target, 74 | } 75 | self.params['sign'] = self.get_sign(self.params) 76 | s = self.call_api(self.params) 77 | contants = s.read() 78 | s.close() 79 | return json.loads(contants) 80 | 81 | def text_chat(self,question): 82 | """图片翻译""" 83 | self.api = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_textchat' 84 | self.params = {'app_id': self.app_id, 85 | 'time_stamp': self._time_stamp(), 86 | 'nonce_str': self._time_stamp(), 87 | 'session': self._time_stamp(), 88 | 'question': question 89 | } 90 | self.params['sign'] = self.get_sign(self.params) 91 | s = self.call_api(self.params) 92 | contants = s.read() 93 | s.close() 94 | return json.loads(contants) 95 | 96 | # class Text(QQAIBase): 97 | # """基础文本分析""" 98 | 99 | # def word_seg(self, text): 100 | # """"分词""" 101 | # self.api = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_wordseg' 102 | # self.params = {'app_id': self.app_id, 103 | # 'time_stamp': self._time_stamp(), 104 | # 'nonce_str': self._time_stamp(), 105 | # 'text': text 106 | # } 107 | # self.params['sign'] = self.get_sign(self.params) 108 | # s = self.call_api(self.params) 109 | # contants = s.read() 110 | # s.close() 111 | # return json.loads(contants) 112 | 113 | # def word_pos(self, text): 114 | # """"词性标注""" 115 | # self.api = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_wordpos' 116 | # self.params = {'app_id': self.app_id, 117 | # 'time_stamp': self._time_stamp(), 118 | # 'nonce_str': self._time_stamp(), 119 | # 'text': text 120 | # } 121 | # self.params['sign'] = self.get_sign(self.params) 122 | # s = self.call_api(self.params) 123 | # contants = s.read() 124 | # s.close() 125 | # return json.loads(contants) 126 | 127 | # def word_ner(self, text): 128 | # """"专有名词识别""" 129 | # self.api = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_wordner' 130 | # self.params = {'app_id': self.app_id, 131 | # 'time_stamp': self._time_stamp(), 132 | # 'nonce_str': self._time_stamp(), 133 | # 'text': text 134 | # } 135 | # self.params['sign'] = self.get_sign(self.params) 136 | # s = self.call_api(self.params) 137 | # contants = s.read() 138 | # s.close() 139 | # return json.loads(contants) 140 | 141 | # def word_syn(self, text): 142 | # """"同义词识别""" 143 | # self.api = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_wordsyn' 144 | # self.params = {'app_id': self.app_id, 145 | # 'time_stamp': self._time_stamp(), 146 | # 'nonce_str': self._time_stamp(), 147 | # 'text': text 148 | # } 149 | # self.params['sign'] = self.get_sign(self.params) 150 | # s = self.call_api(self.params) 151 | # contants = s.read() 152 | # s.close() 153 | # return json.loads(contants) 154 | 155 | # def word_com(self, text): 156 | # """"意图成分识别""" 157 | # self.api = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_wordcom' 158 | # self.params = {'app_id': self.app_id, 159 | # 'time_stamp': self._time_stamp(), 160 | # 'nonce_str': self._time_stamp(), 161 | # 'text': text 162 | # } 163 | # self.params['sign'] = self.get_sign(self.params) 164 | # s = self.call_api(self.params) 165 | # contants = s.read() 166 | # s.close() 167 | # return json.loads(contants) 168 | 169 | # def text_polar(self, text): 170 | # """"情感分析识别""" 171 | # self.api = 'https://api.ai.qq.com/fcgi-bin/nlp/nlp_textpolar' 172 | # self.params = {'app_id': self.app_id, 173 | # 'time_stamp': self._time_stamp(), 174 | # 'nonce_str': self._time_stamp(), 175 | # 'text': text 176 | # } 177 | # self.params['sign'] = self.get_sign(self.params) 178 | # s = self.call_api(self.params) 179 | # contants = s.read() 180 | # s.close() 181 | # return json.loads(contants) 182 | 183 | 184 | -------------------------------------------------------------------------------- /library/qqai/vision/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['picture', 'ocr', 'face'] 2 | 3 | from qqai.vision import picture, ocr, face -------------------------------------------------------------------------------- /library/qqai/vision/face.py: -------------------------------------------------------------------------------- 1 | from qqai.base import * 2 | 3 | class Face(QQAIBase): 4 | 5 | def detect_face(self,image,mode=1): 6 | """人脸检测与分析""" 7 | self.api = 'https://api.ai.qq.com/fcgi-bin/face/face_detectface' 8 | self.params = {'app_id': self.app_id, 9 | 'time_stamp': self._time_stamp(), 10 | 'nonce_str': self._time_stamp(), 11 | 'mode': mode, 12 | 'image': self.get_base64(image) 13 | } 14 | self.params['sign'] = self.get_sign(self.params) 15 | s = self.call_api(self.params) 16 | contants = s.read() 17 | s.close() 18 | return json.loads(contants) 19 | -------------------------------------------------------------------------------- /library/qqai/vision/ocr.py: -------------------------------------------------------------------------------- 1 | from qqai.base import * 2 | 3 | class OCR(QQAIBase): 4 | 5 | def general_ocr(self,image): 6 | """通用OCR""" 7 | self.api = 'https://api.ai.qq.com/fcgi-bin/ocr/ocr_generalocr' 8 | 9 | self.params = {'app_id': self.app_id, 10 | 'time_stamp': self._time_stamp(), 11 | 'nonce_str': self._time_stamp(), 12 | 'image': self.get_base64(image) 13 | } 14 | self.params['sign'] = self.get_sign(self.params) 15 | s = self.call_api(self.params) 16 | contants = s.read() 17 | s.close() 18 | return json.loads(contants) 19 | 20 | def driverlicense_ocr(self,image,type): 21 | """行驶证驾驶证OCR""" 22 | self.api = 'https://api.ai.qq.com/fcgi-bin/ocr/ocr_driverlicenseocr' 23 | 24 | self.params = {'app_id': self.app_id, 25 | 'time_stamp': self._time_stamp(), 26 | 'nonce_str': self._time_stamp(), 27 | 'image': self.get_base64(image), 28 | 'type': type 29 | } 30 | self.params['sign'] = self.get_sign(self.params) 31 | s = self.call_api(self.params) 32 | contants = s.read() 33 | s.close() 34 | return json.loads(contants) 35 | 36 | def handwriting_ocr(self,image): 37 | """手写体OCR""" 38 | self.api = 'https://api.ai.qq.com/fcgi-bin/ocr/ocr_handwritingocr' 39 | 40 | self.params = {'app_id': self.app_id, 41 | 'time_stamp': self._time_stamp(), 42 | 'nonce_str': self._time_stamp(), 43 | 'image': self.get_base64(image), 44 | } 45 | self.params['sign'] = self.get_sign(self.params) 46 | s = self.call_api(self.params) 47 | contants = s.read() 48 | s.close() 49 | return json.loads(contants) 50 | 51 | def plate_ocr(self,image): 52 | """车牌OCR""" 53 | self.api = 'https://api.ai.qq.com/fcgi-bin/ocr/ocr_plateocr' 54 | 55 | self.params = {'app_id': self.app_id, 56 | 'time_stamp': self._time_stamp(), 57 | 'nonce_str': self._time_stamp(), 58 | 'image': self.get_base64(image), 59 | } 60 | self.params['sign'] = self.get_sign(self.params) 61 | s = self.call_api(self.params) 62 | contants = s.read() 63 | s.close() 64 | return json.loads(contants) 65 | 66 | def bc_ocr(self,image): 67 | """名片OCR""" 68 | self.api = 'https://api.ai.qq.com/fcgi-bin/ocr/ocr_bcocr' 69 | 70 | self.params = {'app_id': self.app_id, 71 | 'time_stamp': self._time_stamp(), 72 | 'nonce_str': self._time_stamp(), 73 | 'image': self.get_base64(image), 74 | } 75 | self.params['sign'] = self.get_sign(self.params) 76 | s = self.call_api(self.params) 77 | contants = s.read() 78 | s.close() 79 | return json.loads(contants) 80 | 81 | def creditcard_ocr(self,image): 82 | """银行卡OCR""" 83 | self.api = 'https://api.ai.qq.com/fcgi-bin/ocr/ocr_creditcardocr' 84 | 85 | self.params = {'app_id': self.app_id, 86 | 'time_stamp': self._time_stamp(), 87 | 'nonce_str': self._time_stamp(), 88 | 'image': self.get_base64(image), 89 | } 90 | self.params['sign'] = self.get_sign(self.params) 91 | s = self.call_api(self.params) 92 | contants = s.read() 93 | s.close() 94 | return json.loads(contants) 95 | 96 | 97 | -------------------------------------------------------------------------------- /library/qqai/vision/picture.py: -------------------------------------------------------------------------------- 1 | from qqai.base import * 2 | 3 | class Picture(QQAIBase): 4 | 5 | def imagetotext(self,image): 6 | """看图说话""" 7 | self.api = 'https://api.ai.qq.com/fcgi-bin/vision/vision_imgtotext' 8 | 9 | self.params = {'app_id': self.app_id, 10 | 'session_id': self._time_stamp(), 11 | 'time_stamp': self._time_stamp(), 12 | 'nonce_str': self._time_stamp(), 13 | 'image': self.get_base64(image) 14 | } 15 | self.params['sign'] = self.get_sign(self.params) 16 | s = self.call_api(self.params) 17 | contants = s.read() 18 | s.close() 19 | return json.loads(contants) 20 | 21 | def image_tag(self,image): 22 | """多标签识别""" 23 | self.api = 'https://api.ai.qq.com/fcgi-bin/image/image_tag' 24 | 25 | self.params = {'app_id': self.app_id, 26 | 'time_stamp': self._time_stamp(), 27 | 'nonce_str': self._time_stamp(), 28 | 'image': self.get_base64(image) 29 | } 30 | self.params['sign'] = self.get_sign(self.params) 31 | s = self.call_api(self.params) 32 | contants = s.read() 33 | s.close() 34 | return json.loads(contants) 35 | 36 | def isfuzzy(self,image): 37 | """模糊图片检测""" 38 | self.api = 'https://api.ai.qq.com/fcgi-bin/image/image_fuzzy' 39 | 40 | self.params = {'app_id': self.app_id, 41 | 'time_stamp': self._time_stamp(), 42 | 'nonce_str': self._time_stamp(), 43 | 'image': self.get_base64(image) 44 | } 45 | self.params['sign'] = self.get_sign(self.params) 46 | s = self.call_api(self.params) 47 | contants = s.read() 48 | s.close() 49 | return json.loads(contants) 50 | def isfood(self,image): 51 | """美食图片识别""" 52 | self.api = 'https://api.ai.qq.com/fcgi-bin/image/image_food' 53 | 54 | self.params = {'app_id': self.app_id, 55 | 'time_stamp': self._time_stamp(), 56 | 'nonce_str': self._time_stamp(), 57 | 'image': self.get_base64(image) 58 | } 59 | self.params['sign'] = self.get_sign(self.params) 60 | s = self.call_api(self.params) 61 | contants = s.read() 62 | s.close() 63 | return json.loads(contants) 64 | 65 | # def scener_recog(self,image,topk=5,farmat=1): 66 | # """场景识别 (调试不能使用)""" 67 | # self.api = 'https://api.ai.qq.com/fcgi-bin/vision/vision_scener' 68 | 69 | # self.params = {'app_id': self.app_id, 70 | # 'time_stamp': self._time_stamp(), 71 | # 'nonce_str': self._time_stamp(), 72 | # 'format': farmat , 73 | # 'topk': topk , 74 | # 'image': self.get_base64(image) 75 | # } 76 | # self.params['sign'] = self.get_sign(self.params) 77 | # s = self.call_api(self.params) 78 | # contants = s.read() 79 | # s.close() 80 | # return json.loads(contants) 81 | 82 | # def object_recog(self,image,topk=5,farmat=1): 83 | # """物体识别 (调试不能使用)""" 84 | # self.api = 'https://api.ai.qq.com/fcgi-bin/vision/vision_objectr' 85 | 86 | # self.params = {'app_id': self.app_id, 87 | # 'time_stamp': self._time_stamp(), 88 | # 'nonce_str': self._time_stamp(), 89 | # 'format': farmat , 90 | # 'topk': topk , 91 | # 'image': self.get_base64(image) 92 | # } 93 | # self.params['sign'] = self.get_sign(self.params) 94 | # s = self.call_api(self.params) 95 | # contants = s.read() 96 | # s.close() 97 | # return json.loads(contants) 98 | 99 | def terrorism(self,image): 100 | """暴恐图片识别""" 101 | self.api = 'https://api.ai.qq.com/fcgi-bin/image/image_terrorism' 102 | 103 | self.params = {'app_id': self.app_id, 104 | 'time_stamp': self._time_stamp(), 105 | 'nonce_str': self._time_stamp(), 106 | 'image': self.get_base64(image) 107 | } 108 | self.params['sign'] = self.get_sign(self.params) 109 | s = self.call_api(self.params) 110 | contants = s.read() 111 | s.close() 112 | return json.loads(contants) 113 | 114 | def porn(self,image): 115 | """智能鉴黄""" 116 | self.api = 'https://api.ai.qq.com/fcgi-bin/vision/vision_porn' 117 | self.params = {'app_id': self.app_id, 118 | 'time_stamp': self._time_stamp(), 119 | 'nonce_str': self._time_stamp(), 120 | 'image': self.get_base64(image) 121 | } 122 | self.params['sign'] = self.get_sign(self.params) 123 | s = self.call_api(self.params) 124 | contants = s.read() 125 | s.close() 126 | return json.loads(contants) 127 | 128 | def image_filter_ptu(self,image, save_path,filter=1): 129 | """图片滤镜(天天P图)""" 130 | self.api = 'https://api.ai.qq.com/fcgi-bin/ptu/ptu_imgfilter' 131 | self.params = {'app_id': self.app_id, 132 | 'time_stamp': self._time_stamp(), 133 | 'nonce_str': self._time_stamp(), 134 | 'filter': filter, 135 | 'image': self.get_base64(image) 136 | } 137 | self.params['sign'] = self.get_sign(self.params) 138 | s = self.call_api(self.params) 139 | resp = self.response_base64_decode(s,b"\"image\": \"", save_path) 140 | return resp 141 | 142 | def image_filter_ailab(self,image, save_path,filter=1): 143 | """图片滤镜(AI Lab)""" 144 | self.api = 'https://api.ai.qq.com/fcgi-bin/vision/vision_imgfilter' 145 | self.params = {'app_id': self.app_id, 146 | 'time_stamp': self._time_stamp(), 147 | 'nonce_str': self._time_stamp(), 148 | 'session_id': self._time_stamp(), 149 | 'filter': filter, 150 | 'image': self.get_base64(image) 151 | } 152 | self.params['sign'] = self.get_sign(self.params) 153 | s = self.call_api(self.params) 154 | resp = self.response_base64_decode(s,b"\"image\": \"", save_path) 155 | return resp 156 | 157 | def face_cosmetic(self,image, save_path,cosmetic=1): 158 | """人脸美妆""" 159 | self.api = 'https://api.ai.qq.com/fcgi-bin/ptu/ptu_facecosmetic' 160 | self.params = {'app_id': self.app_id, 161 | 'time_stamp': self._time_stamp(), 162 | 'nonce_str': self._time_stamp(), 163 | 'cosmetic': cosmetic, 164 | 'image': self.get_base64(image) 165 | } 166 | self.params['sign'] = self.get_sign(self.params) 167 | s = self.call_api(self.params) 168 | resp = self.response_base64_decode(s,b"\"image\": \"", save_path) 169 | return resp 170 | 171 | def face_decoration(self,image, save_path,decoration=1): 172 | """人脸变妆""" 173 | self.api = 'https://api.ai.qq.com/fcgi-bin/ptu/ptu_facedecoration' 174 | self.params = {'app_id': self.app_id, 175 | 'time_stamp': self._time_stamp(), 176 | 'nonce_str': self._time_stamp(), 177 | 'decoration': decoration, 178 | 'image': self.get_base64(image) 179 | } 180 | self.params['sign'] = self.get_sign(self.params) 181 | s = self.call_api(self.params) 182 | resp = self.response_base64_decode(s,b"\"image\": \"", save_path) 183 | return resp 184 | 185 | def face_sticker(self,image, save_path,sticker=1): 186 | """大头贴""" 187 | self.api = 'https://api.ai.qq.com/fcgi-bin/ptu/ptu_facesticker' 188 | self.params = {'app_id': self.app_id, 189 | 'time_stamp': self._time_stamp(), 190 | 'nonce_str': self._time_stamp(), 191 | 'sticker': sticker, 192 | 'image': self.get_base64(image) 193 | } 194 | self.params['sign'] = self.get_sign(self.params) 195 | s = self.call_api(self.params) 196 | resp = self.response_base64_decode(s,b"\"image\": \"", save_path) 197 | return resp 198 | 199 | -------------------------------------------------------------------------------- /library/urllib/README.md: -------------------------------------------------------------------------------- 1 | 2 | 用于URL编码,裁剪于[MicroPython-lib@urllib.parse](https://github.com/micropython/micropython-lib/tree/master/urllib.parse) 3 | 4 | 保留以下函数: 5 | - quote 6 | - quote_from_bytes 7 | - urlencode 8 | - quote_plus -------------------------------------------------------------------------------- /library/urllib/parse.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | urllib.paese simplified version 4 | fork by Micropython-lib 5 | 6 | MIT license; Copyright (c) 2019 tangliufeng@LabPlus 7 | """ 8 | 9 | 10 | class defaultdict: 11 | 12 | @staticmethod 13 | def __new__(cls, default_factory=None, **kwargs): 14 | # Some code (e.g. urllib.urlparse) expects that basic defaultdict 15 | # functionality will be available to subclasses without them 16 | # calling __init__(). 17 | self = super(defaultdict, cls).__new__(cls) 18 | self.d = {} 19 | return self 20 | 21 | def __init__(self, default_factory=None, **kwargs): 22 | self.d = kwargs 23 | self.default_factory = default_factory 24 | 25 | def __getitem__(self, key): 26 | try: 27 | return self.d[key] 28 | except KeyError: 29 | v = self.__missing__(key) 30 | self.d[key] = v 31 | return v 32 | 33 | def __setitem__(self, key, v): 34 | self.d[key] = v 35 | 36 | def __delitem__(self, key): 37 | del self.d[key] 38 | 39 | def __contains__(self, key): 40 | return key in self.d 41 | 42 | def __missing__(self, key): 43 | if self.default_factory is None: 44 | raise KeyError(key) 45 | return self.default_factory() 46 | 47 | 48 | 49 | class Quoter(defaultdict): 50 | """A mapping from bytes (in range(0,256)) to strings. 51 | 52 | String values are percent-encoded byte values, unless the key < 128, and 53 | in the "safe" set (either the specified safe set, or default set). 54 | """ 55 | # Keeps a cache internally, using defaultdict, for efficiency (lookups 56 | # of cached keys don't call Python code at all). 57 | def __init__(self, safe): 58 | """safe: bytes object.""" 59 | self.safe = _ALWAYS_SAFE.union(safe) 60 | 61 | def __repr__(self): 62 | # Without this, will just display as a defaultdict 63 | return "" % dict(self) 64 | 65 | def __missing__(self, b): 66 | # Handle a cache miss. Store quoted string in cache and return. 67 | res = chr(b) if b in self.safe else '%{:02X}'.format(b) 68 | self[b] = res 69 | return res 70 | 71 | 72 | 73 | def quote_plus(string, safe='', encoding=None, errors=None): 74 | """Like quote(), but also replace ' ' with '+', as required for quoting 75 | HTML form values. Plus signs in the original string are escaped unless 76 | they are included in safe. It also does not have safe default to '/'. 77 | """ 78 | # Check if ' ' in string, where string may either be a str or bytes. If 79 | # there are no spaces, the regular quote will produce the right answer. 80 | if ((isinstance(string, str) and ' ' not in string) or 81 | (isinstance(string, bytes) and b' ' not in string)): 82 | return quote(string, safe, encoding, errors) 83 | if isinstance(safe, str): 84 | space = ' ' 85 | else: 86 | space = b' ' 87 | string = quote(string, safe + space, encoding, errors) 88 | return string.replace(' ', '+') 89 | 90 | _ALWAYS_SAFE = frozenset(b'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 91 | b'abcdefghijklmnopqrstuvwxyz' 92 | b'0123456789' 93 | b'_.-') 94 | _ALWAYS_SAFE_BYTES = bytes(_ALWAYS_SAFE) 95 | _safe_quoters = {} 96 | 97 | def quote(string, safe='/', encoding=None, errors=None): 98 | """quote('abc def') -> 'abc%20def' 99 | 100 | Each part of a URL, e.g. the path info, the query, etc., has a 101 | different set of reserved characters that must be quoted. 102 | 103 | RFC 2396 Uniform Resource Identifiers (URI): Generic Syntax lists 104 | the following reserved characters. 105 | 106 | reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | 107 | "$" | "," 108 | 109 | Each of these characters is reserved in some component of a URL, 110 | but not necessarily in all of them. 111 | 112 | By default, the quote function is intended for quoting the path 113 | section of a URL. Thus, it will not encode '/'. This character 114 | is reserved, but in typical usage the quote function is being 115 | called on a path where the existing slash characters are used as 116 | reserved characters. 117 | 118 | string and safe may be either str or bytes objects. encoding must 119 | not be specified if string is a str. 120 | 121 | The optional encoding and errors parameters specify how to deal with 122 | non-ASCII characters, as accepted by the str.encode method. 123 | By default, encoding='utf-8' (characters are encoded with UTF-8), and 124 | errors='strict' (unsupported characters raise a UnicodeEncodeError). 125 | """ 126 | if isinstance(string, str): 127 | if not string: 128 | return string 129 | if encoding is None: 130 | encoding = 'utf-8' 131 | if errors is None: 132 | errors = 'strict' 133 | string = string.encode(encoding, errors) 134 | else: 135 | if encoding is not None: 136 | raise TypeError("quote() doesn't support 'encoding' for bytes") 137 | if errors is not None: 138 | raise TypeError("quote() doesn't support 'errors' for bytes") 139 | return quote_from_bytes(string, safe) 140 | 141 | 142 | def quote_from_bytes(bs, safe='/'): 143 | """Like quote(), but accepts a bytes object rather than a str, and does 144 | not perform string-to-bytes encoding. It always returns an ASCII string. 145 | quote_from_bytes(b'abc def\x3f') -> 'abc%20def%3f' 146 | """ 147 | if not isinstance(bs, (bytes, bytearray)): 148 | raise TypeError("quote_from_bytes() expected bytes") 149 | if not bs: 150 | return '' 151 | if isinstance(safe, str): 152 | # Normalize 'safe' by converting to bytes and removing non-ASCII chars 153 | safe = safe.encode('ascii', 'ignore') 154 | else: 155 | safe = bytes([c for c in safe if c < 128]) 156 | if not bs.rstrip(_ALWAYS_SAFE_BYTES + safe): 157 | return bs.decode() 158 | try: 159 | quoter = _safe_quoters[safe] 160 | except KeyError: 161 | _safe_quoters[safe] = quoter = Quoter(safe).__getitem__ 162 | return ''.join([quoter(char) for char in bs]) 163 | 164 | def urlencode(query, doseq=False, safe='', encoding=None, errors=None): 165 | """Encode a dict or sequence of two-element tuples into a URL query string. 166 | 167 | If any values in the query arg are sequences and doseq is true, each 168 | sequence element is converted to a separate parameter. 169 | 170 | If the query arg is a sequence of two-element tuples, the order of the 171 | parameters in the output will match the order of parameters in the 172 | input. 173 | 174 | The components of a query arg may each be either a string or a bytes type. 175 | When a component is a string, the safe, encoding and error parameters are 176 | sent to the quote_plus function for encoding. 177 | """ 178 | 179 | if hasattr(query, "items"): 180 | query = query.items() 181 | else: 182 | # It's a bother at times that strings and string-like objects are 183 | # sequences. 184 | try: 185 | # non-sequence items should not work with len() 186 | # non-empty strings will fail this 187 | if len(query) and not isinstance(query[0], tuple): 188 | raise TypeError 189 | # Zero-length sequences of all types will get here and succeed, 190 | # but that's a minor nit. Since the original implementation 191 | # allowed empty dicts that type of behavior probably should be 192 | # preserved for consistency 193 | except TypeError: 194 | # ty, va, tb = sys.exc_info() 195 | raise TypeError("not a valid non-string sequence " 196 | "or mapping object")#.with_traceback(tb) 197 | 198 | l = [] 199 | if not doseq: 200 | for k, v in query: 201 | if isinstance(k, bytes): 202 | k = quote_plus(k, safe) 203 | else: 204 | k = quote_plus(str(k), safe, encoding, errors) 205 | 206 | if isinstance(v, bytes): 207 | v = quote_plus(v, safe) 208 | else: 209 | v = quote_plus(str(v), safe, encoding, errors) 210 | l.append(k + '=' + v) 211 | else: 212 | for k, v in query: 213 | if isinstance(k, bytes): 214 | k = quote_plus(k, safe) 215 | else: 216 | k = quote_plus(str(k), safe, encoding, errors) 217 | 218 | if isinstance(v, bytes): 219 | v = quote_plus(v, safe) 220 | l.append(k + '=' + v) 221 | elif isinstance(v, str): 222 | v = quote_plus(v, safe, encoding, errors) 223 | l.append(k + '=' + v) 224 | else: 225 | try: 226 | # Is this a sufficient test for sequence-ness? 227 | x = len(v) 228 | except TypeError: 229 | # not a sequence 230 | v = quote_plus(str(v), safe, encoding, errors) 231 | l.append(k + '=' + v) 232 | else: 233 | # loop over the sequence 234 | for elt in v: 235 | if isinstance(elt, bytes): 236 | elt = quote_plus(elt, safe) 237 | else: 238 | elt = quote_plus(str(elt), safe, encoding, errors) 239 | l.append(k + '=' + elt) 240 | return '&'.join(l) -------------------------------------------------------------------------------- /library/yeelight/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 labplus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /library/yeelight/MANIFEST: -------------------------------------------------------------------------------- 1 | # file GENERATED by distutils, do NOT edit 2 | README.md 3 | setup.cfg 4 | setup.py 5 | -------------------------------------------------------------------------------- /library/yeelight/MANIFEST.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/library/yeelight/MANIFEST.in -------------------------------------------------------------------------------- /library/yeelight/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 概述 4 | 5 | `yeelight` 是掌控板或micropython驱动库,用于控制局域网内的YeeLight智能灯泡/小米智能灯具设备。 6 | 7 | ## 库的安装方法 8 | 9 | 可通过以下任一方法进行安装。 10 | 1. 将项目中的`yeelight.py` 11 | 2. 在掌控板REPL界面中,使用upip安装,步骤如下: 12 | * 前置条件需要掌控板连接网络 13 | * 导入upip模块,执行`import upip` 14 | * 执行`upip.install('mPython-yeelight') 15 | 16 | ```python 17 | >>> import upip 18 | >>> upip.install('mPython-yeelight') 19 | ``` 20 | 21 | ## 使用 22 | 23 | 准备工作: 24 | 25 | * YeeLight智能灯泡在使用前,须要先配置好连接好wifi,并将 `局域网控制` 功能打开。 26 | * 掌控板确保已与智能灯泡在同个局域网内,并网络通畅。 27 | 28 | yeelight Library Documentation:https://mpython-lib.readthedocs.io
29 | 30 | YeeLight第三方控制协议:https://www.yeelight.com/download/Yeelight_Inter-Operation_Spec.pdf 31 | 32 | ## 简单示例 33 | 34 | ```python 35 | from mpython import * 36 | from yeelight import * 37 | 38 | my_wifi = wifi() # 连接到与YeeLight相同的局域网内 39 | my_wifi.connectWiFi("","") 40 | 41 | 42 | discover_bulbs() # 发现局域网内YeeLight的设备信息 43 | 44 | bulb=Bulb("192.168.0.7") # 构建Bulb类用于控制,传入IP参数 45 | 46 | bulb.turn_on() # 开灯 47 | sleep(2) 48 | bulb.turn_off() # 关灯 49 | sleep(2) 50 | bulb.toggle() # 翻转 51 | sleep(2) 52 | bulb.set_rgb(255,0,0) # 设置RGB值 53 | bulb.set_brightness(50) # 调节亮度 54 | sleep(2) 55 | bulb.set_hsv(180,100) # 设置HSV值 56 | sleep(2) 57 | ``` 58 | 59 | ## 执照 60 | 61 | 所有代码均在MIT许可下发布。 -------------------------------------------------------------------------------- /library/yeelight/examples/yeelight_simple.py: -------------------------------------------------------------------------------- 1 | from mpython import * 2 | from yeelight import * 3 | 4 | my_wifi = wifi() # 连接到与YeeLight相同的局域网内 5 | my_wifi.connectWiFi("","") 6 | 7 | 8 | discover_bulbs() # 发现局域网内YeeLight的设备信息 9 | 10 | bulb=Bulb("192.168.0.7") # 构建Bulb类用于控制,传入IP参数 11 | 12 | bulb.turn_on() # 开灯 13 | sleep(2) 14 | bulb.turn_off() # 关灯 15 | sleep(2) 16 | bulb.toggle() # 翻转 17 | sleep(2) 18 | bulb.set_rgb(255,0,0) # 设置RGB值 19 | bulb.set_brightness(50) # 调节亮度 20 | sleep(2) 21 | bulb.set_hsv(180,100) # 设置HSV值 22 | sleep(2) 23 | -------------------------------------------------------------------------------- /library/yeelight/images/mpython.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/library/yeelight/images/mpython.png -------------------------------------------------------------------------------- /library/yeelight/images/yeelight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/library/yeelight/images/yeelight.png -------------------------------------------------------------------------------- /library/yeelight/optimize_upip.py: -------------------------------------------------------------------------------- 1 | # 2 | # This script optimizes a Python source distribution tarball as produced by 3 | # "python3 setup.py sdist" command for MicroPython's native package manager, 4 | # upip. Optimization includes: 5 | # * Removing metadata files not used by upip (this includes setup.py) 6 | # * Recompressing gzip archive with 4K dictionary size so it can be 7 | # installed even on low-heap targets. 8 | # 9 | import sys 10 | import os 11 | import zlib 12 | from subprocess import Popen, PIPE 13 | import glob 14 | import tarfile 15 | import re 16 | import io 17 | 18 | 19 | def gzip_4k(inf, fname): 20 | comp = zlib.compressobj(level=9, wbits=16 + 12) 21 | with open(fname + ".out", "wb") as outf: 22 | while 1: 23 | data = inf.read(1024) 24 | if not data: 25 | break 26 | outf.write(comp.compress(data)) 27 | outf.write(comp.flush()) 28 | os.rename(fname, fname + ".orig") 29 | os.rename(fname + ".out", fname) 30 | 31 | 32 | def recompress(fname): 33 | with Popen(["gzip", "-d", "-c", fname], stdout=PIPE).stdout as inf: 34 | gzip_4k(inf, fname) 35 | 36 | def find_latest(dir): 37 | res = [] 38 | for fname in glob.glob(dir + "/*.gz"): 39 | st = os.stat(fname) 40 | res.append((st.st_mtime, fname)) 41 | res.sort() 42 | latest = res[-1][1] 43 | return latest 44 | 45 | 46 | def recompress_latest(dir): 47 | latest = find_latest(dir) 48 | print(latest) 49 | recompress(latest) 50 | 51 | 52 | FILTERS = [ 53 | # include, exclude, repeat 54 | (r".+\.egg-info/(PKG-INFO|requires\.txt)", r"setup.py$"), 55 | (r".+\.py$", r"[^/]+$"), 56 | (None, r".+\.egg-info/.+"), 57 | ] 58 | 59 | 60 | outbuf = io.BytesIO() 61 | 62 | def filter_tar(name): 63 | fin = tarfile.open(name, "r:gz") 64 | fout = tarfile.open(fileobj=outbuf, mode="w") 65 | for info in fin: 66 | # print(info) 67 | if not "/" in info.name: 68 | continue 69 | fname = info.name.split("/", 1)[1] 70 | include = None 71 | 72 | for inc_re, exc_re in FILTERS: 73 | if include is None and inc_re: 74 | if re.match(inc_re, fname): 75 | include = True 76 | 77 | if include is None and exc_re: 78 | if re.match(exc_re, fname): 79 | include = False 80 | 81 | if include is None: 82 | include = True 83 | 84 | if include: 85 | print("Including:", fname) 86 | else: 87 | print("Excluding:", fname) 88 | continue 89 | 90 | farch = fin.extractfile(info) 91 | fout.addfile(info, farch) 92 | fout.close() 93 | fin.close() 94 | 95 | 96 | 97 | from setuptools import Command 98 | 99 | class OptimizeUpip(Command): 100 | 101 | user_options = [] 102 | 103 | def run(self): 104 | latest = find_latest("dist") 105 | filter_tar(latest) 106 | outbuf.seek(0) 107 | gzip_4k(outbuf, latest) 108 | 109 | def initialize_options(self): 110 | pass 111 | 112 | def finalize_options(self): 113 | pass 114 | 115 | 116 | # For testing only 117 | if __name__ == "__main__": 118 | # recompress_latest(sys.argv[1]) 119 | filter_tar(sys.argv[1]) 120 | outbuf.seek(0) 121 | gzip_4k(outbuf, sys.argv[1]) 122 | -------------------------------------------------------------------------------- /library/yeelight/sdist_upip.py: -------------------------------------------------------------------------------- 1 | # 2 | # This module overrides distutils (also compatible with setuptools) "sdist" 3 | # command to perform pre- and post-processing as required for MicroPython's 4 | # upip package manager. 5 | # 6 | # Preprocessing steps: 7 | # * Creation of Python resource module (R.py) from each top-level package's 8 | # resources. 9 | # Postprocessing steps: 10 | # * Removing metadata files not used by upip (this includes setup.py) 11 | # * Recompressing gzip archive with 4K dictionary size so it can be 12 | # installed even on low-heap targets. 13 | # 14 | import sys 15 | import os 16 | import zlib 17 | from subprocess import Popen, PIPE 18 | import glob 19 | import tarfile 20 | import re 21 | import io 22 | 23 | from distutils.filelist import FileList 24 | from setuptools.command.sdist import sdist as _sdist 25 | 26 | 27 | def gzip_4k(inf, fname): 28 | comp = zlib.compressobj(level=9, wbits=16 + 12) 29 | with open(fname + ".out", "wb") as outf: 30 | while 1: 31 | data = inf.read(1024) 32 | if not data: 33 | break 34 | outf.write(comp.compress(data)) 35 | outf.write(comp.flush()) 36 | os.rename(fname, fname + ".orig") 37 | os.rename(fname + ".out", fname) 38 | 39 | 40 | FILTERS = [ 41 | # include, exclude, repeat 42 | (r".+\.egg-info/(PKG-INFO|requires\.txt)", r"setup.py$"), 43 | (r".+\.py$", r"[^/]+$"), 44 | (None, r".+\.egg-info/.+"), 45 | ] 46 | 47 | 48 | outbuf = io.BytesIO() 49 | 50 | def filter_tar(name): 51 | fin = tarfile.open(name, "r:gz") 52 | fout = tarfile.open(fileobj=outbuf, mode="w") 53 | for info in fin: 54 | # print(info) 55 | if not "/" in info.name: 56 | continue 57 | fname = info.name.split("/", 1)[1] 58 | include = None 59 | 60 | for inc_re, exc_re in FILTERS: 61 | if include is None and inc_re: 62 | if re.match(inc_re, fname): 63 | include = True 64 | 65 | if include is None and exc_re: 66 | if re.match(exc_re, fname): 67 | include = False 68 | 69 | if include is None: 70 | include = True 71 | 72 | if include: 73 | print("including:", fname) 74 | else: 75 | print("excluding:", fname) 76 | continue 77 | 78 | farch = fin.extractfile(info) 79 | fout.addfile(info, farch) 80 | fout.close() 81 | fin.close() 82 | 83 | 84 | def make_resource_module(manifest_files): 85 | resources = [] 86 | # Any non-python file included in manifest is resource 87 | for fname in manifest_files: 88 | ext = fname.rsplit(".", 1)[1] 89 | if ext != "py": 90 | resources.append(fname) 91 | 92 | if resources: 93 | print("creating resource module R.py") 94 | resources.sort() 95 | last_pkg = None 96 | r_file = None 97 | for fname in resources: 98 | try: 99 | pkg, res_name = fname.split("/", 1) 100 | except ValueError: 101 | print("not treating %s as a resource" % fname) 102 | continue 103 | if last_pkg != pkg: 104 | last_pkg = pkg 105 | if r_file: 106 | r_file.write("}\n") 107 | r_file.close() 108 | r_file = open(pkg + "/R.py", "w") 109 | r_file.write("R = {\n") 110 | 111 | with open(fname, "rb") as f: 112 | r_file.write("%r: %r,\n" % (res_name, f.read())) 113 | 114 | if r_file: 115 | r_file.write("}\n") 116 | r_file.close() 117 | 118 | 119 | class sdist(_sdist): 120 | 121 | def run(self): 122 | self.filelist = FileList() 123 | self.get_file_list() 124 | make_resource_module(self.filelist.files) 125 | 126 | r = super().run() 127 | 128 | assert len(self.archive_files) == 1 129 | print("filtering files and recompressing with 4K dictionary") 130 | filter_tar(self.archive_files[0]) 131 | outbuf.seek(0) 132 | gzip_4k(outbuf, self.archive_files[0]) 133 | 134 | return r 135 | 136 | 137 | # For testing only 138 | if __name__ == "__main__": 139 | filter_tar(sys.argv[1]) 140 | outbuf.seek(0) 141 | gzip_4k(outbuf, sys.argv[1]) 142 | -------------------------------------------------------------------------------- /library/yeelight/setup.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labplus-cn/awesome-mpython/b4539ad58dc465be065bfca6fe27211b3f5ed05d/library/yeelight/setup.cfg -------------------------------------------------------------------------------- /library/yeelight/setup.py: -------------------------------------------------------------------------------- 1 | # import sys 2 | # sys.path.pop(0) 3 | # sys.path.append("..") 4 | from setuptools import setup 5 | import sdist_upip 6 | from os import path 7 | 8 | # read the contents of your README file 9 | from os import path 10 | this_directory = path.abspath(path.dirname(__file__)) 11 | with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f: 12 | long_description = f.read() 13 | 14 | VERSION= '0.0.1' 15 | 16 | setup(name='mPython-yeelight', 17 | version=VERSION, 18 | description='Yeelight LAN Control for mPython/micropython', 19 | long_description=long_description, 20 | long_description_content_type="text/markdown", 21 | url='https://github.com/labplus-cn/mPython_yeelight', 22 | author='tangliufeng@LabPlus', 23 | author_email='137513285@qq.com', 24 | maintainer='LabPlus Developers', 25 | license='MIT', 26 | cmdclass={'sdist': sdist_upip.sdist}, 27 | py_modules=['yeelight'], 28 | # packages=['email'], 29 | # install_requires=[''] 30 | ) 31 | 32 | -------------------------------------------------------------------------------- /library/yeelight/yeelight.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import json 3 | 4 | def discover_bulbs(timeout=2): 5 | """ 6 | 发现所有局域网内的Yeelight灯泡. 7 | 8 | :param int timeout: 等待回复需要多少秒。发现将总是要花这么长的时间, 9 | 因为它不知道当所有的灯泡都响应完毕时。 10 | :returns: 字典列表,包含网络中每个灯泡的IP,端口和功能。 11 | """ 12 | msg = 'M-SEARCH * HTTP/1.1\r\n' \ 13 | 'ST:wifi_bulb\r\n' \ 14 | 'MAN:"ssdp:discover"\r\n' 15 | 16 | # Set up UDP socket 17 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 18 | s.settimeout(timeout) 19 | s.sendto(msg.encode(), ('239.255.255.250', 1982)) 20 | read_buf = 1024 21 | bulbs = [] 22 | bulb_ips = set() 23 | while True: 24 | try: 25 | data, addr = s.recvfrom(read_buf) 26 | except OSError: 27 | break 28 | 29 | capabilities = dict([x.strip("\r").split(": ") 30 | for x in data.decode().split("\n") if ":" in x]) 31 | parsed_url = capabilities["Location"].split("//")[1] 32 | 33 | bulb_ip = tuple(parsed_url.split(":")) 34 | if bulb_ip in bulb_ips: 35 | continue 36 | 37 | capabilities = {key: value for key, 38 | value in capabilities.items() if key.islower()} 39 | bulbs.append( 40 | {"ip": bulb_ip[0], "port": bulb_ip[1], "capabilities": capabilities}) 41 | bulb_ips.add(bulb_ip) 42 | 43 | return bulbs 44 | 45 | 46 | class BulbException(Exception): 47 | """ 48 | 一般的 yeelight 异常类 49 | 50 | 当灯泡通知错误时会引发此异常,例如,当尝试向灯泡发出不支持的命令时。 51 | """ 52 | pass 53 | 54 | 55 | class Bulb(object): 56 | """ 57 | YeeLight的控制类. 58 | 59 | :param str ip: 灯泡的IP. 60 | :param int port: 连接灯泡的端口号,默认55443. 61 | :param str effect: 效果类型."smooth" or "sudden". 62 | :param int duration: 效果的持续时间,以毫秒为单位.最小值为30.突然效果会忽略此值. 63 | :param bool auto_on: 是否 :py:meth:`ensure_on() 64 | ` 在每次操作之前调用以自动打开灯泡,如果它已关闭。这会在每条消息之前更新灯泡的属性, 65 | 每个命令会花费一个额外的消息。 如果您担心速率限制,请将其关闭并自行检查。:py:meth:`get_properties()` 66 | 或运行 :py:meth:`ensure_on() ` 67 | """ 68 | 69 | def __init__(self, ip, port=55443, effect="smooth", 70 | duration=300, auto_on=False): 71 | 72 | self._ip = ip 73 | self._port = port 74 | self._timeout = 5 75 | 76 | self.effect = effect 77 | self.duration = duration 78 | self.auto_on = auto_on 79 | 80 | self.__cmd_id = 0 # The last command id we used. 81 | self._last_properties = {} # The last set of properties we've seen. 82 | self._music_mode = False # Whether we're currently in music mode. 83 | self.__socket = None # The socket we use to communicate. 84 | 85 | @property 86 | def _cmd_id(self): 87 | ''' 88 | Get next command id in sequence 89 | 90 | :return: command id 91 | ''' 92 | self.__cmd_id += 1 93 | return self.__cmd_id - 1 94 | 95 | @property 96 | def _socket(self): 97 | ''' 98 | Get, optionally create, the communication socket 99 | 100 | :return: the communication socket 101 | ''' 102 | if self.__socket is None: 103 | self.__socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 104 | self.__socket.settimeout(self._timeout) 105 | self.__socket.connect((self._ip, self._port)) 106 | 107 | return self.__socket 108 | 109 | def ensure_on(self): 110 | """Turn the bulb on if it is off.""" 111 | if self._music_mode is True or self.auto_on is False: 112 | return 113 | 114 | self.get_properties() 115 | 116 | if self._last_properties["power"] != "on": 117 | self.turn_on() 118 | 119 | @property 120 | def music_mode(self): 121 | """ 122 | Return whether the music mode is active. 123 | 124 | :rtype: bool 125 | :return: True if music mode is on, False otherwise. 126 | """ 127 | return self._music_mode 128 | 129 | @property 130 | def last_properties(self): 131 | """ 132 | The last properties we've seen the bulb have. 133 | 134 | This might potentially be out of date, as there's no background listener 135 | for the bulb's notifications. To update it, call 136 | :py:meth:`get_properties `. 137 | """ 138 | return self._last_properties 139 | 140 | def get_properties(self): 141 | """ 142 | Retrieve and return the properties of the bulb. 143 | 144 | This method also updates ``last_properties`` when it is called. 145 | 146 | :returns: A dictionary of param: value items. 147 | :rtype: dict 148 | """ 149 | # When we are in music mode, the bulb does not respond to queries 150 | # therefore we need to keep the state up-to-date ourselves 151 | if self._music_mode: 152 | return self._last_properties 153 | 154 | requested_properties = [ 155 | "power", "bright", "ct", "rgb", "hue", "sat", 156 | "color_mode", "flowing", "delayoff", "flow_params", 157 | "music_on", "name" 158 | ] 159 | response = self.send_command("get_prop", requested_properties) 160 | properties = response["result"] 161 | properties = [x if x else None for x in properties] 162 | 163 | self._last_properties = dict(zip(requested_properties, properties)) 164 | return self._last_properties 165 | 166 | def ensure_on(self): 167 | """Turn the bulb on if it is off.""" 168 | if self._music_mode is True or self.auto_on is False: 169 | return 170 | 171 | self.get_properties() 172 | 173 | if self._last_properties["power"] != "on": 174 | self.turn_on() 175 | 176 | @property 177 | def bulb_type(self): 178 | """ 179 | 灯泡类型 180 | 181 | 返回灯泡类型:White or Color.当尝试在属性已知之前访问时,灯泡类型是未知的。 182 | 183 | :return: 灯泡类型. 184 | """ 185 | if not self._last_properties: 186 | return "BulbType.Unknown" 187 | if not all(name in self.last_properties for name in ['ct', 'rgb', 'hue', 'sat']): 188 | return "BulbType.White" 189 | else: 190 | return "BulbType.Color" 191 | 192 | def send_command(self, method, params=None): 193 | """ 194 | 请求信息并返回响应 195 | 196 | :param str method: control method id 197 | :param list params: list of params for the specified method 198 | :return: the command response 199 | """ 200 | 201 | command = {'id': self._cmd_id, 'method': method, 'params': params} 202 | # print(command) 203 | try: 204 | self._socket.send((json.dumps(command) + '\r\n').encode('utf8')) 205 | except Exception as e: 206 | self.__socket.close() 207 | self.__socket = None 208 | raise BulbException( 209 | 'A socket error occurred when sending the command.') 210 | 211 | if self._music_mode: 212 | # We're in music mode, nothing else will happen. 213 | return {"result": ["ok"]} 214 | 215 | # The bulb will send us updates on its state in addition to responses, 216 | # so we want to make sure that we read until we see an actual response. 217 | response = None 218 | while response is None: 219 | try: 220 | data = self._socket.recv(2 * 1024) 221 | except: 222 | self.__socket.close() 223 | self.__socket = None 224 | response = {'error': 'Bulb closed the connection.'} 225 | break 226 | 227 | for line in data.split(b'\r\n'): 228 | if not line: 229 | continue 230 | 231 | try: 232 | line = json.loads(line.decode('utf8')) 233 | except ValueError: 234 | response = {'result': ['invalid command']} 235 | 236 | if line.get('method') != 'props': 237 | response = line 238 | 239 | else: 240 | self._last_properties.update(line["params"]) 241 | 242 | if "error" in response: 243 | raise BulbException(response["error"]) 244 | 245 | return response 246 | 247 | @property 248 | def name(self): 249 | ''' 250 | 设置或返回设备名字 251 | 252 | :return: 返回设备名字 253 | ''' 254 | return self.send_command('get_prop', ['name'])['result'] 255 | 256 | @name.setter 257 | def name(self, name): 258 | ''' 259 | 设置设备名字 260 | :param name: new name 261 | ''' 262 | self.send_command('set_name', [name]) 263 | 264 | @property 265 | def is_on(self): 266 | ''' 267 | 返回灯泡是否打开 268 | 269 | :return:打开则是'on',关闭侧'off'。 270 | ''' 271 | return self.send_command('get_prop', ['power'])['result'][0] == 'on' 272 | 273 | def turn_on(self): 274 | ''' 275 | 打开灯泡 276 | ''' 277 | self.send_command('set_power', ['on', self.effect, self.duration]) 278 | 279 | def turn_off(self): 280 | ''' 281 | 关闭灯泡 282 | ''' 283 | self.send_command('set_power', ['off', self.effect, self.duration]) 284 | 285 | def toggle(self): 286 | """反转灯泡状态.""" 287 | self.send_command('toggle', []) 288 | 289 | def set_rgb(self, red, green, blue): 290 | ''' 291 | 设置灯泡的RGB值 292 | 293 | :param int red: 红色范围 (0-255) 294 | :param int green: 绿色范围 (0-255) 295 | :param int blue: 蓝色范围 (0-255) 296 | ''' 297 | 298 | self.ensure_on() 299 | 300 | red = max(0, min(255, red)) 301 | green = max(0, min(255, green)) 302 | blue = max(0, min(255, blue)) 303 | 304 | self.send_command( 305 | 'set_rgb', [red * 65536 + green * 256 + blue, self.effect, self.duration]) 306 | 307 | 308 | def set_hsv(self, hue, saturation): 309 | """ 310 | Set the bulb's HSV value. 311 | 312 | :param int hue: The hue to set (0-359). 313 | :param int saturation: The saturation to set (0-100). 314 | 315 | """ 316 | self.ensure_on() 317 | 318 | # We fake this using flow so we can add the `value` parameter. 319 | hue = max(0, min(359, hue)) 320 | saturation = max(0, min(100, saturation)) 321 | 322 | self.send_command('set_hsv', [hue, saturation,self.effect, self.duration]) 323 | 324 | 325 | def set_color_temp(self, degrees): 326 | """ 327 | 设置灯泡色温 328 | 329 | :param int degrees: 色温范围(1700-6500). 330 | """ 331 | self.ensure_on() 332 | 333 | degrees = max(1700, min(6500, degrees)) 334 | self.send_command('set_ct_abx', [degrees, self.effect, self.duration]) 335 | 336 | def set_adjust(self, action, prop): 337 | """ 338 | 该方法用于改变智能LED的亮度、CT或颜色,在不知道当前值的情况下. 339 | 340 | :param str action: 调整方向,可以的值是,'increase' : 增加指定属性;100'decrease': 减小指定属性;'circle': 增加指定的属性,当它达到最大值后,回到最小值. 341 | :param str prop: 属性调制. 可以是 "bright" 亮度调整, "ct" 色温调整, "color" 342 | 颜色调整. 343 | """ 344 | 345 | self.send_command('set_adjust', [action, prop]) 346 | 347 | def set_brightness(self, brightness): 348 | """ 349 | 设置灯泡亮度 350 | 351 | :param int brightness: 亮度范围 (1-100). 352 | """ 353 | self.ensure_on() 354 | 355 | brightness = int(max(1, min(100, brightness))) 356 | self.send_command( 357 | 'set_bright', [brightness, self.effect, self.duration]) 358 | 359 | def start_flow(self, flow): 360 | ''' 361 | Start a flow 362 | 363 | :param yeelight.Flow flow: the Flow instance to start 364 | ''' 365 | if not isinstance(flow, Flow): 366 | raise ValueError('Argument is not a Flow instance') 367 | self.ensure_on() 368 | self.send_command('start_cf', [ 369 | flow.count * len(flow.transitions), flow.action, flow.expression]) 370 | 371 | 372 | def cron_add(self, event_type, value): 373 | """ 374 | Add an event to cron. 375 | """ 376 | self.send_command('cron_add', [event_type.value, value]) 377 | 378 | def cron_get(self, event_type): 379 | """ 380 | Retrieve an event from cron. 381 | """ 382 | self.send_command('cron_get', [event_type]) 383 | 384 | def cron_del(self, event_type): 385 | """ 386 | Remove an event from cron. 387 | """ 388 | self.send_command('cron_del', [event_type]) 389 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: 'Awesome mPython' 2 | site_url: 'https://labplus-cn.github.io/awesome-mpython/' 3 | site_description: 'A curated list of mpython libraries, frameworks, software and resources.' 4 | site_author: 'tangliufeng' 5 | repo_name: 'labplus-cn/awesome-micropython' 6 | repo_url: 'https://github.com/labplus-cn/awesome-mpython' 7 | edit_uri: '' 8 | 9 | 10 | theme: 11 | name: 'material' 12 | language: 'en' 13 | # logo: 'img/logo.svg' 14 | favicon: 'images/favicon.ico' 15 | palette: 16 | primary: 'teal' 17 | accent: 'teal' 18 | nav: 19 | - 'MicroPython精选': 'index.md' 20 | 21 | 22 | plugins: 23 | - search 24 | - minify 25 | -------------------------------------------------------------------------------- /optimize_upip.py: -------------------------------------------------------------------------------- 1 | # 2 | # This script optimizes a Python source distribution tarball as produced by 3 | # "python3 setup.py sdist" command for MicroPython's native package manager, 4 | # upip. Optimization includes: 5 | # * Removing metadata files not used by upip (this includes setup.py) 6 | # * Recompressing gzip archive with 4K dictionary size so it can be 7 | # installed even on low-heap targets. 8 | # 9 | import sys 10 | import os 11 | import zlib 12 | from subprocess import Popen, PIPE 13 | import glob 14 | import tarfile 15 | import re 16 | import io 17 | 18 | 19 | def gzip_4k(inf, fname): 20 | comp = zlib.compressobj(level=9, wbits=16 + 12) 21 | with open(fname + ".out", "wb") as outf: 22 | while 1: 23 | data = inf.read(1024) 24 | if not data: 25 | break 26 | outf.write(comp.compress(data)) 27 | outf.write(comp.flush()) 28 | os.rename(fname, fname + ".orig") 29 | os.rename(fname + ".out", fname) 30 | 31 | 32 | def recompress(fname): 33 | with Popen(["gzip", "-d", "-c", fname], stdout=PIPE).stdout as inf: 34 | gzip_4k(inf, fname) 35 | 36 | def find_latest(dir): 37 | res = [] 38 | for fname in glob.glob(dir + "/*.gz"): 39 | st = os.stat(fname) 40 | res.append((st.st_mtime, fname)) 41 | res.sort() 42 | latest = res[-1][1] 43 | return latest 44 | 45 | 46 | def recompress_latest(dir): 47 | latest = find_latest(dir) 48 | print(latest) 49 | recompress(latest) 50 | 51 | 52 | FILTERS = [ 53 | # include, exclude, repeat 54 | (r".+\.egg-info/(PKG-INFO|requires\.txt)", r"setup.py$"), 55 | (r".+\.py$", r"[^/]+$"), 56 | (None, r".+\.egg-info/.+"), 57 | ] 58 | 59 | 60 | outbuf = io.BytesIO() 61 | 62 | def filter_tar(name): 63 | fin = tarfile.open(name, "r:gz") 64 | fout = tarfile.open(fileobj=outbuf, mode="w") 65 | for info in fin: 66 | # print(info) 67 | if not "/" in info.name: 68 | continue 69 | fname = info.name.split("/", 1)[1] 70 | include = None 71 | 72 | for inc_re, exc_re in FILTERS: 73 | if include is None and inc_re: 74 | if re.match(inc_re, fname): 75 | include = True 76 | 77 | if include is None and exc_re: 78 | if re.match(exc_re, fname): 79 | include = False 80 | 81 | if include is None: 82 | include = True 83 | 84 | if include: 85 | print("Including:", fname) 86 | else: 87 | print("Excluding:", fname) 88 | continue 89 | 90 | farch = fin.extractfile(info) 91 | fout.addfile(info, farch) 92 | fout.close() 93 | fin.close() 94 | 95 | 96 | 97 | from setuptools import Command 98 | 99 | class OptimizeUpip(Command): 100 | 101 | user_options = [] 102 | 103 | def run(self): 104 | latest = find_latest("dist") 105 | filter_tar(latest) 106 | outbuf.seek(0) 107 | gzip_4k(outbuf, latest) 108 | 109 | def initialize_options(self): 110 | pass 111 | 112 | def finalize_options(self): 113 | pass 114 | 115 | 116 | # For testing only 117 | if __name__ == "__main__": 118 | # recompress_latest(sys.argv[1]) 119 | filter_tar(sys.argv[1]) 120 | outbuf.seek(0) 121 | gzip_4k(outbuf, sys.argv[1]) 122 | -------------------------------------------------------------------------------- /sdist_upip.py: -------------------------------------------------------------------------------- 1 | # 2 | # This module overrides distutils (also compatible with setuptools) "sdist" 3 | # command to perform pre- and post-processing as required for MicroPython's 4 | # upip package manager. 5 | # 6 | # Preprocessing steps: 7 | # * Creation of Python resource module (R.py) from each top-level package's 8 | # resources. 9 | # Postprocessing steps: 10 | # * Removing metadata files not used by upip (this includes setup.py) 11 | # * Recompressing gzip archive with 4K dictionary size so it can be 12 | # installed even on low-heap targets. 13 | # 14 | import sys 15 | import os 16 | import zlib 17 | from subprocess import Popen, PIPE 18 | import glob 19 | import tarfile 20 | import re 21 | import io 22 | 23 | from distutils.filelist import FileList 24 | from setuptools.command.sdist import sdist as _sdist 25 | 26 | 27 | def gzip_4k(inf, fname): 28 | comp = zlib.compressobj(level=9, wbits=16 + 12) 29 | with open(fname + ".out", "wb") as outf: 30 | while 1: 31 | data = inf.read(1024) 32 | if not data: 33 | break 34 | outf.write(comp.compress(data)) 35 | outf.write(comp.flush()) 36 | os.rename(fname, fname + ".orig") 37 | os.rename(fname + ".out", fname) 38 | 39 | 40 | FILTERS = [ 41 | # include, exclude, repeat 42 | (r".+\.egg-info/(PKG-INFO|requires\.txt)", r"setup.py$"), 43 | (r".+\.py$", r"[^/]+$"), 44 | (None, r".+\.egg-info/.+"), 45 | ] 46 | 47 | 48 | outbuf = io.BytesIO() 49 | 50 | def filter_tar(name): 51 | fin = tarfile.open(name, "r:gz") 52 | fout = tarfile.open(fileobj=outbuf, mode="w") 53 | for info in fin: 54 | # print(info) 55 | if not "/" in info.name: 56 | continue 57 | fname = info.name.split("/", 1)[1] 58 | include = None 59 | 60 | for inc_re, exc_re in FILTERS: 61 | if include is None and inc_re: 62 | if re.match(inc_re, fname): 63 | include = True 64 | 65 | if include is None and exc_re: 66 | if re.match(exc_re, fname): 67 | include = False 68 | 69 | if include is None: 70 | include = True 71 | 72 | if include: 73 | print("including:", fname) 74 | else: 75 | print("excluding:", fname) 76 | continue 77 | 78 | farch = fin.extractfile(info) 79 | fout.addfile(info, farch) 80 | fout.close() 81 | fin.close() 82 | 83 | 84 | def make_resource_module(manifest_files): 85 | resources = [] 86 | # Any non-python file included in manifest is resource 87 | for fname in manifest_files: 88 | ext = fname.rsplit(".", 1)[1] 89 | if ext != "py": 90 | resources.append(fname) 91 | 92 | if resources: 93 | print("creating resource module R.py") 94 | resources.sort() 95 | last_pkg = None 96 | r_file = None 97 | for fname in resources: 98 | try: 99 | pkg, res_name = fname.split("/", 1) 100 | except ValueError: 101 | print("not treating %s as a resource" % fname) 102 | continue 103 | if last_pkg != pkg: 104 | last_pkg = pkg 105 | if r_file: 106 | r_file.write("}\n") 107 | r_file.close() 108 | r_file = open(pkg + "/R.py", "w") 109 | r_file.write("R = {\n") 110 | 111 | with open(fname, "rb") as f: 112 | r_file.write("%r: %r,\n" % (res_name, f.read())) 113 | 114 | if r_file: 115 | r_file.write("}\n") 116 | r_file.close() 117 | 118 | 119 | class sdist(_sdist): 120 | 121 | def run(self): 122 | self.filelist = FileList() 123 | self.get_file_list() 124 | make_resource_module(self.filelist.files) 125 | 126 | r = super().run() 127 | 128 | assert len(self.archive_files) == 1 129 | print("filtering files and recompressing with 4K dictionary") 130 | filter_tar(self.archive_files[0]) 131 | outbuf.seek(0) 132 | gzip_4k(outbuf, self.archive_files[0]) 133 | 134 | return r 135 | 136 | 137 | # For testing only 138 | if __name__ == "__main__": 139 | filter_tar(sys.argv[1]) 140 | outbuf.seek(0) 141 | gzip_4k(outbuf, sys.argv[1]) 142 | --------------------------------------------------------------------------------